diff options
Diffstat (limited to 'drivers')
60 files changed, 4493 insertions, 599 deletions
diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c index ce10d795316..f0cb797d975 100644 --- a/drivers/clk/imx/clk-imx93.c +++ b/drivers/clk/imx/clk-imx93.c @@ -289,7 +289,7 @@ static int imx93_clk_probe(struct udevice *dev) clk_dm(IMX93_CLK_SYS_PLL_PFD2_DIV2, imx_clk_fixed_factor("sys_pll_pfd2_div2", "sys_pll_pfd2", 1, 2)); - base = (void *)ANATOP_BASE_ADDR; + anatop_base = (void *)ANATOP_BASE_ADDR; clk_dm(IMX93_CLK_ARM_PLL, imx_clk_fracn_gppll_integer("arm_pll", "clock-osc-24m", diff --git a/drivers/cpu/imx9_cpu.c b/drivers/cpu/imx9_cpu.c deleted file mode 100644 index 66534fe6d17..00000000000 --- a/drivers/cpu/imx9_cpu.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright 2019 NXP - */ - -#include <common.h> -#include <cpu.h> -#include <dm.h> -#include <thermal.h> -#include <asm/global_data.h> -#include <asm/system.h> -#include <firmware/linux/imx/sci/sci.h> -#include <asm/arch/sys_proto.h> -#include <asm/arch-imx/cpu.h> -#include <asm/armv8/cpu.h> -#include <linux/bitops.h> - -DECLARE_GLOBAL_DATA_PTR; - -struct cpu_imx_plat { - const char *name; - const char *rev; - const char *type; - u32 cpu_rsrc; - u32 cpurev; - u32 freq_mhz; - u32 mpidr; -}; - -const char *get_imx9_type(u32 imxtype) -{ - switch (imxtype) { - case MXC_CPU_IMX93: - return "93"; - default: - return "??"; - } -} - -const char *get_imx9_rev(u32 rev) -{ - switch (rev) { - case CHIP_REV_1_0: - return "1."; - case CHIP_REV_B: - return "B"; - case CHIP_REV_C: - return "C"; - default: - return "?"; - } -} - -static void set_core_data(struct udevice *dev) -{ - struct cpu_imx_plat *plat = dev_get_plat(dev); - - if (device_is_compatible(dev, "arm,cortex-a35")) - plat->name = "A35"; - else - plat->name = "?"; -} - -#if IS_ENABLED(CONFIG_IMX_SCU_THERMAL) -static int cpu_imx_get_temp(struct cpu_imx_plat *plat) -{ - struct udevice *thermal_dev; - int cpu_tmp, ret; - int idx = 1; /* use "cpu-thermal0" device */ - - if (plat->cpu_rsrc == SC_R_A72) - idx = 2; /* use "cpu-thermal1" device */ - - ret = uclass_get_device(UCLASS_THERMAL, idx, &thermal_dev); - if (!ret) { - ret = thermal_get_temp(thermal_dev, &cpu_tmp); - if (ret) - return 0xdeadbeef; - } else { - return 0xdeadbeef; - } - - return cpu_tmp; -} -#else -static int cpu_imx_get_temp(struct cpu_imx_plat *plat) -{ - return 0; -} -#endif - -int cpu_imx_get_desc(const struct udevice *dev, char *buf, int size) -{ - struct cpu_imx_plat *plat = dev_get_plat(dev); - int ret, temp; - - if (size < 100) - return -ENOSPC; - - ret = snprintf(buf, size, "NXP i.MX8%s Rev%s %s at %u MHz", - plat->type, plat->rev, plat->name, plat->freq_mhz); - - if (IS_ENABLED(CONFIG_IMX_SCU_THERMAL)) { - temp = cpu_imx_get_temp(plat); - buf = buf + ret; - size = size - ret; - if (temp != 0xdeadbeef) - ret = snprintf(buf, size, " at %dC", temp); - else - ret = snprintf(buf, size, " - invalid sensor data"); - } - - snprintf(buf + ret, size - ret, "\n"); - - return 0; -} - -static int cpu_imx_get_info(const struct udevice *dev, struct cpu_info *info) -{ - struct cpu_imx_plat *plat = dev_get_plat(dev); - - info->cpu_freq = plat->freq_mhz * 1000; - info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU); - return 0; -} - -static int cpu_imx_get_count(const struct udevice *dev) -{ - ofnode node; - int num = 0; - - ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) { - const char *device_type; - - if (!ofnode_is_enabled(node)) - continue; - - device_type = ofnode_read_string(node, "device_type"); - if (!device_type) - continue; - - if (!strcmp(device_type, "cpu")) - num++; - } - - return num; -} - -static int cpu_imx_get_vendor(const struct udevice *dev, char *buf, int size) -{ - snprintf(buf, size, "NXP"); - return 0; -} - -static int cpu_imx_is_current(struct udevice *dev) -{ - struct cpu_imx_plat *plat = dev_get_plat(dev); - - if (plat->mpidr == (read_mpidr() & 0xffff)) - return 1; - - return 0; -} - -static const struct cpu_ops cpu_imx9_ops = { - .get_desc = cpu_imx_get_desc, - .get_info = cpu_imx_get_info, - .get_count = cpu_imx_get_count, - .get_vendor = cpu_imx_get_vendor, - .is_current = cpu_imx_is_current, -}; - -static const struct udevice_id cpu_imx9_ids[] = { - { .compatible = "arm,cortex-a35" }, - { .compatible = "arm,cortex-a53" }, - { .compatible = "arm,cortex-a72" }, - { } -}; - -static ulong imx9_get_cpu_rate(struct udevice *dev) -{ - struct cpu_imx_plat *plat = dev_get_plat(dev); - ulong rate; - int ret; - - ret = sc_pm_get_clock_rate(-1, plat->cpu_rsrc, SC_PM_CLK_CPU, - (sc_pm_clock_rate_t *)&rate); - if (ret) { - printf("Could not read CPU frequency: %d\n", ret); - return 0; - } - - return rate; -} - -static int imx9_cpu_probe(struct udevice *dev) -{ - struct cpu_imx_plat *plat = dev_get_plat(dev); - u32 cpurev; - - set_core_data(dev); - cpurev = get_cpu_rev(); - plat->cpurev = cpurev; - plat->rev = get_imx9_rev(cpurev & 0xFFF); - plat->type = get_imx9_type((cpurev & 0xFF000) >> 12); - plat->freq_mhz = imx9_get_cpu_rate(dev) / 1000000; - plat->mpidr = dev_read_addr(dev); - if (plat->mpidr == FDT_ADDR_T_NONE) { - printf("%s: Failed to get CPU reg property\n", __func__); - return -EINVAL; - } - - return 0; -} - -U_BOOT_DRIVER(cpu_imx9_drv) = { - .name = "imx9x_cpu", - .id = UCLASS_CPU, - .of_match = cpu_imx9_ids, - .ops = &cpu_imx9_ops, - .probe = imx9_cpu_probe, - .plat_auto = sizeof(struct cpu_imx_plat), - .flags = DM_FLAG_PRE_RELOC, -}; diff --git a/drivers/cpu/riscv_cpu.c b/drivers/cpu/riscv_cpu.c index 5d1026b37da..9b1950efe05 100644 --- a/drivers/cpu/riscv_cpu.c +++ b/drivers/cpu/riscv_cpu.c @@ -21,13 +21,13 @@ DECLARE_GLOBAL_DATA_PTR; static int riscv_cpu_get_desc(const struct udevice *dev, char *buf, int size) { - const char *isa; + const char *cpu; - isa = dev_read_string(dev, "riscv,isa"); - if (size < (strlen(isa) + 1)) + cpu = dev_read_string(dev, "compatible"); + if (size < (strlen(cpu) + 1)) return -ENOSPC; - strcpy(buf, isa); + strcpy(buf, cpu); return 0; } diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index 5e5855a76c2..d6e2be09cf7 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -1,5 +1,4 @@ menu "Fastboot support" - depends on CMDLINE config FASTBOOT bool @@ -13,6 +12,10 @@ config FASTBOOT More information about the protocol and usecases: https://android.googlesource.com/platform/system/core/+/refs/heads/master/fastboot/ + Note that enabling CMDLINE is recommended since fastboot allows U-Boot + commands to be executed on request. The CMDLINE option is required + for anything other than simply booting the OS. + config USB_FUNCTION_FASTBOOT bool "Enable USB fastboot gadget" depends on USB_GADGET diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c index f95f4e4ae15..b8782bfa7fa 100644 --- a/drivers/fastboot/fb_command.c +++ b/drivers/fastboot/fb_command.c @@ -11,6 +11,7 @@ #include <fastboot-internal.h> #include <fb_mmc.h> #include <fb_nand.h> +#include <mapmem.h> #include <part.h> #include <stdlib.h> #include <linux/printk.h> @@ -278,6 +279,7 @@ void fastboot_data_download(const void *fastboot_data, { #define BYTES_PER_DOT 0x20000 u32 pre_dot_num, now_dot_num; + void *buf; if (fastboot_data_len == 0 || (fastboot_bytes_received + fastboot_data_len) > @@ -287,8 +289,10 @@ void fastboot_data_download(const void *fastboot_data, return; } /* Download data to fastboot_buf_addr */ - memcpy(fastboot_buf_addr + fastboot_bytes_received, + buf = map_sysmem(fastboot_buf_addr, 0); + memcpy(buf + fastboot_bytes_received, fastboot_data, fastboot_data_len); + unmap_sysmem(buf); pre_dot_num = fastboot_bytes_received / BYTES_PER_DOT; fastboot_bytes_received += fastboot_data_len; @@ -331,13 +335,16 @@ void fastboot_data_complete(char *response) */ static void __maybe_unused flash(char *cmd_parameter, char *response) { + void *buf = map_sysmem(fastboot_buf_addr, 0); + if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC)) - fastboot_mmc_flash_write(cmd_parameter, fastboot_buf_addr, - image_size, response); + fastboot_mmc_flash_write(cmd_parameter, buf, image_size, + response); if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_NAND)) - fastboot_nand_flash_write(cmd_parameter, fastboot_buf_addr, - image_size, response); + fastboot_nand_flash_write(cmd_parameter, buf, image_size, + response); + unmap_sysmem(buf); } /** diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c index 3576b067729..595954542a6 100644 --- a/drivers/fastboot/fb_common.c +++ b/drivers/fastboot/fb_common.c @@ -11,6 +11,7 @@ */ #include <bcb.h> +#include <bootm.h> #include <common.h> #include <command.h> #include <env.h> @@ -20,7 +21,7 @@ /** * fastboot_buf_addr - base address of the fastboot download buffer */ -void *fastboot_buf_addr; +ulong fastboot_buf_addr; /** * fastboot_buf_size - size of the fastboot download buffer @@ -142,22 +143,19 @@ void (*fastboot_get_progress_callback(void))(const char *) */ void fastboot_boot(void) { - char *s; + char *s = NULL; - s = env_get("fastboot_bootcmd"); - if (s) { - run_command(s, CMD_FLAG_ENV); - } else if (IS_ENABLED(CONFIG_CMD_BOOTM)) { - static char boot_addr_start[20]; - static char *const bootm_args[] = { - "bootm", boot_addr_start, NULL - }; + if (IS_ENABLED(CONFIG_CMDLINE)) { + s = env_get("fastboot_bootcmd"); + if (s) + run_command(s, CMD_FLAG_ENV); + } - snprintf(boot_addr_start, sizeof(boot_addr_start) - 1, - "0x%p", fastboot_buf_addr); - printf("Booting kernel at %s...\n\n\n", boot_addr_start); + if (!s && IS_ENABLED(CONFIG_BOOTM)) { + int ret; - do_bootm(NULL, 0, 2, bootm_args); + printf("Booting kernel at %lx...\n\n\n", fastboot_buf_addr); + ret = bootm_boot_start(fastboot_buf_addr, NULL); /* * This only happens if image is somehow faulty so we start @@ -214,16 +212,9 @@ void fastboot_set_progress_callback(void (*progress)(const char *msg)) fastboot_progress_callback = progress; } -/* - * fastboot_init() - initialise new fastboot protocol session - * - * @buf_addr: Pointer to download buffer, or NULL for default - * @buf_size: Size of download buffer, or zero for default - */ -void fastboot_init(void *buf_addr, u32 buf_size) +void fastboot_init(ulong buf_addr, u32 buf_size) { - fastboot_buf_addr = buf_addr ? buf_addr : - (void *)CONFIG_FASTBOOT_BUF_ADDR; + fastboot_buf_addr = buf_addr ? buf_addr : CONFIG_FASTBOOT_BUF_ADDR; fastboot_buf_size = buf_size ? buf_size : CONFIG_FASTBOOT_BUF_SIZE; fastboot_set_progress_callback(NULL); } diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index ee092185588..6c581b9df9c 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -16,6 +16,7 @@ #include <dm/device.h> #include <dm/device_compat.h> #include <dm/devres.h> +#include <dm/lists.h> #include <linux/bitops.h> #include <linux/compat.h> #include <linux/err.h> @@ -2840,6 +2841,12 @@ static int ti_sci_probe(struct udevice *dev) INIT_LIST_HEAD(&info->dev_list); + if (IS_ENABLED(CONFIG_SYSRESET_TI_SCI)) { + ret = device_bind_driver(dev, "ti-sci-sysreset", "sysreset", NULL); + if (ret) + dev_warn(dev, "cannot bind SYSRESET (ret = %d)\n", ret); + } + return 0; } diff --git a/drivers/gpio/imx_rgpio2p.c b/drivers/gpio/imx_rgpio2p.c index 175e460aff5..3227a8d5b57 100644 --- a/drivers/gpio/imx_rgpio2p.c +++ b/drivers/gpio/imx_rgpio2p.c @@ -21,6 +21,12 @@ enum imx_rgpio2p_direction { #define GPIO_PER_BANK 32 +struct imx_rgpio2p_soc_data { + bool have_dual_base; +}; + +#define IMX8ULP_GPIO_BASE_OFF 0x40 + struct imx_rgpio2p_data { struct gpio_regs *regs; }; @@ -165,6 +171,9 @@ static int imx_rgpio2p_probe(struct udevice *dev) static int imx_rgpio2p_bind(struct udevice *dev) { struct imx_rgpio2p_plat *plat = dev_get_plat(dev); + struct imx_rgpio2p_soc_data *data = + (struct imx_rgpio2p_soc_data *)dev_get_driver_data(dev); + bool dual_base = data->have_dual_base; fdt_addr_t addr; /* @@ -176,9 +185,26 @@ static int imx_rgpio2p_bind(struct udevice *dev) if (plat) return 0; - addr = devfdt_get_addr_index(dev, 1); - if (addr == FDT_ADDR_T_NONE) - return -EINVAL; + /* + * Handle legacy compatible combinations which used two reg values + * for the i.MX8ULP and i.MX93. + */ + if (device_is_compatible(dev, "fsl,imx7ulp-gpio") && + (device_is_compatible(dev, "fsl,imx93-gpio") || + (device_is_compatible(dev, "fsl,imx8ulp-gpio")))) + dual_base = true; + + if (dual_base) { + addr = devfdt_get_addr_index(dev, 1); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + } else { + addr = devfdt_get_addr_index(dev, 0); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + addr += IMX8ULP_GPIO_BASE_OFF; + } /* * TODO: @@ -202,9 +228,17 @@ static int imx_rgpio2p_bind(struct udevice *dev) return 0; } +static struct imx_rgpio2p_soc_data imx7ulp_data = { + .have_dual_base = true, +}; + +static struct imx_rgpio2p_soc_data imx8ulp_data = { + .have_dual_base = false, +}; static const struct udevice_id imx_rgpio2p_ids[] = { - { .compatible = "fsl,imx7ulp-gpio" }, + { .compatible = "fsl,imx7ulp-gpio", .data = (ulong)&imx7ulp_data }, + { .compatible = "fsl,imx8ulp-gpio", .data = (ulong)&imx8ulp_data }, { } }; diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 9e829905f12..e53d52c47b3 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -91,4 +91,4 @@ obj-$(CONFIG_K3_AVS0) += k3_avs.o obj-$(CONFIG_ESM_K3) += k3_esm.o obj-$(CONFIG_ESM_PMIC) += esm_pmic.o obj-$(CONFIG_SL28CPLD) += sl28cpld.o -obj-$(CONFIG_SPL_SOCFPGA_SEC_REG) += socfpga_dtreg.o +obj-$(CONFIG_SPL_SOCFPGA_DT_REG) += socfpga_dtreg.o diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index cef05790dd9..06e32e75696 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -20,11 +20,19 @@ config MMC_WRITE config MMC_PWRSEQ bool "HW reset support for eMMC" - depends on PWRSEQ + depends on PWRSEQ && DM_GPIO help - Ths select Hardware reset support aka pwrseq-emmc for eMMC + This select Hardware reset support aka pwrseq-emmc for eMMC devices. +config SPL_MMC_PWRSEQ + bool "HW reset support for eMMC in SPL" + depends on SPL_PWRSEQ && SPL_DM_GPIO + default y if MMC_PWRSEQ + help + This select Hardware reset support aka pwrseq-emmc for eMMC + devices in SPL. + config MMC_BROKEN_CD bool "Poll for broken card detection case" help @@ -79,11 +87,12 @@ config MMC_SPI_CRC_ON config ARM_PL180_MMCI bool "ARM AMBA Multimedia Card Interface and compatible support" + depends on DM_MMC help This selects the ARM(R) AMBA(R) PrimeCell Multimedia Card Interface (PL180, PL181 and compatible) support. If you have an ARM(R) platform with a Multimedia Card slot, - say Y or M here. + say Y here. config MMC_QUIRKS bool "Enable quirks" @@ -568,6 +577,19 @@ config MMC_SDHCI_CADENCE If unsure, say N. +config MMC_SDHCI_CV1800B + bool "SDHCI support for the CV1800B SD/SDIO/eMMC controller" + depends on BLK && DM_MMC + depends on MMC_SDHCI + depends on OF_CONTROL + help + This selects the CV1800B SD/SDIO/eMMC driver. + + If you have a controller with this interface, + say Y here. + + If unsure, say N. + config MMC_SDHCI_AM654 bool "SDHCI Controller on TI's Am654 devices" depends on ARCH_K3 @@ -586,7 +608,7 @@ config MMC_SDHCI_IPROC This selects the iProc SD/MMC controller. If you have a Broadcom IPROC platform with SD or MMC devices, - say Y or M here. + say Y here. If unsure, say N. @@ -597,7 +619,7 @@ config MMC_SDHCI_F_SDH30 help This selects the Secure Digital Host Controller Interface (SDHCI) Needed by some Fujitsu/Socionext SoC for MMC / SD / SDIO support. - If you have a controller with this interface, say Y or M here. + If you have a controller with this interface, say Y here. If unsure, say N. config MMC_SDHCI_KONA @@ -791,7 +813,7 @@ config STM32_SDMMC2 help This selects support for the SD/MMC controller on STM32H7 SoCs. If you have a board based on such a SoC and with a SD/MMC slot, - say Y or M here. + say Y here. config FTSDC010 bool "Ftsdc010 SD/MMC controller Support" @@ -811,7 +833,7 @@ config MMC_MTK depends on OF_CONTROL help This selects the MediaTek(R) Secure digital and Multimedia card Interface. - If you have a machine with a integrated SD/MMC card reader, say Y or M here. + If you have a machine with a integrated SD/MMC card reader, say Y here. This is needed if support for any SD/SDIO/MMC devices is required. If unsure, say N. diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index e9cf1fcc640..72c3fb66ce0 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -11,7 +11,7 @@ obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += mmc_bootdev.o endif obj-$(CONFIG_$(SPL_TPL_)MMC_WRITE) += mmc_write.o -obj-$(CONFIG_MMC_PWRSEQ) += mmc-pwrseq.o +obj-$(CONFIG_$(SPL_)MMC_PWRSEQ) += mmc-pwrseq.o obj-$(CONFIG_MMC_SDHCI_ADMA_HELPERS) += sdhci-adma.o ifndef CONFIG_$(SPL_)BLK @@ -60,6 +60,7 @@ obj-$(CONFIG_MMC_SDHCI_ATMEL) += atmel_sdhci.o obj-$(CONFIG_MMC_SDHCI_BCM2835) += bcm2835_sdhci.o obj-$(CONFIG_MMC_SDHCI_BCMSTB) += bcmstb_sdhci.o obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o +obj-$(CONFIG_MMC_SDHCI_CV1800B) += cv1800b_sdhci.o obj-$(CONFIG_MMC_SDHCI_AM654) += am654_sdhci.o obj-$(CONFIG_MMC_SDHCI_IPROC) += iproc_sdhci.o obj-$(CONFIG_MMC_SDHCI_KONA) += kona_sdhci.o diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c index 05595bdac39..2139fea04d5 100644 --- a/drivers/mmc/am654_sdhci.c +++ b/drivers/mmc/am654_sdhci.c @@ -390,7 +390,7 @@ static int am654_sdhci_execute_tuning(struct mmc *mmc, u8 opcode) for (itap = 0; itap < ITAP_MAX; itap++) { am654_sdhci_write_itapdly(plat, itap); - cur_val = !mmc_send_tuning(mmc, opcode, NULL); + cur_val = !mmc_send_tuning(mmc, opcode); if (cur_val && !prev_val) pass_window = itap; diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c index 5cf5502ed54..2666b65362b 100644 --- a/drivers/mmc/arm_pl180_mmci.c +++ b/drivers/mmc/arm_pl180_mmci.c @@ -18,6 +18,7 @@ #include <malloc.h> #include <mmc.h> #include <dm/device_compat.h> +#include <dm.h> #include <asm/io.h> #include <asm-generic/gpio.h> @@ -25,8 +26,6 @@ #include "arm_pl180_mmci.h" #include <linux/delay.h> -#ifdef CONFIG_DM_MMC -#include <dm.h> #define MMC_CLOCK_MAX 48000000 #define MMC_CLOCK_MIN 400000 @@ -34,7 +33,6 @@ struct arm_pl180_mmc_plat { struct mmc_config cfg; struct mmc mmc; }; -#endif static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd) { @@ -358,65 +356,6 @@ static int host_set_ios(struct mmc *dev) return 0; } -#ifndef CONFIG_DM_MMC -/* MMC uses open drain drivers in the enumeration phase */ -static int mmc_host_reset(struct mmc *dev) -{ - struct pl180_mmc_host *host = dev->priv; - - writel(host->pwr_init, &host->base->power); - - return 0; -} - -static const struct mmc_ops arm_pl180_mmci_ops = { - .send_cmd = host_request, - .set_ios = host_set_ios, - .init = mmc_host_reset, -}; - -/* - * mmc_host_init - initialize the mmc controller. - * Set initial clock and power for mmc slot. - * Initialize mmc struct and register with mmc framework. - */ - -int arm_pl180_mmci_init(struct pl180_mmc_host *host, struct mmc **mmc) -{ - u32 sdi_u32; - - writel(host->pwr_init, &host->base->power); - writel(host->clkdiv_init, &host->base->clock); - udelay(CLK_CHANGE_DELAY); - - /* Disable mmc interrupts */ - sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; - writel(sdi_u32, &host->base->mask0); - - host->cfg.name = host->name; - host->cfg.ops = &arm_pl180_mmci_ops; - - /* TODO remove the duplicates */ - host->cfg.host_caps = host->caps; - host->cfg.voltages = host->voltages; - host->cfg.f_min = host->clock_min; - host->cfg.f_max = host->clock_max; - if (host->b_max != 0) - host->cfg.b_max = host->b_max; - else - host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; - - *mmc = mmc_create(&host->cfg, host); - if (!*mmc) - return -1; - debug("registered mmc interface number is:%d\n", - (*mmc)->block_dev.devnum); - - return 0; -} -#endif - -#ifdef CONFIG_DM_MMC static void arm_pl180_mmc_init(struct pl180_mmc_host *host) { u32 sdi_u32; @@ -477,7 +416,7 @@ static int arm_pl180_mmc_probe(struct udevice *dev) host->version2 = true; break; default: - host->version2 = true; + host->version2 = false; /* ARM variant */ } gpio_request_by_name(dev, "cd-gpios", 0, &host->cd_gpio, GPIOD_IS_IN); @@ -561,4 +500,3 @@ U_BOOT_DRIVER(arm_pl180_mmc) = { .priv_auto = sizeof(struct pl180_mmc_host), .plat_auto = sizeof(struct arm_pl180_mmc_plat), }; -#endif diff --git a/drivers/mmc/cv1800b_sdhci.c b/drivers/mmc/cv1800b_sdhci.c new file mode 100644 index 00000000000..9af6b971984 --- /dev/null +++ b/drivers/mmc/cv1800b_sdhci.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Kongyang Liu <seashell11234455@gmail.com> + */ + +#include <dm.h> +#include <mmc.h> +#include <sdhci.h> +#include <linux/delay.h> + +#define SDHCI_PHY_TX_RX_DLY 0x240 +#define MMC_MAX_CLOCK 375000000 +#define TUNE_MAX_PHCODE 128 + +struct cv1800b_sdhci_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +static void cv1800b_set_tap_delay(struct sdhci_host *host, u16 tap) +{ + sdhci_writel(host, tap << 16, SDHCI_PHY_TX_RX_DLY); +} + +static void cv1800b_sdhci_reset(struct sdhci_host *host, u8 mask) +{ + sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); + while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) + udelay(10); +} + +static int cv1800b_execute_tuning(struct mmc *mmc, u8 opcode) +{ + struct sdhci_host *host = dev_get_priv(mmc->dev); + + u16 tap; + + int current_size = 0; + int max_size = 0; + int max_window = 0; + + for (tap = 0; tap < TUNE_MAX_PHCODE; tap++) { + cv1800b_set_tap_delay(host, tap); + + if (mmc_send_tuning(host->mmc, opcode)) { + current_size = 0; + } else { + current_size++; + if (current_size > max_size) { + max_size = current_size; + max_window = tap; + } + } + } + + cv1800b_sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + + cv1800b_set_tap_delay(host, max_window - max_size / 2); + + return 0; +} + +const struct sdhci_ops cv1800b_sdhci_sd_ops = { + .platform_execute_tuning = cv1800b_execute_tuning, +}; + +static int cv1800b_sdhci_bind(struct udevice *dev) +{ + struct cv1800b_sdhci_plat *plat = dev_get_plat(dev); + + return sdhci_bind(dev, &plat->mmc, &plat->cfg); +} + +static int cv1800b_sdhci_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct cv1800b_sdhci_plat *plat = dev_get_plat(dev); + struct sdhci_host *host = dev_get_priv(dev); + int ret; + + host->name = dev->name; + host->ioaddr = devfdt_get_addr_ptr(dev); + + upriv->mmc = &plat->mmc; + host->mmc = &plat->mmc; + host->mmc->priv = host; + host->mmc->dev = dev; + host->ops = &cv1800b_sdhci_sd_ops; + host->max_clk = MMC_MAX_CLOCK; + + ret = mmc_of_parse(dev, &plat->cfg); + if (ret) + return ret; + + ret = sdhci_setup_cfg(&plat->cfg, host, 0, 200000); + if (ret) + return ret; + + return sdhci_probe(dev); +} + +static const struct udevice_id cv1800b_sdhci_match[] = { + { .compatible = "sophgo,cv1800b-dwcmshc" }, + { } +}; + +U_BOOT_DRIVER(cv1800b_sdhci) = { + .name = "sdhci-cv1800b", + .id = UCLASS_MMC, + .of_match = cv1800b_sdhci_match, + .bind = cv1800b_sdhci_bind, + .probe = cv1800b_sdhci_probe, + .priv_auto = sizeof(struct sdhci_host), + .plat_auto = sizeof(struct cv1800b_sdhci_plat), + .ops = &sdhci_ops, +}; diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 400066fa99a..e1036641452 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -262,8 +262,8 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { if (get_timer(start) > timeout) { - debug("%s: Timeout on data busy\n", __func__); - return -ETIMEDOUT; + debug("%s: Timeout on data busy, continue anyway\n", __func__); + break; } } diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index d5066666698..d44dfa5d06f 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -1123,7 +1123,7 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode) esdhc_write32(®s->irqstaten, IRQSTATEN_BRR); for (i = 0; i < MAX_TUNING_LOOP; i++) { - mmc_send_tuning(mmc, opcode, NULL); + mmc_send_tuning(mmc, opcode); mdelay(1); val = esdhc_read32(®s->autoc12err); diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index 7c39c86c5e9..b74c0140020 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -882,7 +882,7 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode) esdhc_write32(®s->mixctrl, val); /* We are using STD tuning, no need to check return value */ - mmc_send_tuning(mmc, opcode, NULL); + mmc_send_tuning(mmc, opcode); ctrl = esdhc_read32(®s->autoc12err); if ((!(ctrl & MIX_CTRL_EXE_TUNE)) && diff --git a/drivers/mmc/hi6220_dw_mmc.c b/drivers/mmc/hi6220_dw_mmc.c index 71962cd47e0..dc0210402bd 100644 --- a/drivers/mmc/hi6220_dw_mmc.c +++ b/drivers/mmc/hi6220_dw_mmc.c @@ -5,15 +5,24 @@ */ #include <common.h> +#include <clk.h> #include <dm.h> #include <dwmmc.h> #include <errno.h> #include <fdtdec.h> #include <malloc.h> +#include <reset.h> #include <asm/global_data.h> +#include <dm/device_compat.h> DECLARE_GLOBAL_DATA_PTR; +enum hi6220_dwmmc_clk_type { + HI6220_DWMMC_CLK_BIU, + HI6220_DWMMC_CLK_CIU, + HI6220_DWMMC_CLK_CNT, +}; + struct hi6220_dwmmc_plat { struct mmc_config cfg; struct mmc mmc; @@ -21,18 +30,43 @@ struct hi6220_dwmmc_plat { struct hi6220_dwmmc_priv_data { struct dwmci_host host; + struct clk *clks[HI6220_DWMMC_CLK_CNT]; + struct reset_ctl_bulk rsts; }; struct hisi_mmc_data { unsigned int clock; bool use_fifo; + u32 fifoth_val; }; static int hi6220_dwmmc_of_to_plat(struct udevice *dev) { struct hi6220_dwmmc_priv_data *priv = dev_get_priv(dev); struct dwmci_host *host = &priv->host; + int ret; + if (CONFIG_IS_ENABLED(CLK) && CONFIG_IS_ENABLED(DM_RESET)) { + priv->clks[HI6220_DWMMC_CLK_BIU] = devm_clk_get(dev, "biu"); + if (IS_ERR(priv->clks[HI6220_DWMMC_CLK_BIU])) { + ret = PTR_ERR(priv->clks[HI6220_DWMMC_CLK_BIU]); + dev_err(dev, "Failed to get BIU clock(ret = %d).\n", ret); + return log_msg_ret("clk", ret); + } + + priv->clks[HI6220_DWMMC_CLK_CIU] = devm_clk_get(dev, "ciu"); + if (IS_ERR(priv->clks[HI6220_DWMMC_CLK_CIU])) { + ret = PTR_ERR(priv->clks[HI6220_DWMMC_CLK_CIU]); + dev_err(dev, "Failed to get CIU clock(ret = %d).\n", ret); + return log_msg_ret("clk", ret); + } + + ret = reset_get_bulk(dev, &priv->rsts); + if (ret) { + dev_err(dev, "Failed to get resets(ret = %d)", ret); + return log_msg_ret("rst", ret); + } + } host->name = dev->name; host->ioaddr = dev_read_addr_ptr(dev); host->buswidth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), @@ -56,16 +90,43 @@ static int hi6220_dwmmc_probe(struct udevice *dev) struct hi6220_dwmmc_priv_data *priv = dev_get_priv(dev); struct dwmci_host *host = &priv->host; struct hisi_mmc_data *mmc_data; + int ret; mmc_data = (struct hisi_mmc_data *)dev_get_driver_data(dev); - /* Use default bus speed due to absence of clk driver */ host->bus_hz = mmc_data->clock; + if (CONFIG_IS_ENABLED(CLK) && CONFIG_IS_ENABLED(DM_RESET)) { + ret = clk_prepare_enable(priv->clks[HI6220_DWMMC_CLK_BIU]); + if (ret) { + dev_err(dev, "Failed to enable biu clock(ret = %d).\n", ret); + return log_msg_ret("clk", ret); + } + + ret = clk_prepare_enable(priv->clks[HI6220_DWMMC_CLK_CIU]); + if (ret) { + dev_err(dev, "Failed to enable ciu clock(ret = %d).\n", ret); + return log_msg_ret("clk", ret); + } + + ret = reset_deassert_bulk(&priv->rsts); + if (ret) { + dev_err(dev, "Failed to deassert resets(ret = %d).\n", ret); + return log_msg_ret("rst", ret); + } + + host->bus_hz = clk_get_rate(priv->clks[HI6220_DWMMC_CLK_CIU]); + if (host->bus_hz <= 0) { + dev_err(dev, "Failed to get ciu clock rate(ret = %d).\n", ret); + return log_msg_ret("clk", ret); + } + } + dev_dbg(dev, "bus clock rate: %d.\n", host->bus_hz); dwmci_setup_cfg(&plat->cfg, host, host->bus_hz, 400000); host->mmc = &plat->mmc; host->fifo_mode = mmc_data->use_fifo; + host->fifoth_val = mmc_data->fifoth_val; host->mmc->priv = &priv->host; upriv->mmc = host->mmc; host->mmc->dev = dev; @@ -95,13 +156,20 @@ static const struct hisi_mmc_data hi6220_mmc_data = { .use_fifo = false, }; +static const struct hisi_mmc_data hi3798mv2x_mmc_data = { + .clock = 50000000, + .use_fifo = false, + // FIFO depth is 256 + .fifoth_val = MSIZE(4) | RX_WMARK(0x7f) | TX_WMARK(0x80), +}; + static const struct udevice_id hi6220_dwmmc_ids[] = { { .compatible = "hisilicon,hi6220-dw-mshc", .data = (ulong)&hi6220_mmc_data }, { .compatible = "hisilicon,hi3798cv200-dw-mshc", .data = (ulong)&hi6220_mmc_data }, { .compatible = "hisilicon,hi3798mv200-dw-mshc", - .data = (ulong)&hi6220_mmc_data }, + .data = (ulong)&hi3798mv2x_mmc_data }, { .compatible = "hisilicon,hi3660-dw-mshc", .data = (ulong)&hi3660_mmc_data }, { } diff --git a/drivers/mmc/meson_gx_mmc.c b/drivers/mmc/meson_gx_mmc.c index fcf4f03d1e2..0825c0a2a83 100644 --- a/drivers/mmc/meson_gx_mmc.c +++ b/drivers/mmc/meson_gx_mmc.c @@ -288,7 +288,7 @@ static int meson_mmc_probe(struct udevice *dev) mmc_set_clock(mmc, cfg->f_min, MMC_CLK_ENABLE); -#ifdef CONFIG_MMC_PWRSEQ +#if CONFIG_IS_ENABLED(MMC_PWRSEQ) /* Enable power if needed */ ret = mmc_pwrseq_get_power(dev, cfg); if (!ret) { diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 328456831dd..1e03901e9dc 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -124,7 +124,13 @@ static int dm_mmc_execute_tuning(struct udevice *dev, uint opcode) int mmc_execute_tuning(struct mmc *mmc, uint opcode) { - return dm_mmc_execute_tuning(mmc->dev, opcode); + int ret; + + mmc->tuning = true; + ret = dm_mmc_execute_tuning(mmc->dev, opcode); + mmc->tuning = false; + + return ret; } #endif @@ -494,10 +500,7 @@ static int mmc_blk_probe(struct udevice *dev) if (ret) { debug("Probing %s failed (err=%d)\n", dev->name, ret); - if (CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || - CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || - CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) - mmc_deinit(mmc); + mmc_deinit(mmc); return ret; } @@ -505,9 +508,6 @@ static int mmc_blk_probe(struct udevice *dev) return 0; } -#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ - CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ - CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) static int mmc_blk_remove(struct udevice *dev) { struct udevice *mmc_dev = dev_get_parent(dev); @@ -516,7 +516,6 @@ static int mmc_blk_remove(struct udevice *dev) return mmc_deinit(mmc); } -#endif static const struct blk_ops mmc_blk_ops = { .read = mmc_bread, @@ -532,12 +531,8 @@ U_BOOT_DRIVER(mmc_blk) = { .id = UCLASS_BLK, .ops = &mmc_blk_ops, .probe = mmc_blk_probe, -#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ - CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ - CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) .remove = mmc_blk_remove, .flags = DM_FLAG_OS_PREPARE, -#endif }; #endif /* CONFIG_BLK */ diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index d96db7a0f83..7b068c71ff3 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -360,7 +360,7 @@ static const u8 tuning_blk_pattern_8bit[] = { 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, }; -int mmc_send_tuning(struct mmc *mmc, u32 opcode, int *cmd_error) +int mmc_send_tuning(struct mmc *mmc, u32 opcode) { struct mmc_cmd cmd; struct mmc_data data; @@ -1570,13 +1570,20 @@ static int sd_read_ssr(struct mmc *mmc) return 0; } #endif -/* frequency bases */ -/* divided by 10 to be nice to platforms without floating point */ +/* + * TRAN_SPEED bits 0:2 encode the frequency unit: + * 0 = 100KHz, 1 = 1MHz, 2 = 10MHz, 3 = 100MHz, values 4 - 7 are reserved. + * The values in fbase[] are divided by 10 to avoid floats in multiplier[]. + */ static const int fbase[] = { 10000, 100000, 1000000, 10000000, + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ }; /* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice @@ -2027,9 +2034,9 @@ static int mmc_select_hs400(struct mmc *mmc) mmc_set_clock(mmc, mmc->tran_speed, false); /* execute tuning if needed */ - mmc->hs400_tuning = 1; + mmc->hs400_tuning = true; err = mmc_execute_tuning(mmc, MMC_CMD_SEND_TUNING_BLOCK_HS200); - mmc->hs400_tuning = 0; + mmc->hs400_tuning = false; if (err) { debug("tuning failed\n"); return err; @@ -2250,6 +2257,16 @@ error: return -ENOTSUPP; } +#else +static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) +{ + return 0; +}; + +static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) +{ + return 0; +}; #endif #if CONFIG_IS_ENABLED(MMC_TINY) @@ -2560,6 +2577,8 @@ static int mmc_startup(struct mmc *mmc) mult = multipliers[((cmd.response[0] >> 3) & 0xf)]; mmc->legacy_speed = freq * mult; + if (!mmc->legacy_speed) + log_debug("TRAN_SPEED: reserved value"); mmc_select_mode(mmc, MMC_LEGACY); mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1); @@ -3010,13 +3029,15 @@ int mmc_init(struct mmc *mmc) return err; } -#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ - CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ - CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) int mmc_deinit(struct mmc *mmc) { u32 caps_filtered; + if (!CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) && + !CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) && + !CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) + return 0; + if (!mmc->has_init) return 0; @@ -3034,7 +3055,6 @@ int mmc_deinit(struct mmc *mmc) return mmc_select_mode_and_width(mmc, caps_filtered); } } -#endif int mmc_set_dsr(struct mmc *mmc, u16 val) { diff --git a/drivers/mmc/mtk-sd.c b/drivers/mmc/mtk-sd.c index 5a0c61daed5..296aaee7331 100644 --- a/drivers/mmc/mtk-sd.c +++ b/drivers/mmc/mtk-sd.c @@ -1131,7 +1131,7 @@ static int hs400_tune_response(struct udevice *dev, u32 opcode) i << PAD_CMD_TUNE_RX_DLY3_S); for (j = 0; j < 3; j++) { - mmc_send_tuning(mmc, opcode, &cmd_err); + cmd_err = mmc_send_tuning(mmc, opcode); if (!cmd_err) { cmd_delay |= (1 << i); } else { @@ -1181,7 +1181,7 @@ static int msdc_tune_response(struct udevice *dev, u32 opcode) i << MSDC_PAD_TUNE_CMDRDLY_S); for (j = 0; j < 3; j++) { - mmc_send_tuning(mmc, opcode, &cmd_err); + cmd_err = mmc_send_tuning(mmc, opcode); if (!cmd_err) { rise_delay |= (1 << i); } else { @@ -1203,7 +1203,7 @@ static int msdc_tune_response(struct udevice *dev, u32 opcode) i << MSDC_PAD_TUNE_CMDRDLY_S); for (j = 0; j < 3; j++) { - mmc_send_tuning(mmc, opcode, &cmd_err); + cmd_err = mmc_send_tuning(mmc, opcode); if (!cmd_err) { fall_delay |= (1 << i); } else { @@ -1238,7 +1238,7 @@ skip_fall: clrsetbits_le32(tune_reg, MSDC_PAD_TUNE_CMDRRDLY_M, i << MSDC_PAD_TUNE_CMDRRDLY_S); - mmc_send_tuning(mmc, opcode, &cmd_err); + cmd_err = mmc_send_tuning(mmc, opcode); if (!cmd_err) internal_delay |= (1 << i); } @@ -1264,7 +1264,6 @@ static int msdc_tune_data(struct udevice *dev, u32 opcode) struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0, }; u8 final_delay, final_maxlen; void __iomem *tune_reg = &host->base->pad_tune; - int cmd_err; int i, ret; if (host->dev_comp->pad_tune0) @@ -1277,10 +1276,10 @@ static int msdc_tune_data(struct udevice *dev, u32 opcode) clrsetbits_le32(tune_reg, MSDC_PAD_TUNE_DATRRDLY_M, i << MSDC_PAD_TUNE_DATRRDLY_S); - ret = mmc_send_tuning(mmc, opcode, &cmd_err); + ret = mmc_send_tuning(mmc, opcode); if (!ret) { rise_delay |= (1 << i); - } else if (cmd_err) { + } else { /* in this case, retune response is needed */ ret = msdc_tune_response(dev, opcode); if (ret) @@ -1300,10 +1299,10 @@ static int msdc_tune_data(struct udevice *dev, u32 opcode) clrsetbits_le32(tune_reg, MSDC_PAD_TUNE_DATRRDLY_M, i << MSDC_PAD_TUNE_DATRRDLY_S); - ret = mmc_send_tuning(mmc, opcode, &cmd_err); + ret = mmc_send_tuning(mmc, opcode); if (!ret) { fall_delay |= (1 << i); - } else if (cmd_err) { + } else { /* in this case, retune response is needed */ ret = msdc_tune_response(dev, opcode); if (ret) @@ -1362,7 +1361,7 @@ static int msdc_tune_together(struct udevice *dev, u32 opcode) for (i = 0; i < PAD_DELAY_MAX; i++) { msdc_set_cmd_delay(host, i); msdc_set_data_delay(host, i); - ret = mmc_send_tuning(mmc, opcode, NULL); + ret = mmc_send_tuning(mmc, opcode); if (!ret) rise_delay |= (1 << i); } @@ -1378,7 +1377,7 @@ static int msdc_tune_together(struct udevice *dev, u32 opcode) for (i = 0; i < PAD_DELAY_MAX; i++) { msdc_set_cmd_delay(host, i); msdc_set_data_delay(host, i); - ret = mmc_send_tuning(mmc, opcode, NULL); + ret = mmc_send_tuning(mmc, opcode); if (!ret) fall_delay |= (1 << i); } diff --git a/drivers/mmc/octeontx_hsmmc.c b/drivers/mmc/octeontx_hsmmc.c index 4ee62df9d40..7f9c4f4d36d 100644 --- a/drivers/mmc/octeontx_hsmmc.c +++ b/drivers/mmc/octeontx_hsmmc.c @@ -1653,6 +1653,12 @@ static int octeontx_mmc_test_cmd(struct mmc *mmc, u32 opcode, int *statp) return err; } +static int octeontx_mmc_send_tuning(struct mmc *mmc, u32 opcode, int *error) +{ + *error = 0; + return mmc_send_tuning(mmc, opcode); +} + static int octeontx_mmc_test_get_ext_csd(struct mmc *mmc, u32 opcode, int *statp) { @@ -2006,7 +2012,7 @@ struct adj adj[] = { { "CMD_IN", 48, octeontx_mmc_test_cmd, MMC_CMD_SEND_STATUS, false, false, false, 2, }, /* { "CMD_OUT", 32, octeontx_mmc_test_cmd, MMC_CMD_SEND_STATUS, },*/ - { "DATA_IN(HS200)", 16, mmc_send_tuning, + { "DATA_IN(HS200)", 16, octeontx_mmc_send_tuning, MMC_CMD_SEND_TUNING_BLOCK_HS200, false, true, false, 2, }, { "DATA_IN", 16, octeontx_mmc_test_get_ext_csd, 0, false, false, true, 2, }, diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index a2595d19e7f..99f21b2c546 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -666,7 +666,7 @@ static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode) while (phase_delay <= MAX_PHASE_DELAY) { omap_hsmmc_set_dll(mmc, phase_delay); - cur_match = !mmc_send_tuning(mmc, opcode, NULL); + cur_match = !mmc_send_tuning(mmc, opcode); if (cur_match) { if (prev_match) { @@ -731,7 +731,7 @@ static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode) */ for (i = 3; i <= 10; i++) { omap_hsmmc_set_dll(mmc, phase_delay + i); - if (mmc_send_tuning(mmc, opcode, NULL)) { + if (mmc_send_tuning(mmc, opcode)) { if (temperature < 10000) phase_delay += i + 6; else if (temperature < 20000) @@ -749,7 +749,7 @@ static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode) for (i = 2; i >= -10; i--) { omap_hsmmc_set_dll(mmc, phase_delay + i); - if (mmc_send_tuning(mmc, opcode, NULL)) { + if (mmc_send_tuning(mmc, opcode)) { if (temperature < 10000) phase_delay += i + 12; else if (temperature < 20000) diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index 20b1e9277eb..23db2a75c44 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -568,8 +568,8 @@ int renesas_sdhi_execute_tuning(struct udevice *dev, uint opcode) struct mmc *mmc = upriv->mmc; unsigned int tap_num; unsigned int taps = 0; - int i, ret = 0; - u32 caps; + int i, ret = 0, sret; + u32 caps, reg; /* Only supported on Renesas RCar */ if (!(priv->caps & TMIO_SD_CAP_RCAR_UHS)) @@ -605,15 +605,15 @@ int renesas_sdhi_execute_tuning(struct udevice *dev, uint opcode) caps = priv->caps; priv->caps &= ~TMIO_SD_CAP_DMA_INTERNAL; - ret = mmc_send_tuning(mmc, opcode, NULL); + ret = mmc_send_tuning(mmc, opcode); priv->caps = caps; if (ret == 0) taps |= BIT(i); - ret = renesas_sdhi_compare_scc_data(priv); - if (ret == 0) + reg = renesas_sdhi_compare_scc_data(priv); + if (reg == 0) priv->smpcmp |= BIT(i); mdelay(1); @@ -624,9 +624,9 @@ int renesas_sdhi_execute_tuning(struct udevice *dev, uint opcode) * eMMC. */ if (ret && (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200)) { - ret = mmc_send_stop_transmission(mmc, false); - if (ret < 0) - dev_dbg(dev, "Tuning abort fail (%d)\n", ret); + sret = mmc_send_stop_transmission(mmc, false); + if (sret < 0) + dev_dbg(dev, "Tuning abort fail (%d)\n", sret); } } @@ -798,9 +798,12 @@ static int renesas_sdhi_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct tmio_sd_priv *priv = dev_get_priv(dev); + struct mmc *mmc = upriv->mmc; - renesas_sdhi_check_scc_error(dev); + if (!mmc->tuning) + renesas_sdhi_check_scc_error(dev); if (cmd->cmdidx == MMC_CMD_SEND_STATUS) renesas_sdhi_adjust_hs400_mode_enable(priv); diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c index 72c820ee633..ad4529d6afa 100644 --- a/drivers/mmc/rockchip_dw_mmc.c +++ b/drivers/mmc/rockchip_dw_mmc.c @@ -145,7 +145,7 @@ static int rockchip_dwmmc_probe(struct udevice *dev) host->fifo_mode = priv->fifo_mode; -#ifdef CONFIG_MMC_PWRSEQ +#if CONFIG_IS_ENABLED(MMC_PWRSEQ) /* Enable power if needed */ ret = mmc_pwrseq_get_power(dev, &plat->cfg); if (!ret) { diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c index 327a05ad11d..c0a9f60b149 100644 --- a/drivers/mmc/sdhci-cadence.c +++ b/drivers/mmc/sdhci-cadence.c @@ -224,7 +224,7 @@ static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev, for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) { if (sdhci_cdns_set_tune_val(plat, i) || - mmc_send_tuning(mmc, opcode, NULL)) { /* bad */ + mmc_send_tuning(mmc, opcode)) { /* bad */ cur_streak = 0; } else { /* good */ cur_streak++; diff --git a/drivers/mmc/tmio-common.c b/drivers/mmc/tmio-common.c index 890c496b535..719c4830bc3 100644 --- a/drivers/mmc/tmio-common.c +++ b/drivers/mmc/tmio-common.c @@ -299,7 +299,13 @@ static int tmio_sd_dma_wait_for_irq(struct udevice *dev, u32 flag, struct tmio_sd_priv *priv = dev_get_priv(dev); long wait = 1000000 + 10 * blocks; - while (!(tmio_sd_readl(priv, TMIO_SD_DMA_INFO1) & flag)) { + for (;;) { + if (tmio_sd_readl(priv, TMIO_SD_DMA_INFO1) & flag) + break; + + if (tmio_sd_readl(priv, TMIO_SD_INFO1) & TMIO_SD_INFO1_CMP) + break; + if (wait-- < 0) { dev_err(dev, "timeout during DMA\n"); return -ETIMEDOUT; diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index bb9994b8626..9f3f1267cbd 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -1,6 +1,6 @@ - menuconfig MTD_RAW_NAND bool "Raw NAND Device Support" + if MTD_RAW_NAND config SYS_NAND_SELF_INIT @@ -49,12 +49,12 @@ config SYS_NAND_NO_SUBPAGE_WRITE depends on NAND_ARASAN || NAND_DAVINCI || NAND_KIRKWOOD config DM_NAND_ATMEL - bool "Support Atmel NAND controller with DM support" - select SYS_NAND_SELF_INIT - imply SYS_NAND_USE_FLASH_BBT - help - Enable this driver for NAND flash platforms using an Atmel NAND - controller. + bool "Support Atmel NAND controller with DM support" + select SYS_NAND_SELF_INIT + imply SYS_NAND_USE_FLASH_BBT + help + Enable this driver for NAND flash platforms using an Atmel NAND + controller. config NAND_ATMEL bool "Support Atmel NAND controller" @@ -133,35 +133,35 @@ config NAND_BRCMNAND_6753 Enable support for broadcom nand driver on bcm6753. config NAND_BRCMNAND_68360 - bool "Support Broadcom NAND controller on bcm68360" - depends on NAND_BRCMNAND && BCM6856 - help - Enable support for broadcom nand driver on bcm68360. + bool "Support Broadcom NAND controller on bcm68360" + depends on NAND_BRCMNAND && BCM6856 + help + Enable support for broadcom nand driver on bcm68360. config NAND_BRCMNAND_6838 - bool "Support Broadcom NAND controller on bcm6838" - depends on NAND_BRCMNAND && ARCH_BMIPS && SOC_BMIPS_BCM6838 - help - Enable support for broadcom nand driver on bcm6838. + bool "Support Broadcom NAND controller on bcm6838" + depends on NAND_BRCMNAND && ARCH_BMIPS && SOC_BMIPS_BCM6838 + help + Enable support for broadcom nand driver on bcm6838. config NAND_BRCMNAND_6858 - bool "Support Broadcom NAND controller on bcm6858" - depends on NAND_BRCMNAND && BCM6858 - help - Enable support for broadcom nand driver on bcm6858. + bool "Support Broadcom NAND controller on bcm6858" + depends on NAND_BRCMNAND && BCM6858 + help + Enable support for broadcom nand driver on bcm6858. config NAND_BRCMNAND_63158 - bool "Support Broadcom NAND controller on bcm63158" - depends on NAND_BRCMNAND && BCM63158 - help - Enable support for broadcom nand driver on bcm63158. + bool "Support Broadcom NAND controller on bcm63158" + depends on NAND_BRCMNAND && BCM63158 + help + Enable support for broadcom nand driver on bcm63158. config NAND_BRCMNAND_IPROC - bool "Support Broadcom NAND controller on the iproc family" - depends on NAND_BRCMNAND - help - Enable support for broadcom nand driver on the Broadcom - iproc family such as Northstar (BCM5301x, BCM4708...) + bool "Support Broadcom NAND controller on the iproc family" + depends on NAND_BRCMNAND + help + Enable support for broadcom nand driver on the Broadcom + iproc family such as Northstar (BCM5301x, BCM4708...) config NAND_DAVINCI bool "Support TI Davinci NAND controller" @@ -413,10 +413,10 @@ config NAND_VF610_NFC if NAND_VF610_NFC config NAND_VF610_NFC_DT - bool "Support Vybrid's vf610 NAND controller as a DT device" - depends on OF_CONTROL && DM_MTD - help - Enable the driver for Vybrid's vf610 NAND flash on platforms + bool "Support Vybrid's vf610 NAND controller as a DT device" + depends on OF_CONTROL && DM_MTD + help + Enable the driver for Vybrid's vf610 NAND flash on platforms using device tree. choice @@ -472,11 +472,11 @@ config NAND_SUNXI select SPL_NAND_SUPPORT select SPL_SYS_NAND_SELF_INIT imply CMD_NAND - ---help--- - Enable support for NAND. This option enables the standard and - SPL drivers. - The SPL driver only supports reading from the NAND using DMA - transfers. + help + Enable support for NAND. This option enables the standard and + SPL drivers. + The SPL driver only supports reading from the NAND using DMA + transfers. if NAND_SUNXI @@ -504,6 +504,15 @@ config NAND_ARASAN controller. This uses the hardware ECC for read and write operations. +config NAND_MESON + bool "Meson NAND support" + select SYS_NAND_SELF_INIT + depends on DM_MTD && ARCH_MESON + imply CMD_NAND + help + This enables Nand driver support for Meson raw NAND flash + controller. + config NAND_MXC bool "MXC NAND support" depends on CPU_ARM926EJS || CPU_ARM1136 || MX5 @@ -577,16 +586,16 @@ config NAND_OCTEONTX select SYS_NAND_SELF_INIT imply CMD_NAND help - This enables Nand flash controller hardware found on the OcteonTX - processors. + This enables Nand flash controller hardware found on the OcteonTX + processors. config NAND_OCTEONTX_HW_ECC bool "Support Hardware ECC for OcteonTX NAND controller" depends on NAND_OCTEONTX default y help - This enables Hardware BCH engine found on the OcteonTX processors to - support ECC for NAND flash controller. + This enables Hardware BCH engine found on the OcteonTX processors to + support ECC for NAND flash controller. config NAND_STM32_FMC2 bool "Support for NAND controller on STM32MP SoCs" @@ -751,37 +760,37 @@ config SYS_NAND_BAD_BLOCK_POS config SYS_NAND_U_BOOT_LOCATIONS bool "Define U-Boot binaries locations in NAND" help - Enable CONFIG_SYS_NAND_U_BOOT_OFFS though Kconfig. - This option should not be enabled when compiling U-Boot for boards - defining CONFIG_SYS_NAND_U_BOOT_OFFS in their include/configs/<board>.h - file. + Enable CONFIG_SYS_NAND_U_BOOT_OFFS though Kconfig. + This option should not be enabled when compiling U-Boot for boards + defining CONFIG_SYS_NAND_U_BOOT_OFFS in their include/configs/<board>.h + file. config SYS_NAND_U_BOOT_OFFS hex "Location in NAND to read U-Boot from" default 0x800000 if NAND_SUNXI depends on SYS_NAND_U_BOOT_LOCATIONS help - Set the offset from the start of the nand where u-boot should be - loaded from. + Set the offset from the start of the nand where u-boot should be + loaded from. config SYS_NAND_U_BOOT_OFFS_REDUND hex "Location in NAND to read U-Boot from" default SYS_NAND_U_BOOT_OFFS depends on SYS_NAND_U_BOOT_LOCATIONS help - Set the offset from the start of the nand where the redundant u-boot - should be loaded from. + Set the offset from the start of the nand where the redundant u-boot + should be loaded from. config SPL_NAND_AM33XX_BCH bool "Enables SPL-NAND driver which supports ELM based" depends on SPL_NAND_SUPPORT && NAND_OMAP_GPMC && !OMAP34XX default y - help + help Hardware ECC correction. This is useful for platforms which have ELM hardware engine and use NAND boot mode. Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine, so those platforms should use CONFIG_SPL_NAND_SIMPLE for enabling - SPL-NAND driver with software ECC correction support. + SPL-NAND driver with software ECC correction support. config SPL_NAND_DENALI bool "Support Denali NAND controller for SPL" @@ -810,6 +819,6 @@ config SYS_NAND_HW_ECC_OOBFIRST bool "In SPL, read the OOB first and then the data from NAND" depends on SPL_NAND_SIMPLE -endif +endif # if SPL -endif # if NAND +endif # if MTD_RAW_NAND diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index ddbba899e58..46fead6fa48 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o obj-$(CONFIG_NAND_LPC32XX_MLC) += lpc32xx_nand_mlc.o obj-$(CONFIG_NAND_LPC32XX_SLC) += lpc32xx_nand_slc.o obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o +obj-$(CONFIG_NAND_MESON) += meson_nand.o obj-$(CONFIG_NAND_MXC) += mxc_nand.o obj-$(CONFIG_NAND_MXS) += mxs_nand.o obj-$(CONFIG_NAND_MXS_DT) += mxs_nand_dt.o diff --git a/drivers/mtd/nand/raw/arasan_nfc.c b/drivers/mtd/nand/raw/arasan_nfc.c index 14766401bf6..ffcd963b3da 100644 --- a/drivers/mtd/nand/raw/arasan_nfc.c +++ b/drivers/mtd/nand/raw/arasan_nfc.c @@ -1232,7 +1232,8 @@ static int arasan_probe(struct udevice *dev) struct nand_config *nand = &info->config; struct mtd_info *mtd; ofnode child; - int err = -1; + int ret; + const char *str; info->reg = dev_read_addr_ptr(dev); mtd = nand_to_mtd(nand_chip); @@ -1258,9 +1259,16 @@ static int arasan_probe(struct udevice *dev) writel(0x0, &info->reg->pgm_reg); /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL)) { + ret = nand_scan_ident(mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL); + if (ret) { printf("%s: nand_scan_ident failed\n", __func__); - goto fail; + return ret; + } + + str = ofnode_read_string(nand_chip->flash_node, "nand-ecc-mode"); + if (!str || strcmp(str, "hw") != 0) { + printf("%s ecc mode is not supported\n", str); + return -EINVAL; } nand_chip->ecc.mode = NAND_ECC_HW; @@ -1282,26 +1290,26 @@ static int arasan_probe(struct udevice *dev) nand_chip->ecc.bytes = 0; nand_chip->ecc.layout = &ondie_nand_oob_64; } else { - if (arasan_nand_ecc_init(mtd)) { + ret = arasan_nand_ecc_init(mtd); + if (ret) { printf("%s: nand_ecc_init failed\n", __func__); - goto fail; + return ret; } } - if (nand_scan_tail(mtd)) { + ret = nand_scan_tail(mtd); + if (ret) { printf("%s: nand_scan_tail failed\n", __func__); - goto fail; + return ret; } - if (nand_register(0, mtd)) { + ret = nand_register(0, mtd); + if (ret) { printf("Nand Register Fail\n"); - goto fail; + return ret; } - return 0; -fail: - free(nand); - return err; + return ret; } static const struct udevice_id arasan_nand_dt_ids[] = { diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 0e0441472b8..ee4ec6da587 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -1267,7 +1267,7 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, return ret; /* - * The write cycle timing is directly matching tWC, but is also + * The read cycle timing is directly matching tRC, but is also * dependent on the setup and hold timings we calculated earlier, * which gives: * @@ -1429,8 +1429,6 @@ static int atmel_nand_setup_data_interface(struct mtd_info *mtd, int csline, return nc->caps->ops->setup_data_interface(nand, csline, conf); } -#define NAND_KEEP_TIMINGS 0x00800000 - static void atmel_nand_init(struct atmel_nand_controller *nc, struct atmel_nand *nand) { diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c new file mode 100644 index 00000000000..12499a79478 --- /dev/null +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -0,0 +1,1247 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Amlogic Meson Nand Flash Controller Driver + * + * Copyright (c) 2018 Amlogic, inc. + * Author: Liang Yang <liang.yang@amlogic.com> + * + * Copyright (c) 2023 SaluteDevices, Inc. + * Author: Arseniy Krasnov <avkrasnov@salutedevices.com> + */ + +#include <nand.h> +#include <asm/io.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/ofnode.h> +#include <dm/uclass.h> +#include <linux/bug.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/iopoll.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/rawnand.h> +#include <linux/sizes.h> + +#define NFC_CMD_IDLE (0xc << 14) +#define NFC_CMD_CLE (0x5 << 14) +#define NFC_CMD_ALE (0x6 << 14) +#define NFC_CMD_DWR (0x4 << 14) +#define NFC_CMD_DRD (0x8 << 14) +#define NFC_CMD_ADL ((0 << 16) | (3 << 20)) +#define NFC_CMD_ADH ((1 << 16) | (3 << 20)) +#define NFC_CMD_AIL ((2 << 16) | (3 << 20)) +#define NFC_CMD_AIH ((3 << 16) | (3 << 20)) +#define NFC_CMD_SEED ((8 << 16) | (3 << 20)) +#define NFC_CMD_M2N ((0 << 17) | (2 << 20)) +#define NFC_CMD_N2M ((1 << 17) | (2 << 20)) +#define NFC_CMD_RB BIT(20) +#define NFC_CMD_SCRAMBLER_ENABLE BIT(19) +#define NFC_CMD_SCRAMBLER_DISABLE 0 +#define NFC_CMD_SHORTMODE_DISABLE 0 +#define NFC_CMD_RB_INT BIT(14) +#define NFC_CMD_RB_INT_NO_PIN ((0xb << 10) | BIT(18) | BIT(16)) + +#define NFC_CMD_GET_SIZE(x) (((x) >> 22) & GENMASK(4, 0)) + +#define NFC_REG_CMD 0x00 +#define NFC_REG_CFG 0x04 +#define NFC_REG_DADR 0x08 +#define NFC_REG_IADR 0x0c +#define NFC_REG_BUF 0x10 +#define NFC_REG_INFO 0x14 +#define NFC_REG_DC 0x18 +#define NFC_REG_ADR 0x1c +#define NFC_REG_DL 0x20 +#define NFC_REG_DH 0x24 +#define NFC_REG_CADR 0x28 +#define NFC_REG_SADR 0x2c +#define NFC_REG_PINS 0x30 +#define NFC_REG_VER 0x38 + +#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages) \ + ( \ + (cmd_dir) | \ + (ran) | \ + ((bch) << 14) | \ + ((short_mode) << 13) | \ + (((page_size) & 0x7f) << 6) | \ + ((pages) & 0x3f) \ + ) + +#define GENCMDDADDRL(adl, addr) ((adl) | ((addr) & 0xffff)) +#define GENCMDDADDRH(adh, addr) ((adh) | (((addr) >> 16) & 0xffff)) +#define GENCMDIADDRL(ail, addr) ((ail) | ((addr) & 0xffff)) +#define GENCMDIADDRH(aih, addr) ((aih) | (((addr) >> 16) & 0xffff)) + +#define DMA_DIR(dir) ((dir) ? NFC_CMD_N2M : NFC_CMD_M2N) + +#define ECC_CHECK_RETURN_FF -1 + +#define NAND_CE0 (0xe << 10) +#define NAND_CE1 (0xd << 10) + +#define DMA_BUSY_TIMEOUT_US 1000000 +#define CMD_DRAIN_TIMEOUT_US 1000 +#define ECC_POLL_TIMEOUT_US 15 + +#define MAX_CE_NUM 2 + +/* eMMC clock register, misc control */ +#define CLK_SELECT_NAND BIT(31) +#define CLK_ALWAYS_ON_NAND BIT(24) +#define CLK_ENABLE_VALUE 0x245 + +#define DIRREAD 1 +#define DIRWRITE 0 + +#define ECC_PARITY_BCH8_512B 14 +#define ECC_COMPLETE BIT(31) +#define ECC_ERR_CNT(x) (((x) >> 24) & GENMASK(5, 0)) +#define ECC_ZERO_CNT(x) (((x) >> 16) & GENMASK(5, 0)) +#define ECC_UNCORRECTABLE 0x3f + +#define PER_INFO_BYTE 8 + +#define NFC_SEND_CMD(host, cmd) \ + (writel((cmd), (host)->reg_base + NFC_REG_CMD)) + +#define NFC_GET_CMD(host) \ + (readl((host)->reg_base + NFC_REG_CMD)) + +#define NFC_CMDFIFO_SIZE(host) ((NFC_GET_CMD((host)) >> 22) & GENMASK(4, 0)) + +#define NFC_CMD_MAKE_IDLE(ce, delay) ((ce) | NFC_CMD_IDLE | ((delay) & 0x3ff)) +#define NFC_CMD_MAKE_DRD(ce, size) ((ce) | NFC_CMD_DRD | (size)) +#define NFC_CMD_MAKE_DWR(ce, data) ((ce) | NFC_CMD_DWR | ((data) & 0xff)) +#define NFC_CMD_MAKE_CLE(ce, cmd_val) ((ce) | NFC_CMD_CLE | ((cmd_val) & 0xff)) +#define NFC_CMD_MAKE_ALE(ce, addr) ((ce) | NFC_CMD_ALE | ((addr) & 0xff)) + +#define NAND_TWB_TIME_CYCLE 10 + +#define NFC_DEV_READY_TICK_MAX 5000 + +/* Both values are recommended by vendor, as the most + * tested with almost all SLC NAND flash. Second value + * could be calculated dynamically from timing parameters, + * but we need both values for initial start of the NAND + * controller (e.g. before NAND subsystem processes timings), + * so use hardcoded constants. + */ +#define NFC_DEFAULT_BUS_CYCLE 6 +#define NFC_DEFAULT_BUS_TIMING 7 + +#define NFC_SEED_OFFSET 0xc2 +#define NFC_SEED_MASK 0x7fff + +#define DMA_ADDR_ALIGN 8 + +struct meson_nfc_nand_chip { + struct list_head node; + struct nand_chip nand; + + u32 bch_mode; + u8 *data_buf; + __le64 *info_buf; + u32 nsels; + u8 sels[]; +}; + +struct meson_nfc_param { + u32 chip_select; + u32 rb_select; +}; + +struct meson_nfc { + void __iomem *reg_base; + void __iomem *reg_clk; + struct list_head chips; + struct meson_nfc_param param; + struct udevice *dev; + dma_addr_t daddr; + dma_addr_t iaddr; + u32 data_bytes; + u32 info_bytes; + u64 assigned_cs; +}; + +struct meson_nand_ecc { + u32 bch; + u32 strength; + u32 size; +}; + +enum { + NFC_ECC_BCH8_512 = 1, + NFC_ECC_BCH8_1K, + NFC_ECC_BCH24_1K, + NFC_ECC_BCH30_1K, + NFC_ECC_BCH40_1K, + NFC_ECC_BCH50_1K, + NFC_ECC_BCH60_1K, +}; + +#define MESON_ECC_DATA(b, s, sz) { .bch = (b), .strength = (s), .size = (sz) } + +static struct meson_nand_ecc meson_ecc[] = { + MESON_ECC_DATA(NFC_ECC_BCH8_512, 8, 512), + MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8, 1024), +}; + +static int meson_nand_calc_ecc_bytes(int step_size, int strength) +{ + int ecc_bytes; + + if (step_size == 512 && strength == 8) + return ECC_PARITY_BCH8_512B; + + ecc_bytes = DIV_ROUND_UP(strength * fls(step_size * 8), 8); + ecc_bytes = ALIGN(ecc_bytes, 2); + + return ecc_bytes; +} + +static struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand) +{ + return container_of(nand, struct meson_nfc_nand_chip, nand); +} + +static void meson_nfc_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + struct meson_nfc *nfc = nand_get_controller_data(nand); + + nfc->param.chip_select = meson_chip->sels[chip] ? NAND_CE1 : NAND_CE0; +} + +static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time) +{ + writel(NFC_CMD_MAKE_IDLE(nfc->param.chip_select, time), + nfc->reg_base + NFC_REG_CMD); +} + +static void meson_nfc_cmd_seed(const struct meson_nfc *nfc, u32 seed) +{ + writel(NFC_CMD_SEED | (NFC_SEED_OFFSET + (seed & NFC_SEED_MASK)), + nfc->reg_base + NFC_REG_CMD); +} + +static void meson_nfc_cmd_access(struct nand_chip *nand, bool raw, bool dir, + int scrambler) +{ + struct mtd_info *mtd = nand_to_mtd(nand); + const struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); + const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + u32 bch = meson_chip->bch_mode, cmd; + int len = mtd->writesize, pagesize, pages; + + pagesize = nand->ecc.size; + + if (raw) { + len = mtd->writesize + mtd->oobsize; + cmd = len | scrambler | DMA_DIR(dir); + writel(cmd, nfc->reg_base + NFC_REG_CMD); + return; + } + + pages = len / nand->ecc.size; + + cmd = CMDRWGEN(DMA_DIR(dir), scrambler, bch, + NFC_CMD_SHORTMODE_DISABLE, pagesize, pages); + + writel(cmd, nfc->reg_base + NFC_REG_CMD); +} + +static void meson_nfc_drain_cmd(struct meson_nfc *nfc) +{ + /* + * Insert two commands to make sure all valid commands are finished. + * + * The Nand flash controller is designed as two stages pipleline - + * a) fetch and b) execute. + * There might be cases when the driver see command queue is empty, + * but the Nand flash controller still has two commands buffered, + * one is fetched into NFC request queue (ready to run), and another + * is actively executing. So pushing 2 "IDLE" commands guarantees that + * the pipeline is emptied. + */ + meson_nfc_cmd_idle(nfc, 0); + meson_nfc_cmd_idle(nfc, 0); +} + +static int meson_nfc_wait_cmd_finish(const struct meson_nfc *nfc, + unsigned int timeout_us) +{ + u32 cmd_size = 0; + + /* wait cmd fifo is empty */ + return readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size, + !NFC_CMD_GET_SIZE(cmd_size), + timeout_us); +} + +static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc) +{ + meson_nfc_drain_cmd(nfc); + + return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT_US); +} + +static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i) +{ + const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + int len; + + len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i; + + return meson_chip->data_buf + len; +} + +static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i) +{ + const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + int len, temp; + + temp = nand->ecc.size + nand->ecc.bytes; + len = (temp + 2) * i; + + return meson_chip->data_buf + len; +} + +static void meson_nfc_get_data_oob(struct nand_chip *nand, + u8 *buf, u8 *oobbuf) +{ + u8 *dsrc, *osrc; + int i, oob_len; + + oob_len = nand->ecc.bytes + 2; + for (i = 0; i < nand->ecc.steps; i++) { + if (buf) { + dsrc = meson_nfc_data_ptr(nand, i); + memcpy(buf, dsrc, nand->ecc.size); + buf += nand->ecc.size; + } + + if (oobbuf) { + osrc = meson_nfc_oob_ptr(nand, i); + memcpy(oobbuf, osrc, oob_len); + oobbuf += oob_len; + } + } +} + +static void meson_nfc_set_data_oob(struct nand_chip *nand, + const u8 *buf, u8 *oobbuf) +{ + int i, oob_len; + + oob_len = nand->ecc.bytes + 2; + for (i = 0; i < nand->ecc.steps; i++) { + u8 *osrc; + + if (buf) { + u8 *dsrc; + + dsrc = meson_nfc_data_ptr(nand, i); + memcpy(dsrc, buf, nand->ecc.size); + buf += nand->ecc.size; + } + + osrc = meson_nfc_oob_ptr(nand, i); + memcpy(osrc, oobbuf, oob_len); + oobbuf += oob_len; + } +} + +static void meson_nfc_set_user_byte(struct nand_chip *nand, const u8 *oob_buf) +{ + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + int i, count; + + for (i = 0, count = 0; i < nand->ecc.steps; i++, count += (2 + nand->ecc.bytes)) { + __le64 *info = &meson_chip->info_buf[i]; + + *info |= oob_buf[count]; + *info |= oob_buf[count + 1] << 8; + } +} + +static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf) +{ + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + int i, count; + + for (i = 0, count = 0; i < nand->ecc.steps; i++, count += (2 + nand->ecc.bytes)) { + const __le64 *info = &meson_chip->info_buf[i]; + + oob_buf[count] = *info; + oob_buf[count + 1] = *info >> 8; + } +} + +static int meson_nfc_ecc_correct(struct nand_chip *nand, u32 *bitflips, + u64 *correct_bitmap) +{ + struct mtd_info *mtd = nand_to_mtd(nand); + int ret = 0, i; + + for (i = 0; i < nand->ecc.steps; i++) { + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + const __le64 *info = &meson_chip->info_buf[i]; + + if (ECC_ERR_CNT(*info) != ECC_UNCORRECTABLE) { + mtd->ecc_stats.corrected += ECC_ERR_CNT(*info); + *bitflips = max_t(u32, *bitflips, ECC_ERR_CNT(*info)); + *correct_bitmap |= BIT_ULL(i); + continue; + } + + if ((nand->options & NAND_NEED_SCRAMBLING) && + ECC_ZERO_CNT(*info) < nand->ecc.strength) { + mtd->ecc_stats.corrected += ECC_ZERO_CNT(*info); + *bitflips = max_t(u32, *bitflips, + ECC_ZERO_CNT(*info)); + ret = ECC_CHECK_RETURN_FF; + } else { + ret = -EBADMSG; + } + } + + return ret; +} + +static int meson_nfc_dma_buffer_setup(struct nand_chip *nand, void *databuf, + int datalen, void *infobuf, int infolen, + enum dma_data_direction dir) +{ + struct meson_nfc *nfc = nand_get_controller_data(nand); + int ret; + u32 cmd; + + nfc->daddr = dma_map_single(databuf, datalen, DMA_BIDIRECTIONAL); + ret = dma_mapping_error(nfc->dev, nfc->daddr); + if (ret) + return ret; + + cmd = GENCMDDADDRL(NFC_CMD_ADL, nfc->daddr); + writel(cmd, nfc->reg_base + NFC_REG_CMD); + + cmd = GENCMDDADDRH(NFC_CMD_ADH, nfc->daddr); + writel(cmd, nfc->reg_base + NFC_REG_CMD); + + if (infobuf) { + nfc->iaddr = dma_map_single(infobuf, infolen, + DMA_BIDIRECTIONAL); + ret = dma_mapping_error(nfc->dev, nfc->iaddr); + if (ret) { + dma_unmap_single(nfc->daddr, datalen, dir); + return ret; + } + + nfc->info_bytes = infolen; + cmd = GENCMDIADDRL(NFC_CMD_AIL, nfc->iaddr); + writel(cmd, nfc->reg_base + NFC_REG_CMD); + + cmd = GENCMDIADDRH(NFC_CMD_AIH, nfc->iaddr); + writel(cmd, nfc->reg_base + NFC_REG_CMD); + } + + return 0; +} + +static void meson_nfc_dma_buffer_release(struct nand_chip *nand, + int datalen, int infolen, + enum dma_data_direction dir) +{ + struct meson_nfc *nfc = nand_get_controller_data(nand); + + dma_unmap_single(nfc->daddr, datalen, dir); + + if (infolen) { + dma_unmap_single(nfc->iaddr, infolen, dir); + nfc->info_bytes = 0; + } +} + +static void meson_nfc_read_buf(struct mtd_info *mtd, u8 *buf, int size) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct meson_nfc *nfc = nand_get_controller_data(nand); + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + u8 *dma_buf; + int ret; + u32 cmd; + + if ((uintptr_t)buf % DMA_ADDR_ALIGN) { + unsigned long tmp_addr; + + dma_buf = dma_alloc_coherent(size, &tmp_addr); + if (!dma_buf) + return; + } else { + dma_buf = buf; + } + + ret = meson_nfc_dma_buffer_setup(nand, dma_buf, size, meson_chip->info_buf, + PER_INFO_BYTE, DMA_FROM_DEVICE); + if (ret) { + pr_err("Failed to setup DMA buffer %p/%p\n", dma_buf, + meson_chip->info_buf); + return; + } + + cmd = NFC_CMD_N2M | size; + writel(cmd, nfc->reg_base + NFC_REG_CMD); + + meson_nfc_drain_cmd(nfc); + meson_nfc_wait_cmd_finish(nfc, CMD_DRAIN_TIMEOUT_US); + meson_nfc_dma_buffer_release(nand, size, PER_INFO_BYTE, DMA_FROM_DEVICE); + + if (buf != dma_buf) { + memcpy(buf, dma_buf, size); + dma_free_coherent(dma_buf); + } +} + +static void meson_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int size) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct meson_nfc *nfc = nand_get_controller_data(nand); + u8 *dma_buf; + int ret; + u32 cmd; + + if ((uintptr_t)buf % DMA_ADDR_ALIGN) { + unsigned long tmp_addr; + + dma_buf = dma_alloc_coherent(size, &tmp_addr); + if (!dma_buf) + return; + + memcpy(dma_buf, buf, size); + } else { + dma_buf = (u8 *)buf; + } + + ret = meson_nfc_dma_buffer_setup(nand, (void *)dma_buf, size, NULL, + 0, DMA_TO_DEVICE); + if (ret) { + pr_err("Failed to setup DMA buffer %p\n", dma_buf); + return; + } + + cmd = NFC_CMD_M2N | size; + writel(cmd, nfc->reg_base + NFC_REG_CMD); + + meson_nfc_drain_cmd(nfc); + meson_nfc_wait_cmd_finish(nfc, CMD_DRAIN_TIMEOUT_US); + meson_nfc_dma_buffer_release(nand, size, 0, DMA_TO_DEVICE); + + if (buf != dma_buf) + dma_free_coherent(dma_buf); +} + +static int meson_nfc_write_page_sub(struct nand_chip *nand, + int page, bool raw) +{ + const struct mtd_info *mtd = nand_to_mtd(nand); + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + struct meson_nfc *nfc = nand_get_controller_data(nand); + int data_len, info_len; + int ret; + u32 cmd; + + data_len = mtd->writesize + mtd->oobsize; + info_len = nand->ecc.steps * PER_INFO_BYTE; + + ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf, + data_len, meson_chip->info_buf, + info_len, DMA_TO_DEVICE); + if (ret) { + pr_err("Failed to setup DMA buffer %p/%p\n", + meson_chip->data_buf, meson_chip->info_buf); + return ret; + } + + if (nand->options & NAND_NEED_SCRAMBLING) { + meson_nfc_cmd_seed(nfc, page); + meson_nfc_cmd_access(nand, raw, DIRWRITE, + NFC_CMD_SCRAMBLER_ENABLE); + } else { + meson_nfc_cmd_access(nand, raw, DIRWRITE, + NFC_CMD_SCRAMBLER_DISABLE); + } + + cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG; + writel(cmd, nfc->reg_base + NFC_REG_CMD); + + meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE); + + return 0; +} + +static int meson_nfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + meson_nfc_set_data_oob(chip, buf, oob_required ? chip->oob_poi : NULL); + + return meson_nfc_write_page_sub(chip, page, true); +} + +static int meson_nfc_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(chip); + + if (buf) + memcpy(meson_chip->data_buf, buf, mtd->writesize); + + memset(meson_chip->info_buf, 0, chip->ecc.steps * PER_INFO_BYTE); + + if (oob_required) + meson_nfc_set_user_byte(chip, chip->oob_poi); + + return meson_nfc_write_page_sub(chip, page, false); +} + +static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc, + struct nand_chip *nand, bool raw) +{ + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + __le64 *info; + u32 neccpages; + int ret; + + neccpages = raw ? 1 : nand->ecc.steps; + info = &meson_chip->info_buf[neccpages - 1]; + do { + udelay(ECC_POLL_TIMEOUT_US); + /* info is updated by nfc dma engine*/ + rmb(); + invalidate_dcache_range(nfc->iaddr, nfc->iaddr + nfc->info_bytes); + ret = *info & ECC_COMPLETE; + } while (!ret); +} + +static int meson_nfc_read_page_sub(struct nand_chip *nand, + int page, bool raw) +{ + struct mtd_info *mtd = nand_to_mtd(nand); + struct meson_nfc *nfc = nand_get_controller_data(nand); + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + u32 data_len, info_len; + int ret; + + data_len = mtd->writesize + mtd->oobsize; + info_len = nand->ecc.steps * PER_INFO_BYTE; + + ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf, data_len, + meson_chip->info_buf, info_len, + DMA_FROM_DEVICE); + if (ret) + return ret; + + if (nand->options & NAND_NEED_SCRAMBLING) { + meson_nfc_cmd_seed(nfc, page); + meson_nfc_cmd_access(nand, raw, DIRREAD, + NFC_CMD_SCRAMBLER_ENABLE); + } else { + meson_nfc_cmd_access(nand, raw, DIRREAD, + NFC_CMD_SCRAMBLER_DISABLE); + } + + meson_nfc_wait_dma_finish(nfc); + meson_nfc_check_ecc_pages_valid(nfc, nand, raw); + + meson_nfc_dma_buffer_release(nand, data_len, info_len, + DMA_FROM_DEVICE); + + return 0; +} + +static int meson_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + int ret; + + ret = meson_nfc_read_page_sub(chip, page, true); + if (ret) + return ret; + + meson_nfc_get_data_oob(chip, buf, oob_required ? chip->oob_poi : NULL); + + return 0; +} + +static int meson_nfc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(chip); + u64 correct_bitmap = 0; + u32 bitflips = 0; + int ret; + + ret = meson_nfc_read_page_sub(chip, page, false); + if (ret) + return ret; + + if (oob_required) + meson_nfc_get_user_byte(chip, chip->oob_poi); + + ret = meson_nfc_ecc_correct(chip, &bitflips, &correct_bitmap); + + if (ret == ECC_CHECK_RETURN_FF) { + if (buf) + memset(buf, 0xff, mtd->writesize); + + if (oob_required) + memset(chip->oob_poi, 0xff, mtd->oobsize); + } else if (ret < 0) { + struct nand_ecc_ctrl *ecc; + int i; + + if ((chip->options & NAND_NEED_SCRAMBLING) || !buf) { + mtd->ecc_stats.failed++; + return bitflips; + } + + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + ret = meson_nfc_read_page_raw(mtd, chip, buf, 1, page); + if (ret) + return ret; + + ecc = &chip->ecc; + + for (i = 0; i < chip->ecc.steps ; i++) { + u8 *data = buf + i * ecc->size; + u8 *oob = chip->oob_poi + i * (ecc->bytes + 2); + + if (correct_bitmap & BIT_ULL(i)) + continue; + + ret = nand_check_erased_ecc_chunk(data, ecc->size, + oob, ecc->bytes + 2, + NULL, 0, + ecc->strength); + if (ret < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += ret; + bitflips = max_t(u32, bitflips, ret); + } + } + } else if (buf && buf != meson_chip->data_buf) { + memcpy(buf, meson_chip->data_buf, mtd->writesize); + } + + return bitflips; +} + +static int meson_nfc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int ret; + + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + return meson_nfc_read_page_raw(mtd, chip, NULL, 1, page); +} + +static int meson_nfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int ret; + + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + return meson_nfc_read_page_hwecc(mtd, chip, NULL, 1, page); +} + +static int meson_nfc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int ret; + + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + ret = meson_nfc_write_page_raw(mtd, chip, NULL, 1, page); + if (ret) + return ret; + + return nand_prog_page_end_op(chip); +} + +static int meson_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int ret; + + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + ret = meson_nfc_write_page_hwecc(mtd, chip, NULL, 1, page); + if (ret) + return ret; + + return nand_prog_page_end_op(chip); +} + +static void meson_nfc_nand_cmd_function(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + + if (column != -1 || page_addr != -1) { + int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) + column >>= 1; + + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + /* Only output a single addr cycle for 8bits + * opcodes. + */ + if (!nand_opcode_8bits(command)) + chip->cmd_ctrl(mtd, column >> 8, ctrl); + } + + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + chip->cmd_ctrl(mtd, page_addr >> 8, NAND_NCE | + NAND_ALE); + /* One more address cycle for devices > 128MiB */ + if (chip->chipsize > SZ_128M) + chip->cmd_ctrl(mtd, page_addr >> 16, + NAND_NCE | NAND_ALE); + } + + switch (command) { + case NAND_CMD_READ0: + chip->cmd_ctrl(mtd, NAND_CMD_READSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + fallthrough; + case NAND_CMD_PARAM: + nand_wait_ready(mtd); + nand_exit_status_op(chip); + } + } +} + +static void meson_nfc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct meson_nfc *nfc = nand_get_controller_data(nand); + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + cmd = NFC_CMD_MAKE_CLE(nfc->param.chip_select, cmd); + else + cmd = NFC_CMD_MAKE_ALE(nfc->param.chip_select, cmd); + + writel(cmd, nfc->reg_base + NFC_REG_CMD); +} + +static void meson_nfc_wait_cmd_fifo(struct meson_nfc *nfc) +{ + while ((NFC_GET_CMD(nfc) >> 22) & GENMASK(4, 0)) + ; +} + +static u8 meson_nfc_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct meson_nfc *nfc = nand_get_controller_data(nand); + + writel(NFC_CMD_MAKE_DRD(nfc->param.chip_select, 0), nfc->reg_base + NFC_REG_CMD); + + meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE); + meson_nfc_cmd_idle(nfc, 0); + meson_nfc_cmd_idle(nfc, 0); + + meson_nfc_wait_cmd_fifo(nfc); + + return readl(nfc->reg_base + NFC_REG_BUF); +} + +static void meson_nfc_nand_write_byte(struct mtd_info *mtd, u8 val) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct meson_nfc *nfc = nand_get_controller_data(nand); + + meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE); + + writel(NFC_CMD_MAKE_DWR(nfc->param.chip_select, val), nfc->reg_base + NFC_REG_CMD); + + meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE); + meson_nfc_cmd_idle(nfc, 0); + meson_nfc_cmd_idle(nfc, 0); + + meson_nfc_wait_cmd_fifo(nfc); +} + +static int meson_nfc_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + unsigned int time_out_cnt = 0; + + chip->select_chip(mtd, 0); + + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + + do { + int status; + + status = (int)chip->read_byte(mtd); + if (status & NAND_STATUS_READY) + break; + } while (time_out_cnt++ < NFC_DEV_READY_TICK_MAX); + + return time_out_cnt != NFC_DEV_READY_TICK_MAX; +} + +static int meson_chip_buffer_init(struct nand_chip *nand) +{ + const struct mtd_info *mtd = nand_to_mtd(nand); + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + u32 page_bytes, info_bytes, nsectors; + unsigned long tmp_addr; + + nsectors = mtd->writesize / nand->ecc.size; + + page_bytes = mtd->writesize + mtd->oobsize; + info_bytes = nsectors * PER_INFO_BYTE; + + meson_chip->data_buf = dma_alloc_coherent(page_bytes, &tmp_addr); + if (!meson_chip->data_buf) + return -ENOMEM; + + meson_chip->info_buf = dma_alloc_coherent(info_bytes, &tmp_addr); + if (!meson_chip->info_buf) { + dma_free_coherent(meson_chip->data_buf); + return -ENOMEM; + } + + return 0; +} + +static const int axg_stepinfo_strengths[] = { 8 }; +static const struct nand_ecc_step_info axg_stepinfo_1024 = { + .stepsize = 1024, + .strengths = axg_stepinfo_strengths, + .nstrengths = ARRAY_SIZE(axg_stepinfo_strengths) +}; + +static const struct nand_ecc_step_info axg_stepinfo_512 = { + .stepsize = 512, + .strengths = axg_stepinfo_strengths, + .nstrengths = ARRAY_SIZE(axg_stepinfo_strengths) +}; + +static const struct nand_ecc_step_info axg_stepinfo[] = { axg_stepinfo_1024, axg_stepinfo_512 }; + +static const struct nand_ecc_caps meson_axg_ecc_caps = { + .stepinfos = axg_stepinfo, + .nstepinfos = ARRAY_SIZE(axg_stepinfo), + .calc_ecc_bytes = meson_nand_calc_ecc_bytes, +}; + +/* + * OOB layout: + * + * For ECC with 512 bytes step size: + * 0x00: AA AA BB BB BB BB BB BB BB BB BB BB BB BB BB BB + * 0x10: AA AA CC CC CC CC CC CC CC CC CC CC CC CC CC CC + * 0x20: + * 0x30: + * + * For ECC with 1024 bytes step size: + * 0x00: AA AA BB BB BB BB BB BB BB BB BB BB BB BB BB BB + * 0x10: AA AA CC CC CC CC CC CC CC CC CC CC CC CC CC CC + * 0x20: AA AA DD DD DD DD DD DD DD DD DD DD DD DD DD DD + * 0x30: AA AA EE EE EE EE EE EE EE EE EE EE EE EE EE EE + * + * AA - user bytes. + * BB, CC, DD, EE - ECC code bytes for each step. + */ +static struct nand_ecclayout nand_oob; + +static void meson_nfc_init_nand_oob(struct nand_chip *nand) +{ + int section_size = 2 + nand->ecc.bytes; + int i; + int k; + + nand_oob.eccbytes = nand->ecc.steps * nand->ecc.bytes; + k = 0; + + for (i = 0; i < nand->ecc.steps; i++) { + int j; + + for (j = 0; j < nand->ecc.bytes; j++) + nand_oob.eccpos[k++] = (i * section_size) + 2 + j; + + nand_oob.oobfree[i].offset = (i * section_size); + nand_oob.oobfree[i].length = 2; + } + + nand_oob.oobavail = 2 * nand->ecc.steps; + nand->ecc.layout = &nand_oob; +} + +static int meson_nfc_init_ecc(struct nand_chip *nand, ofnode node) +{ + const struct mtd_info *mtd = nand_to_mtd(nand); + int ret; + int i; + + ret = nand_check_ecc_caps(nand, &meson_axg_ecc_caps, mtd->oobsize - 2); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(meson_ecc); i++) { + if (meson_ecc[i].strength == nand->ecc.strength && + meson_ecc[i].size == nand->ecc.size) { + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + + nand->ecc.steps = mtd->writesize / nand->ecc.size; + meson_chip->bch_mode = meson_ecc[i].bch; + + meson_nfc_init_nand_oob(nand); + + return 0; + } + } + + return -EINVAL; +} + +static int meson_nfc_nand_chip_init(struct udevice *dev, struct meson_nfc *nfc, + ofnode node) +{ + struct meson_nfc_nand_chip *meson_chip; + struct nand_chip *nand; + struct mtd_info *mtd; + u32 cs[MAX_CE_NUM]; + u32 nsels; + int ret; + int i; + + if (!ofnode_get_property(node, "reg", &nsels)) { + dev_err(dev, "\"reg\" property is not found\n"); + return -ENODEV; + } + + nsels /= sizeof(u32); + if (nsels >= MAX_CE_NUM) { + dev_err(dev, "invalid size of CS array, max is %d\n", + MAX_CE_NUM); + return -EINVAL; + } + + ret = ofnode_read_u32_array(node, "reg", cs, nsels); + if (ret < 0) { + dev_err(dev, "failed to read \"reg\" property\n"); + return ret; + } + + for (i = 0; i < nsels; i++) { + if (test_and_set_bit(cs[i], &nfc->assigned_cs)) { + dev_err(dev, "CS %d already assigned\n", cs[i]); + return -EINVAL; + } + } + + meson_chip = malloc(sizeof(*meson_chip) + nsels * sizeof(meson_chip->sels[0])); + if (!meson_chip) { + dev_err(dev, "failed to allocate memory for chip\n"); + return -ENOMEM; + } + + meson_chip->nsels = nsels; + nand = &meson_chip->nand; + + nand->flash_node = node; + nand_set_controller_data(nand, nfc); + /* Set the driver entry points for MTD */ + nand->cmdfunc = meson_nfc_nand_cmd_function; + nand->cmd_ctrl = meson_nfc_cmd_ctrl; + nand->select_chip = meson_nfc_nand_select_chip; + nand->read_byte = meson_nfc_nand_read_byte; + nand->write_byte = meson_nfc_nand_write_byte; + nand->dev_ready = meson_nfc_dev_ready; + + /* Buffer read/write routines */ + nand->read_buf = meson_nfc_read_buf; + nand->write_buf = meson_nfc_write_buf; + nand->options |= NAND_NO_SUBPAGE_WRITE; + + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.hwctl = NULL; + nand->ecc.read_page = meson_nfc_read_page_hwecc; + nand->ecc.write_page = meson_nfc_write_page_hwecc; + nand->ecc.read_page_raw = meson_nfc_read_page_raw; + nand->ecc.write_page_raw = meson_nfc_write_page_raw; + + nand->ecc.read_oob = meson_nfc_read_oob; + nand->ecc.write_oob = meson_nfc_write_oob; + nand->ecc.read_oob_raw = meson_nfc_read_oob_raw; + nand->ecc.write_oob_raw = meson_nfc_write_oob_raw; + + nand->ecc.algo = NAND_ECC_BCH; + + mtd = nand_to_mtd(nand); + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) { + dev_err(dev, "'nand_scan_ident()' failed: %d\n", ret); + goto err_chip_free; + } + + ret = meson_nfc_init_ecc(nand, node); + if (ret) { + dev_err(dev, "failed to init ECC settings: %d\n", ret); + goto err_chip_free; + } + + ret = meson_chip_buffer_init(nand); + if (ret) { + dev_err(dev, "failed to init DMA buffers: %d\n", ret); + goto err_chip_free; + } + + /* 'nand_scan_tail()' needs ECC parameters to be already + * set and correct. + */ + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(dev, "'nand_scan_tail()' failed: %d\n", ret); + goto err_chip_buf_free; + } + + ret = nand_register(0, mtd); + if (ret) { + dev_err(dev, "'nand_register()' failed: %d\n", ret); + goto err_chip_buf_free; + } + + list_add_tail(&meson_chip->node, &nfc->chips); + + return 0; + +err_chip_buf_free: + dma_free_coherent(meson_chip->info_buf); + dma_free_coherent(meson_chip->data_buf); + +err_chip_free: + free(meson_chip); + + return ret; +} + +static int meson_nfc_nand_chips_init(struct udevice *dev, + struct meson_nfc *nfc) +{ + ofnode parent = dev_ofnode(dev); + ofnode node; + + ofnode_for_each_subnode(node, parent) { + int ret = meson_nfc_nand_chip_init(dev, nfc, node); + + if (ret) + return ret; + } + + return 0; +} + +static void meson_nfc_clk_init(struct meson_nfc *nfc) +{ + u32 bus_cycle = NFC_DEFAULT_BUS_CYCLE; + u32 bus_timing = NFC_DEFAULT_BUS_TIMING; + u32 bus_cfg_val; + + writel(CLK_ALWAYS_ON_NAND | CLK_SELECT_NAND | CLK_ENABLE_VALUE, nfc->reg_clk); + writel(0, nfc->reg_base + NFC_REG_CFG); + + bus_cfg_val = (((bus_cycle - 1) & 31) | ((bus_timing & 31) << 5)); + writel(bus_cfg_val, nfc->reg_base + NFC_REG_CFG); + writel(BIT(31), nfc->reg_base + NFC_REG_CMD); +} + +static int meson_probe(struct udevice *dev) +{ + struct meson_nfc *nfc = dev_get_priv(dev); + void *addr; + int ret; + + addr = dev_read_addr_ptr(dev); + if (!addr) { + dev_err(dev, "base register address not found\n"); + return -EINVAL; + } + + nfc->reg_base = addr; + + addr = dev_read_addr_index_ptr(dev, 1); + if (!addr) { + dev_err(dev, "clk register address not found\n"); + return -EINVAL; + } + + nfc->reg_clk = addr; + nfc->dev = dev; + + meson_nfc_clk_init(nfc); + + ret = meson_nfc_nand_chips_init(dev, nfc); + if (ret) { + dev_err(nfc->dev, "failed to init chips\n"); + return ret; + } + + return 0; +} + +static const struct udevice_id meson_nand_dt_ids[] = { + {.compatible = "amlogic,meson-axg-nfc",}, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(meson_nand) = { + .name = "meson_nand", + .id = UCLASS_MTD, + .of_match = meson_nand_dt_ids, + .probe = meson_probe, + .priv_auto = sizeof(struct meson_nfc), +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MTD, + DM_DRIVER_GET(meson_nand), &dev); + + if (ret && ret != -ENODEV) + pr_err("Failed to initialize: %d\n", ret); +} diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index c40a0f23d7b..688d17ba3c2 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4118,7 +4118,7 @@ static int nand_get_bits_per_cell(u8 cellinfo) */ void nand_decode_ext_id(struct nand_chip *chip) { - struct mtd_info *mtd = &chip->mtd; + struct mtd_info *mtd = nand_to_mtd(chip); int extid; /* The 3rd id byte holds MLC / multichip data */ chip->bits_per_cell = nand_get_bits_per_cell(chip->id.data[2]); @@ -4185,7 +4185,7 @@ static int nand_manufacturer_init(struct nand_chip *chip) */ static void nand_decode_id(struct nand_chip *chip, struct nand_flash_dev *type) { - struct mtd_info *mtd = &chip->mtd; + struct mtd_info *mtd = nand_to_mtd(chip); mtd->erasesize = type->erasesize; mtd->writesize = type->pagesize; @@ -4265,7 +4265,7 @@ static const struct nand_manufacturer *nand_get_manufacturer_desc(u8 id) int nand_detect(struct nand_chip *chip, int *maf_id, int *dev_id, struct nand_flash_dev *type) { - struct mtd_info *mtd = &chip->mtd; + struct mtd_info *mtd = nand_to_mtd(chip); const struct nand_manufacturer *manufacturer_desc; int busw, ret; u8 *id_data = chip->id.data; diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index f172f4787f8..65b836b34ca 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 spinand-objs := core.o esmt.o gigadevice.o macronix.o micron.o paragon.o -spinand-objs += toshiba.o winbond.o +spinand-objs += toshiba.o winbond.o xtx.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 8ca33459f96..62c28aa422d 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -829,6 +829,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { &toshiba_spinand_manufacturer, &winbond_spinand_manufacturer, &esmt_c8_spinand_manufacturer, + &xtx_spinand_manufacturer, }; static int spinand_manufacturer_match(struct spinand_device *spinand, diff --git a/drivers/mtd/nand/spi/xtx.c b/drivers/mtd/nand/spi/xtx.c new file mode 100644 index 00000000000..aee1849a71f --- /dev/null +++ b/drivers/mtd/nand/spi/xtx.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: + * Felix Matouschek <felix@matouschek.org> + */ + +#include <linux/bitfield.h> +#ifndef __UBOOT__ +#include <linux/device.h> +#include <linux/kernel.h> +#endif +#include <linux/mtd/spinand.h> + +#define SPINAND_MFR_XTX 0x0B + +#define XT26G0XA_STATUS_ECC_MASK GENMASK(5, 2) +#define XT26G0XA_STATUS_ECC_NO_DETECTED (0 << 2) +#define XT26G0XA_STATUS_ECC_8_CORRECTED (3 << 4) +#define XT26G0XA_STATUS_ECC_UNCOR_ERROR (2 << 4) + +#define XT26XXXD_STATUS_ECC3_ECC2_MASK GENMASK(7, 6) +#define XT26XXXD_STATUS_ECC_NO_DETECTED (0) +#define XT26XXXD_STATUS_ECC_1_7_CORRECTED (1) +#define XT26XXXD_STATUS_ECC_8_CORRECTED (3) +#define XT26XXXD_STATUS_ECC_UNCOR_ERROR (2) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 48; + region->length = 16; + + return 0; +} + +static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 1; + region->length = 47; + + return 0; +} + +static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = { + .ecc = xt26g0xa_ooblayout_ecc, + .rfree = xt26g0xa_ooblayout_free, +}; + +static int xt26g0xa_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + status = status & XT26G0XA_STATUS_ECC_MASK; + + switch (status) { + case XT26G0XA_STATUS_ECC_NO_DETECTED: + return 0; + case XT26G0XA_STATUS_ECC_8_CORRECTED: + return 8; + case XT26G0XA_STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + default: + break; + } + + /* At this point values greater than (2 << 4) are invalid */ + if (status > XT26G0XA_STATUS_ECC_UNCOR_ERROR) + return -EINVAL; + + /* (1 << 2) through (7 << 2) are 1-7 corrected errors */ + return status >> 2; +} + +static int xt26xxxd_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = mtd->oobsize / 2; + region->length = mtd->oobsize / 2; + + return 0; +} + +static int xt26xxxd_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 2; + region->length = mtd->oobsize / 2 - 2; + + return 0; +} + +static const struct mtd_ooblayout_ops xt26xxxd_ooblayout = { + .ecc = xt26xxxd_ooblayout_ecc, + .rfree = xt26xxxd_ooblayout_free, +}; + +static int xt26xxxd_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (FIELD_GET(STATUS_ECC_MASK, status)) { + case XT26XXXD_STATUS_ECC_NO_DETECTED: + return 0; + case XT26XXXD_STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + case XT26XXXD_STATUS_ECC_1_7_CORRECTED: + return 4 + FIELD_GET(XT26XXXD_STATUS_ECC3_ECC2_MASK, status); + case XT26XXXD_STATUS_ECC_8_CORRECTED: + return 8; + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info xtx_spinand_table[] = { + SPINAND_INFO("XT26G01A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&xt26g0xa_ooblayout, + xt26g0xa_ecc_get_status)), + SPINAND_INFO("XT26G02A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE2), + NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&xt26g0xa_ooblayout, + xt26g0xa_ecc_get_status)), + SPINAND_INFO("XT26G04A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE3), + NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&xt26g0xa_ooblayout, + xt26g0xa_ecc_get_status)), + SPINAND_INFO("XT26G01D", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x31), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&xt26xxxd_ooblayout, + xt26xxxd_ecc_get_status)), + SPINAND_INFO("XT26G11D", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x34), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&xt26xxxd_ooblayout, + xt26xxxd_ecc_get_status)), + SPINAND_INFO("XT26Q01D", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&xt26xxxd_ooblayout, + xt26xxxd_ecc_get_status)), + SPINAND_INFO("XT26G02D", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x32), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&xt26xxxd_ooblayout, + xt26xxxd_ecc_get_status)), + SPINAND_INFO("XT26G12D", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x35), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&xt26xxxd_ooblayout, + xt26xxxd_ecc_get_status)), + SPINAND_INFO("XT26Q02D", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x52), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&xt26xxxd_ooblayout, + xt26xxxd_ecc_get_status)), + SPINAND_INFO("XT26G04D", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x33), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&xt26xxxd_ooblayout, + xt26xxxd_ecc_get_status)), + SPINAND_INFO("XT26Q04D", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x53), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&xt26xxxd_ooblayout, + xt26xxxd_ecc_get_status)), +}; + +static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = { +}; + +const struct spinand_manufacturer xtx_spinand_manufacturer = { + .id = SPINAND_MFR_XTX, + .name = "XTX", + .chips = xtx_spinand_table, + .nchips = ARRAY_SIZE(xtx_spinand_table), + .ops = &xtx_spinand_manuf_ops, +}; diff --git a/drivers/net/designware.c b/drivers/net/designware.c index c222197b114..4c1642b29a8 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -352,6 +352,11 @@ static int dw_adjust_link(struct dw_eth_dev *priv, struct eth_mac_regs *mac_p, (phydev->duplex) ? "full" : "half", (phydev->port == PORT_FIBRE) ? ", fiber mode" : ""); +#ifdef CONFIG_ARCH_NPCM8XX + /* Pass all Multicast Frames */ + setbits_le32(&mac_p->framefilt, BIT(4)); + +#endif return 0; } @@ -554,6 +559,11 @@ static int _dw_free_pkt(struct dw_eth_dev *priv) ulong desc_start = (ulong)desc_p; ulong desc_end = desc_start + roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN); + ulong data_start = desc_p->dmamac_addr; + ulong data_end = data_start + roundup(CFG_ETH_BUFSIZE, ARCH_DMA_MINALIGN); + + /* Invalidate the descriptor buffer data */ + invalidate_dcache_range(data_start, data_end); /* * Make the current descriptor valid again and go to diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 9b3bce1dc87..67d80d987ff 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -159,7 +159,7 @@ static int eqos_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad, ret = eqos_mdio_wait_idle(eqos); if (ret) { - pr_err("MDIO not idle at entry"); + pr_err("MDIO not idle at entry\n"); return ret; } @@ -179,7 +179,7 @@ static int eqos_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad, ret = eqos_mdio_wait_idle(eqos); if (ret) { - pr_err("MDIO read didn't complete"); + pr_err("MDIO read didn't complete\n"); return ret; } @@ -203,7 +203,7 @@ static int eqos_mdio_write(struct mii_dev *bus, int mdio_addr, int mdio_devad, ret = eqos_mdio_wait_idle(eqos); if (ret) { - pr_err("MDIO not idle at entry"); + pr_err("MDIO not idle at entry\n"); return ret; } @@ -225,7 +225,7 @@ static int eqos_mdio_write(struct mii_dev *bus, int mdio_addr, int mdio_devad, ret = eqos_mdio_wait_idle(eqos); if (ret) { - pr_err("MDIO read didn't complete"); + pr_err("MDIO read didn't complete\n"); return ret; } @@ -242,37 +242,37 @@ static int eqos_start_clks_tegra186(struct udevice *dev) ret = clk_enable(&eqos->clk_slave_bus); if (ret < 0) { - pr_err("clk_enable(clk_slave_bus) failed: %d", ret); + pr_err("clk_enable(clk_slave_bus) failed: %d\n", ret); goto err; } ret = clk_enable(&eqos->clk_master_bus); if (ret < 0) { - pr_err("clk_enable(clk_master_bus) failed: %d", ret); + pr_err("clk_enable(clk_master_bus) failed: %d\n", ret); goto err_disable_clk_slave_bus; } ret = clk_enable(&eqos->clk_rx); if (ret < 0) { - pr_err("clk_enable(clk_rx) failed: %d", ret); + pr_err("clk_enable(clk_rx) failed: %d\n", ret); goto err_disable_clk_master_bus; } ret = clk_enable(&eqos->clk_ptp_ref); if (ret < 0) { - pr_err("clk_enable(clk_ptp_ref) failed: %d", ret); + pr_err("clk_enable(clk_ptp_ref) failed: %d\n", ret); goto err_disable_clk_rx; } ret = clk_set_rate(&eqos->clk_ptp_ref, 125 * 1000 * 1000); if (ret < 0) { - pr_err("clk_set_rate(clk_ptp_ref) failed: %d", ret); + pr_err("clk_set_rate(clk_ptp_ref) failed: %d\n", ret); goto err_disable_clk_ptp_ref; } ret = clk_enable(&eqos->clk_tx); if (ret < 0) { - pr_err("clk_enable(clk_tx) failed: %d", ret); + pr_err("clk_enable(clk_tx) failed: %d\n", ret); goto err_disable_clk_ptp_ref; } #endif @@ -305,26 +305,26 @@ static int eqos_start_clks_stm32(struct udevice *dev) ret = clk_enable(&eqos->clk_master_bus); if (ret < 0) { - pr_err("clk_enable(clk_master_bus) failed: %d", ret); + pr_err("clk_enable(clk_master_bus) failed: %d\n", ret); goto err; } ret = clk_enable(&eqos->clk_rx); if (ret < 0) { - pr_err("clk_enable(clk_rx) failed: %d", ret); + pr_err("clk_enable(clk_rx) failed: %d\n", ret); goto err_disable_clk_master_bus; } ret = clk_enable(&eqos->clk_tx); if (ret < 0) { - pr_err("clk_enable(clk_tx) failed: %d", ret); + pr_err("clk_enable(clk_tx) failed: %d\n", ret); goto err_disable_clk_rx; } if (clk_valid(&eqos->clk_ck) && !eqos->clk_ck_enabled) { ret = clk_enable(&eqos->clk_ck); if (ret < 0) { - pr_err("clk_enable(clk_ck) failed: %d", ret); + pr_err("clk_enable(clk_ck) failed: %d\n", ret); goto err_disable_clk_tx; } eqos->clk_ck_enabled = true; @@ -390,7 +390,7 @@ static int eqos_start_resets_tegra186(struct udevice *dev) ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 1); if (ret < 0) { - pr_err("dm_gpio_set_value(phy_reset, assert) failed: %d", ret); + pr_err("dm_gpio_set_value(phy_reset, assert) failed: %d\n", ret); return ret; } @@ -398,13 +398,13 @@ static int eqos_start_resets_tegra186(struct udevice *dev) ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0); if (ret < 0) { - pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d", ret); + pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d\n", ret); return ret; } ret = reset_assert(&eqos->reset_ctl); if (ret < 0) { - pr_err("reset_assert() failed: %d", ret); + pr_err("reset_assert() failed: %d\n", ret); return ret; } @@ -412,7 +412,7 @@ static int eqos_start_resets_tegra186(struct udevice *dev) ret = reset_deassert(&eqos->reset_ctl); if (ret < 0) { - pr_err("reset_deassert() failed: %d", ret); + pr_err("reset_deassert() failed: %d\n", ret); return ret; } @@ -448,14 +448,14 @@ static int eqos_calibrate_pads_tegra186(struct udevice *dev) ret = wait_for_bit_le32(&eqos->tegra186_regs->auto_cal_status, EQOS_AUTO_CAL_STATUS_ACTIVE, true, 10, false); if (ret) { - pr_err("calibrate didn't start"); + pr_err("calibrate didn't start\n"); goto failed; } ret = wait_for_bit_le32(&eqos->tegra186_regs->auto_cal_status, EQOS_AUTO_CAL_STATUS_ACTIVE, false, 10, false); if (ret) { - pr_err("calibrate didn't finish"); + pr_err("calibrate didn't finish\n"); goto failed; } @@ -586,13 +586,13 @@ static int eqos_set_tx_clk_speed_tegra186(struct udevice *dev) rate = 2.5 * 1000 * 1000; break; default: - pr_err("invalid speed %d", eqos->phy->speed); + pr_err("invalid speed %d\n", eqos->phy->speed); return -EINVAL; } ret = clk_set_rate(&eqos->clk_tx, rate); if (ret < 0) { - pr_err("clk_set_rate(tx_clk, %lu) failed: %d", rate, ret); + pr_err("clk_set_rate(tx_clk, %lu) failed: %d\n", rate, ret); return ret; } #endif @@ -613,7 +613,7 @@ static int eqos_adjust_link(struct udevice *dev) else ret = eqos_set_half_duplex(dev); if (ret < 0) { - pr_err("eqos_set_*_duplex() failed: %d", ret); + pr_err("eqos_set_*_duplex() failed: %d\n", ret); return ret; } @@ -631,32 +631,32 @@ static int eqos_adjust_link(struct udevice *dev) ret = eqos_set_mii_speed_10(dev); break; default: - pr_err("invalid speed %d", eqos->phy->speed); + pr_err("invalid speed %d\n", eqos->phy->speed); return -EINVAL; } if (ret < 0) { - pr_err("eqos_set_*mii_speed*() failed: %d", ret); + pr_err("eqos_set_*mii_speed*() failed: %d\n", ret); return ret; } if (en_calibration) { ret = eqos->config->ops->eqos_calibrate_pads(dev); if (ret < 0) { - pr_err("eqos_calibrate_pads() failed: %d", + pr_err("eqos_calibrate_pads() failed: %d\n", ret); return ret; } } else { ret = eqos->config->ops->eqos_disable_calibration(dev); if (ret < 0) { - pr_err("eqos_disable_calibration() failed: %d", + pr_err("eqos_disable_calibration() failed: %d\n", ret); return ret; } } ret = eqos->config->ops->eqos_set_tx_clk_speed(dev); if (ret < 0) { - pr_err("eqos_set_tx_clk_speed() failed: %d", ret); + pr_err("eqos_set_tx_clk_speed() failed: %d\n", ret); return ret; } @@ -755,7 +755,7 @@ static int eqos_start(struct udevice *dev) ret = eqos->config->ops->eqos_start_resets(dev); if (ret < 0) { - pr_err("eqos_start_resets() failed: %d", ret); + pr_err("eqos_start_resets() failed: %d\n", ret); goto err; } @@ -773,13 +773,13 @@ static int eqos_start(struct udevice *dev) EQOS_DMA_MODE_SWR, false, eqos->config->swr_wait, false); if (ret) { - pr_err("EQOS_DMA_MODE_SWR stuck"); + pr_err("EQOS_DMA_MODE_SWR stuck\n"); goto err_stop_resets; } ret = eqos->config->ops->eqos_calibrate_pads(dev); if (ret < 0) { - pr_err("eqos_calibrate_pads() failed: %d", ret); + pr_err("eqos_calibrate_pads() failed: %d\n", ret); goto err_stop_resets; } @@ -812,7 +812,7 @@ static int eqos_start(struct udevice *dev) } if (!eqos->phy) { - pr_err("phy_connect() failed"); + pr_err("phy_connect() failed\n"); ret = -ENODEV; goto err_stop_resets; } @@ -820,7 +820,7 @@ static int eqos_start(struct udevice *dev) if (eqos->max_speed) { ret = phy_set_supported(eqos->phy, eqos->max_speed); if (ret) { - pr_err("phy_set_supported() failed: %d", ret); + pr_err("phy_set_supported() failed: %d\n", ret); goto err_shutdown_phy; } } @@ -828,26 +828,26 @@ static int eqos_start(struct udevice *dev) eqos->phy->node = eqos->phy_of_node; ret = phy_config(eqos->phy); if (ret < 0) { - pr_err("phy_config() failed: %d", ret); + pr_err("phy_config() failed: %d\n", ret); goto err_shutdown_phy; } } ret = phy_startup(eqos->phy); if (ret < 0) { - pr_err("phy_startup() failed: %d", ret); + pr_err("phy_startup() failed: %d\n", ret); goto err_shutdown_phy; } if (!eqos->phy->link) { - pr_err("No link"); + pr_err("No link\n"); ret = -EAGAIN; goto err_shutdown_phy; } ret = eqos_adjust_link(dev); if (ret < 0) { - pr_err("eqos_adjust_link() failed: %d", ret); + pr_err("eqos_adjust_link() failed: %d\n", ret); goto err_shutdown_phy; } @@ -1090,7 +1090,7 @@ err_shutdown_phy: err_stop_resets: eqos->config->ops->eqos_stop_resets(dev); err: - pr_err("FAILED: %d", ret); + pr_err("FAILED: %d\n", ret); return ret; } @@ -1217,7 +1217,7 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length) struct eqos_priv *eqos = dev_get_priv(dev); u32 idx, idx_mask = eqos->desc_per_cacheline - 1; uchar *packet_expected; - struct eqos_desc *rx_desc; + struct eqos_desc *rx_desc = NULL; debug("%s(packet=%p, length=%d)\n", __func__, packet, length); @@ -1361,7 +1361,7 @@ static int eqos_probe_resources_tegra186(struct udevice *dev) ret = reset_get_by_name(dev, "eqos", &eqos->reset_ctl); if (ret) { - pr_err("reset_get_by_name(rst) failed: %d", ret); + pr_err("reset_get_by_name(rst) failed: %d\n", ret); return ret; } @@ -1369,37 +1369,37 @@ static int eqos_probe_resources_tegra186(struct udevice *dev) &eqos->phy_reset_gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); if (ret) { - pr_err("gpio_request_by_name(phy reset) failed: %d", ret); + pr_err("gpio_request_by_name(phy reset) failed: %d\n", ret); goto err_free_reset_eqos; } ret = clk_get_by_name(dev, "slave_bus", &eqos->clk_slave_bus); if (ret) { - pr_err("clk_get_by_name(slave_bus) failed: %d", ret); + pr_err("clk_get_by_name(slave_bus) failed: %d\n", ret); goto err_free_gpio_phy_reset; } ret = clk_get_by_name(dev, "master_bus", &eqos->clk_master_bus); if (ret) { - pr_err("clk_get_by_name(master_bus) failed: %d", ret); + pr_err("clk_get_by_name(master_bus) failed: %d\n", ret); goto err_free_gpio_phy_reset; } ret = clk_get_by_name(dev, "rx", &eqos->clk_rx); if (ret) { - pr_err("clk_get_by_name(rx) failed: %d", ret); + pr_err("clk_get_by_name(rx) failed: %d\n", ret); goto err_free_gpio_phy_reset; } ret = clk_get_by_name(dev, "ptp_ref", &eqos->clk_ptp_ref); if (ret) { - pr_err("clk_get_by_name(ptp_ref) failed: %d", ret); + pr_err("clk_get_by_name(ptp_ref) failed: %d\n", ret); goto err_free_gpio_phy_reset; } ret = clk_get_by_name(dev, "tx", &eqos->clk_tx); if (ret) { - pr_err("clk_get_by_name(tx) failed: %d", ret); + pr_err("clk_get_by_name(tx) failed: %d\n", ret); goto err_free_gpio_phy_reset; } @@ -1436,19 +1436,19 @@ static int eqos_probe_resources_stm32(struct udevice *dev) ret = clk_get_by_name(dev, "stmmaceth", &eqos->clk_master_bus); if (ret) { - pr_err("clk_get_by_name(master_bus) failed: %d", ret); + pr_err("clk_get_by_name(master_bus) failed: %d\n", ret); goto err_probe; } ret = clk_get_by_name(dev, "mac-clk-rx", &eqos->clk_rx); if (ret) { - pr_err("clk_get_by_name(rx) failed: %d", ret); + pr_err("clk_get_by_name(rx) failed: %d\n", ret); goto err_probe; } ret = clk_get_by_name(dev, "mac-clk-tx", &eqos->clk_tx); if (ret) { - pr_err("clk_get_by_name(tx) failed: %d", ret); + pr_err("clk_get_by_name(tx) failed: %d\n", ret); goto err_probe; } @@ -1502,7 +1502,7 @@ static int eqos_probe(struct udevice *dev) eqos->regs = dev_read_addr(dev); if (eqos->regs == FDT_ADDR_T_NONE) { - pr_err("dev_read_addr() failed"); + pr_err("dev_read_addr() failed\n"); return -ENODEV; } eqos->mac_regs = (void *)(eqos->regs + EQOS_MAC_REGS_BASE); @@ -1514,19 +1514,19 @@ static int eqos_probe(struct udevice *dev) ret = eqos_probe_resources_core(dev); if (ret < 0) { - pr_err("eqos_probe_resources_core() failed: %d", ret); + pr_err("eqos_probe_resources_core() failed: %d\n", ret); return ret; } ret = eqos->config->ops->eqos_probe_resources(dev); if (ret < 0) { - pr_err("eqos_probe_resources() failed: %d", ret); + pr_err("eqos_probe_resources() failed: %d\n", ret); goto err_remove_resources_core; } ret = eqos->config->ops->eqos_start_clks(dev); if (ret < 0) { - pr_err("eqos_start_clks() failed: %d", ret); + pr_err("eqos_start_clks() failed: %d\n", ret); goto err_remove_resources_tegra; } @@ -1536,7 +1536,7 @@ static int eqos_probe(struct udevice *dev) if (!eqos->mii) { eqos->mii = mdio_alloc(); if (!eqos->mii) { - pr_err("mdio_alloc() failed"); + pr_err("mdio_alloc() failed\n"); ret = -ENOMEM; goto err_stop_clks; } @@ -1547,7 +1547,7 @@ static int eqos_probe(struct udevice *dev) ret = mdio_register(eqos->mii); if (ret < 0) { - pr_err("mdio_register() failed: %d", ret); + pr_err("mdio_register() failed: %d\n", ret); goto err_free_mdio; } } diff --git a/drivers/net/ti/Kconfig b/drivers/net/ti/Kconfig index 72eccc99e5f..ddfa95a0b7e 100644 --- a/drivers/net/ti/Kconfig +++ b/drivers/net/ti/Kconfig @@ -57,3 +57,16 @@ config MDIO_TI_CPSW help This driver supports the TI CPSW MDIO interface found in various TI SoCs. + +config TI_ICSSG_PRUETH + bool "TI Gigabit PRU Ethernet driver" + depends on ARCH_K3 + imply DM_MDIO + imply MISC_INIT_R + imply MISC + imply MDIO_TI_CPSW + select PHYLIB + select FS_LOADER + help + Support Gigabit Ethernet ports over the ICSSG PRU Subsystem + This subsystem is available starting with the AM65 platform. diff --git a/drivers/net/ti/Makefile b/drivers/net/ti/Makefile index 30c4c4b6d5a..b2b3aa3b180 100644 --- a/drivers/net/ti/Makefile +++ b/drivers/net/ti/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_net.o cpsw_mdio.o obj-$(CONFIG_TI_AM65_CPSW_NUSS) += am65-cpsw-nuss.o obj-$(CONFIG_MDIO_TI_CPSW) += cpsw_mdio.o +obj-$(CONFIG_TI_ICSSG_PRUETH) += icssg_prueth.o icssg_classifier.o icssg_config.o icssg_queues.o diff --git a/drivers/net/ti/am65-cpsw-nuss.c b/drivers/net/ti/am65-cpsw-nuss.c index d68ed671836..b151e25d6a4 100644 --- a/drivers/net/ti/am65-cpsw-nuss.c +++ b/drivers/net/ti/am65-cpsw-nuss.c @@ -664,7 +664,7 @@ static int am65_cpsw_port_probe(struct udevice *dev) struct am65_cpsw_priv *priv = dev_get_priv(dev); struct eth_pdata *pdata = dev_get_plat(dev); struct am65_cpsw_common *cpsw_common; - char portname[15]; + char portname[32]; int ret; priv->dev = dev; @@ -672,7 +672,7 @@ static int am65_cpsw_port_probe(struct udevice *dev) cpsw_common = dev_get_priv(dev->parent); priv->cpsw_common = cpsw_common; - sprintf(portname, "%s%s", dev->parent->name, dev->name); + snprintf(portname, sizeof(portname), "%s%s", dev->parent->name, dev->name); device_set_name(dev, portname); ret = am65_cpsw_ofdata_parse_phy(dev); diff --git a/drivers/net/ti/icss_mii_rt.h b/drivers/net/ti/icss_mii_rt.h new file mode 100644 index 00000000000..fd95d4d7c1f --- /dev/null +++ b/drivers/net/ti/icss_mii_rt.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* PRU-ICSS MII_RT register definitions + * + * Copyright (C) 2015-2024 Texas Instruments Incorporated - https://www.ti.com + */ + +#ifndef __NET_PRUSS_MII_RT_H__ +#define __NET_PRUSS_MII_RT_H__ + +#include <regmap.h> + +/* PRUSS_MII_RT Registers */ +#define PRUSS_MII_RT_RXCFG0 0x0 +#define PRUSS_MII_RT_RXCFG1 0x4 +#define PRUSS_MII_RT_TXCFG0 0x10 +#define PRUSS_MII_RT_TXCFG1 0x14 +#define PRUSS_MII_RT_TX_CRC0 0x20 +#define PRUSS_MII_RT_TX_CRC1 0x24 +#define PRUSS_MII_RT_TX_IPG0 0x30 +#define PRUSS_MII_RT_TX_IPG1 0x34 +#define PRUSS_MII_RT_PRS0 0x38 +#define PRUSS_MII_RT_PRS1 0x3c +#define PRUSS_MII_RT_RX_FRMS0 0x40 +#define PRUSS_MII_RT_RX_FRMS1 0x44 +#define PRUSS_MII_RT_RX_PCNT0 0x48 +#define PRUSS_MII_RT_RX_PCNT1 0x4c +#define PRUSS_MII_RT_RX_ERR0 0x50 +#define PRUSS_MII_RT_RX_ERR1 0x54 + +/* PRUSS_MII_RT_RXCFG0/1 bits */ +#define PRUSS_MII_RT_RXCFG_RX_ENABLE BIT(0) +#define PRUSS_MII_RT_RXCFG_RX_DATA_RDY_MODE_DIS BIT(1) +#define PRUSS_MII_RT_RXCFG_RX_CUT_PREAMBLE BIT(2) +#define PRUSS_MII_RT_RXCFG_RX_MUX_SEL BIT(3) +#define PRUSS_MII_RT_RXCFG_RX_L2_EN BIT(4) +#define PRUSS_MII_RT_RXCFG_RX_BYTE_SWAP BIT(5) +#define PRUSS_MII_RT_RXCFG_RX_AUTO_FWD_PRE BIT(6) +#define PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS BIT(9) + +/* PRUSS_MII_RT_TXCFG0/1 bits */ +#define PRUSS_MII_RT_TXCFG_TX_ENABLE BIT(0) +#define PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE BIT(1) +#define PRUSS_MII_RT_TXCFG_TX_EN_MODE BIT(2) +#define PRUSS_MII_RT_TXCFG_TX_BYTE_SWAP BIT(3) +#define PRUSS_MII_RT_TXCFG_TX_MUX_SEL BIT(8) +#define PRUSS_MII_RT_TXCFG_PRE_TX_AUTO_SEQUENCE BIT(9) +#define PRUSS_MII_RT_TXCFG_PRE_TX_AUTO_ESC_ERR BIT(10) +#define PRUSS_MII_RT_TXCFG_TX_32_MODE_EN BIT(11) +#define PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN BIT(12) /* SR2.0 onwards */ + +#define PRUSS_MII_RT_TXCFG_TX_START_DELAY_SHIFT 16 +#define PRUSS_MII_RT_TXCFG_TX_START_DELAY_MASK GENMASK(25, 16) + +#define PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT 28 +#define PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_MASK GENMASK(30, 28) + +/* PRUSS_MII_RT_TX_IPG0/1 bits */ +#define PRUSS_MII_RT_TX_IPG_IPG_SHIFT 0 +#define PRUSS_MII_RT_TX_IPG_IPG_MASK GENMASK(9, 0) + +/* PRUSS_MII_RT_PRS0/1 bits */ +#define PRUSS_MII_RT_PRS_COL BIT(0) +#define PRUSS_MII_RT_PRS_CRS BIT(1) + +/* PRUSS_MII_RT_RX_FRMS0/1 bits */ +#define PRUSS_MII_RT_RX_FRMS_MIN_FRM_SHIFT 0 +#define PRUSS_MII_RT_RX_FRMS_MIN_FRM_MASK GENMASK(15, 0) + +#define PRUSS_MII_RT_RX_FRMS_MAX_FRM_SHIFT 16 +#define PRUSS_MII_RT_RX_FRMS_MAX_FRM_MASK GENMASK(31, 16) + +/* Min/Max in MII_RT_RX_FRMS */ +/* For EMAC and Switch */ +#define PRUSS_MII_RT_RX_FRMS_MAX (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN) +#define PRUSS_MII_RT_RX_FRMS_MIN_FRM (64) + +/* for HSR and PRP */ +#define PRUSS_MII_RT_RX_FRMS_MAX_FRM_LRE (PRUSS_MII_RT_RX_FRMS_MAX + \ + ICSS_LRE_TAG_RCT_SIZE) +/* PRUSS_MII_RT_RX_PCNT0/1 bits */ +#define PRUSS_MII_RT_RX_PCNT_MIN_PCNT_SHIFT 0 +#define PRUSS_MII_RT_RX_PCNT_MIN_PCNT_MASK GENMASK(3, 0) + +#define PRUSS_MII_RT_RX_PCNT_MAX_PCNT_SHIFT 4 +#define PRUSS_MII_RT_RX_PCNT_MAX_PCNT_MASK GENMASK(7, 4) + +/* PRUSS_MII_RT_RX_ERR0/1 bits */ +#define PRUSS_MII_RT_RX_ERR_MIN_PCNT_ERR BIT(0) +#define PRUSS_MII_RT_RX_ERR_MAX_PCNT_ERR BIT(1) +#define PRUSS_MII_RT_RX_ERR_MIN_FRM_ERR BIT(2) +#define PRUSS_MII_RT_RX_ERR_MAX_FRM_ERR BIT(3) + +#define ICSSG_CFG_OFFSET 0 +#define RGMII_CFG_OFFSET 4 + +/* Constant to choose between MII0 and MII1 */ +#define ICSS_MII0 0 +#define ICSS_MII1 1 + +/* ICSSG_CFG Register bits */ +#define ICSSG_CFG_SGMII_MODE BIT(16) +#define ICSSG_CFG_TX_PRU_EN BIT(11) +#define ICSSG_CFG_RX_SFD_TX_SOF_EN BIT(10) +#define ICSSG_CFG_RTU_PRU_PSI_SHARE_EN BIT(9) +#define ICSSG_CFG_IEP1_TX_EN BIT(8) +#define ICSSG_CFG_MII1_MODE GENMASK(6, 5) +#define ICSSG_CFG_MII1_MODE_SHIFT 5 +#define ICSSG_CFG_MII0_MODE GENMASK(4, 3) +#define ICSSG_CFG_MII0_MODE_SHIFT 3 +#define ICSSG_CFG_RX_L2_G_EN BIT(2) +#define ICSSG_CFG_TX_L2_EN BIT(1) +#define ICSSG_CFG_TX_L1_EN BIT(0) + +enum mii_mode { MII_MODE_MII = 0, MII_MODE_RGMII, MII_MODE_SGMII }; + +/* RGMII CFG Register bits */ +#define RGMII_CFG_INBAND_EN_MII0 BIT(16) +#define RGMII_CFG_GIG_EN_MII0 BIT(17) +#define RGMII_CFG_INBAND_EN_MII1 BIT(20) +#define RGMII_CFG_GIG_EN_MII1 BIT(21) +#define RGMII_CFG_FULL_DUPLEX_MII0 BIT(18) +#define RGMII_CFG_FULL_DUPLEX_MII1 BIT(22) +#define RGMII_CFG_SPEED_MII0 GENMASK(2, 1) +#define RGMII_CFG_SPEED_MII1 GENMASK(6, 5) +#define RGMII_CFG_SPEED_MII0_SHIFT 1 +#define RGMII_CFG_SPEED_MII1_SHIFT 5 +#define RGMII_CFG_FULLDUPLEX_MII0 BIT(3) +#define RGMII_CFG_FULLDUPLEX_MII1 BIT(7) +#define RGMII_CFG_FULLDUPLEX_MII0_SHIFT 3 +#define RGMII_CFG_FULLDUPLEX_MII1_SHIFT 7 +#define RGMII_CFG_SPEED_10M 0 +#define RGMII_CFG_SPEED_100M 1 +#define RGMII_CFG_SPEED_1G 2 + +static inline void icssg_mii_update_ipg(struct regmap *mii_rt, int mii, u32 ipg) +{ + u32 val; + + if (mii == ICSS_MII0) { + regmap_write(mii_rt, PRUSS_MII_RT_TX_IPG0, ipg); + } else { + /* Errata workaround: IEP1 is not read by h/w unless IEP0 is written */ + regmap_read(mii_rt, PRUSS_MII_RT_TX_IPG0, &val); + regmap_write(mii_rt, PRUSS_MII_RT_TX_IPG1, ipg); + regmap_write(mii_rt, PRUSS_MII_RT_TX_IPG0, val); + } +} + +static inline void icssg_update_rgmii_cfg(struct regmap *miig_rt, int speed, + bool full_duplex, int slice, struct prueth_priv *priv) +{ + u32 gig_en_mask, gig_val = 0, full_duplex_mask, full_duplex_val = 0; + u32 inband_en_mask, inband_val = 0; + + gig_en_mask = (slice == ICSS_MII0) ? RGMII_CFG_GIG_EN_MII0 : + RGMII_CFG_GIG_EN_MII1; + if (speed == SPEED_1000) + gig_val = gig_en_mask; + regmap_update_bits(miig_rt, RGMII_CFG_OFFSET, gig_en_mask, gig_val); + + inband_en_mask = (slice == ICSS_MII0) ? RGMII_CFG_INBAND_EN_MII0 : + RGMII_CFG_INBAND_EN_MII1; + if (speed == SPEED_10 && phy_interface_is_rgmii(priv->phydev)) + inband_val = inband_en_mask; + regmap_update_bits(miig_rt, RGMII_CFG_OFFSET, inband_en_mask, inband_val); + + full_duplex_mask = (slice == ICSS_MII0) ? RGMII_CFG_FULL_DUPLEX_MII0 : + RGMII_CFG_FULL_DUPLEX_MII1; + if (full_duplex) + full_duplex_val = full_duplex_mask; + regmap_update_bits(miig_rt, RGMII_CFG_OFFSET, full_duplex_mask, + full_duplex_val); +} + +static inline void icssg_miig_set_interface_mode(struct regmap *miig_rt, int mii, int phy_if) +{ + u32 val, mask, shift; + + mask = mii == ICSS_MII0 ? ICSSG_CFG_MII0_MODE : ICSSG_CFG_MII1_MODE; + shift = mii == ICSS_MII0 ? ICSSG_CFG_MII0_MODE_SHIFT : ICSSG_CFG_MII1_MODE_SHIFT; + + val = MII_MODE_RGMII; + if (phy_if == PHY_INTERFACE_MODE_MII) + val = MII_MODE_MII; + + val <<= shift; + regmap_update_bits(miig_rt, ICSSG_CFG_OFFSET, mask, val); + regmap_read(miig_rt, ICSSG_CFG_OFFSET, &val); +} + +#endif /* __NET_PRUSS_MII_RT_H__ */ diff --git a/drivers/net/ti/icssg_classifier.c b/drivers/net/ti/icssg_classifier.c new file mode 100644 index 00000000000..e510a1cd3e5 --- /dev/null +++ b/drivers/net/ti/icssg_classifier.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Texas Instruments ICSSG Ethernet Driver + * + * Copyright (C) 2018-2024 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#include <dm/ofnode.h> +#include <regmap.h> + +#define ICSSG_NUM_CLASSIFIERS 16 +#define ICSSG_NUM_FT1_SLOTS 8 +#define ICSSG_NUM_FT3_SLOTS 16 + +#define ICSSG_NUM_CLASSIFIERS_IN_USE 1 + +/* Filter 1 - FT1 */ +#define FT1_NUM_SLOTS 8 +#define FT1_SLOT_SIZE 0x10 /* bytes */ + +/* offsets from FT1 slot base i.e. slot 1 start */ +#define FT1_DA0 0x0 +#define FT1_DA1 0x4 +#define FT1_DA0_MASK 0x8 +#define FT1_DA1_MASK 0xc + +#define FT1_N_REG(slize, n, reg) (offs[slice].ft1_slot_base + FT1_SLOT_SIZE * (n) + (reg)) + +#define FT1_LEN_MASK GENMASK(19, 16) +#define FT1_LEN_SHIFT 16 +#define FT1_LEN(len) (((len) << FT1_LEN_SHIFT) & FT1_LEN_MASK) + +#define FT1_START_MASK GENMASK(14, 0) +#define FT1_START(start) ((start) & FT1_START_MASK) + +#define FT1_MATCH_SLOT(n) (GENMASK(23, 16) & (BIT(n) << 16)) + +enum ft1_cfg_type { + FT1_CFG_TYPE_DISABLED = 0, + FT1_CFG_TYPE_EQ, + FT1_CFG_TYPE_GT, + FT1_CFG_TYPE_LT, +}; + +#define FT1_CFG_SHIFT(n) (2 * (n)) +#define FT1_CFG_MASK(n) (0x3 << FT1_CFG_SHIFT((n))) + +/* Filter 3 - FT3 */ +#define FT3_NUM_SLOTS 16 +#define FT3_SLOT_SIZE 0x20 /* bytes */ + +/* offsets from FT3 slot n's base */ +#define FT3_START 0 +#define FT3_START_AUTO 0x4 +#define FT3_START_OFFSET 0x8 +#define FT3_JUMP_OFFSET 0xc +#define FT3_LEN 0x10 +#define FT3_CFG 0x14 +#define FT3_T 0x18 +#define FT3_T_MASK 0x1c + +#define FT3_N_REG(slize, n, reg) \ + (offs[slice].ft3_slot_base + FT3_SLOT_SIZE * (n) + (reg)) + +/* offsets from rx_class n's base */ +#define RX_CLASS_AND_EN 0 +#define RX_CLASS_OR_EN 0x4 + +#define RX_CLASS_NUM_SLOTS 16 +#define RX_CLASS_EN_SIZE 0x8 /* bytes */ + +#define RX_CLASS_N_REG(slice, n, reg) \ + (offs[slice].rx_class_base + RX_CLASS_EN_SIZE * (n) + (reg)) + +/* RX Class Gates */ +#define RX_CLASS_GATES_SIZE 0x4 /* bytes */ + +#define RX_CLASS_GATES_N_REG(slice, n) \ + (offs[slice].rx_class_gates_base + RX_CLASS_GATES_SIZE * (n)) + +#define RX_CLASS_GATES_ALLOW_MASK BIT(6) +#define RX_CLASS_GATES_RAW_MASK BIT(5) +#define RX_CLASS_GATES_PHASE_MASK BIT(4) + +/* RX Class traffic data matching bits */ +#define RX_CLASS_FT_UC BIT(31) +#define RX_CLASS_FT_MC BIT(30) +#define RX_CLASS_FT_BC BIT(29) +#define RX_CLASS_FT_FW BIT(28) +#define RX_CLASS_FT_RCV BIT(27) +#define RX_CLASS_FT_VLAN BIT(26) +#define RX_CLASS_FT_DA_P BIT(25) +#define RX_CLASS_FT_DA_I BIT(24) +#define RX_CLASS_FT_FT1_MATCH_MASK GENMASK(23, 16) +#define RX_CLASS_FT_FT1_MATCH_SHIFT 16 +#define RX_CLASS_FT_FT3_MATCH_MASK GENMASK(15, 0) +#define RX_CLASS_FT_FT3_MATCH_SHIFT 0 + +#define RX_CLASS_FT_FT1_MATCH(slot) \ + ((BIT(slot) << RX_CLASS_FT_FT1_MATCH_SHIFT) & \ + RX_CLASS_FT_FT1_MATCH_MASK) + +enum rx_class_sel_type { + RX_CLASS_SEL_TYPE_OR = 0, + RX_CLASS_SEL_TYPE_AND = 1, + RX_CLASS_SEL_TYPE_OR_AND_AND = 2, + RX_CLASS_SEL_TYPE_OR_OR_AND = 3, +}; + +#define FT1_CFG_SHIFT(n) (2 * (n)) +#define FT1_CFG_MASK(n) (0x3 << FT1_CFG_SHIFT((n))) + +#define RX_CLASS_SEL_SHIFT(n) (2 * (n)) +#define RX_CLASS_SEL_MASK(n) (0x3 << RX_CLASS_SEL_SHIFT((n))) + +#define ICSSG_CFG_OFFSET 0 +#define MAC_INTERFACE_0 0x18 +#define MAC_INTERFACE_1 0x1c + +#define ICSSG_CFG_RX_L2_G_EN BIT(2) + +/* these are register offsets per PRU */ +struct miig_rt_offsets { + u32 mac0; + u32 mac1; + u32 ft1_start_len; + u32 ft1_cfg; + u32 ft1_slot_base; + u32 ft3_slot_base; + u32 ft3_p_base; + u32 ft_rx_ptr; + u32 rx_class_base; + u32 rx_class_cfg1; + u32 rx_class_cfg2; + u32 rx_class_gates_base; + u32 rx_green; + u32 rx_rate_cfg_base; + u32 rx_rate_src_sel0; + u32 rx_rate_src_sel1; + u32 tx_rate_cfg_base; + u32 stat_base; + u32 tx_hsr_tag; + u32 tx_hsr_seq; + u32 tx_vlan_type; + u32 tx_vlan_ins; +}; + +static struct miig_rt_offsets offs[] = { + /* PRU0 */ + { + 0x8, + 0xc, + 0x80, + 0x84, + 0x88, + 0x108, + 0x308, + 0x408, + 0x40c, + 0x48c, + 0x490, + 0x494, + 0x4d4, + 0x4e4, + 0x504, + 0x508, + 0x50c, + 0x54c, + 0x63c, + 0x640, + 0x644, + 0x648, + }, + /* PRU1 */ + { + 0x10, + 0x14, + 0x64c, + 0x650, + 0x654, + 0x6d4, + 0x8d4, + 0x9d4, + 0x9d8, + 0xa58, + 0xa5c, + 0xa60, + 0xaa0, + 0xab0, + 0xad0, + 0xad4, + 0xad8, + 0xb18, + 0xc08, + 0xc0c, + 0xc10, + 0xc14, + }, +}; + +static inline u32 addr_to_da0(const u8 *addr) +{ + return (u32)(addr[0] | addr[1] << 8 | + addr[2] << 16 | addr[3] << 24); +}; + +static inline u32 addr_to_da1(const u8 *addr) +{ + return (u32)(addr[4] | addr[5] << 8); +}; + +static void rx_class_ft1_set_start_len(struct regmap *miig_rt, int slice, + u16 start, u8 len) +{ + u32 offset, val; + + offset = offs[slice].ft1_start_len; + val = FT1_LEN(len) | FT1_START(start); + regmap_write(miig_rt, offset, val); +} + +static void rx_class_ft1_set_da(struct regmap *miig_rt, int slice, + int n, const u8 *addr) +{ + u32 offset; + + offset = FT1_N_REG(slice, n, FT1_DA0); + regmap_write(miig_rt, offset, addr_to_da0(addr)); + offset = FT1_N_REG(slice, n, FT1_DA1); + regmap_write(miig_rt, offset, addr_to_da1(addr)); +} + +static void rx_class_ft1_set_da_mask(struct regmap *miig_rt, int slice, + int n, const u8 *addr) +{ + u32 offset; + + offset = FT1_N_REG(slice, n, FT1_DA0_MASK); + regmap_write(miig_rt, offset, addr_to_da0(addr)); + offset = FT1_N_REG(slice, n, FT1_DA1_MASK); + regmap_write(miig_rt, offset, addr_to_da1(addr)); +} + +static void rx_class_ft1_cfg_set_type(struct regmap *miig_rt, int slice, int n, + enum ft1_cfg_type type) +{ + u32 offset; + + offset = offs[slice].ft1_cfg; + regmap_update_bits(miig_rt, offset, FT1_CFG_MASK(n), + type << FT1_CFG_SHIFT(n)); +} + +static void rx_class_sel_set_type(struct regmap *miig_rt, int slice, int n, + enum rx_class_sel_type type) +{ + u32 offset; + + offset = offs[slice].rx_class_cfg1; + regmap_update_bits(miig_rt, offset, RX_CLASS_SEL_MASK(n), + type << RX_CLASS_SEL_SHIFT(n)); +} + +static void rx_class_set_and(struct regmap *miig_rt, int slice, int n, + u32 data) +{ + u32 offset; + + offset = RX_CLASS_N_REG(slice, n, RX_CLASS_AND_EN); + regmap_write(miig_rt, offset, data); +} + +static void rx_class_set_or(struct regmap *miig_rt, int slice, int n, + u32 data) +{ + u32 offset; + + offset = RX_CLASS_N_REG(slice, n, RX_CLASS_OR_EN); + regmap_write(miig_rt, offset, data); +} + +void icssg_class_set_host_mac_addr(struct regmap *miig_rt, u8 *mac) +{ + regmap_write(miig_rt, MAC_INTERFACE_0, addr_to_da0(mac)); + regmap_write(miig_rt, MAC_INTERFACE_1, addr_to_da1(mac)); +} + +void icssg_class_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac) +{ + regmap_write(miig_rt, offs[slice].mac0, addr_to_da0(mac)); + regmap_write(miig_rt, offs[slice].mac1, addr_to_da1(mac)); +} + +void icssg_class_disable_n(struct regmap *miig_rt, int slice, int n) +{ + u32 data, offset; + + /* AND_EN = 0 */ + rx_class_set_and(miig_rt, slice, n, 0); + /* OR_EN = 0 */ + rx_class_set_or(miig_rt, slice, n, 0); + + /* set CFG1 to OR */ + rx_class_sel_set_type(miig_rt, slice, n, RX_CLASS_SEL_TYPE_OR); + + /* configure gate */ + offset = RX_CLASS_GATES_N_REG(slice, n); + regmap_read(miig_rt, offset, &data); + /* clear class_raw so we go through filters */ + data &= ~RX_CLASS_GATES_RAW_MASK; + /* set allow and phase mask */ + data |= RX_CLASS_GATES_ALLOW_MASK | RX_CLASS_GATES_PHASE_MASK; + regmap_write(miig_rt, offset, data); +} + +/* disable all RX traffic */ +void icssg_class_disable(struct regmap *miig_rt, int slice) +{ + int n; + + /* Enable RX_L2_G */ + regmap_update_bits(miig_rt, ICSSG_CFG_OFFSET, ICSSG_CFG_RX_L2_G_EN, + ICSSG_CFG_RX_L2_G_EN); + + for (n = 0; n < ICSSG_NUM_CLASSIFIERS; n++) + icssg_class_disable_n(miig_rt, slice, n); + + /* FT1 Disabled */ + for (n = 0; n < ICSSG_NUM_FT1_SLOTS; n++) { + u8 addr[] = { 0, 0, 0, 0, 0, 0, }; + + rx_class_ft1_cfg_set_type(miig_rt, slice, n, + FT1_CFG_TYPE_DISABLED); + rx_class_ft1_set_da(miig_rt, slice, n, addr); + rx_class_ft1_set_da_mask(miig_rt, slice, n, addr); + } + + /* clear CFG2 */ + regmap_write(miig_rt, offs[slice].rx_class_cfg2, 0); +} + +void icssg_class_default(struct regmap *miig_rt, int slice, bool allmulti) +{ + u32 data; + + /* defaults */ + icssg_class_disable(miig_rt, slice); + + /* Setup Classifier */ + /* match on Broadcast or MAC_PRU address */ + data = RX_CLASS_FT_BC | RX_CLASS_FT_DA_P; + + /* multicast? */ + if (allmulti) + data |= RX_CLASS_FT_MC; + + rx_class_set_or(miig_rt, slice, 0, data); + + /* set CFG1 for OR_OR_AND for classifier */ + rx_class_sel_set_type(miig_rt, slice, 0, + RX_CLASS_SEL_TYPE_OR_OR_AND); + + /* clear CFG2 */ + regmap_write(miig_rt, offs[slice].rx_class_cfg2, 0); +} + +/* required for SR2 for SAV check */ +void icssg_ft1_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac_addr) +{ + u8 mask_addr[] = { 0, 0, 0, 0, 0, 0, }; + + rx_class_ft1_set_start_len(miig_rt, slice, 6, 6); + rx_class_ft1_set_da(miig_rt, slice, 0, mac_addr); + rx_class_ft1_set_da_mask(miig_rt, slice, 0, mask_addr); + rx_class_ft1_cfg_set_type(miig_rt, slice, 0, FT1_CFG_TYPE_EQ); +} diff --git a/drivers/net/ti/icssg_config.c b/drivers/net/ti/icssg_config.c new file mode 100644 index 00000000000..5f132d0525c --- /dev/null +++ b/drivers/net/ti/icssg_config.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 +/* ICSSG Ethernet driver + * + * Copyright (C) 2018-2024 Texas Instruments Incorporated - https://www.ti.com + */ + +#include <phy.h> +#include "icssg_prueth.h" +#include "icssg_switch_map.h" +#include "icss_mii_rt.h" +#include <dm/device_compat.h> +#include <linux/iopoll.h> + +/* TX IPG Values to be set for 100M and 1G link speeds. These values are + * in ocp_clk cycles. So need change if ocp_clk is changed for a specific + * h/w design. + */ + +/* SR2.0 IPG is in rgmii_clk (125MHz) clock cycles + 1 */ +#define MII_RT_TX_IPG_100M 0x17 +#define MII_RT_TX_IPG_1G 0xb + +#define ICSSG_QUEUES_MAX 64 +#define ICSSG_QUEUE_OFFSET 0xd00 +#define ICSSG_QUEUE_PEEK_OFFSET 0xe00 +#define ICSSG_QUEUE_CNT_OFFSET 0xe40 +#define ICSSG_QUEUE_RESET_OFFSET 0xf40 + +#define ICSSG_NUM_TX_QUEUES 8 + +#define RECYCLE_Q_SLICE0 16 +#define RECYCLE_Q_SLICE1 17 + +#define ICSSG_NUM_OTHER_QUEUES 5 /* port, host and special queues */ + +#define PORT_HI_Q_SLICE0 32 +#define PORT_LO_Q_SLICE0 33 +#define HOST_HI_Q_SLICE0 34 +#define HOST_LO_Q_SLICE0 35 +#define HOST_SPL_Q_SLICE0 40 /* Special Queue */ + +#define PORT_HI_Q_SLICE1 36 +#define PORT_LO_Q_SLICE1 37 +#define HOST_HI_Q_SLICE1 38 +#define HOST_LO_Q_SLICE1 39 +#define HOST_SPL_Q_SLICE1 41 /* Special Queue */ + +#define MII_RXCFG_DEFAULT (PRUSS_MII_RT_RXCFG_RX_ENABLE | \ + PRUSS_MII_RT_RXCFG_RX_DATA_RDY_MODE_DIS | \ + PRUSS_MII_RT_RXCFG_RX_L2_EN | \ + PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS) + +#define MII_TXCFG_DEFAULT (PRUSS_MII_RT_TXCFG_TX_ENABLE | \ + PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE | \ + PRUSS_MII_RT_TXCFG_TX_32_MODE_EN | \ + PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN) + +#define ICSSG_CFG_DEFAULT (ICSSG_CFG_TX_L1_EN | \ + ICSSG_CFG_TX_L2_EN | ICSSG_CFG_RX_L2_G_EN | \ + ICSSG_CFG_TX_PRU_EN | /* SR2.0 only */ \ + ICSSG_CFG_SGMII_MODE) + +#define FDB_GEN_CFG1 0x60 +#define SMEM_VLAN_OFFSET 8 +#define SMEM_VLAN_OFFSET_MASK GENMASK(25, 8) + +#define FDB_GEN_CFG2 0x64 +#define FDB_VLAN_EN BIT(6) +#define FDB_HOST_EN BIT(2) +#define FDB_PRU1_EN BIT(1) +#define FDB_PRU0_EN BIT(0) +#define FDB_EN_ALL (FDB_PRU0_EN | FDB_PRU1_EN | \ + FDB_HOST_EN | FDB_VLAN_EN) + +struct map { + int queue; + u32 pd_addr_start; + u32 flags; + bool special; +}; + +struct map hwq_map[2][ICSSG_NUM_OTHER_QUEUES] = { + { + { PORT_HI_Q_SLICE0, PORT_DESC0_HI, 0x200000, 0 }, + { PORT_LO_Q_SLICE0, PORT_DESC0_LO, 0, 0 }, + { HOST_HI_Q_SLICE0, HOST_DESC0_HI, 0x200000, 0 }, + { HOST_LO_Q_SLICE0, HOST_DESC0_LO, 0, 0 }, + { HOST_SPL_Q_SLICE0, HOST_SPPD0, 0x400000, 1 }, + }, + { + { PORT_HI_Q_SLICE1, PORT_DESC1_HI, 0xa00000, 0 }, + { PORT_LO_Q_SLICE1, PORT_DESC1_LO, 0x800000, 0 }, + { HOST_HI_Q_SLICE1, HOST_DESC1_HI, 0xa00000, 0 }, + { HOST_LO_Q_SLICE1, HOST_DESC1_LO, 0x800000, 0 }, + { HOST_SPL_Q_SLICE1, HOST_SPPD1, 0xc00000, 1 }, + }, +}; + +static void icssg_config_mii_init(struct prueth_priv *priv, int slice) +{ + struct prueth *prueth = priv->prueth; + struct regmap *mii_rt = prueth->mii_rt; + u32 txcfg_reg, pcnt_reg; + u32 txcfg; + + txcfg_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 : + PRUSS_MII_RT_TXCFG1; + pcnt_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 : + PRUSS_MII_RT_RX_PCNT1; + + txcfg = MII_TXCFG_DEFAULT; + + if (prueth->phy_interface == PHY_INTERFACE_MODE_MII && slice == ICSS_MII0) + txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; + else if (prueth->phy_interface != PHY_INTERFACE_MODE_MII && slice == ICSS_MII1) + txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; + + regmap_write(mii_rt, txcfg_reg, txcfg); + regmap_write(mii_rt, pcnt_reg, 0x1); +} + +static void icssg_miig_queues_init(struct prueth_priv *priv, int slice) +{ + struct prueth *prueth = priv->prueth; + void __iomem *smem = (void __iomem *)prueth->shram.pa; + struct regmap *miig_rt = prueth->miig_rt; + int queue = 0, i, j; + u8 pd[ICSSG_SPECIAL_PD_SIZE]; + u32 *pdword; + + /* reset hwqueues */ + if (slice) + queue = ICSSG_NUM_TX_QUEUES; + + for (i = 0; i < ICSSG_NUM_TX_QUEUES; i++) { + regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET, queue); + queue++; + } + + queue = slice ? RECYCLE_Q_SLICE1 : RECYCLE_Q_SLICE0; + regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET, queue); + + for (i = 0; i < ICSSG_NUM_OTHER_QUEUES; i++) { + regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET, + hwq_map[slice][i].queue); + } + + /* initialize packet descriptors in SMEM */ + /* push pakcet descriptors to hwqueues */ + + pdword = (u32 *)pd; + for (j = 0; j < ICSSG_NUM_OTHER_QUEUES; j++) { + struct map *mp; + int pd_size, num_pds; + u32 pdaddr; + + mp = &hwq_map[slice][j]; + if (mp->special) { + pd_size = ICSSG_SPECIAL_PD_SIZE; + num_pds = ICSSG_NUM_SPECIAL_PDS; + } else { + pd_size = ICSSG_NORMAL_PD_SIZE; + num_pds = ICSSG_NUM_NORMAL_PDS; + } + + for (i = 0; i < num_pds; i++) { + memset(pd, 0, pd_size); + + pdword[0] &= cpu_to_le32(ICSSG_FLAG_MASK); + pdword[0] |= cpu_to_le32(mp->flags); + pdaddr = mp->pd_addr_start + i * pd_size; + + memcpy_toio(smem + pdaddr, pd, pd_size); + queue = mp->queue; + regmap_write(miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue, + pdaddr); + } + } +} + +void icssg_config_ipg(struct prueth_priv *priv, int speed, int mii) +{ + struct prueth *prueth = priv->prueth; + + switch (speed) { + case SPEED_1000: + icssg_mii_update_ipg(prueth->mii_rt, mii, MII_RT_TX_IPG_1G); + break; + case SPEED_100: + icssg_mii_update_ipg(prueth->mii_rt, mii, MII_RT_TX_IPG_100M); + break; + default: + /* Other links speeds not supported */ + pr_err("Unsupported link speed\n"); + return; + } +} + +static void emac_r30_cmd_init(struct prueth_priv *priv) +{ + struct prueth *prueth = priv->prueth; + struct icssg_r30_cmd *p; + int i; + + p = (struct icssg_r30_cmd *)(prueth->dram[priv->port_id].pa + MGR_R30_CMD_OFFSET); + + for (i = 0; i < 4; i++) + writel(EMAC_NONE, &p->cmd[i]); +} + +static int emac_r30_is_done(struct prueth_priv *priv) +{ + struct prueth *prueth = priv->prueth; + const struct icssg_r30_cmd *p; + int i; + u32 cmd; + + p = (const struct icssg_r30_cmd *)(prueth->dram[priv->port_id].pa + MGR_R30_CMD_OFFSET); + + for (i = 0; i < 4; i++) { + cmd = readl(&p->cmd[i]); + if (cmd != EMAC_NONE) + return 0; + } + + return 1; +} + +static int prueth_emac_buffer_setup(struct prueth_priv *priv) +{ + struct prueth *prueth = priv->prueth; + struct icssg_buffer_pool_cfg *bpool_cfg; + struct icssg_rxq_ctx *rxq_ctx; + int slice = priv->port_id; + u32 addr; + int i; + + /* Layout to have 64KB aligned buffer pool + * |BPOOL0|BPOOL1|RX_CTX0|RX_CTX1| + */ + + addr = lower_32_bits(prueth->sram_pa); + if (slice) + addr += PRUETH_NUM_BUF_POOLS * PRUETH_EMAC_BUF_POOL_SIZE; + + if (addr % SZ_64K) { + dev_warn(prueth->dev, "buffer pool needs to be 64KB aligned\n"); + return -EINVAL; + } + + bpool_cfg = (struct icssg_buffer_pool_cfg *)(prueth->dram[priv->port_id].pa + BUFFER_POOL_0_ADDR_OFFSET); + /* workaround for f/w bug. bpool 0 needs to be initilalized */ + bpool_cfg[0].addr = cpu_to_le32(addr); + bpool_cfg[0].len = 0; + + for (i = PRUETH_EMAC_BUF_POOL_START; + i < (PRUETH_EMAC_BUF_POOL_START + PRUETH_NUM_BUF_POOLS); + i++) { + bpool_cfg[i].addr = cpu_to_le32(addr); + bpool_cfg[i].len = cpu_to_le32(PRUETH_EMAC_BUF_POOL_SIZE); + addr += PRUETH_EMAC_BUF_POOL_SIZE; + } + + if (!slice) + addr += PRUETH_NUM_BUF_POOLS * PRUETH_EMAC_BUF_POOL_SIZE; + else + addr += PRUETH_EMAC_RX_CTX_BUF_SIZE * 2; + + rxq_ctx = (struct icssg_rxq_ctx *)(prueth->dram[priv->port_id].pa + HOST_RX_Q_PRE_CONTEXT_OFFSET); + + for (i = 0; i < 3; i++) + rxq_ctx->start[i] = cpu_to_le32(addr); + + addr += PRUETH_EMAC_RX_CTX_BUF_SIZE; + rxq_ctx->end = cpu_to_le32(addr); + + /* Express RX buffer queue */ + rxq_ctx = (struct icssg_rxq_ctx *)(prueth->dram[priv->port_id].pa + HOST_RX_Q_EXP_CONTEXT_OFFSET); + for (i = 0; i < 3; i++) + rxq_ctx->start[i] = cpu_to_le32(addr); + + addr += PRUETH_EMAC_RX_CTX_BUF_SIZE; + rxq_ctx->end = cpu_to_le32(addr); + + return 0; +} + +static void icssg_init_emac_mode(struct prueth *prueth) +{ + u8 mac[6] = { 0 }; + + regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK, 0); + regmap_write(prueth->miig_rt, FDB_GEN_CFG2, 0); + /* Clear host MAC address */ + icssg_class_set_host_mac_addr(prueth->miig_rt, mac); +} + +int icssg_config(struct prueth_priv *priv) +{ + struct prueth *prueth = priv->prueth; + void *config = (void *)(prueth->dram[priv->port_id].pa + ICSSG_CONFIG_OFFSET); + u8 *cfg_byte_ptr = config; + struct icssg_flow_cfg *flow_cfg; + u32 mask; + int ret; + + int slice = priv->port_id; + + icssg_init_emac_mode(prueth); + + memset_io(config, 0, TAS_GATE_MASK_LIST0); + icssg_miig_queues_init(priv, slice); + + prueth->speed = SPEED_1000; + prueth->duplex = DUPLEX_FULL; + if (!phy_interface_is_rgmii(priv->phydev)) { + prueth->speed = SPEED_100; + prueth->duplex = DUPLEX_FULL; + } + + regmap_update_bits(prueth->miig_rt, ICSSG_CFG_OFFSET, + ICSSG_CFG_DEFAULT, ICSSG_CFG_DEFAULT); + icssg_miig_set_interface_mode(prueth->miig_rt, ICSS_MII0, prueth->phy_interface); + icssg_miig_set_interface_mode(prueth->miig_rt, ICSS_MII1, prueth->phy_interface); + icssg_config_mii_init(priv, slice); + + icssg_config_ipg(priv, SPEED_1000, slice); + icssg_update_rgmii_cfg(prueth->miig_rt, SPEED_1000, true, slice, priv); + + /* set GPI mode */ + pruss_cfg_gpimode(prueth->pruss, slice, PRUSS_GPI_MODE_MII); + + /* enable XFR shift for PRU and RTU */ + mask = PRUSS_SPP_XFER_SHIFT_EN | PRUSS_SPP_RTU_XFR_SHIFT_EN; + pruss_cfg_update(prueth->pruss, PRUSS_CFG_SPP, mask, mask); + + flow_cfg = config + PSI_L_REGULAR_FLOW_ID_BASE_OFFSET; + flow_cfg->rx_base_flow = prueth->dma_rx.id; + flow_cfg->mgm_base_flow = 0; + *(cfg_byte_ptr + SPL_PKT_DEFAULT_PRIORITY) = 0; + *(cfg_byte_ptr + QUEUE_NUM_UNTAGGED) = 0x0; + + ret = prueth_emac_buffer_setup(priv); + + if (ret) + return ret; + + emac_r30_cmd_init(priv); + return 0; +} + +/* commands to program ICSSG R30 registers */ +static struct icssg_r30_cmd emac_r32_bitmask[] = { + {{0xffff0004, 0xffff0100, 0xffff0004, EMAC_NONE}}, /* EMAC_PORT_DISABLE */ + {{0xfffb0040, 0xfeff0200, 0xfeff0200, EMAC_NONE}}, /* EMAC_PORT_BLOCK */ + {{0xffbb0000, 0xfcff0000, 0xdcfb0000, EMAC_NONE}}, /* EMAC_PORT_FORWARD */ + {{0xffbb0000, 0xfcff0000, 0xfcff2000, EMAC_NONE}}, /* EMAC_PORT_FORWARD_WO_LEARNING */ + {{0xffff0001, EMAC_NONE, EMAC_NONE, EMAC_NONE}}, /* ACCEPT ALL */ + {{0xfffe0002, EMAC_NONE, EMAC_NONE, EMAC_NONE}}, /* ACCEPT TAGGED */ + {{0xfffc0000, EMAC_NONE, EMAC_NONE, EMAC_NONE}}, /* ACCEPT UNTAGGED and PRIO */ + {{EMAC_NONE, 0xffff0020, EMAC_NONE, EMAC_NONE}}, /* TAS Trigger List change */ + {{EMAC_NONE, 0xdfff1000, EMAC_NONE, EMAC_NONE}}, /* TAS set state ENABLE*/ + {{EMAC_NONE, 0xefff2000, EMAC_NONE, EMAC_NONE}}, /* TAS set state RESET*/ + {{EMAC_NONE, 0xcfff0000, EMAC_NONE, EMAC_NONE}}, /* TAS set state DISABLE*/ + {{EMAC_NONE, EMAC_NONE, 0xffff0400, EMAC_NONE}}, /* UC flooding ENABLE*/ + {{EMAC_NONE, EMAC_NONE, 0xfbff0000, EMAC_NONE}}, /* UC flooding DISABLE*/ + {{EMAC_NONE, EMAC_NONE, 0xffff0800, EMAC_NONE}}, /* MC flooding ENABLE*/ + {{EMAC_NONE, EMAC_NONE, 0xf7ff0000, EMAC_NONE}}, /* MC flooding DISABLE*/ + {{EMAC_NONE, 0xffff4000, EMAC_NONE, EMAC_NONE}}, /* Preemption on Tx ENABLE*/ + {{EMAC_NONE, 0xbfff0000, EMAC_NONE, EMAC_NONE}} /* Preemption on Tx DISABLE*/ +}; + +int emac_set_port_state(struct prueth_priv *priv, + enum icssg_port_state_cmd cmd) +{ + struct prueth *prueth = priv->prueth; + struct icssg_r30_cmd *p; + int ret = -ETIMEDOUT; + int timeout = 10; + int i; + + p = (struct icssg_r30_cmd *)(prueth->dram[priv->port_id].pa + MGR_R30_CMD_OFFSET); + + if (cmd >= ICSSG_EMAC_PORT_MAX_COMMANDS) { + dev_err(prueth->dev, "invalid port command\n"); + return -EINVAL; + } + + for (i = 0; i < 4; i++) + writel(emac_r32_bitmask[cmd].cmd[i], &p->cmd[i]); + + /* wait for done */ + while (timeout) { + if (emac_r30_is_done(priv)) { + ret = 0; + break; + } + + udelay(2000); + timeout--; + } + + if (ret == -ETIMEDOUT) + dev_err(prueth->dev, "timeout waiting for command done\n"); + + return ret; +} + +int icssg_send_fdb_msg(struct prueth_priv *priv, struct mgmt_cmd *cmd, + struct mgmt_cmd_rsp *rsp) +{ + struct prueth *prueth = priv->prueth; + int slice = priv->port_id; + int ret, addr; + + addr = icssg_queue_pop(prueth, slice == 0 ? + ICSSG_CMD_POP_SLICE0 : ICSSG_CMD_POP_SLICE1); + if (addr < 0) + return addr; + + /* First 4 bytes have FW owned buffer linking info which should + * not be touched + */ + memcpy_toio((void __iomem *)prueth->shram.pa + addr + 4, cmd, sizeof(*cmd)); + icssg_queue_push(prueth, slice == 0 ? + ICSSG_CMD_PUSH_SLICE0 : ICSSG_CMD_PUSH_SLICE1, addr); + ret = read_poll_timeout(icssg_queue_pop, addr, addr >= 0, + 2000, 20000000, prueth, slice == 0 ? + ICSSG_RSP_POP_SLICE0 : ICSSG_RSP_POP_SLICE1); + + if (ret) { + dev_err(prueth->dev, "Timedout sending HWQ message\n"); + return ret; + } + + memcpy_fromio(rsp, (void __iomem *)prueth->shram.pa + addr, sizeof(*rsp)); + /* Return buffer back for to pool */ + icssg_queue_push(prueth, slice == 0 ? + ICSSG_RSP_PUSH_SLICE0 : ICSSG_RSP_PUSH_SLICE1, addr); + + return 0; +} + +int emac_fdb_flow_id_updated(struct prueth_priv *priv) +{ + struct mgmt_cmd_rsp fdb_cmd_rsp = { 0 }; + struct prueth *prueth = priv->prueth; + struct mgmt_cmd fdb_cmd = { 0 }; + int slice = priv->port_id; + int ret = 0; + + fdb_cmd.header = ICSSG_FW_MGMT_CMD_HEADER; + fdb_cmd.type = ICSSG_FW_MGMT_FDB_CMD_TYPE_RX_FLOW; + fdb_cmd.seqnum = ++(prueth->icssg_hwcmdseq); + fdb_cmd.param = 0; + + fdb_cmd.param |= (slice << 4); + fdb_cmd.cmd_args[0] = 0; + + ret = icssg_send_fdb_msg(priv, &fdb_cmd, &fdb_cmd_rsp); + if (ret) + return ret; + + if (fdb_cmd.seqnum != fdb_cmd_rsp.seqnum) { + dev_err(prueth->dev, "seqnum doesn't match, cmd.seqnum %d != rsp.seqnum %d\n", + fdb_cmd.seqnum, fdb_cmd_rsp.seqnum); + return -EINVAL; + } + + if (fdb_cmd_rsp.status == 1) + return 0; + + return -EINVAL; +} diff --git a/drivers/net/ti/icssg_config.h b/drivers/net/ti/icssg_config.h new file mode 100644 index 00000000000..d388484c035 --- /dev/null +++ b/drivers/net/ti/icssg_config.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Texas Instruments ICSSG Ethernet driver + * + * Copyright (C) 2018-2024 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#ifndef __NET_TI_ICSSG_CONFIG_H +#define __NET_TI_ICSSG_CONFIG_H + +struct icssg_buffer_pool_cfg { + __le32 addr; + __le32 len; +} __packed; + +struct icssg_flow_cfg { + __le16 rx_base_flow; + __le16 mgm_base_flow; +} __packed; + +/* Config area lies in shared RAM */ +#define ICSSG_CONFIG_OFFSET_SLICE0 0 +#define ICSSG_CONFIG_OFFSET_SLICE1 0x8000 + +/* pstate speed/duplex command to set speed and duplex settings + * in firmware. + * Command format : 0x8102ssPN. ss - sequence number: currently not + * used by driver, P - port number: For switch, N - Speed/Duplex state + * - Possible values of N: + * 0x0 - 10Mbps/Half duplex ; + * 0x8 - 10Mbps/Full duplex ; + * 0x2 - 100Mbps/Half duplex; + * 0xa - 100Mbps/Full duplex; + * 0xc - 1Gbps/Full duplex; + * NOTE: The above are same as bits [3..1](slice 0) or bits [8..6](slice 1) of + * RGMII CFG register. So suggested to read the register to populate the command + * bits. + */ +#define ICSSG_PSTATE_SPEED_DUPLEX_CMD 0x81020000 +#define ICSSG_PSTATE_FULL_DUPLEX BIT(3) +#define ICSSG_PSTATE_SPEED_100 BIT(1) +#define ICSSG_PSTATE_SPEED_1000 BIT(2) + +/* Flow IDs used in config structure to firmware. Should match with + * flow_id in struct dma for rx channels. + */ +#define ICSSG_RX_CHAN_FLOW_ID 0 /* flow id for host port */ +#define ICSSG_RX_MGM_CHAN_FLOW_ID 1 /* flow id for command response */ + +/* Used to notify the FW of the current link speed */ +#define PORT_LINK_SPEED_OFFSET 0x00A8 + +#define FW_LINK_SPEED_1G (0x00) +#define FW_LINK_SPEED_100M (0x01) +#define FW_LINK_SPEED_10M (0x02) +#define FW_LINK_SPEED_HD (0x80) + +#define PRUETH_PKT_TYPE_CMD 0x10 +#define PRUETH_NAV_PS_DATA_SIZE 16 /* Protocol specific data size */ +#define PRUETH_NAV_SW_DATA_SIZE 16 /* SW related data size */ +#define PRUETH_MAX_RX_FLOWS 1 /* excluding default flow */ +#define PRUETH_RX_FLOW_DATA 0 /* FIXME: f/w bug to change to highest priority flow */ + +#define PRUETH_EMAC_BUF_POOL_SIZE SZ_8K +#define PRUETH_EMAC_POOLS_PER_SLICE 24 +#define PRUETH_EMAC_BUF_POOL_START 8 +#define PRUETH_NUM_BUF_POOLS 8 +#define PRUETH_EMAC_RX_CTX_BUF_SIZE SZ_16K /* per slice */ +#define MSMC_RAM_SIZE (2 * (PRUETH_EMAC_BUF_POOL_SIZE * PRUETH_NUM_BUF_POOLS + \ + PRUETH_EMAC_RX_CTX_BUF_SIZE)) + +struct icssg_rxq_ctx { + __le32 start[3]; + __le32 end; +} __packed; + +/* Load time Fiwmware Configuration */ + +#define ICSSG_FW_MGMT_CMD_HEADER 0x81 +#define ICSSG_FW_MGMT_FDB_CMD_TYPE 0x03 +#define ICSSG_FW_MGMT_CMD_TYPE 0x04 +#define ICSSG_FW_MGMT_PKT 0x80000000 +#define ICSSG_FW_MGMT_FDB_CMD_TYPE_RX_FLOW 0x05 + +struct icssg_r30_cmd { + u32 cmd[4]; +} __packed; + +enum icssg_port_state_cmd { + ICSSG_EMAC_PORT_DISABLE = 0, + ICSSG_EMAC_PORT_BLOCK, + ICSSG_EMAC_PORT_FORWARD, + ICSSG_EMAC_PORT_FORWARD_WO_LEARNING, + ICSSG_EMAC_PORT_ACCEPT_ALL, + ICSSG_EMAC_PORT_ACCEPT_TAGGED, + ICSSG_EMAC_PORT_ACCEPT_UNTAGGED_N_PRIO, + ICSSG_EMAC_PORT_TAS_TRIGGER, + ICSSG_EMAC_PORT_TAS_ENABLE, + ICSSG_EMAC_PORT_TAS_RESET, + ICSSG_EMAC_PORT_TAS_DISABLE, + ICSSG_EMAC_PORT_UC_FLOODING_ENABLE, + ICSSG_EMAC_PORT_UC_FLOODING_DISABLE, + ICSSG_EMAC_PORT_MC_FLOODING_ENABLE, + ICSSG_EMAC_PORT_MC_FLOODING_DISABLE, + ICSSG_EMAC_PORT_PREMPT_TX_ENABLE, + ICSSG_EMAC_PORT_PREMPT_TX_DISABLE, + ICSSG_EMAC_PORT_MAX_COMMANDS +}; + +#define EMAC_NONE 0xffff0000 +#define EMAC_PRU0_P_DI 0xffff0004 +#define EMAC_PRU1_P_DI 0xffff0040 +#define EMAC_TX_P_DI 0xffff0100 + +#define EMAC_PRU0_P_EN 0xfffb0000 +#define EMAC_PRU1_P_EN 0xffbf0000 +#define EMAC_TX_P_EN 0xfeff0000 + +#define EMAC_P_BLOCK 0xffff0040 +#define EMAC_TX_P_BLOCK 0xffff0200 +#define EMAC_P_UNBLOCK 0xffbf0000 +#define EMAC_TX_P_UNBLOCK 0xfdff0000 +#define EMAC_LEAN_EN 0xfff70000 +#define EMAC_LEAN_DI 0xffff0008 + +#define EMAC_ACCEPT_ALL 0xffff0001 +#define EMAC_ACCEPT_TAG 0xfffe0002 +#define EMAC_ACCEPT_PRIOR 0xfffc0000 + +/* Config area lies in DRAM */ +#define ICSSG_CONFIG_OFFSET 0x0 + +#define ICSSG_NUM_NORMAL_PDS 64 +#define ICSSG_NUM_SPECIAL_PDS 16 + +#define ICSSG_NORMAL_PD_SIZE 8 +#define ICSSG_SPECIAL_PD_SIZE 20 + +#define ICSSG_FLAG_MASK 0xff00ffff + +struct icssg_setclock_desc { + u8 request; + u8 restore; + u8 acknowledgment; + u8 cmp_status; + u32 margin; + u32 cyclecounter0_set; + u32 cyclecounter1_set; + u32 iepcount_set; + u32 rsvd1; + u32 rsvd2; + u32 CMP0_current; + u32 iepcount_current; + u32 difference; + u32 cyclecounter0_new; + u32 cyclecounter1_new; + u32 CMP0_new; +} __packed; + +struct mgmt_cmd { + u8 param; + u8 seqnum; + u8 type; + u8 header; + u32 cmd_args[3]; +} __packed; + +struct mgmt_cmd_rsp { + u32 reserved; + u8 status; + u8 seqnum; + u8 type; + u8 header; + u32 cmd_args[3]; +} __packed; + +#define ICSSG_CMD_POP_SLICE0 56 +#define ICSSG_CMD_POP_SLICE1 60 + +#define ICSSG_CMD_PUSH_SLICE0 57 +#define ICSSG_CMD_PUSH_SLICE1 61 + +#define ICSSG_RSP_POP_SLICE0 58 +#define ICSSG_RSP_POP_SLICE1 62 + +#define ICSSG_RSP_PUSH_SLICE0 56 +#define ICSSG_RSP_PUSH_SLICE1 60 + +#define ICSSG_TS_POP_SLICE0 59 +#define ICSSG_TS_POP_SLICE1 63 + +#define ICSSG_TS_PUSH_SLICE0 40 +#define ICSSG_TS_PUSH_SLICE1 41 + +#endif /* __NET_TI_ICSSG_CONFIG_H */ diff --git a/drivers/net/ti/icssg_prueth.c b/drivers/net/ti/icssg_prueth.c new file mode 100644 index 00000000000..2639f960631 --- /dev/null +++ b/drivers/net/ti/icssg_prueth.c @@ -0,0 +1,691 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments K3 AM65 PRU Ethernet Driver + * + * Copyright (C) 2018-2024 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#include <asm/io.h> +#include <asm/processor.h> +#include <clk.h> +#include <dm/lists.h> +#include <dm/device.h> +#include <dma-uclass.h> +#include <dm/of_access.h> +#include <dm/pinctrl.h> +#include <fs_loader.h> +#include <miiphy.h> +#include <net.h> +#include <phy.h> +#include <power-domain.h> +#include <linux/soc/ti/ti-udma.h> +#include <regmap.h> +#include <remoteproc.h> +#include <syscon.h> +#include <soc.h> +#include <linux/pruss_driver.h> +#include <dm/device_compat.h> + +#include "icssg_prueth.h" +#include "icss_mii_rt.h" + +#define ICSS_SLICE0 0 +#define ICSS_SLICE1 1 + +#ifdef PKTSIZE_ALIGN +#define UDMA_RX_BUF_SIZE PKTSIZE_ALIGN +#else +#define UDMA_RX_BUF_SIZE ALIGN(PKTSIZE, ARCH_DMA_MINALIGN) +#endif + +#ifdef PKTBUFSRX +#define UDMA_RX_DESC_NUM PKTBUFSRX +#else +#define UDMA_RX_DESC_NUM 4 +#endif + +/* Config region lies in shared RAM */ +#define ICSS_CONFIG_OFFSET_SLICE0 0 +#define ICSS_CONFIG_OFFSET_SLICE1 0x8000 + +/* Firmware flags */ +#define ICSS_SET_RUN_FLAG_VLAN_ENABLE BIT(0) /* switch only */ +#define ICSS_SET_RUN_FLAG_FLOOD_UNICAST BIT(1) /* switch only */ +#define ICSS_SET_RUN_FLAG_PROMISC BIT(2) /* MAC only */ +#define ICSS_SET_RUN_FLAG_MULTICAST_PROMISC BIT(3) /* MAC only */ + +/* CTRLMMR_ICSSG_RGMII_CTRL register bits */ +#define ICSSG_CTRL_RGMII_ID_MODE BIT(24) + +/* Management packet type */ +#define PRUETH_PKT_TYPE_CMD 0x10 + +/* Number of PRU Cores per Slice */ +#define ICSSG_NUM_PRU_CORES 3 + +static int icssg_gmii_select(struct prueth_priv *priv) +{ + struct phy_device *phydev = priv->phydev; + + if (phydev->interface != PHY_INTERFACE_MODE_MII && + phydev->interface < PHY_INTERFACE_MODE_RGMII && + phydev->interface > PHY_INTERFACE_MODE_RGMII_TXID) { + dev_err(priv->dev, "PHY mode unsupported %s\n", + phy_string_for_interface(phydev->interface)); + return -EINVAL; + } + + /* AM65 SR2.0 has TX Internal delay always enabled by hardware + * and it is not possible to disable TX Internal delay. The below + * switch case block describes how we handle different phy modes + * based on hardware restriction. + */ + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII_ID: + phydev->interface = PHY_INTERFACE_MODE_RGMII_RXID; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + phydev->interface = PHY_INTERFACE_MODE_RGMII; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_RXID: + dev_err(priv->dev, "RGMII mode without TX delay is not supported"); + return -EINVAL; + default: + break; + } + + return 0; +} + +static int icssg_phy_init(struct udevice *dev) +{ + struct prueth_priv *priv = dev_get_priv(dev); + struct phy_device *phydev; + u32 supported = PHY_GBIT_FEATURES; + int ret; + + phydev = dm_eth_phy_connect(dev); + if (!phydev) { + dev_err(dev, "phy_connect() failed\n"); + return -ENODEV; + } + + /* disable unsupported features */ + supported &= ~(PHY_10BT_FEATURES | + SUPPORTED_100baseT_Half | + SUPPORTED_1000baseT_Half | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); + + phydev->supported &= supported; + phydev->advertising = phydev->supported; + priv->phydev = phydev; + + ret = icssg_gmii_select(priv); + if (ret) + goto out; + + ret = phy_config(phydev); + if (ret < 0) + dev_err(dev, "phy_config() failed: %d", ret); +out: + return ret; +} + +static void icssg_config_set_speed(struct prueth_priv *priv, int speed) +{ + struct prueth *prueth = priv->prueth; + u8 fw_speed; + + switch (speed) { + case SPEED_1000: + fw_speed = FW_LINK_SPEED_1G; + break; + case SPEED_100: + fw_speed = FW_LINK_SPEED_100M; + break; + case SPEED_10: + fw_speed = FW_LINK_SPEED_10M; + break; + default: + /* Other links speeds not supported */ + dev_err(priv->dev, "Unsupported link speed\n"); + return; + } + + writeb(fw_speed, prueth->dram[priv->port_id].pa + PORT_LINK_SPEED_OFFSET); +} + +static int icssg_update_link(struct prueth_priv *priv) +{ + struct phy_device *phy = priv->phydev; + struct prueth *prueth = priv->prueth; + bool gig_en = false, full_duplex = false; + + if (phy->link) { /* link up */ + if (phy->speed == SPEED_1000) + gig_en = true; + if (phy->duplex == DUPLEX_FULL) + full_duplex = true; + /* Set the RGMII cfg for gig en and full duplex */ + icssg_update_rgmii_cfg(prueth->miig_rt, phy->speed, full_duplex, + priv->port_id, priv); + /* update the Tx IPG based on 100M/1G speed */ + icssg_config_ipg(priv, phy->speed, priv->port_id); + + /* Send command to firmware to update Speed setting */ + icssg_config_set_speed(priv, phy->speed); + + /* Enable PORT FORWARDING */ + emac_set_port_state(priv, ICSSG_EMAC_PORT_FORWARD); + + printf("link up on port %d, speed %d, %s duplex\n", + priv->port_id, phy->speed, + (phy->duplex == DUPLEX_FULL) ? "full" : "half"); + } else { + emac_set_port_state(priv, ICSSG_EMAC_PORT_DISABLE); + printf("link down on port %d\n", priv->port_id); + } + + return phy->link; +} + +struct icssg_firmwares { + char *pru; + char *rtu; + char *txpru; +}; + +static struct icssg_firmwares icssg_emac_firmwares[] = { + { + .pru = "/lib/firmware/ti-pruss/am65x-sr2-pru0-prueth-fw.elf", + .rtu = "/lib/firmware/ti-pruss/am65x-sr2-rtu0-prueth-fw.elf", + .txpru = "/lib/firmware/ti-pruss/am65x-sr2-txpru0-prueth-fw.elf", + }, + { + .pru = "/lib/firmware/ti-pruss/am65x-sr2-pru1-prueth-fw.elf", + .rtu = "/lib/firmware/ti-pruss/am65x-sr2-rtu1-prueth-fw.elf", + .txpru = "/lib/firmware/ti-pruss/am65x-sr2-txpru1-prueth-fw.elf", + } +}; + +static int icssg_start_pru_cores(struct udevice *dev) +{ + struct prueth_priv *priv = dev_get_priv(dev); + struct prueth *prueth = priv->prueth; + struct icssg_firmwares *firmwares; + struct udevice *rproc_dev = NULL; + int ret, slice; + u32 phandle; + u8 index; + + slice = priv->port_id; + index = slice * ICSSG_NUM_PRU_CORES; + firmwares = icssg_emac_firmwares; + + ofnode_read_u32_index(dev_ofnode(prueth->dev), "ti,prus", index, &phandle); + ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle, &rproc_dev); + if (ret) { + dev_err(dev, "Unknown remote processor with phandle '0x%x' requested(%d)\n", + phandle, ret); + return ret; + } + + prueth->pru_core_id = dev_seq(rproc_dev); + ret = rproc_set_firmware(rproc_dev, firmwares[slice].pru); + if (ret) + return ret; + + ret = rproc_boot(rproc_dev); + if (ret) { + dev_err(dev, "failed to boot PRU%d: %d\n", slice, ret); + return -EINVAL; + } + + ofnode_read_u32_index(dev_ofnode(prueth->dev), "ti,prus", index + 1, &phandle); + ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle, &rproc_dev); + if (ret) { + dev_err(dev, "Unknown remote processor with phandle '0x%x' requested(%d)\n", + phandle, ret); + goto halt_pru; + } + + prueth->rtu_core_id = dev_seq(rproc_dev); + ret = rproc_set_firmware(rproc_dev, firmwares[slice].rtu); + if (ret) + goto halt_pru; + + ret = rproc_boot(rproc_dev); + if (ret) { + dev_err(dev, "failed to boot RTU%d: %d\n", slice, ret); + goto halt_pru; + } + + ofnode_read_u32_index(dev_ofnode(prueth->dev), "ti,prus", index + 2, &phandle); + ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle, &rproc_dev); + if (ret) { + dev_err(dev, "Unknown remote processor with phandle '0x%x' requested(%d)\n", + phandle, ret); + goto halt_rtu; + } + + prueth->txpru_core_id = dev_seq(rproc_dev); + ret = rproc_set_firmware(rproc_dev, firmwares[slice].txpru); + if (ret) + goto halt_rtu; + + ret = rproc_boot(rproc_dev); + if (ret) { + dev_err(dev, "failed to boot TXPRU%d: %d\n", slice, ret); + goto halt_rtu; + } + + return 0; + +halt_rtu: + rproc_stop(prueth->rtu_core_id); + +halt_pru: + rproc_stop(prueth->pru_core_id); + return ret; +} + +static int icssg_stop_pru_cores(struct udevice *dev) +{ + struct prueth_priv *priv = dev_get_priv(dev); + struct prueth *prueth = priv->prueth; + + rproc_stop(prueth->pru_core_id); + rproc_stop(prueth->rtu_core_id); + rproc_stop(prueth->txpru_core_id); + + return 0; +} + +static int prueth_start(struct udevice *dev) +{ + struct ti_udma_drv_chan_cfg_data *dma_rx_cfg_data; + struct eth_pdata *pdata = dev_get_plat(dev); + struct prueth_priv *priv = dev_get_priv(dev); + struct prueth *prueth = priv->prueth; + struct icssg_flow_cfg *flow_cfg; + u8 *hwaddr = pdata->enetaddr; + char chn_name[16]; + void *config; + int ret, i; + + icssg_class_set_mac_addr(prueth->miig_rt, priv->port_id, hwaddr); + icssg_ft1_set_mac_addr(prueth->miig_rt, priv->port_id, hwaddr); + icssg_class_default(prueth->miig_rt, priv->port_id, 0); + + /* Set Load time configuration */ + icssg_config(priv); + + ret = icssg_start_pru_cores(dev); + if (ret) + return ret; + + /* To differentiate channels for SLICE0 vs SLICE1 */ + snprintf(chn_name, sizeof(chn_name), "tx%d-0", priv->port_id); + + ret = dma_get_by_name(prueth->dev, chn_name, &prueth->dma_tx); + if (ret) + dev_err(dev, "TX dma get failed %d\n", ret); + + snprintf(chn_name, sizeof(chn_name), "rx%d", priv->port_id); + ret = dma_get_by_name(prueth->dev, chn_name, &prueth->dma_rx); + if (ret) + dev_err(dev, "RX dma get failed %d\n", ret); + + for (i = 0; i < UDMA_RX_DESC_NUM; i++) { + ret = dma_prepare_rcv_buf(&prueth->dma_rx, + net_rx_packets[i], + UDMA_RX_BUF_SIZE); + if (ret) + dev_err(dev, "RX dma add buf failed %d\n", ret); + } + + ret = dma_enable(&prueth->dma_tx); + if (ret) { + dev_err(dev, "TX dma_enable failed %d\n", ret); + goto tx_fail; + } + + ret = dma_enable(&prueth->dma_rx); + if (ret) { + dev_err(dev, "RX dma_enable failed %d\n", ret); + goto rx_fail; + } + + /* check if the rx_flow_id of dma_rx is as expected since + * driver hardcode that value in config struct to firmware + * in probe. Just add this sanity check to catch any change + * to rx channel assignment in the future. + */ + dma_get_cfg(&prueth->dma_rx, 0, (void **)&dma_rx_cfg_data); + config = (void *)(prueth->dram[priv->port_id].pa + ICSSG_CONFIG_OFFSET); + + flow_cfg = config + PSI_L_REGULAR_FLOW_ID_BASE_OFFSET; + writew(dma_rx_cfg_data->flow_id_base, &flow_cfg->rx_base_flow); + writew(0, &flow_cfg->mgm_base_flow); + + dev_info(dev, "K3 ICSSG: rflow_id_base: %u, chn_name = %s\n", + dma_rx_cfg_data->flow_id_base, chn_name); + + ret = emac_fdb_flow_id_updated(priv); + if (ret) { + dev_err(dev, "Failed to update Rx Flow ID %d", ret); + goto phy_fail; + } + + ret = phy_startup(priv->phydev); + if (ret) { + dev_err(dev, "phy_startup failed\n"); + goto phy_fail; + } + + ret = icssg_update_link(priv); + if (!ret) { + ret = -ENODEV; + goto phy_shut; + } + + return 0; + +phy_shut: + phy_shutdown(priv->phydev); +phy_fail: + dma_disable(&prueth->dma_rx); + dma_free(&prueth->dma_rx); +rx_fail: + dma_disable(&prueth->dma_tx); + dma_free(&prueth->dma_tx); + +tx_fail: + icssg_class_disable(prueth->miig_rt, priv->port_id); + + return ret; +} + +static int prueth_send(struct udevice *dev, void *packet, int length) +{ + struct prueth_priv *priv = dev_get_priv(dev); + struct prueth *prueth = priv->prueth; + int ret; + + ret = dma_send(&prueth->dma_tx, packet, length, NULL); + + return ret; +} + +static int prueth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct prueth_priv *priv = dev_get_priv(dev); + struct prueth *prueth = priv->prueth; + int ret; + + /* try to receive a new packet */ + ret = dma_receive(&prueth->dma_rx, (void **)packetp, NULL); + + return ret; +} + +static int prueth_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct prueth_priv *priv = dev_get_priv(dev); + struct prueth *prueth = priv->prueth; + int ret = 0; + + if (length > 0) { + u32 pkt = prueth->rx_next % UDMA_RX_DESC_NUM; + + dev_dbg(dev, "%s length:%d pkt:%u\n", __func__, length, pkt); + + ret = dma_prepare_rcv_buf(&prueth->dma_rx, + net_rx_packets[pkt], + UDMA_RX_BUF_SIZE); + prueth->rx_next++; + } + + return ret; +} + +static void prueth_stop(struct udevice *dev) +{ + struct prueth_priv *priv = dev_get_priv(dev); + struct prueth *prueth = priv->prueth; + + phy_shutdown(priv->phydev); + + dma_disable(&prueth->dma_tx); + dma_disable(&prueth->dma_rx); + + icssg_stop_pru_cores(dev); + + dma_free(&prueth->dma_tx); + dma_free(&prueth->dma_rx); +} + +static const struct eth_ops prueth_ops = { + .start = prueth_start, + .send = prueth_send, + .recv = prueth_recv, + .free_pkt = prueth_free_pkt, + .stop = prueth_stop, +}; + +static int icssg_ofdata_parse_phy(struct udevice *dev) +{ + struct prueth_priv *priv = dev_get_priv(dev); + + dev_read_u32(dev, "reg", &priv->port_id); + priv->phy_interface = dev_read_phy_mode(dev); + if (priv->phy_interface == PHY_INTERFACE_MODE_NA) { + dev_err(dev, "Invalid PHY mode '%s', port %u\n", + phy_string_for_interface(priv->phy_interface), + priv->port_id); + return -EINVAL; + } + + return 0; +} + +static int prueth_port_probe(struct udevice *dev) +{ + struct prueth_priv *priv = dev_get_priv(dev); + struct prueth *prueth; + char portname[15]; + int ret; + + priv->dev = dev; + prueth = dev_get_priv(dev->parent); + priv->prueth = prueth; + + sprintf(portname, "%s-%s", dev->parent->name, dev->name); + + device_set_name(dev, portname); + + ret = icssg_ofdata_parse_phy(dev); + if (ret) + goto out; + + ret = icssg_phy_init(dev); + if (ret) + goto out; + + ret = pruss_request_mem_region(prueth->pruss, + priv->port_id ? PRUSS_MEM_DRAM1 : PRUSS_MEM_DRAM0, + &prueth->dram[priv->port_id]); + if (ret) { + dev_err(dev, "could not request DRAM%d region\n", priv->port_id); + return ret; + } +out: + return ret; +} + +static int prueth_probe(struct udevice *dev) +{ + ofnode node, pruss_node, mdio_node, sram_node, curr_sram_node; + struct prueth *prueth = dev_get_priv(dev); + u32 phandle, err, sp, prev_end_addr; + struct udevice **prussdev = NULL; + ofnode eth_ports_node, eth_node; + struct udevice *port_dev; + int ret = 0; + + prueth->dev = dev; + + err = ofnode_read_u32(dev_ofnode(dev), "ti,prus", &phandle); + if (err) + return err; + + node = ofnode_get_by_phandle(phandle); + if (!ofnode_valid(node)) + return -EINVAL; + + pruss_node = ofnode_get_parent(node); + ret = device_get_global_by_ofnode(pruss_node, prussdev); + if (ret) + dev_err(dev, "error getting the pruss dev\n"); + prueth->pruss = *prussdev; + + ret = pruss_request_mem_region(*prussdev, PRUSS_MEM_SHRD_RAM2, + &prueth->shram); + if (ret) + return ret; + + ret = pruss_request_tm_region(*prussdev, &prueth->tmaddr); + if (ret) + return ret; + + prueth->miig_rt = syscon_regmap_lookup_by_phandle(dev, "ti,mii-g-rt"); + if (!prueth->miig_rt) { + dev_err(dev, "couldn't get mii-g-rt syscon regmap\n"); + return -ENODEV; + } + + prueth->mii_rt = syscon_regmap_lookup_by_phandle(dev, "ti,mii-rt"); + if (!prueth->mii_rt) { + dev_err(dev, "couldn't get mii-rt syscon regmap\n"); + return -ENODEV; + } + + ret = ofnode_read_u32(dev_ofnode(dev), "sram", &sp); + if (ret) { + dev_err(dev, "sram node fetch failed %d\n", ret); + return ret; + } + + sram_node = ofnode_get_by_phandle(sp); + if (!ofnode_valid(sram_node)) + return -EINVAL; + + prev_end_addr = ofnode_get_addr(sram_node); + + ofnode_for_each_subnode(curr_sram_node, sram_node) { + u32 start_addr, size, end_addr, avail; + const char *name; + + name = ofnode_get_name(curr_sram_node); + start_addr = ofnode_get_addr(curr_sram_node); + size = ofnode_get_size(curr_sram_node); + end_addr = start_addr + size; + avail = start_addr - prev_end_addr; + + if (avail > MSMC_RAM_SIZE) + break; + + prev_end_addr = end_addr; + } + + prueth->sram_pa = prev_end_addr; + if (prueth->sram_pa % SZ_64K != 0) { + /* This is constraint for SR2.0 firmware */ + dev_err(dev, "sram address needs to be 64KB aligned\n"); + return -EINVAL; + } + dev_dbg(dev, "sram: addr %x size %x\n", prueth->sram_pa, MSMC_RAM_SIZE); + + mdio_node = ofnode_find_subnode(pruss_node, "mdio"); + prueth->mdio_base = ofnode_get_addr(mdio_node); + ofnode_read_u32(mdio_node, "bus_freq", &prueth->mdio_freq); + + ret = clk_get_by_name_nodev(mdio_node, "fck", &prueth->mdiofck); + if (ret) { + dev_err(dev, "failed to get clock %d\n", ret); + return ret; + } + + ret = clk_enable(&prueth->mdiofck); + if (ret) { + dev_err(dev, "clk_enable failed %d\n", ret); + return ret; + } + + eth_ports_node = dev_read_subnode(dev, "ethernet-ports"); + if (!ofnode_valid(eth_ports_node)) + return -ENOENT; + + ofnode_for_each_subnode(eth_node, eth_ports_node) { + const char *node_name; + u32 port_id; + bool disabled; + + node_name = ofnode_get_name(eth_node); + disabled = !ofnode_is_enabled(eth_node); + ret = ofnode_read_u32(eth_node, "reg", &port_id); + if (ret) + dev_err(dev, "%s: error reading port_id (%d)\n", node_name, ret); + + if (port_id >= PRUETH_NUM_MACS) { + dev_err(dev, "%s: invalid port_id (%d)\n", node_name, port_id); + return -EINVAL; + } + + if (port_id < 0) + continue; + if (disabled) + continue; + + ret = device_bind_driver_to_node(dev, "prueth_port", + ofnode_get_name(eth_node), + eth_node, &port_dev); + if (ret) { + dev_err(dev, "Failed to bind to %s node\n", ofnode_get_name(eth_node)); + goto out; + } + } + + return 0; +out: + clk_disable(&prueth->mdiofck); + + return ret; +} + +static const struct udevice_id prueth_ids[] = { + { .compatible = "ti,am654-icssg-prueth" }, + { .compatible = "ti,am642-icssg-prueth" }, + { } +}; + +U_BOOT_DRIVER(prueth) = { + .name = "prueth", + .id = UCLASS_MISC, + .of_match = prueth_ids, + .probe = prueth_probe, + .priv_auto = sizeof(struct prueth), +}; + +U_BOOT_DRIVER(prueth_port) = { + .name = "prueth_port", + .id = UCLASS_ETH, + .probe = prueth_port_probe, + .ops = &prueth_ops, + .priv_auto = sizeof(struct prueth_priv), + .plat_auto = sizeof(struct eth_pdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/net/ti/icssg_prueth.h b/drivers/net/ti/icssg_prueth.h new file mode 100644 index 00000000000..c69cfd4f162 --- /dev/null +++ b/drivers/net/ti/icssg_prueth.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Texas Instruments K3 AM65 Ethernet Switch SubSystem Driver + * + * Copyright (C) 2018-2024 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#ifndef __NET_TI_ICSSG_PRUETH_H +#define __NET_TI_ICSSG_PRUETH_H + +#include <asm/io.h> +#include <clk.h> +#include <dm/lists.h> +#include <dm/ofnode.h> +#include <dm/device.h> +#include <dma-uclass.h> +#include <regmap.h> +#include <linux/sizes.h> +#include <linux/pruss_driver.h> +#include "icssg_config.h" +#include "icssg_switch_map.h" + +void icssg_class_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac); +void icssg_class_set_host_mac_addr(struct regmap *miig_rt, u8 *mac); +void icssg_class_disable(struct regmap *miig_rt, int slice); +void icssg_class_default(struct regmap *miig_rt, int slice, bool allmulti); +void icssg_ft1_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac_addr); + +enum prueth_mac { + PRUETH_MAC0 = 0, + PRUETH_MAC1, + PRUETH_NUM_MACS, +}; + +enum prueth_port { + PRUETH_PORT_HOST = 0, /* host side port */ + PRUETH_PORT_MII0, /* physical port MII 0 */ + PRUETH_PORT_MII1, /* physical port MII 1 */ +}; + +struct prueth { + struct udevice *dev; + struct udevice *pruss; + struct regmap *miig_rt; + struct regmap *mii_rt; + fdt_addr_t mdio_base; + struct pruss_mem_region shram; + struct pruss_mem_region dram[PRUETH_NUM_MACS]; + phys_addr_t tmaddr; + struct mii_dev *bus; + u32 sram_pa; + ofnode eth_node[PRUETH_NUM_MACS]; + u32 mdio_freq; + int phy_interface; + struct clk mdiofck; + struct dma dma_tx; + struct dma dma_rx; + struct dma dma_rx_mgm; + u32 rx_next; + u32 rx_pend; + int slice; + bool mdio_manual_mode; + int speed; + int duplex; + u8 pru_core_id; + u8 rtu_core_id; + u8 txpru_core_id; + u8 icssg_hwcmdseq; +}; + +struct prueth_priv { + struct udevice *dev; + struct prueth *prueth; + u32 port_id; + struct phy_device *phydev; + bool has_phy; + ofnode phy_node; + u32 phy_addr; + int phy_interface; +}; + +/* config helpers */ +void icssg_config_ipg(struct prueth_priv *priv, int speed, int mii); +int icssg_config(struct prueth_priv *priv); +int emac_set_port_state(struct prueth_priv *priv, enum icssg_port_state_cmd cmd); + +/* Buffer queue helpers */ +int icssg_queue_pop(struct prueth *prueth, u8 queue); +void icssg_queue_push(struct prueth *prueth, int queue, u16 addr); +u32 icssg_queue_level(struct prueth *prueth, int queue); + +/* FDB helpers */ +int icssg_send_fdb_msg(struct prueth_priv *priv, struct mgmt_cmd *cmd, + struct mgmt_cmd_rsp *rsp); +int emac_fdb_flow_id_updated(struct prueth_priv *priv); + +#endif /* __NET_TI_ICSSG_PRUETH_H */ diff --git a/drivers/net/ti/icssg_queues.c b/drivers/net/ti/icssg_queues.c new file mode 100644 index 00000000000..fc4d33dbb25 --- /dev/null +++ b/drivers/net/ti/icssg_queues.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* ICSSG Buffer queue helpers + * + * Copyright (C) 2018-2024 Texas Instruments Incorporated - https://www.ti.com + */ + +#include <dm/ofnode.h> +#include <regmap.h> +#include "icssg_prueth.h" + +#define ICSSG_QUEUES_MAX 64 +#define ICSSG_QUEUE_OFFSET 0xd00 +#define ICSSG_QUEUE_PEEK_OFFSET 0xe00 +#define ICSSG_QUEUE_CNT_OFFSET 0xe40 +#define ICSSG_QUEUE_RESET_OFFSET 0xf40 + +int icssg_queue_pop(struct prueth *prueth, u8 queue) +{ + u32 val, cnt; + + if (queue >= ICSSG_QUEUES_MAX) + return -EINVAL; + + regmap_read(prueth->miig_rt, ICSSG_QUEUE_CNT_OFFSET + 4 * queue, &cnt); + if (!cnt) + return -EINVAL; + + regmap_read(prueth->miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue, &val); + + return val; +} + +void icssg_queue_push(struct prueth *prueth, int queue, u16 addr) +{ + if (queue >= ICSSG_QUEUES_MAX) + return; + + regmap_write(prueth->miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue, addr); +} + +u32 icssg_queue_level(struct prueth *prueth, int queue) +{ + u32 reg; + + if (queue >= ICSSG_QUEUES_MAX) + return 0; + + regmap_read(prueth->miig_rt, ICSSG_QUEUE_CNT_OFFSET + 4 * queue, ®); + + return reg; +} diff --git a/drivers/net/ti/icssg_switch_map.h b/drivers/net/ti/icssg_switch_map.h new file mode 100644 index 00000000000..b62c51407b8 --- /dev/null +++ b/drivers/net/ti/icssg_switch_map.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Texas Instruments ICSSG Ethernet driver + * + * Copyright (C) 2020-2024 Texas Instruments Incorporated - https://www.ti.com/ + * + */ + +#ifndef __NET_TI_ICSSG_SWITCH_MAP_H +#define __NET_TI_ICSSG_SWITCH_MAP_H + +/*Time after which FDB entries are checked for aged out values. Value in nanoseconds*/ +#define FDB_AGEING_TIMEOUT_OFFSET 0x0014 + +/*default VLAN tag for Host Port*/ +#define HOST_PORT_DF_VLAN_OFFSET 0x001C + +/*Same as HOST_PORT_DF_VLAN_OFFSET*/ +#define EMAC_ICSSG_SWITCH_PORT0_DEFAULT_VLAN_OFFSET HOST_PORT_DF_VLAN_OFFSET + +/*default VLAN tag for P1 Port*/ +#define P1_PORT_DF_VLAN_OFFSET 0x0020 + +/*Same as P1_PORT_DF_VLAN_OFFSET*/ +#define EMAC_ICSSG_SWITCH_PORT1_DEFAULT_VLAN_OFFSET P1_PORT_DF_VLAN_OFFSET + +/*default VLAN tag for P2 Port*/ +#define P2_PORT_DF_VLAN_OFFSET 0x0024 + +/*Same as P2_PORT_DF_VLAN_OFFSET*/ +#define EMAC_ICSSG_SWITCH_PORT2_DEFAULT_VLAN_OFFSET P2_PORT_DF_VLAN_OFFSET + +/*VLAN-FID Table offset. 4096 VIDs. 2B per VID = 8KB = 0x2000*/ +#define VLAN_STATIC_REG_TABLE_OFFSET 0x0100 + +/*VLAN-FID Table offset for EMAC*/ +#define EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET VLAN_STATIC_REG_TABLE_OFFSET + +/*packet descriptor Q reserved memory*/ +#define PORT_DESC0_HI 0x2104 + +/*packet descriptor Q reserved memory*/ +#define PORT_DESC0_LO 0x2F6C + +/*packet descriptor Q reserved memory*/ +#define PORT_DESC1_HI 0x3DD4 + +/*packet descriptor Q reserved memory*/ +#define PORT_DESC1_LO 0x4C3C + +/*packet descriptor Q reserved memory*/ +#define HOST_DESC0_HI 0x5AA4 + +/*packet descriptor Q reserved memory*/ +#define HOST_DESC0_LO 0x5F0C + +/*packet descriptor Q reserved memory*/ +#define HOST_DESC1_HI 0x6374 + +/*packet descriptor Q reserved memory*/ +#define HOST_DESC1_LO 0x67DC + +/*special packet descriptor Q reserved memory*/ +#define HOST_SPPD0 0x7AAC + +/*special packet descriptor Q reserved memory*/ +#define HOST_SPPD1 0x7EAC + +/*_Small_Description_*/ +#define TIMESYNC_FW_WC_CYCLECOUNT_OFFSET 0x83EC + +/*IEP count hi roll over count*/ +#define TIMESYNC_FW_WC_HI_ROLLOVER_COUNT_OFFSET 0x83F4 + +/*_Small_Description_*/ +#define TIMESYNC_FW_WC_COUNT_HI_SW_OFFSET_OFFSET 0x83F8 + +/*Set clock descriptor*/ +#define TIMESYNC_FW_WC_SETCLOCK_DESC_OFFSET 0x83FC + +/*_Small_Description_*/ +#define TIMESYNC_FW_WC_SYNCOUT_REDUCTION_FACTOR_OFFSET 0x843C + +/*_Small_Description_*/ +#define TIMESYNC_FW_WC_SYNCOUT_REDUCTION_COUNT_OFFSET 0x8440 + +/*_Small_Description_*/ +#define TIMESYNC_FW_WC_SYNCOUT_START_TIME_CYCLECOUNT_OFFSET 0x8444 + +/*Control variable to generate SYNC1*/ +#define TIMESYNC_FW_WC_ISOM_PIN_SIGNAL_EN_OFFSET 0x844C + +/*SystemTime Sync0 periodicity*/ +#define TIMESYNC_FW_ST_SYNCOUT_PERIOD_OFFSET 0x8450 + +/*pktTxDelay for P1 = link speed dependent p1 mac delay + p1 phy delay*/ +#define TIMESYNC_FW_WC_PKTTXDELAY_P1_OFFSET 0x8454 + +/*pktTxDelay for P2 = link speed dependent p2 mac delay + p2 phy delay*/ +#define TIMESYNC_FW_WC_PKTTXDELAY_P2_OFFSET 0x8458 + +/*Set clock operation done signal for next task*/ +#define TIMESYNC_FW_SIG_PNFW_OFFSET 0x845C + +/*Set clock operation done signal for next task*/ +#define TIMESYNC_FW_SIG_TIMESYNCFW_OFFSET 0x8460 + +/*New list is copied at this time*/ +#define TAS_CONFIG_CHANGE_TIME 0x000C + +/*config change error counter*/ +#define TAS_CONFIG_CHANGE_ERROR_COUNTER 0x0014 + +/*TAS List update pending flag*/ +#define TAS_CONFIG_PENDING 0x0018 + +/*TAS list update trigger flag*/ +#define TAS_CONFIG_CHANGE 0x0019 + +/*List length for new TAS schedule*/ +#define TAS_ADMIN_LIST_LENGTH 0x001A + +/*Currently active TAS list index*/ +#define TAS_ACTIVE_LIST_INDEX 0x001B + +/*Cycle time for the new TAS schedule*/ +#define TAS_ADMIN_CYCLE_TIME 0x001C + +/*Cycle counts remaining till the TAS list update*/ +#define TAS_CONFIG_CHANGE_CYCLE_COUNT 0x0020 + +/*Base Flow ID for sending packets to Host for Slice0*/ +#define PSI_L_REGULAR_FLOW_ID_BASE_OFFSET 0x0024 + +/*Same as PSI_L_REGULAR_FLOW_ID_BASE_OFFSET*/ +#define EMAC_ICSSG_SWITCH_PSI_L_REGULAR_FLOW_ID_BASE_OFFSET PSI_L_REGULAR_FLOW_ID_BASE_OFFSET + +/*Base Flow ID for sending mgmt and Tx TS to Host for Slice0*/ +#define PSI_L_MGMT_FLOW_ID_OFFSET 0x0026 + +/*Same as PSI_L_MGMT_FLOW_ID_OFFSET*/ +#define EMAC_ICSSG_SWITCH_PSI_L_MGMT_FLOW_ID_BASE_OFFSET PSI_L_MGMT_FLOW_ID_OFFSET + +/*Queue number for Special packets written here*/ +#define SPL_PKT_DEFAULT_PRIORITY 0x0028 + +/*Express Preemptible Queue Mask*/ +#define EXPRESS_PRE_EMPTIVE_Q_MASK 0x0029 + +/*Port1/Port2 Default Queue number for untagged packets, only 1B is used*/ +#define QUEUE_NUM_UNTAGGED 0x002A + +/*Stores the table used for priority regeneration. 1B per PCP/Queue*/ +#define PORT_Q_PRIORITY_REGEN_OFFSET 0x002C + +/* For marking Packet as priority/express (this feature is disabled) or + * cut-through/S&F. + */ +#define EXPRESS_PRE_EMPTIVE_Q_MAP 0x0034 + +/*Stores the table used for priority mapping. 1B per PCP/Queue*/ +#define PORT_Q_PRIORITY_MAPPING_OFFSET 0x003C + +/*TAS gate mask for windows list0*/ +#define TAS_GATE_MASK_LIST0 0x0100 + +/*TAS gate mask for windows list1*/ +#define TAS_GATE_MASK_LIST1 0x0350 + +/*Memory to Enable/Disable Preemption on TX side*/ +#define PRE_EMPTION_ENABLE_TX 0x05A0 + +/*Active State of Preemption on TX side*/ +#define PRE_EMPTION_ACTIVE_TX 0x05A1 + +/*Memory to Enable/Disable Verify State Machine Preemption*/ +#define PRE_EMPTION_ENABLE_VERIFY 0x05A2 + +/*Verify Status of State Machine*/ +#define PRE_EMPTION_VERIFY_STATUS 0x05A3 + +/*Non Final Fragment Size supported by Link Partner*/ +#define PRE_EMPTION_ADD_FRAG_SIZE_REMOTE 0x05A4 + +/*Non Final Fragment Size supported by Firmware*/ +#define PRE_EMPTION_ADD_FRAG_SIZE_LOCAL 0x05A6 + +/*Time in ms the State machine waits for respond packet*/ +#define PRE_EMPTION_VERIFY_TIME 0x05A8 + +/*Memory used for R30 related management commands*/ +#define MGR_R30_CMD_OFFSET 0x05AC + +/*HW Buffer Pool0 base address*/ +#define BUFFER_POOL_0_ADDR_OFFSET 0x05BC + +/*16B for Host Egress MSMC Q (Pre-emptible) context*/ +#define HOST_RX_Q_PRE_CONTEXT_OFFSET 0x0684 + +/*Buffer for 8 FDB entries to be added by 'Add Multiple FDB entries IOCTL*/ +#define FDB_CMD_BUFFER 0x0894 + +/*16B for Host Egress MSMC Q (Express) context*/ +#define HOST_RX_Q_EXP_CONTEXT_OFFSET 0x0940 + +/*Start of 32 bits PA_STAT counters*/ +#define PA_STAT_32b_START_OFFSET 0x0080 + +#endif +/* __NET_TI_ICSSG_SWITCH_MAP_H */ diff --git a/drivers/rtc/goldfish_rtc.c b/drivers/rtc/goldfish_rtc.c index 1ace9903858..3231eb0daf8 100644 --- a/drivers/rtc/goldfish_rtc.c +++ b/drivers/rtc/goldfish_rtc.c @@ -72,7 +72,7 @@ static int goldfish_rtc_set(struct udevice *dev, const struct rtc_time *time) return 0; } -int goldfish_rtc_probe(struct udevice *dev) +static int goldfish_rtc_probe(struct udevice *dev) { struct goldfish_rtc *priv = dev_get_priv(dev); fdt_addr_t addr; diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c index ce08a6b4486..3f2be72b830 100644 --- a/drivers/serial/serial_lpuart.c +++ b/drivers/serial/serial_lpuart.c @@ -109,28 +109,35 @@ u32 __weak get_lpuart_clk(void) } #if CONFIG_IS_ENABLED(CLK) -static int get_lpuart_clk_rate(struct udevice *dev, u32 *clk) +static int get_lpuart_clk_rate(struct udevice *dev, u32 *clk_rate) { - struct clk per_clk; + struct lpuart_serial_plat *plat = dev_get_plat(dev); + struct clk clk; ulong rate; int ret; + char *name; - ret = clk_get_by_name(dev, "per", &per_clk); + if (plat->devtype == DEV_MX7ULP) + name = "ipg"; + else + name = "per"; + + ret = clk_get_by_name(dev, name, &clk); if (ret) { - dev_err(dev, "Failed to get per clk: %d\n", ret); + dev_err(dev, "Failed to get clk: %d\n", ret); return ret; } - rate = clk_get_rate(&per_clk); + rate = clk_get_rate(&clk); if ((long)rate <= 0) { - dev_err(dev, "Failed to get per clk rate: %ld\n", (long)rate); + dev_err(dev, "Failed to get clk rate: %ld\n", (long)rate); return ret; } - *clk = rate; + *clk_rate = rate; return 0; } #else -static inline int get_lpuart_clk_rate(struct udevice *dev, u32 *clk) +static inline int get_lpuart_clk_rate(struct udevice *dev, u32 *clk_rate) { return -ENOSYS; } #endif @@ -479,19 +486,22 @@ static int lpuart_serial_pending(struct udevice *dev, bool input) static int lpuart_serial_probe(struct udevice *dev) { #if CONFIG_IS_ENABLED(CLK) + struct lpuart_serial_plat *plat = dev_get_plat(dev); struct clk per_clk; struct clk ipg_clk; int ret; - ret = clk_get_by_name(dev, "per", &per_clk); - if (!ret) { - ret = clk_enable(&per_clk); - if (ret) { - dev_err(dev, "Failed to enable per clk: %d\n", ret); - return ret; + if (plat->devtype != DEV_MX7ULP) { + ret = clk_get_by_name(dev, "per", &per_clk); + if (!ret) { + ret = clk_enable(&per_clk); + if (ret) { + dev_err(dev, "Failed to enable per clk: %d\n", ret); + return ret; + } + } else { + debug("%s: Failed to get per clk: %d\n", __func__, ret); } - } else { - debug("%s: Failed to get per clk: %d\n", __func__, ret); } ret = clk_get_by_name(dev, "ipg", &ipg_clk); diff --git a/drivers/serial/serial_xuartlite.c b/drivers/serial/serial_xuartlite.c index b6197da97cc..35df413321f 100644 --- a/drivers/serial/serial_xuartlite.c +++ b/drivers/serial/serial_xuartlite.c @@ -23,7 +23,7 @@ #define ULITE_CONTROL_RST_TX 0x01 #define ULITE_CONTROL_RST_RX 0x02 -static bool little_endian; +static bool little_endian __section(".data"); struct uartlite { unsigned int rx_fifo; diff --git a/drivers/soc/soc_xilinx_zynqmp.c b/drivers/soc/soc_xilinx_zynqmp.c index 786825d920c..d8b4f172a39 100644 --- a/drivers/soc/soc_xilinx_zynqmp.c +++ b/drivers/soc/soc_xilinx_zynqmp.c @@ -44,6 +44,7 @@ enum { ZYNQMP_VARIANT_DR = BIT(3), ZYNQMP_VARIANT_DR_SE = BIT(4), ZYNQMP_VARIANT_EG_SE = BIT(5), + ZYNQMP_VARIANT_TEG = BIT(6), }; struct zynqmp_device { @@ -75,6 +76,11 @@ static const struct zynqmp_device zynqmp_devices[] = { .variants = ZYNQMP_VARIANT_EG | ZYNQMP_VARIANT_CG, }, { + .id = 0x04718093, + .device = 3, + .variants = ZYNQMP_VARIANT_TEG, + }, + { .id = 0x04721093, .device = 4, .variants = ZYNQMP_VARIANT_EG | ZYNQMP_VARIANT_CG | @@ -299,6 +305,8 @@ static int soc_xilinx_zynqmp_detect_machine(struct udevice *dev, u32 idcode, strlcat(priv->machine, "dr", sizeof(priv->machine)); } else if (device->variants & ZYNQMP_VARIANT_DR_SE) { strlcat(priv->machine, "dr_SE", sizeof(priv->machine)); + } else if (device->variants & ZYNQMP_VARIANT_TEG) { + strlcat(priv->machine, "teg", sizeof(priv->machine)); } return 0; diff --git a/drivers/sysreset/sysreset-ti-sci.c b/drivers/sysreset/sysreset-ti-sci.c index 5fc05c46cb0..0de132633a8 100644 --- a/drivers/sysreset/sysreset-ti-sci.c +++ b/drivers/sysreset/sysreset-ti-sci.c @@ -60,15 +60,9 @@ static struct sysreset_ops ti_sci_sysreset_ops = { .request = ti_sci_sysreset_request, }; -static const struct udevice_id ti_sci_sysreset_of_match[] = { - { .compatible = "ti,sci-sysreset", }, - { /* sentinel */ }, -}; - U_BOOT_DRIVER(ti_sci_sysreset) = { .name = "ti-sci-sysreset", .id = UCLASS_SYSRESET, - .of_match = ti_sci_sysreset_of_match, .probe = ti_sci_sysreset_probe, .priv_auto = sizeof(struct ti_sci_sysreset_data), .ops = &ti_sci_sysreset_ops, diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index b60661fe05e..910c5f3352b 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -685,6 +685,9 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, reset_ep(udev, ep_index); ring = virt_dev->eps[ep_index].ring; + if (!ring) + return -EINVAL; + /* * How much data is (potentially) left before the 64KB boundary? * XHCI Spec puts restriction( TABLE 49 and 6.4.1 section of XHCI Spec) @@ -871,6 +874,8 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, ep_index = usb_pipe_ep_index(pipe); ep_ring = virt_dev->eps[ep_index].ring; + if (!ep_ring) + return -EINVAL; /* * Check to see if the max packet size for the default control diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index d13cbff9b37..741e186ee05 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -475,67 +475,34 @@ static int xhci_configure_endpoints(struct usb_device *udev, bool ctx_change) } /** - * Configure the endpoint, programming the device contexts. + * Fill endpoint contexts for interface descriptor ifdesc. * - * @param udev pointer to the USB device structure - * Return: returns the status of the xhci_configure_endpoints + * @param udev pointer to the USB device structure + * @param ctrl pointer to the xhci pravte device structure + * @param virt_dev pointer to the xhci virtual device structure + * @param ifdesc pointer to the USB interface config descriptor + * Return: returns the status of xhci_init_ep_contexts_if */ -static int xhci_set_configuration(struct usb_device *udev) +static int xhci_init_ep_contexts_if(struct usb_device *udev, + struct xhci_ctrl *ctrl, + struct xhci_virt_device *virt_dev, + struct usb_interface *ifdesc + ) { - struct xhci_container_ctx *in_ctx; - struct xhci_container_ctx *out_ctx; - struct xhci_input_control_ctx *ctrl_ctx; - struct xhci_slot_ctx *slot_ctx; struct xhci_ep_ctx *ep_ctx[MAX_EP_CTX_NUM]; int cur_ep; - int max_ep_flag = 0; int ep_index; unsigned int dir; unsigned int ep_type; - struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); - int num_of_ep; - int ep_flag = 0; u64 trb_64 = 0; - int slot_id = udev->slot_id; - struct xhci_virt_device *virt_dev = ctrl->devs[slot_id]; - struct usb_interface *ifdesc; u32 max_esit_payload; unsigned int interval; unsigned int mult; unsigned int max_burst; unsigned int avg_trb_len; unsigned int err_count = 0; + int num_of_ep = ifdesc->no_of_ep; - out_ctx = virt_dev->out_ctx; - in_ctx = virt_dev->in_ctx; - - num_of_ep = udev->config.if_desc[0].no_of_ep; - ifdesc = &udev->config.if_desc[0]; - - ctrl_ctx = xhci_get_input_control_ctx(in_ctx); - /* Initialize the input context control */ - ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG); - ctrl_ctx->drop_flags = 0; - - /* EP_FLAG gives values 1 & 4 for EP1OUT and EP2IN */ - for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) { - ep_flag = xhci_get_ep_index(&ifdesc->ep_desc[cur_ep]); - ctrl_ctx->add_flags |= cpu_to_le32(1 << (ep_flag + 1)); - if (max_ep_flag < ep_flag) - max_ep_flag = ep_flag; - } - - xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size); - - /* slot context */ - xhci_slot_copy(ctrl, in_ctx, out_ctx); - slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx); - slot_ctx->dev_info &= ~(cpu_to_le32(LAST_CTX_MASK)); - slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(max_ep_flag + 1) | 0); - - xhci_endpoint_copy(ctrl, in_ctx, out_ctx, 0); - - /* filling up ep contexts */ for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) { struct usb_endpoint_descriptor *endpt_desc = NULL; struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc = NULL; @@ -561,7 +528,8 @@ static int xhci_set_configuration(struct usb_device *udev) avg_trb_len = max_esit_payload; ep_index = xhci_get_ep_index(endpt_desc); - ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); + ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, virt_dev->in_ctx, + ep_index); /* Allocate the ep rings */ virt_dev->eps[ep_index].ring = xhci_ring_alloc(ctrl, 1, true); @@ -614,6 +582,72 @@ static int xhci_set_configuration(struct usb_device *udev) } } + return 0; +} + +/** + * Configure the endpoint, programming the device contexts. + * + * @param udev pointer to the USB device structure + * Return: returns the status of the xhci_configure_endpoints + */ +static int xhci_set_configuration(struct usb_device *udev) +{ + struct xhci_container_ctx *out_ctx; + struct xhci_container_ctx *in_ctx; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_slot_ctx *slot_ctx; + int err; + int cur_ep; + int max_ep_flag = 0; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + int num_of_ep; + int ep_flag = 0; + int slot_id = udev->slot_id; + struct xhci_virt_device *virt_dev = ctrl->devs[slot_id]; + struct usb_interface *ifdesc; + unsigned int ifnum; + unsigned int max_ifnum = min((unsigned int)USB_MAX_ACTIVE_INTERFACES, + (unsigned int)udev->config.no_of_if); + + out_ctx = virt_dev->out_ctx; + in_ctx = virt_dev->in_ctx; + + ctrl_ctx = xhci_get_input_control_ctx(in_ctx); + /* Initialize the input context control */ + ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG); + ctrl_ctx->drop_flags = 0; + + for (ifnum = 0; ifnum < max_ifnum; ifnum++) { + ifdesc = &udev->config.if_desc[ifnum]; + num_of_ep = ifdesc->no_of_ep; + /* EP_FLAG gives values 1 & 4 for EP1OUT and EP2IN */ + for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) { + ep_flag = xhci_get_ep_index(&ifdesc->ep_desc[cur_ep]); + ctrl_ctx->add_flags |= cpu_to_le32(1 << (ep_flag + 1)); + if (max_ep_flag < ep_flag) + max_ep_flag = ep_flag; + } + } + + xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size); + + /* slot context */ + xhci_slot_copy(ctrl, in_ctx, out_ctx); + slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx); + slot_ctx->dev_info &= ~(cpu_to_le32(LAST_CTX_MASK)); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(max_ep_flag + 1) | 0); + + xhci_endpoint_copy(ctrl, in_ctx, out_ctx, 0); + + /* filling up ep contexts */ + for (ifnum = 0; ifnum < max_ifnum; ifnum++) { + ifdesc = &udev->config.if_desc[ifnum]; + err = xhci_init_ep_contexts_if(udev, ctrl, virt_dev, ifdesc); + if (err < 0) + return err; + } + return xhci_configure_endpoints(udev, false); } diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 6f319ba0d54..39c82521be1 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -180,6 +180,7 @@ config CONSOLE_ROTATION config CONSOLE_TRUETYPE bool "Support a console that uses TrueType fonts" + select X86_HARDFP if X86 help TrueTrype fonts can provide outline-drawing capability rather than needing to provide a bitmap for each font and size that is needed. diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 362458aecd4..28665a32757 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -8,6 +8,7 @@ #include <dm.h> #include <log.h> #include <malloc.h> +#include <spl.h> #include <video.h> #include <video_console.h> @@ -802,6 +803,9 @@ static int truetype_entry_save(struct udevice *dev, struct abuf *buf) struct console_tt_store store; const uint size = sizeof(store); + if (spl_phase() <= PHASE_SPL) + return -ENOSYS; + /* * store the whole priv structure as it is simpler that picking out * what we need @@ -823,6 +827,9 @@ static int truetype_entry_restore(struct udevice *dev, struct abuf *buf) struct console_tt_priv *priv = dev_get_priv(dev); struct console_tt_store store; + if (spl_phase() <= PHASE_SPL) + return -ENOSYS; + memcpy(&store, abuf_data(buf), sizeof(store)); vc_priv->xcur_frac = store.cur.xpos_frac; @@ -847,6 +854,9 @@ static int truetype_set_cursor_visible(struct udevice *dev, bool visible, uint out, val; int ret; + if (spl_phase() <= PHASE_SPL) + return -ENOSYS; + if (!visible) return 0; diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index 5f89f6a5219..5d06e51ff23 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -126,6 +126,7 @@ void vidconsole_set_cursor_pos(struct udevice *dev, int x, int y) priv->xcur_frac = VID_TO_POS(x); priv->xstart_frac = priv->xcur_frac; priv->ycur = y; + vidconsole_entry_start(dev); } /** @@ -135,8 +136,10 @@ void vidconsole_set_cursor_pos(struct udevice *dev, int x, int y) * @row: new row * @col: new column */ -static void set_cursor_position(struct vidconsole_priv *priv, int row, int col) +static void set_cursor_position(struct udevice *dev, int row, int col) { + struct vidconsole_priv *priv = dev_get_uclass_priv(dev); + /* * Ensure we stay in the bounds of the screen. */ @@ -145,9 +148,7 @@ static void set_cursor_position(struct vidconsole_priv *priv, int row, int col) if (col >= priv->cols) col = priv->cols - 1; - priv->ycur = row * priv->y_charsize; - priv->xcur_frac = priv->xstart_frac + - VID_TO_POS(col * priv->x_charsize); + vidconsole_position_cursor(dev, col, row); } /** @@ -194,7 +195,7 @@ static void vidconsole_escape_char(struct udevice *dev, char ch) int row = priv->row_saved; int col = priv->col_saved; - set_cursor_position(priv, row, col); + set_cursor_position(dev, row, col); priv->escape = 0; return; } @@ -256,7 +257,7 @@ static void vidconsole_escape_char(struct udevice *dev, char ch) if (row < 0) row = 0; /* Right and bottom overflows are handled in the callee. */ - set_cursor_position(priv, row, col); + set_cursor_position(dev, row, col); break; } case 'H': @@ -280,7 +281,7 @@ static void vidconsole_escape_char(struct udevice *dev, char ch) if (col) --col; - set_cursor_position(priv, row, col); + set_cursor_position(dev, row, col); break; } |