diff options
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | arch/riscv/cpu/th1520/dram.c | 16 | ||||
-rw-r--r-- | arch/riscv/dts/th1520-lichee-module-4a.dtsi | 119 | ||||
-rw-r--r-- | arch/riscv/dts/th1520.dtsi | 42 | ||||
-rw-r--r-- | board/microchip/mpfs_icicle/mpfs_icicle.c | 11 | ||||
-rw-r--r-- | configs/microchip_mpfs_icicle_defconfig | 1 | ||||
-rw-r--r-- | configs/th1520_lpi4a_defconfig | 7 | ||||
-rw-r--r-- | drivers/clk/thead/clk-th1520-ap.c | 4 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 5 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/mpfs_gpio.c | 198 | ||||
-rw-r--r-- | drivers/mmc/Kconfig | 26 | ||||
-rw-r--r-- | drivers/mmc/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/kona_sdhci.c | 132 | ||||
-rw-r--r-- | drivers/mmc/mmc.c | 6 | ||||
-rw-r--r-- | drivers/net/Kconfig | 8 | ||||
-rw-r--r-- | drivers/net/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/dwmac_thead.c | 288 | ||||
-rw-r--r-- | drivers/spi/microchip_coreqspi.c | 113 |
19 files changed, 824 insertions, 156 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index f98acbc8885..ce7f7e03505 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1576,6 +1576,7 @@ M: Yao Zi <ziyao@disroot.org> S: Maintained F: arch/riscv/cpu/th1520/ F: drivers/clk/thead/clk-th1520-ap.c +F: drivers/net/dwmac_thead.c F: drivers/pinctrl/pinctrl-th1520.c F: drivers/ram/thead/th1520_ddr.c diff --git a/arch/riscv/cpu/th1520/dram.c b/arch/riscv/cpu/th1520/dram.c index 91007c0a3d3..8a0ca26785e 100644 --- a/arch/riscv/cpu/th1520/dram.c +++ b/arch/riscv/cpu/th1520/dram.c @@ -19,3 +19,19 @@ int dram_init_banksize(void) { return fdtdec_setup_memory_banksize(); } + +phys_addr_t board_get_usable_ram_top(phys_size_t total_size) +{ + /* + * Ensure that we run from first 4GB so that all + * addresses used by U-Boot are 32bit addresses. + * + * This in-turn ensures that 32bit DMA capable + * devices work fine because DMA mapping APIs will + * provide 32bit DMA addresses only. + */ + if (gd->ram_top > SZ_4G) + return SZ_4G; + + return gd->ram_top; +} diff --git a/arch/riscv/dts/th1520-lichee-module-4a.dtsi b/arch/riscv/dts/th1520-lichee-module-4a.dtsi index 9b255f8243c..eecd3e9832a 100644 --- a/arch/riscv/dts/th1520-lichee-module-4a.dtsi +++ b/arch/riscv/dts/th1520-lichee-module-4a.dtsi @@ -11,6 +11,11 @@ model = "Sipeed Lichee Module 4A"; compatible = "sipeed,lichee-module-4a", "thead,th1520"; + aliases { + ethernet0 = &gmac0; + ethernet1 = &gmac1; + }; + memory@0 { device_type = "memory"; reg = <0x0 0x00000000 0x2 0x00000000>; @@ -38,6 +43,120 @@ status = "okay"; }; +&gmac0 { + pinctrl-names = "default"; + pinctrl-0 = <&gmac0_pins>, <&mdio0_pins>; + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; + status = "okay"; +}; + +&gmac1 { + pinctrl-names = "default"; + pinctrl-0 = <&gmac1_pins>; + phy-handle = <&phy1>; + phy-mode = "rgmii-id"; + status = "okay"; +}; + +&mdio0 { + phy0: ethernet-phy@1 { + reg = <1>; + }; + + phy1: ethernet-phy@2 { + reg = <2>; + }; +}; + +&padctrl0_apsys { + gmac0_pins: gmac0-0 { + tx-pins { + pins = "GMAC0_TX_CLK", + "GMAC0_TXEN", + "GMAC0_TXD0", + "GMAC0_TXD1", + "GMAC0_TXD2", + "GMAC0_TXD3"; + function = "gmac0"; + bias-disable; + drive-strength = <25>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + + rx-pins { + pins = "GMAC0_RX_CLK", + "GMAC0_RXDV", + "GMAC0_RXD0", + "GMAC0_RXD1", + "GMAC0_RXD2", + "GMAC0_RXD3"; + function = "gmac0"; + bias-disable; + drive-strength = <1>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + + gmac1_pins: gmac1-0 { + tx-pins { + pins = "GPIO2_18", /* GMAC1_TX_CLK */ + "GPIO2_20", /* GMAC1_TXEN */ + "GPIO2_21", /* GMAC1_TXD0 */ + "GPIO2_22", /* GMAC1_TXD1 */ + "GPIO2_23", /* GMAC1_TXD2 */ + "GPIO2_24"; /* GMAC1_TXD3 */ + function = "gmac1"; + bias-disable; + drive-strength = <25>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + + rx-pins { + pins = "GPIO2_19", /* GMAC1_RX_CLK */ + "GPIO2_25", /* GMAC1_RXDV */ + "GPIO2_30", /* GMAC1_RXD0 */ + "GPIO2_31", /* GMAC1_RXD1 */ + "GPIO3_0", /* GMAC1_RXD2 */ + "GPIO3_1"; /* GMAC1_RXD3 */ + function = "gmac1"; + bias-disable; + drive-strength = <1>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + + mdio0_pins: mdio0-0 { + mdc-pins { + pins = "GMAC0_MDC"; + function = "gmac0"; + bias-disable; + drive-strength = <13>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + + mdio-pins { + pins = "GMAC0_MDIO"; + function = "gmac0"; + bias-disable; + drive-strength = <13>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; +}; + &sdio0 { bus-width = <4>; max-frequency = <198000000>; diff --git a/arch/riscv/dts/th1520.dtsi b/arch/riscv/dts/th1520.dtsi index 8306eda5521..c46925a132a 100644 --- a/arch/riscv/dts/th1520.dtsi +++ b/arch/riscv/dts/th1520.dtsi @@ -177,6 +177,48 @@ status = "disabled"; }; + gmac1: ethernet@ffe7060000 { + compatible = "thead,th1520-gmac", "snps,dwmac-3.70a"; + reg = <0xff 0xe7060000 0x0 0x2000>, <0xff 0xec004000 0x0 0x1000>; + reg-names = "dwmac", "apb"; + interrupts = <67 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq"; + clocks = <&clk CLK_GMAC_AXI>, <&clk CLK_GMAC1>; + clock-names = "stmmaceth", "pclk"; + snps,pbl = <32>; + snps,fixed-burst; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <32>; + status = "disabled"; + + mdio1: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + gmac0: ethernet@ffe7070000 { + compatible = "thead,th1520-gmac", "snps,dwmac-3.70a"; + reg = <0xff 0xe7070000 0x0 0x2000>, <0xff 0xec003000 0x0 0x1000>; + reg-names = "dwmac", "apb"; + interrupts = <66 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq"; + clocks = <&clk CLK_GMAC_AXI>, <&clk CLK_GMAC0>; + clock-names = "stmmaceth", "pclk"; + snps,pbl = <32>; + snps,fixed-burst; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <32>; + status = "disabled"; + + mdio0: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + emmc: mmc@ffe7080000 { compatible = "thead,th1520-dwcmshc"; reg = <0xff 0xe7080000 0x0 0x10000>; diff --git a/board/microchip/mpfs_icicle/mpfs_icicle.c b/board/microchip/mpfs_icicle/mpfs_icicle.c index 6b6984eae3f..ba622e38ee5 100644 --- a/board/microchip/mpfs_icicle/mpfs_icicle.c +++ b/board/microchip/mpfs_icicle/mpfs_icicle.c @@ -73,13 +73,22 @@ int board_fit_config_name_match(const char *name) for (int i = 0; i < list_len; i++) { int len, match; const char *compat; + char copy[64]; char *devendored; compat = fdt_stringlist_get(fdt, 0, "compatible", i, &len); if (!compat) return -EINVAL; - strtok((char *)compat, ","); + /* + * The naming scheme for compatibles doesn't produce anything + * close to this long. + */ + if (len >= 64) + return -EINVAL; + + strncpy(copy, compat, 64); + strtok(copy, ","); devendored = strtok(NULL, ","); if (!devendored) diff --git a/configs/microchip_mpfs_icicle_defconfig b/configs/microchip_mpfs_icicle_defconfig index bb110225488..6937aa224a1 100644 --- a/configs/microchip_mpfs_icicle_defconfig +++ b/configs/microchip_mpfs_icicle_defconfig @@ -7,6 +7,7 @@ CONFIG_ENV_SIZE=0x2000 CONFIG_DEFAULT_DEVICE_TREE="microchip/mpfs-icicle-kit" CONFIG_SYS_LOAD_ADDR=0x80200000 CONFIG_SYS_MEM_TOP_HIDE=0x400000 +# CONFIG_DEBUG_UART is not set CONFIG_TARGET_MICROCHIP_ICICLE=y CONFIG_ARCH_RV64I=y CONFIG_RISCV_SMODE=y diff --git a/configs/th1520_lpi4a_defconfig b/configs/th1520_lpi4a_defconfig index 78e3b25ab82..2763cbb428a 100644 --- a/configs/th1520_lpi4a_defconfig +++ b/configs/th1520_lpi4a_defconfig @@ -76,7 +76,7 @@ CONFIG_MMC_SPEED_MODE_SET=y CONFIG_PARTITION_TYPE_GUID=y CONFIG_ENV_RELOC_GD_ENV_ADDR=y CONFIG_VERSION_VARIABLE=y -CONFIG_NO_NET=y +CONFIG_NET_RANDOM_ETHADDR=y # CONFIG_BLOCK_CACHE is not set CONFIG_DWAPB_GPIO=y # CONFIG_I2C is not set @@ -90,6 +90,11 @@ CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_ADMA=y CONFIG_MMC_SDHCI_SNPS=y # CONFIG_MTD is not set +CONFIG_PHY_REALTEK=y +CONFIG_DM_MDIO=y +CONFIG_DM_ETH_PHY=y +CONFIG_ETH_DESIGNWARE=y +CONFIG_ETH_DESIGNWARE_THEAD=y CONFIG_PINCTRL=y # CONFIG_POWER is not set CONFIG_RAM=y diff --git a/drivers/clk/thead/clk-th1520-ap.c b/drivers/clk/thead/clk-th1520-ap.c index b80ad05b8ad..822cf0809d5 100644 --- a/drivers/clk/thead/clk-th1520-ap.c +++ b/drivers/clk/thead/clk-th1520-ap.c @@ -32,6 +32,7 @@ struct ccu_internal { struct ccu_div_internal { u8 shift; u8 width; + unsigned long flags; }; struct ccu_common { @@ -79,6 +80,7 @@ struct ccu_pll { { \ .shift = _shift, \ .width = _width, \ + .flags = _flags, \ } #define CCU_GATE(_clkid, _struct, _name, _parent, _reg, _gate, _flags) \ @@ -182,7 +184,7 @@ static unsigned long ccu_div_get_rate(struct clk *clk) val = val >> cd->div.shift; val &= GENMASK(cd->div.width - 1, 0); rate = divider_recalc_rate(clk, clk_get_parent_rate(clk), val, NULL, - 0, cd->div.width); + cd->div.flags, cd->div.width); return rate; } diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c7da1f8a52a..58e464106a3 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -719,5 +719,10 @@ config SPL_ADP5585_GPIO depends on SPL_DM_GPIO && SPL_I2C help Support ADP5585 GPIO expander in SPL. +config MPFS_GPIO + bool "Enable Polarfire SoC GPIO driver" + depends on DM_GPIO + help + Enable to support the GPIO driver on Polarfire SoC endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index a5ef1c9e0d8..83e10c79b91 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -80,3 +80,4 @@ obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o obj-$(CONFIG_FTGPIO010) += ftgpio010.o obj-$(CONFIG_$(PHASE_)ADP5585_GPIO) += adp5585_gpio.o obj-$(CONFIG_RZG2L_GPIO) += rzg2l-gpio.o +obj-$(CONFIG_MPFS_GPIO) += mpfs_gpio.o diff --git a/drivers/gpio/mpfs_gpio.c b/drivers/gpio/mpfs_gpio.c new file mode 100644 index 00000000000..9bbeada4ef5 --- /dev/null +++ b/drivers/gpio/mpfs_gpio.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Microchip Technology Inc. + * Eoin Dickson <eoin.dickson@microchip.com> + */ + +#include <dm.h> +#include <asm-generic/gpio.h> +#include <asm/io.h> +#include <errno.h> +#include <asm/gpio.h> +#include <linux/bitops.h> + +#define MPFS_INP_REG 0x84 +#define COREGPIO_INP_REG 0x90 +#define MPFS_OUTP_REG 0x88 +#define COREGPIO_OUTP_REG 0xA0 +#define MPFS_GPIO_CTRL(i) (0x4 * (i)) +#define MPFS_MAX_NUM_GPIO 32 +#define MPFS_GPIO_EN_OUT_BUF BIT(2) +#define MPFS_GPIO_EN_IN BIT(1) +#define MPFS_GPIO_EN_OUT BIT(0) + +struct mpfs_gpio_reg_offsets { + u8 inp; + u8 outp; +}; + +struct mchp_gpio_plat { + void *base; + const struct mpfs_gpio_reg_offsets *regs; +}; + +static void mchp_update_gpio_reg(void *bptr, u32 offset, bool value) +{ + void __iomem *ptr = (void __iomem *)bptr; + + u32 old = readl(ptr); + + if (value) + writel(old | offset, ptr); + else + writel(old & ~offset, ptr); +} + +static int mchp_gpio_direction_input(struct udevice *dev, u32 offset) +{ + struct mchp_gpio_plat *plat = dev_get_plat(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + if (offset > uc_priv->gpio_count) + return -EINVAL; + + mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_IN, true); + mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_OUT, false); + mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_OUT_BUF, false); + + return 0; +} + +static int mchp_gpio_direction_output(struct udevice *dev, u32 offset, int value) +{ + struct mchp_gpio_plat *plat = dev_get_plat(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + if (offset > uc_priv->gpio_count) + return -EINVAL; + + mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_IN, false); + mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_OUT, true); + mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_OUT_BUF, true); + + mchp_update_gpio_reg(plat->base + plat->regs->outp, BIT(offset), value); + + return 0; +} + +static bool mchp_gpio_get_value(struct udevice *dev, u32 offset) +{ + struct mchp_gpio_plat *plat = dev_get_plat(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + int val, input; + + if (offset > uc_priv->gpio_count) + return -EINVAL; + + input = readl(plat->base + MPFS_GPIO_CTRL(offset)) & MPFS_GPIO_EN_IN; + + if (input) + val = (readl(plat->base + plat->regs->inp) & BIT(offset)); + else + val = (readl(plat->base + plat->regs->outp) & BIT(offset)); + + return val >> offset; +} + +static int mchp_gpio_set_value(struct udevice *dev, u32 offset, int value) +{ + struct mchp_gpio_plat *plat = dev_get_plat(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + if (offset > uc_priv->gpio_count) + return -EINVAL; + + mchp_update_gpio_reg(plat->base + plat->regs->outp, BIT(offset), value); + + return 0; +} + +static int mchp_gpio_get_function(struct udevice *dev, unsigned int offset) +{ + struct mchp_gpio_plat *plat = dev_get_plat(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + u32 outdir, indir, val; + + if (offset > uc_priv->gpio_count) + return -EINVAL; + + /* Get direction of the pin */ + outdir = readl(plat->base + MPFS_GPIO_CTRL(offset)) & MPFS_GPIO_EN_OUT; + indir = readl(plat->base + MPFS_GPIO_CTRL(offset)) & MPFS_GPIO_EN_IN; + + if (outdir) + val = GPIOF_OUTPUT; + else if (indir) + val = GPIOF_INPUT; + else + val = GPIOF_UNUSED; + + return val; +} + +static int mchp_gpio_probe(struct udevice *dev) +{ + struct mchp_gpio_plat *plat = dev_get_plat(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + char name[18], *str; + + plat->regs = dev_get_driver_data(dev); + sprintf(name, "gpio@%4lx_", (uintptr_t)plat->base); + str = strdup(name); + if (!str) + return -ENOMEM; + uc_priv->bank_name = str; + uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", MPFS_MAX_NUM_GPIO); + + return 0; +} + +static const struct mpfs_gpio_reg_offsets mpfs_reg_offsets = { + .inp = MPFS_INP_REG, + .outp = MPFS_OUTP_REG, +}; + +static const struct mpfs_gpio_reg_offsets coregpio_reg_offsets = { + .inp = COREGPIO_INP_REG, + .outp = COREGPIO_OUTP_REG, +}; + +static const struct udevice_id mchp_gpio_match[] = { + { + .compatible = "microchip,mpfs-gpio", + .data = &mpfs_reg_offsets, + }, { + .compatible = "microchip,coregpio-rtl-v3", + .data = &coregpio_reg_offsets, + }, + { /* end of list */ } +}; + +static const struct dm_gpio_ops mchp_gpio_ops = { + .direction_input = mchp_gpio_direction_input, + .direction_output = mchp_gpio_direction_output, + .get_value = mchp_gpio_get_value, + .set_value = mchp_gpio_set_value, + .get_function = mchp_gpio_get_function, +}; + +static int mchp_gpio_of_to_plat(struct udevice *dev) +{ + struct mchp_gpio_plat *plat = dev_get_plat(dev); + + plat->base = dev_read_addr_ptr(dev); + if (!plat->base) + return -EINVAL; + + return 0; +} + +U_BOOT_DRIVER(gpio_mpfs) = { + .name = "gpio_mpfs", + .id = UCLASS_GPIO, + .of_match = mchp_gpio_match, + .of_to_plat = of_match_ptr(mchp_gpio_of_to_plat), + .plat_auto = sizeof(struct mchp_gpio_plat), + .ops = &mchp_gpio_ops, + .probe = mchp_gpio_probe, +}; diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 845b653731f..4c46df0ffb8 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -284,7 +284,7 @@ config MMC_DW_K3 config MMC_DW_ROCKCHIP bool "Rockchip SD/MMC controller support" - depends on OF_CONTROL + depends on OF_CONTROL && ARCH_ROCKCHIP depends on MMC_DW help This enables support for the Rockchip SD/MMM controller, which is @@ -382,6 +382,7 @@ config MVEBU_MMC config MMC_OMAP_HS bool "TI OMAP High Speed Multimedia Card Interface support" + depends on ARCH_KEYSTONE || ARCH_OMAP2PLUS select DM_REGULATOR_PBIAS if DM_REGULATOR help This selects the TI OMAP High Speed Multimedia card Interface. @@ -588,7 +589,7 @@ config MMC_SDHCI_BCM2835 config MMC_SDHCI_BCMSTB tristate "SDHCI support for the BCMSTB SD/MMC Controller" - depends on MMC_SDHCI + depends on MMC_SDHCI && (ARCH_BCMSTB || ARCH_BCM283X) help This selects the Broadcom set-top box SD/MMC controller. @@ -651,19 +652,9 @@ config MMC_SDHCI_F_SDH30 If you have a controller with this interface, say Y here. If unsure, say N. -config MMC_SDHCI_KONA - bool "SDHCI support on Broadcom KONA platform" - depends on MMC_SDHCI - help - This selects the Broadcom Kona Secure Digital Host Controller - Interface(SDHCI) support. - This is used in Broadcom mobile SoCs. - - If you have a controller with this interface, say Y here. - config MMC_SDHCI_MSM bool "Qualcomm SDHCI controller" - depends on MMC_SDHCI + depends on MMC_SDHCI && ARCH_SNAPDRAGON help Enables support for SDHCI 2.0 controller present on some Qualcomm Snapdragon devices. This device is compatible with eMMC v4.5 and @@ -709,7 +700,7 @@ config MMC_SDHCI_ROCKCHIP config MMC_SDHCI_S5P bool "SDHCI support on Samsung S5P SoC" - depends on MMC_SDHCI + depends on MMC_SDHCI && S5P help This selects the Secure Digital Host Controller Interface (SDHCI) on Samsung S5P SoCs. @@ -731,7 +722,7 @@ config MMC_SDHCI_SNPS config MMC_SDHCI_STI bool "SDHCI support for STMicroelectronics SoC" - depends on MMC_SDHCI && OF_CONTROL + depends on MMC_SDHCI && OF_CONTROL && ARCH_STI help This selects the Secure Digital Host Controller Interface (SDHCI) on STMicroelectronics STiH410 SoC. @@ -785,6 +776,7 @@ config TEGRA124_MMC_DISABLE_EXT_LOOPBACK config MMC_SDHCI_ZYNQ bool "Arasan SDHCI controller support" depends on OF_CONTROL + depends on ARCH_VERSAL || ARCH_VERSAL_NET || ARCH_VERSAL2 || ARCH_ZYNQ || ARCH_ZYNQMP depends on MMC_SDHCI help Support for Arasan SDHCI host controller on Zynq/ZynqMP ARM SoCs platform @@ -843,7 +835,7 @@ config GENERIC_ATMEL_MCI config STM32_SDMMC2 bool "STMicroelectronics STM32H7 SD/MMC Host Controller support" - depends on OF_CONTROL + depends on OF_CONTROL && (ARCH_STM32 || ARCH_STM32MP) 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, @@ -877,6 +869,7 @@ config FSL_SDHC_V2_3 config FSL_ESDHC bool "Freescale/NXP eSDHC controller support" + depends on ARCH_LS1021A || FSL_LSCH2 || FSL_LSCH3 || PPC select FSL_SDHC_V2_3 if ARCH_P1010 || ARCH_BSC9131 || ARCH_BSC9132 \ || ARCH_C29X help @@ -929,6 +922,7 @@ config ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE config FSL_ESDHC_IMX bool "Freescale/NXP i.MX eSDHC controller support" + depends on MACH_IMX help This selects support for the i.MX eSDHC (Enhanced Secure Digital Host Controller) found on numerous Freescale/NXP SoCs. diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 280187c6bab..a23336d7d8d 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -63,7 +63,6 @@ obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence6.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 obj-$(CONFIG_MMC_SDHCI_MSM) += msm_sdhci.o obj-$(CONFIG_MMC_SDHCI_MV) += mv_sdhci.o obj-$(CONFIG_MMC_SDHCI_NPCM) += npcm_sdhci.o diff --git a/drivers/mmc/kona_sdhci.c b/drivers/mmc/kona_sdhci.c deleted file mode 100644 index 83f14122632..00000000000 --- a/drivers/mmc/kona_sdhci.c +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright 2013 Broadcom Corporation. - */ - -#include <malloc.h> -#include <sdhci.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <asm/kona-common/clk.h> - -#define SDHCI_CORECTRL_OFFSET 0x00008000 -#define SDHCI_CORECTRL_EN 0x01 -#define SDHCI_CORECTRL_RESET 0x02 - -#define SDHCI_CORESTAT_OFFSET 0x00008004 -#define SDHCI_CORESTAT_CD_SW 0x01 - -#define SDHCI_COREIMR_OFFSET 0x00008008 -#define SDHCI_COREIMR_IP 0x01 - -static int init_kona_mmc_core(struct sdhci_host *host) -{ - unsigned int mask; - unsigned int timeout; - - if (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & SDHCI_RESET_ALL) { - printf("%s: sd host controller reset error\n", __func__); - return -EBUSY; - } - - /* For kona a hardware reset before anything else. */ - mask = sdhci_readl(host, SDHCI_CORECTRL_OFFSET) | SDHCI_CORECTRL_RESET; - sdhci_writel(host, mask, SDHCI_CORECTRL_OFFSET); - - /* Wait max 100 ms */ - timeout = 1000; - do { - if (timeout == 0) { - printf("%s: reset timeout error\n", __func__); - return -ETIMEDOUT; - } - timeout--; - udelay(100); - } while (0 == - (sdhci_readl(host, SDHCI_CORECTRL_OFFSET) & - SDHCI_CORECTRL_RESET)); - - /* Clear the reset bit. */ - mask = mask & ~SDHCI_CORECTRL_RESET; - sdhci_writel(host, mask, SDHCI_CORECTRL_OFFSET); - - /* Enable AHB clock */ - mask = sdhci_readl(host, SDHCI_CORECTRL_OFFSET); - sdhci_writel(host, mask | SDHCI_CORECTRL_EN, SDHCI_CORECTRL_OFFSET); - - /* Enable interrupts */ - sdhci_writel(host, SDHCI_COREIMR_IP, SDHCI_COREIMR_OFFSET); - - /* Make sure Card is detected in controller */ - mask = sdhci_readl(host, SDHCI_CORESTAT_OFFSET); - sdhci_writel(host, mask | SDHCI_CORESTAT_CD_SW, SDHCI_CORESTAT_OFFSET); - - /* Wait max 100 ms */ - timeout = 1000; - while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { - if (timeout == 0) { - printf("%s: CARD DETECT timeout error\n", __func__); - return -ETIMEDOUT; - } - timeout--; - udelay(100); - } - return 0; -} - -int kona_sdhci_init(int dev_index, u32 min_clk, u32 quirks) -{ - int ret = 0; - u32 max_clk; - void *reg_base; - struct sdhci_host *host = NULL; - - host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); - if (!host) { - printf("%s: sdhci host malloc fail!\n", __func__); - return -ENOMEM; - } - switch (dev_index) { - case 0: - reg_base = (void *)CONFIG_SYS_SDIO_BASE0; - ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO0_MAX_CLK, - &max_clk); - break; - case 1: - reg_base = (void *)CONFIG_SYS_SDIO_BASE1; - ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO1_MAX_CLK, - &max_clk); - break; - case 2: - reg_base = (void *)CONFIG_SYS_SDIO_BASE2; - ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO2_MAX_CLK, - &max_clk); - break; - case 3: - reg_base = (void *)CONFIG_SYS_SDIO_BASE3; - ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO3_MAX_CLK, - &max_clk); - break; - default: - printf("%s: sdio dev index %d not supported\n", - __func__, dev_index); - ret = -EINVAL; - } - if (ret) { - free(host); - return ret; - } - - host->name = "kona-sdhci"; - host->ioaddr = reg_base; - host->quirks = quirks; - host->max_clk = max_clk; - - if (init_kona_mmc_core(host)) { - free(host); - return -EINVAL; - } - - add_sdhci(host, 0, min_clk); - return ret; -} diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 2c1f4f9c336..5f2efbe6df9 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -2365,8 +2365,10 @@ static int mmc_startup_v4(struct mmc *mmc) return -ENOMEM; memcpy(mmc->ext_csd, ext_csd, MMC_MAX_BLOCK_LEN); #endif - if (ext_csd[EXT_CSD_REV] >= ARRAY_SIZE(mmc_versions)) - return -EINVAL; + if (ext_csd[EXT_CSD_REV] >= ARRAY_SIZE(mmc_versions)) { + err = -EINVAL; + goto error; + } mmc->version = mmc_versions[ext_csd[EXT_CSD_REV]]; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 950ed0f25a9..d942fa4e202 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -411,6 +411,14 @@ config ETH_DESIGNWARE_S700 This provides glue layer to use Synopsys Designware Ethernet MAC present on Actions S700 SoC. +config ETH_DESIGNWARE_THEAD + bool "T-Head glue driver for Synopsys Designware Ethernet MAC" + depends on ETH_DESIGNWARE + select DW_ALTDESCRIPTOR + help + This provides glue layer to use Synopsys Designware Ethernet MAC + present on T-Head SoCs. + config DW_ALTDESCRIPTOR bool "Designware Ethernet MAC uses alternate (enhanced) descriptors" depends on ETH_DESIGNWARE diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 67bba3a8536..79cc8b422b0 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_ETH_DESIGNWARE) += designware.o obj-$(CONFIG_ETH_DESIGNWARE_MESON8B) += dwmac_meson8b.o obj-$(CONFIG_ETH_DESIGNWARE_S700) += dwmac_s700.o obj-$(CONFIG_ETH_DESIGNWARE_SOCFPGA) += dwmac_socfpga.o +obj-$(CONFIG_ETH_DESIGNWARE_THEAD) += dwmac_thead.o obj-$(CONFIG_ETH_SANDBOX) += sandbox.o obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw-bus.o obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw.o diff --git a/drivers/net/dwmac_thead.c b/drivers/net/dwmac_thead.c new file mode 100644 index 00000000000..138d71a6202 --- /dev/null +++ b/drivers/net/dwmac_thead.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * T-HEAD DWMAC platform driver + * + * Copyright (C) 2021 Alibaba Group Holding Limited. + * Copyright (C) 2023 Jisheng Zhang <jszhang@kernel.org> + * Copyright (C) 2025 Yao Zi <ziyao@disroot.org> + * + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <linux/bitfield.h> +#include <phy.h> + +#include "designware.h" + +#define GMAC_CLK_EN 0x00 +#define GMAC_TX_CLK_EN BIT(1) +#define GMAC_TX_CLK_N_EN BIT(2) +#define GMAC_TX_CLK_OUT_EN BIT(3) +#define GMAC_RX_CLK_EN BIT(4) +#define GMAC_RX_CLK_N_EN BIT(5) +#define GMAC_EPHY_REF_CLK_EN BIT(6) +#define GMAC_RXCLK_DELAY_CTRL 0x04 +#define GMAC_RXCLK_BYPASS BIT(15) +#define GMAC_RXCLK_INVERT BIT(14) +#define GMAC_RXCLK_DELAY GENMASK(4, 0) +#define GMAC_TXCLK_DELAY_CTRL 0x08 +#define GMAC_TXCLK_BYPASS BIT(15) +#define GMAC_TXCLK_INVERT BIT(14) +#define GMAC_TXCLK_DELAY GENMASK(4, 0) +#define GMAC_PLLCLK_DIV 0x0c +#define GMAC_PLLCLK_DIV_EN BIT(31) +#define GMAC_PLLCLK_DIV_NUM GENMASK(7, 0) +#define GMAC_GTXCLK_SEL 0x18 +#define GMAC_GTXCLK_SEL_PLL BIT(0) +#define GMAC_INTF_CTRL 0x1c +#define PHY_INTF_MASK BIT(0) +#define PHY_INTF_RGMII FIELD_PREP(PHY_INTF_MASK, 1) +#define PHY_INTF_MII_GMII FIELD_PREP(PHY_INTF_MASK, 0) +#define GMAC_TXCLK_OEN 0x20 +#define TXCLK_DIR_MASK BIT(0) +#define TXCLK_DIR_OUTPUT FIELD_PREP(TXCLK_DIR_MASK, 0) +#define TXCLK_DIR_INPUT FIELD_PREP(TXCLK_DIR_MASK, 1) + +#define GMAC_RGMII_CLK_RATE 125000000 + +struct dwmac_thead_plat { + struct dw_eth_pdata dw_eth_pdata; + void __iomem *apb_base; +}; + +static int dwmac_thead_set_phy_if(struct dwmac_thead_plat *plat) +{ + u32 phyif; + + switch (plat->dw_eth_pdata.eth_pdata.phy_interface) { + case PHY_INTERFACE_MODE_MII: + phyif = PHY_INTF_MII_GMII; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + phyif = PHY_INTF_RGMII; + break; + default: + return -EINVAL; + } + + writel(phyif, plat->apb_base + GMAC_INTF_CTRL); + return 0; +} + +static int dwmac_thead_set_txclk_dir(struct dwmac_thead_plat *plat) +{ + u32 txclk_dir; + + switch (plat->dw_eth_pdata.eth_pdata.phy_interface) { + case PHY_INTERFACE_MODE_MII: + txclk_dir = TXCLK_DIR_INPUT; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + txclk_dir = TXCLK_DIR_OUTPUT; + break; + default: + return -EINVAL; + } + + writel(txclk_dir, plat->apb_base + GMAC_TXCLK_OEN); + return 0; +} + +static unsigned long dwmac_thead_rgmii_tx_rate(int speed) +{ + switch (speed) { + case 10: + return 2500000; + case 100: + return 25000000; + case 1000: + return 125000000; + } + + return -EINVAL; +} + +static int dwmac_thead_set_clk_tx_rate(struct dwmac_thead_plat *plat, + struct dw_eth_dev *edev, + unsigned long tx_rate) +{ + unsigned long rate; + u32 div, reg; + + rate = clk_get_rate(&edev->clocks[0]); + + writel(0, plat->apb_base + GMAC_PLLCLK_DIV); + + div = rate / tx_rate; + if (rate != tx_rate * div) { + pr_err("invalid gmac rate %lu\n", rate); + return -EINVAL; + } + + reg = FIELD_PREP(GMAC_PLLCLK_DIV_EN, 1) | + FIELD_PREP(GMAC_PLLCLK_DIV_NUM, div); + writel(reg, plat->apb_base + GMAC_PLLCLK_DIV); + + return 0; +} + +static int dwmac_thead_enable_clk(struct dwmac_thead_plat *plat) +{ + u32 reg; + + switch (plat->dw_eth_pdata.eth_pdata.phy_interface) { + case PHY_INTERFACE_MODE_MII: + reg = GMAC_RX_CLK_EN | GMAC_TX_CLK_EN; + break; + + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + /* use pll */ + writel(GMAC_GTXCLK_SEL_PLL, plat->apb_base + GMAC_GTXCLK_SEL); + reg = GMAC_TX_CLK_EN | GMAC_TX_CLK_N_EN | GMAC_TX_CLK_OUT_EN | + GMAC_RX_CLK_EN | GMAC_RX_CLK_N_EN; + break; + + default: + return -EINVAL; + } + + writel(reg, plat->apb_base + GMAC_CLK_EN); + return 0; +} + +static int dwmac_thead_eth_start(struct udevice *dev) +{ + struct dwmac_thead_plat *plat = dev_get_plat(dev); + struct dw_eth_dev *edev = dev_get_priv(dev); + phy_interface_t interface; + bool is_rgmii; + long tx_rate; + int ret; + + interface = plat->dw_eth_pdata.eth_pdata.phy_interface; + is_rgmii = (interface == PHY_INTERFACE_MODE_RGMII) | + (interface == PHY_INTERFACE_MODE_RGMII_ID) | + (interface == PHY_INTERFACE_MODE_RGMII_RXID) | + (interface == PHY_INTERFACE_MODE_RGMII_TXID); + + /* + * When operating in RGMII mode, the TX clock is generated by an + * internal divider and fed to the MAC. Configure and enable it before + * initializing the MAC. + */ + if (is_rgmii) { + ret = dwmac_thead_set_clk_tx_rate(plat, edev, + GMAC_RGMII_CLK_RATE); + if (ret) + return ret; + } + + ret = designware_eth_init(edev, plat->dw_eth_pdata.eth_pdata.enetaddr); + if (ret) + return ret; + + if (is_rgmii) { + tx_rate = dwmac_thead_rgmii_tx_rate(edev->phydev->speed); + if (tx_rate < 0) + return tx_rate; + + ret = dwmac_thead_set_clk_tx_rate(plat, edev, tx_rate); + if (ret) + return ret; + } + + ret = designware_eth_enable(edev); + if (ret) + return ret; + + return 0; +} + +static int dwmac_thead_probe(struct udevice *dev) +{ + struct dwmac_thead_plat *plat = dev_get_plat(dev); + unsigned int reg; + int ret; + + ret = designware_eth_probe(dev); + if (ret) + return ret; + + ret = dwmac_thead_set_phy_if(plat); + if (ret) { + pr_err("failed to set phy interface: %d\n", ret); + return ret; + } + + ret = dwmac_thead_set_txclk_dir(plat); + if (ret) { + pr_err("failed to set TX clock direction: %d\n", ret); + return ret; + } + + reg = readl(plat->apb_base + GMAC_RXCLK_DELAY_CTRL); + reg &= ~(GMAC_RXCLK_DELAY); + reg |= FIELD_PREP(GMAC_RXCLK_DELAY, 0); + writel(reg, plat->apb_base + GMAC_RXCLK_DELAY_CTRL); + + reg = readl(plat->apb_base + GMAC_TXCLK_DELAY_CTRL); + reg &= ~(GMAC_TXCLK_DELAY); + reg |= FIELD_PREP(GMAC_TXCLK_DELAY, 0); + writel(reg, plat->apb_base + GMAC_TXCLK_DELAY_CTRL); + + ret = dwmac_thead_enable_clk(plat); + if (ret) + pr_err("failed to enable clock: %d\n", ret); + + return ret; +} + +static int dwmac_thead_of_to_plat(struct udevice *dev) +{ + struct dwmac_thead_plat *pdata = dev_get_plat(dev); + + pdata->apb_base = dev_read_addr_index_ptr(dev, 1); + if (!pdata->apb_base) { + pr_err("failed to get apb registers\n"); + return -ENOENT; + } + + return designware_eth_of_to_plat(dev); +} + +static const struct eth_ops dwmac_thead_eth_ops = { + .start = dwmac_thead_eth_start, + .send = designware_eth_send, + .recv = designware_eth_recv, + .free_pkt = designware_eth_free_pkt, + .stop = designware_eth_stop, + .write_hwaddr = designware_eth_write_hwaddr, +}; + +static const struct udevice_id dwmac_thead_match[] = { + { .compatible = "thead,th1520-gmac" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(dwmac_thead) = { + .name = "dwmac_thead", + .id = UCLASS_ETH, + .of_match = dwmac_thead_match, + .of_to_plat = dwmac_thead_of_to_plat, + .probe = dwmac_thead_probe, + .ops = &dwmac_thead_eth_ops, + .priv_auto = sizeof(struct dw_eth_dev), + .plat_auto = sizeof(struct dwmac_thead_plat), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/spi/microchip_coreqspi.c b/drivers/spi/microchip_coreqspi.c index 234b1688272..a84b257fb1a 100644 --- a/drivers/spi/microchip_coreqspi.c +++ b/drivers/spi/microchip_coreqspi.c @@ -16,6 +16,7 @@ #include <linux/delay.h> #include <linux/types.h> #include <linux/sizes.h> +#include <asm/gpio.h> DECLARE_GLOBAL_DATA_PTR; @@ -97,6 +98,8 @@ DECLARE_GLOBAL_DATA_PTR; #define REG_X4_TX_DATA (0x4c) #define REG_FRAMESUP (0x50) +#define MAX_CS_COUNT 1 + /** * struct mchp_coreqspi - Defines qspi driver instance * @regs: Address of the QSPI controller registers @@ -113,6 +116,7 @@ struct mchp_coreqspi { u8 *rxbuf; int tx_len; int rx_len; + struct gpio_desc cs_gpios[MAX_CS_COUNT]; }; static void mchp_coreqspi_init_hw(struct mchp_coreqspi *qspi) @@ -172,7 +176,7 @@ static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word) while (qspi->tx_len >= 4) { while (readl(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) ; - data = *(u32 *)qspi->txbuf; + data = qspi->txbuf ? *((u32 *)qspi->txbuf) : 0xFF; qspi->txbuf += 4; qspi->tx_len -= 4; writel(data, qspi->regs + REG_X4_TX_DATA); @@ -184,7 +188,7 @@ static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word) while (qspi->tx_len--) { while (readl(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) ; - data = *qspi->txbuf++; + data = qspi->txbuf ? *qspi->txbuf++ : 0xFF; writel(data, qspi->regs + REG_TX_DATA); } } @@ -471,6 +475,110 @@ static int mchp_coreqspi_probe(struct udevice *dev) /* Init the mpfs qspi hw */ mchp_coreqspi_init_hw(qspi); + if (CONFIG_IS_ENABLED(DM_GPIO)) { + int i; + + ret = gpio_request_list_by_name(dev, "cs-gpios", qspi->cs_gpios, + ARRAY_SIZE(qspi->cs_gpios), 0); + + if (ret < 0) { + pr_err("Can't get %s gpios! Error: %d", dev->name, ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(qspi->cs_gpios); i++) { + if (!dm_gpio_is_valid(&qspi->cs_gpios[i])) + continue; + dm_gpio_set_dir_flags(&qspi->cs_gpios[i], GPIOD_IS_OUT); + } + } + + u32 control = readl(qspi->regs + REG_CONTROL); + + control |= (CONTROL_MASTER | CONTROL_ENABLE); + control &= ~CONTROL_CLKIDLE; + writel(control, qspi->regs + REG_CONTROL); + + return 0; +} + +static void mchp_coreqspi_cs_activate(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct mchp_coreqspi *qspi = dev_get_priv(bus); + struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev); + u32 cs = slave_plat->cs[0]; + + if (CONFIG_IS_ENABLED(DM_GPIO) && dm_gpio_is_valid(&qspi->cs_gpios[cs])) + dm_gpio_set_value(&qspi->cs_gpios[cs], 1); +} + +static void mchp_coreqspi_cs_deactivate(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct mchp_coreqspi *qspi = dev_get_priv(bus); + struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev); + u32 cs = slave_plat->cs[0]; + + if (CONFIG_IS_ENABLED(DM_GPIO) && dm_gpio_is_valid(&qspi->cs_gpios[cs])) + dm_gpio_set_value(&qspi->cs_gpios[cs], 0); +} + +static int mchp_coreqspi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev_get_parent(dev); + struct mchp_coreqspi *qspi = dev_get_priv(bus); + struct spi_slave *slave = dev_get_parent_priv(dev); + uint total_bytes = bitlen >> 3; /* fixed 8-bit word length */ + u32 control, frames; + + int err = 0; + + err = mchp_coreqspi_wait_for_ready(slave); + if (err) + return err; + + control = readl(qspi->regs + REG_CONTROL); + control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0); + writel(control, qspi->regs + REG_CONTROL); + + frames = total_bytes & BYTESUPPER_MASK; + writel(frames, qspi->regs + REG_FRAMESUP); + + frames |= FRAMES_FLAGBYTE; + writel(frames, qspi->regs + REG_FRAMES); + + if (flags & SPI_XFER_BEGIN) + mchp_coreqspi_cs_activate(dev); + + if (bitlen == 0) + goto out; + + if (bitlen % 8) { // Non byte aligned SPI transfer + flags |= SPI_XFER_END; + goto out; + } + + qspi->txbuf = (u8 *)dout; + qspi->rxbuf = (u8 *)din; + + while (total_bytes) { + qspi->tx_len = 1; + qspi->rx_len = 1; + total_bytes--; + + if (din) { + mchp_coreqspi_write_op(qspi, true); + mchp_coreqspi_read_op(qspi); + } else { + mchp_coreqspi_write_op(qspi, true); + } + } +out: + if (flags & SPI_XFER_END) + mchp_coreqspi_cs_deactivate(dev); + return 0; } @@ -483,6 +591,7 @@ static const struct spi_controller_mem_ops mchp_coreqspi_mem_ops = { static const struct dm_spi_ops mchp_coreqspi_ops = { .claim_bus = mchp_coreqspi_claim_bus, .release_bus = mchp_coreqspi_release_bus, + .xfer = mchp_coreqspi_xfer, .set_speed = mchp_coreqspi_set_speed, .set_mode = mchp_coreqspi_set_mode, .mem_ops = &mchp_coreqspi_mem_ops, |