summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/thead/clk-th1520-ap.c4
-rw-r--r--drivers/dfu/dfu.c2
-rw-r--r--drivers/fastboot/Kconfig6
-rw-r--r--drivers/fastboot/Makefile1
-rw-r--r--drivers/fastboot/fb_command.c10
-rw-r--r--drivers/fastboot/fb_getvar.c6
-rw-r--r--drivers/fastboot/fb_spi_flash.c251
-rw-r--r--drivers/gpio/Kconfig5
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/mpfs_gpio.c198
-rw-r--r--drivers/i2c/Kconfig11
-rw-r--r--drivers/i2c/iproc_i2c.c1
-rw-r--r--drivers/i2c/muxes/Kconfig7
-rw-r--r--drivers/i2c/muxes/Makefile1
-rw-r--r--drivers/i2c/muxes/pca9541.c297
-rw-r--r--drivers/i2c/muxes/pca954x.c6
-rw-r--r--drivers/input/Kconfig1
-rw-r--r--drivers/misc/Kconfig45
-rw-r--r--drivers/misc/Makefile2
-rw-r--r--drivers/misc/ds4510.c379
-rw-r--r--drivers/misc/ds4510.h52
-rw-r--r--drivers/misc/pca9551_led.c170
-rw-r--r--drivers/mmc/Kconfig35
-rw-r--r--drivers/mmc/Makefile2
-rw-r--r--drivers/mmc/kona_sdhci.c132
-rw-r--r--drivers/mmc/mmc.c6
-rw-r--r--drivers/mmc/mxcmmc.c523
-rw-r--r--drivers/net/Kconfig8
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/airoha_eth.c82
-rw-r--r--drivers/net/dwmac_thead.c288
-rw-r--r--drivers/spi/microchip_coreqspi.c113
32 files changed, 1289 insertions, 1357 deletions
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/dfu/dfu.c b/drivers/dfu/dfu.c
index 756569217bb..eefdf44ec87 100644
--- a/drivers/dfu/dfu.c
+++ b/drivers/dfu/dfu.c
@@ -147,7 +147,7 @@ int dfu_config_interfaces(char *env)
break;
a = strsep(&s, "&");
if (!a)
- a = s;
+ a = d;
do {
part = strsep(&a, ";");
part = skip_spaces(part);
diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig
index 70207573de2..843171902ae 100644
--- a/drivers/fastboot/Kconfig
+++ b/drivers/fastboot/Kconfig
@@ -91,7 +91,7 @@ config FASTBOOT_USB_DEV
config FASTBOOT_FLASH
bool "Enable FASTBOOT FLASH command"
default y if ARCH_SUNXI || ARCH_ROCKCHIP
- depends on MMC || (MTD_RAW_NAND && CMD_MTDPARTS)
+ depends on MMC || (MTD_RAW_NAND && CMD_MTDPARTS) || DM_SPI_FLASH
select IMAGE_SPARSE
help
The fastboot protocol includes a "flash" command for writing
@@ -119,6 +119,10 @@ config FASTBOOT_FLASH_NAND
bool "FASTBOOT on NAND"
depends on MTD_RAW_NAND && CMD_MTDPARTS
+config FASTBOOT_FLASH_SPI
+ bool "FASTBOOT on SPI flash"
+ depends on DM_SPI_FLASH
+
endchoice
config FASTBOOT_FLASH_MMC_DEV
diff --git a/drivers/fastboot/Makefile b/drivers/fastboot/Makefile
index 048af5aa823..adedba0bf24 100644
--- a/drivers/fastboot/Makefile
+++ b/drivers/fastboot/Makefile
@@ -5,3 +5,4 @@ obj-y += fb_getvar.o
obj-y += fb_command.o
obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fb_mmc.o
obj-$(CONFIG_FASTBOOT_FLASH_NAND) += fb_nand.o
+obj-$(CONFIG_FASTBOOT_FLASH_SPI) += fb_spi_flash.o
diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c
index 2cdbac50ac4..791088bc094 100644
--- a/drivers/fastboot/fb_command.c
+++ b/drivers/fastboot/fb_command.c
@@ -10,6 +10,7 @@
#include <fastboot-internal.h>
#include <fb_mmc.h>
#include <fb_nand.h>
+#include <fb_spi_flash.h>
#include <part.h>
#include <stdlib.h>
#include <vsprintf.h>
@@ -344,6 +345,10 @@ static void __maybe_unused flash(char *cmd_parameter, char *response)
if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_NAND))
fastboot_nand_flash_write(cmd_parameter, fastboot_buf_addr,
image_size, response);
+
+ if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_SPI))
+ fastboot_spi_flash_write(cmd_parameter, fastboot_buf_addr,
+ image_size, response);
}
/**
@@ -362,6 +367,9 @@ static void __maybe_unused erase(char *cmd_parameter, char *response)
if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_NAND))
fastboot_nand_erase(cmd_parameter, response);
+
+ if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_SPI))
+ fastboot_spi_flash_erase(cmd_parameter, response);
}
/**
@@ -405,7 +413,7 @@ static void __maybe_unused run_acmd(char *cmd_parameter, char *response)
return;
}
- if (strlen(cmd_parameter) > sizeof(g_a_cmd_buff)) {
+ if (strlen(cmd_parameter) >= sizeof(g_a_cmd_buff)) {
pr_err("too long command\n");
fastboot_fail("too long command", response);
return;
diff --git a/drivers/fastboot/fb_getvar.c b/drivers/fastboot/fb_getvar.c
index 9c2ce65a4e5..6775ea397ab 100644
--- a/drivers/fastboot/fb_getvar.c
+++ b/drivers/fastboot/fb_getvar.c
@@ -8,6 +8,7 @@
#include <fastboot-internal.h>
#include <fb_mmc.h>
#include <fb_nand.h>
+#include <fb_spi_flash.h>
#include <fs.h>
#include <part.h>
#include <version.h>
@@ -123,6 +124,11 @@ static int getvar_get_part_info(const char *part_name, char *response,
r = fastboot_nand_get_part_info(part_name, &part_info, response);
if (r >= 0 && size)
*size = part_info->size;
+ } else if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_SPI)) {
+ r = fastboot_spi_flash_get_part_info(part_name, &disk_part,
+ response);
+ if (r >= 0 && size)
+ *size = disk_part.size * disk_part.blksz;
} else {
fastboot_fail("this storage is not supported in bootloader", response);
r = -ENODEV;
diff --git a/drivers/fastboot/fb_spi_flash.c b/drivers/fastboot/fb_spi_flash.c
new file mode 100644
index 00000000000..691be7c7ef7
--- /dev/null
+++ b/drivers/fastboot/fb_spi_flash.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 Collabora Ltd.
+ */
+
+#include <blk.h>
+#include <config.h>
+#include <env.h>
+#include <fastboot.h>
+#include <image-sparse.h>
+#include <spi.h>
+#include <spi_flash.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+
+static struct spi_flash *flash;
+
+__weak int board_fastboot_spi_flash_write_setup(void)
+{
+ return 0;
+}
+
+__weak int board_fastboot_spi_flash_erase_setup(void)
+{
+ return 0;
+}
+
+static int raw_part_get_info_by_name(const char *name,
+ struct disk_partition *part_info)
+{
+ /* strlen("fastboot_raw_partition_") + PART_NAME_LEN + 1 */
+ char env_desc_name[23 + PART_NAME_LEN + 1];
+ char *raw_part_desc;
+ const char *argv[2];
+ const char **parg = argv;
+
+ /* check for raw partition descriptor */
+ strcpy(env_desc_name, "fastboot_raw_partition_");
+ strlcat(env_desc_name, name, sizeof(env_desc_name));
+ raw_part_desc = strdup(env_get(env_desc_name));
+ if (!raw_part_desc)
+ return -ENODEV;
+
+ /* parse partition descriptor: <start> <size> */
+ for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
+ *parg = strsep(&raw_part_desc, " ");
+ if (!*parg) {
+ pr_err("Invalid number of arguments.\n");
+ return -ENODEV;
+ }
+ }
+
+ part_info->start = simple_strtoul(argv[0], NULL, 0);
+ part_info->size = simple_strtoul(argv[1], NULL, 0);
+ strlcpy((char *)part_info->name, name, PART_NAME_LEN);
+
+ return 0;
+}
+
+static int fastboot_spi_flash_probe(void)
+{
+ unsigned int bus = CONFIG_SF_DEFAULT_BUS;
+ unsigned int cs = CONFIG_SF_DEFAULT_CS;
+ struct udevice *new, *bus_dev;
+ int ret;
+
+ /* Remove the old device, otherwise probe will just be a nop */
+ ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new);
+ if (!ret)
+ device_remove(new, DM_REMOVE_NORMAL);
+
+ spi_flash_probe_bus_cs(bus, cs, &new);
+ flash = dev_get_uclass_priv(new);
+ if (!flash) {
+ printf("Failed to initialize SPI flash at %u:%u (error %d)\n",
+ bus, cs, ret);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int fastboot_spi_flash_unlock(struct spi_flash *flash,
+ struct disk_partition *part_info)
+{
+ int ret = spi_flash_protect(flash, part_info->start, part_info->size,
+ false);
+
+ if (ret && ret != -EOPNOTSUPP) {
+ printf("Failed to unlock SPI flash (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static lbaint_t fb_spi_flash_sparse_write(struct sparse_storage *info,
+ lbaint_t blk, lbaint_t blkcnt,
+ const void *buffer)
+{
+ size_t len = blkcnt * info->blksz;
+ u32 offset = blk * info->blksz;
+ int ret;
+
+ ret = spi_flash_erase(flash, offset, ROUND(len, flash->erase_size));
+ if (ret < 0) {
+ printf("Failed to erase sparse chunk (%d)\n", ret);
+ return ret;
+ }
+
+ ret = spi_flash_write(flash, offset, len, buffer);
+ if (ret < 0) {
+ printf("Failed to write sparse chunk (%d)\n", ret);
+ return ret;
+ }
+
+ return blkcnt;
+}
+
+static lbaint_t fb_spi_flash_sparse_reserve(struct sparse_storage *info,
+ lbaint_t blk, lbaint_t blkcnt)
+{
+ return blkcnt;
+}
+
+/**
+ * fastboot_spi_flash_get_part_info() - Lookup SPI partition by name
+ *
+ * @part_name: Named device to lookup
+ * @part_info: Pointer to returned struct disk_partition
+ * @response: Pointer to fastboot response buffer
+ * Return: 0 if OK, -ENOENT if no partition name was given, -ENODEV on invalid
+ * raw partition descriptor
+ */
+int fastboot_spi_flash_get_part_info(const char *part_name,
+ struct disk_partition *part_info,
+ char *response)
+{
+ int ret;
+
+ if (!part_name || !strcmp(part_name, "")) {
+ fastboot_fail("partition not given", response);
+ return -ENOENT;
+ }
+
+ /* TODO: Support partitions on the device */
+ ret = raw_part_get_info_by_name(part_name, part_info);
+ if (ret < 0)
+ fastboot_fail("invalid partition or device", response);
+
+ return ret;
+}
+
+/**
+ * fastboot_spi_flash_write() - Write image to SPI for fastboot
+ *
+ * @cmd: Named device to write image to
+ * @download_buffer: Pointer to image data
+ * @download_bytes: Size of image data
+ * @response: Pointer to fastboot response buffer
+ */
+void fastboot_spi_flash_write(const char *cmd, void *download_buffer,
+ u32 download_bytes, char *response)
+{
+ struct disk_partition part_info;
+ int ret;
+
+ if (fastboot_spi_flash_get_part_info(cmd, &part_info, response))
+ return;
+
+ if (fastboot_spi_flash_probe())
+ return;
+
+ if (board_fastboot_spi_flash_write_setup())
+ return;
+
+ if (fastboot_spi_flash_unlock(flash, &part_info))
+ return;
+
+ if (is_sparse_image(download_buffer)) {
+ struct sparse_storage sparse;
+
+ sparse.blksz = flash->sector_size;
+ sparse.start = part_info.start / sparse.blksz;
+ sparse.size = part_info.size / sparse.blksz;
+ sparse.write = fb_spi_flash_sparse_write;
+ sparse.reserve = fb_spi_flash_sparse_reserve;
+ sparse.mssg = fastboot_fail;
+
+ printf("Flashing sparse image at offset " LBAFU "\n",
+ sparse.start);
+
+ ret = write_sparse_image(&sparse, cmd, download_buffer,
+ response);
+ } else {
+ printf("Flashing raw image at offset " LBAFU "\n",
+ part_info.start);
+
+ ret = spi_flash_erase(flash, part_info.start,
+ ROUND(download_bytes, flash->erase_size));
+ if (ret < 0) {
+ printf("Failed to erase raw image (%d)\n", ret);
+ return;
+ }
+ ret = spi_flash_write(flash, part_info.start, download_bytes,
+ download_buffer);
+ if (ret < 0) {
+ printf("Failed to write raw image (%d)\n", ret);
+ return;
+ }
+ printf("........ wrote %u bytes\n", download_bytes);
+ }
+
+ if (ret)
+ fastboot_fail("error writing the image", response);
+ else
+ fastboot_okay(NULL, response);
+}
+
+/**
+ * fastboot_spi_flash_erase() - Erase SPI for fastboot
+ *
+ * @cmd: Named device to erase
+ * @response: Pointer to fastboot response buffer
+ */
+void fastboot_spi_flash_erase(const char *cmd, char *response)
+{
+ struct disk_partition part_info;
+ int ret;
+
+ if (fastboot_spi_flash_get_part_info(cmd, &part_info, response))
+ return;
+
+ if (fastboot_spi_flash_probe())
+ return;
+
+ if (board_fastboot_spi_flash_erase_setup())
+ return;
+
+ if (fastboot_spi_flash_unlock(flash, &part_info))
+ return;
+
+ ret = spi_flash_erase(flash, part_info.start, part_info.size);
+ if (ret < 0) {
+ pr_err("failed erasing from SPI flash");
+ fastboot_fail("failed erasing from SPI flash", response);
+ return;
+ }
+
+ fastboot_okay(NULL, response);
+}
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/i2c/Kconfig b/drivers/i2c/Kconfig
index 775b2b4e9af..108b24b3dd2 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -181,6 +181,7 @@ config SYS_I2C_IPROC
config SYS_I2C_FSL
bool "Freescale I2C bus driver"
+ depends on M68K || PPC
help
Add support for Freescale I2C busses as used on MPC8240, MPC8245, and
MPC85xx processors.
@@ -240,7 +241,7 @@ config SYS_I2C_DW
config SYS_I2C_DW_PCI
bool "Designware PCI I2C Controller"
- depends on SYS_I2C_DW && PCI && ACPIGEN
+ depends on SYS_I2C_DW && PCI && ACPIGEN && X86
default y
help
Say yes here to select the Designware PCI I2C Host Controller.
@@ -277,6 +278,7 @@ config SYS_I2C_INTEL
config SYS_I2C_IMX_LPI2C
bool "NXP i.MX LPI2C driver"
+ depends on MACH_IMX
help
Add support for the NXP i.MX LPI2C driver.
@@ -314,6 +316,7 @@ config SYS_I2C_MICROCHIP
config SYS_I2C_MXC
bool "NXP MXC I2C driver"
+ depends on ARCH_LS1021A || FSL_LSCH2 || FSL_LSCH3 || MACH_IMX
help
Add support for the NXP I2C driver. This supports up to four bus
channels and operating on standard mode up to 100 kbits/s and fast
@@ -485,7 +488,7 @@ endif
config SYS_I2C_NEXELL
bool "Nexell I2C driver"
- depends on DM_I2C
+ depends on DM_I2C && ARCH_NEXELL
help
Add support for the Nexell I2C driver. This is used with various
Nexell parts such as S5Pxx18 series SoCs. All chips
@@ -494,6 +497,7 @@ config SYS_I2C_NEXELL
config SYS_I2C_NPCM
bool "Nuvoton NPCM I2C driver"
+ depends on ARCH_NPCM
help
Support for Nuvoton I2C controller driver.
@@ -533,7 +537,7 @@ config SYS_I2C_RCAR_IIC
config SYS_I2C_ROCKCHIP
bool "Rockchip I2C driver"
- depends on DM_I2C
+ depends on DM_I2C && ARCH_ROCKCHIP
help
Add support for the Rockchip I2C driver. This is used with various
Rockchip parts such as RK3126, RK3128, RK3036 and RK3288. All chips
@@ -751,6 +755,7 @@ config SYS_I2C_MV
config SYS_I2C_MVTWSI
bool "Marvell I2C driver"
+ depends on ARCH_KIRKWOOD || ARCH_MVEBU || ARCH_SUNXI
help
Support for Marvell I2C controllers as used on the orion5x and
kirkwood SoC families.
diff --git a/drivers/i2c/iproc_i2c.c b/drivers/i2c/iproc_i2c.c
index 6570f64fe77..8f94dfe117e 100644
--- a/drivers/i2c/iproc_i2c.c
+++ b/drivers/i2c/iproc_i2c.c
@@ -8,6 +8,7 @@
#include <asm/io.h>
#include <config.h>
#include <dm.h>
+#include <linux/delay.h>
#include <linux/printk.h>
#include "errno.h"
#include <i2c.h>
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index cd5579aa55a..65319bb6fd8 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -25,6 +25,13 @@ config I2C_ARB_GPIO_CHALLENGE
response mechanism where masters have to claim the bus by asserting
a GPIO.
+config I2C_MUX_PCA9541
+ tristate "NXP PCA9541 I2C Master Selector"
+ depends on I2C_MUX
+ help
+ If you say yes here you get support for the NXP PCA9541
+ I2C Master Selector.
+
config I2C_MUX_PCA954x
tristate "TI PCA954x I2C Mux/switches"
depends on I2C_MUX
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index b690821199f..844d4520e43 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -3,5 +3,6 @@
# Copyright (c) 2015 Google, Inc
obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
obj-$(CONFIG_I2C_MUX) += i2c-mux-uclass.o
+obj-$(CONFIG_I2C_MUX_PCA9541) += pca9541.o
obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o
obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
diff --git a/drivers/i2c/muxes/pca9541.c b/drivers/i2c/muxes/pca9541.c
new file mode 100644
index 00000000000..021088acaee
--- /dev/null
+++ b/drivers/i2c/muxes/pca9541.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
+ * Copyright (c) 2010 Ericsson AB.
+ * Copyright (c) 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <i2c.h>
+#include <log.h>
+#include <malloc.h>
+#include <linux/delay.h>
+
+/*
+ * The PCA9541 is a bus master selector. It supports two I2C masters connected
+ * to a single slave bus.
+ *
+ * Before each bus transaction, a master has to acquire bus ownership. After the
+ * transaction is complete, bus ownership has to be released. This fits well
+ * into the I2C multiplexer framework, which provides select and release
+ * functions for this purpose. For this reason, this driver is modeled as
+ * single-channel I2C bus multiplexer.
+ *
+ * This driver assumes that the two bus masters are controlled by two different
+ * hosts. If a single host controls both masters, platform code has to ensure
+ * that only one of the masters is instantiated at any given time.
+ */
+
+#define PCA9541_CONTROL 0x01
+#define PCA9541_ISTAT 0x02
+
+#define PCA9541_CTL_MYBUS BIT(0)
+#define PCA9541_CTL_NMYBUS BIT(1)
+#define PCA9541_CTL_BUSON BIT(2)
+#define PCA9541_CTL_NBUSON BIT(3)
+#define PCA9541_CTL_BUSINIT BIT(4)
+#define PCA9541_CTL_TESTON BIT(6)
+#define PCA9541_CTL_NTESTON BIT(7)
+
+#define PCA9541_ISTAT_INTIN BIT(0)
+#define PCA9541_ISTAT_BUSINIT BIT(1)
+#define PCA9541_ISTAT_BUSOK BIT(2)
+#define PCA9541_ISTAT_BUSLOST BIT(3)
+#define PCA9541_ISTAT_MYTEST BIT(6)
+#define PCA9541_ISTAT_NMYTEST BIT(7)
+
+#define BUSON (PCA9541_CTL_BUSON | PCA9541_CTL_NBUSON)
+#define MYBUS (PCA9541_CTL_MYBUS | PCA9541_CTL_NMYBUS)
+
+/* arbitration timeouts, in jiffies */
+#define ARB_TIMEOUT_US 125000 /* 125 ms until forcing bus ownership */
+#define ARB2_TIMEOUT_US 250000 /* 250 ms until acquisition failure */
+
+/* arbitration retry delays, in us */
+#define SELECT_DELAY_SHORT 50
+#define SELECT_DELAY_LONG 1000
+
+struct pca9541_plat {
+ u32 addr;
+};
+
+struct pca9541_priv {
+ u32 addr;
+ unsigned long select_timeout;
+ long arb_timeout;
+};
+
+static inline int mybus(int x)
+{
+ return !(x & MYBUS) || ((x & MYBUS) == MYBUS);
+}
+
+static inline int busoff(int x)
+{
+ return !(x & BUSON) || ((x & BUSON) == BUSON);
+}
+
+static int pca9541_reg_write(struct udevice *mux, struct pca9541_priv *client,
+ u8 command, u8 val)
+{
+ return dm_i2c_write(mux, command, &val, 1);
+}
+
+static int pca9541_reg_read(struct udevice *mux, struct pca9541_priv *client,
+ u8 command)
+{
+ int ret;
+ uchar byte;
+
+ ret = dm_i2c_read(mux, command, &byte, 1);
+
+ return ret ?: byte;
+}
+
+/*
+ * Arbitration management functions
+ */
+
+/* Release bus. Also reset NTESTON and BUSINIT if it was set. */
+static void pca9541_release_bus(struct udevice *mux, struct pca9541_priv *client)
+{
+ int reg;
+
+ reg = pca9541_reg_read(mux, client, PCA9541_CONTROL);
+ if (reg >= 0 && !busoff(reg) && mybus(reg))
+ pca9541_reg_write(mux, client, PCA9541_CONTROL,
+ (reg & PCA9541_CTL_NBUSON) >> 1);
+}
+
+/*
+ * Arbitration is defined as a two-step process. A bus master can only activate
+ * the slave bus if it owns it; otherwise it has to request ownership first.
+ * This multi-step process ensures that access contention is resolved
+ * gracefully.
+ *
+ * Bus Ownership Other master Action
+ * state requested access
+ * ----------------------------------------------------
+ * off - yes wait for arbitration timeout or
+ * for other master to drop request
+ * off no no take ownership
+ * off yes no turn on bus
+ * on yes - done
+ * on no - wait for arbitration timeout or
+ * for other master to release bus
+ *
+ * The main contention point occurs if the slave bus is off and both masters
+ * request ownership at the same time. In this case, one master will turn on
+ * the slave bus, believing that it owns it. The other master will request
+ * bus ownership. Result is that the bus is turned on, and master which did
+ * _not_ own the slave bus before ends up owning it.
+ */
+
+/* Control commands per PCA9541 datasheet */
+static const u8 pca9541_control[16] = {
+ 4, 0, 1, 5, 4, 4, 5, 5, 0, 0, 1, 1, 0, 4, 5, 1
+};
+
+/*
+ * Channel arbitration
+ *
+ * Return values:
+ * <0: error
+ * 0 : bus not acquired
+ * 1 : bus acquired
+ */
+static int pca9541_arbitrate(struct udevice *mux, struct pca9541_priv *client)
+{
+ int reg, ret = 0;
+
+ reg = pca9541_reg_read(mux, client, PCA9541_CONTROL);
+ if (reg < 0)
+ return reg;
+
+ if (busoff(reg)) {
+ int istat;
+
+ /*
+ * Bus is off. Request ownership or turn it on unless
+ * other master requested ownership.
+ */
+ istat = pca9541_reg_read(mux, client, PCA9541_ISTAT);
+ if (!(istat & PCA9541_ISTAT_NMYTEST) ||
+ client->arb_timeout <= 0) {
+ /*
+ * Other master did not request ownership,
+ * or arbitration timeout expired. Take the bus.
+ */
+ pca9541_reg_write(mux, client, PCA9541_CONTROL,
+ pca9541_control[reg & 0x0f]
+ | PCA9541_CTL_NTESTON);
+ client->select_timeout = SELECT_DELAY_SHORT;
+ } else {
+ /*
+ * Other master requested ownership.
+ * Set extra long timeout to give it time to acquire it.
+ */
+ client->select_timeout = SELECT_DELAY_LONG * 2;
+ }
+ } else if (mybus(reg)) {
+ /*
+ * Bus is on, and we own it. We are done with acquisition.
+ * Reset NTESTON and BUSINIT, then return success.
+ */
+ if (reg & (PCA9541_CTL_NTESTON | PCA9541_CTL_BUSINIT))
+ pca9541_reg_write(mux, client, PCA9541_CONTROL,
+ reg & ~(PCA9541_CTL_NTESTON
+ | PCA9541_CTL_BUSINIT));
+ ret = 1;
+ } else {
+ /*
+ * Other master owns the bus.
+ * If arbitration timeout has expired, force ownership.
+ * Otherwise request it.
+ */
+ client->select_timeout = SELECT_DELAY_LONG;
+ if (client->arb_timeout <= 0) {
+ /* Time is up, take the bus and reset it. */
+ pca9541_reg_write(mux, client, PCA9541_CONTROL,
+ pca9541_control[reg & 0x0f]
+ | PCA9541_CTL_BUSINIT
+ | PCA9541_CTL_NTESTON);
+ } else {
+ /* Request bus ownership if needed */
+ if (!(reg & PCA9541_CTL_NTESTON))
+ pca9541_reg_write(mux, client, PCA9541_CONTROL,
+ reg | PCA9541_CTL_NTESTON);
+ }
+ }
+
+ return ret;
+}
+
+static int pca9541_select_chan(struct udevice *mux, struct udevice *bus,
+ uint channel)
+{
+ struct pca9541_priv *priv = dev_get_priv(mux);
+ int ret;
+ long timeout = ARB2_TIMEOUT_US; /* Give up after this time */
+
+ /* Force bus ownership after this time */
+ priv->arb_timeout = ARB_TIMEOUT_US;
+ do {
+ ret = pca9541_arbitrate(mux, priv);
+ if (ret)
+ return ret < 0 ? ret : 0;
+
+ udelay(priv->select_timeout);
+ timeout -= priv->select_timeout;
+ priv->arb_timeout -= priv->select_timeout;
+ } while (timeout > 0);
+
+ debug("I2C Arbitration select timeout\n");
+
+ return -ETIMEDOUT;
+}
+
+static int pca9541_release_chan(struct udevice *mux, struct udevice *bus,
+ uint channel)
+{
+ struct pca9541_priv *priv = dev_get_priv(mux);
+
+ pca9541_release_bus(mux, priv);
+
+ return 0;
+}
+
+/*
+ * I2C init/probing/exit functions
+ */
+static int pca9541_of_to_plat(struct udevice *dev)
+{
+ struct pca9541_plat *plat = dev_get_plat(dev);
+
+ plat->addr = dev_read_u32_default(dev, "reg", 0);
+ if (!plat->addr) {
+ debug("Reg property is not found\n");
+ return -ENODEV;
+ }
+
+ debug("Device %s at 0x%x\n", dev->name, plat->addr);
+
+ return 0;
+}
+
+static int pca9541_probe(struct udevice *dev)
+{
+ struct pca9541_plat *plat = dev_get_plat(dev);
+ struct pca9541_priv *priv = dev_get_priv(dev);
+
+ priv->addr = plat->addr;
+
+ return 0;
+}
+
+static const struct i2c_mux_ops pca9541_ops = {
+ .select = pca9541_select_chan,
+ .deselect = pca9541_release_chan,
+};
+
+static const struct udevice_id pca9541_ids[] = {
+ { .compatible = "nxp,pca9541", },
+ { }
+};
+
+U_BOOT_DRIVER(pca9541) = {
+ .name = "pca9541",
+ .id = UCLASS_I2C_MUX,
+ .of_match = pca9541_ids,
+ .probe = pca9541_probe,
+ .ops = &pca9541_ops,
+ .of_to_plat = pca9541_of_to_plat,
+ .plat_auto = sizeof(struct pca9541_plat),
+ .priv_auto = sizeof(struct pca9541_priv),
+};
diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c
index 9dd26972703..d13947a0d9c 100644
--- a/drivers/i2c/muxes/pca954x.c
+++ b/drivers/i2c/muxes/pca954x.c
@@ -22,6 +22,7 @@ enum pca_type {
MAX7369,
PCA9543,
PCA9544,
+ PCA9545,
PCA9546,
PCA9547,
PCA9548,
@@ -79,6 +80,10 @@ static const struct chip_desc chips[] = {
.muxtype = pca954x_ismux,
.width = 4,
},
+ [PCA9545] = {
+ .muxtype = pca954x_isswi,
+ .width = 4,
+ },
[PCA9546] = {
.muxtype = pca954x_isswi,
.width = 4,
@@ -141,6 +146,7 @@ static const struct udevice_id pca954x_ids[] = {
{ .compatible = "maxim,max7369", .data = MAX7369 },
{ .compatible = "nxp,pca9543", .data = PCA9543 },
{ .compatible = "nxp,pca9544", .data = PCA9544 },
+ { .compatible = "nxp,pca9545", .data = PCA9545 },
{ .compatible = "nxp,pca9546", .data = PCA9546 },
{ .compatible = "nxp,pca9547", .data = PCA9547 },
{ .compatible = "nxp,pca9548", .data = PCA9548 },
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index c09f0ae795e..47ce0ea690f 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -98,6 +98,7 @@ config I8042_KEYB
config TEGRA_KEYBOARD
bool "NVIDIA Tegra internal matrix keyboard controller support"
+ depends on ARCH_TEGRA
help
A matrix keyboard connected directly to the internal keyboard
controller on Tegra SoCs.
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 515d3668395..966783e4b62 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -290,40 +290,10 @@ config CROS_EC_SPI
provides a faster and more robust interface than I2C but the bugs
are less interesting.
-config DS4510
- bool "Enable support for DS4510 CPU supervisor"
- help
- Enable support for the Maxim DS4510 CPU supervisor. It has an
- integrated 64-byte EEPROM, four programmable non-volatile I/O pins
- and a configurable timer for the supervisor function. The device is
- connected over I2C.
-
config FSL_IIM
bool "Enable FSL IC Identification Module (IIM) driver"
depends on ARCH_MX5
-config FSL_SEC_MON
- bool "Enable FSL SEC_MON Driver"
- help
- Freescale Security Monitor block is responsible for monitoring
- system states.
- Security Monitor can be transitioned on any security failures,
- like software violations or hardware security violations.
-
-choice
- prompt "Security monitor interaction endianess"
- depends on FSL_SEC_MON
- default SYS_FSL_SEC_MON_BE if PPC
- default SYS_FSL_SEC_MON_LE
-
-config SYS_FSL_SEC_MON_LE
- bool "Security monitor interactions are little endian"
-
-config SYS_FSL_SEC_MON_BE
- bool "Security monitor interactions are big endian"
-
-endchoice
-
config IRQ
bool "Interrupt controller"
help
@@ -458,19 +428,6 @@ config SPL_PWRSEQ
device. When the device is started up, its power sequence can be
initiated.
-config PCA9551_LED
- bool "Enable PCA9551 LED driver"
- help
- Enable driver for PCA9551 LED controller. This controller
- is connected via I2C. So I2C needs to be enabled.
-
-config PCA9551_I2C_ADDR
- hex "I2C address of PCA9551 LED controller"
- depends on PCA9551_LED
- default 0x60
- help
- The I2C address of the PCA9551 LED controller.
-
config STM32MP_FUSE
bool "Enable STM32MP fuse wrapper providing the fuse API"
depends on ARCH_STM32MP && MISC
@@ -672,7 +629,7 @@ config GDSYS_SOC
config IHS_FPGA
bool "Enable IHS FPGA driver"
- depends on MISC
+ depends on MISC && (GDSYS_LEGACY_DRIVERS || SYS_FPGA_FLAVOR_GAZERBEAM)
help
Support IHS (Integrated Hardware Systems) FPGA, the main FPGAs on
gdsys devices, which supply the majority of the functionality offered
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 248068d5b43..09dfd8072db 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -32,7 +32,6 @@ endif
obj-$(CONFIG_ALTERA_SYSID) += altera_sysid.o
obj-$(CONFIG_ATSHA204A) += atsha204a-i2c.o
obj-$(CONFIG_CBMEM_CONSOLE) += cbmem_console.o
-obj-$(CONFIG_DS4510) += ds4510.o
obj-$(CONFIG_FSL_DEVICE_DISABLE) += fsl_devdis.o
obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
obj-$(CONFIG_FSL_IIM) += fsl_iim.o
@@ -59,7 +58,6 @@ obj-$(CONFIG_NPCM_OTP) += npcm_otp.o
obj-$(CONFIG_NPCM_HOST) += npcm_host_intf.o
obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o
obj-$(CONFIG_P2SB) += p2sb-uclass.o
-obj-$(CONFIG_PCA9551_LED) += pca9551_led.o
obj-$(CONFIG_$(PHASE_)PWRSEQ) += pwrseq-uclass.o
ifdef CONFIG_QFW
obj-y += qfw.o
diff --git a/drivers/misc/ds4510.c b/drivers/misc/ds4510.c
deleted file mode 100644
index 302015e2793..00000000000
--- a/drivers/misc/ds4510.c
+++ /dev/null
@@ -1,379 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright 2008 Extreme Engineering Solutions, Inc.
- */
-
-/*
- * Driver for DS4510, a CPU supervisor with integrated EEPROM, SRAM,
- * and 4 programmable non-volatile GPIO pins.
- */
-
-#include <i2c.h>
-#include <command.h>
-#include <linux/delay.h>
-#include "ds4510.h"
-
-enum {
- DS4510_CMD_INFO,
- DS4510_CMD_DEVICE,
- DS4510_CMD_NV,
- DS4510_CMD_RSTDELAY,
- DS4510_CMD_OUTPUT,
- DS4510_CMD_INPUT,
- DS4510_CMD_PULLUP,
- DS4510_CMD_EEPROM,
- DS4510_CMD_SEEPROM,
- DS4510_CMD_SRAM,
-};
-
-/*
- * Write to DS4510, taking page boundaries into account
- */
-static int ds4510_mem_write(uint8_t chip, int offset, uint8_t *buf, int count)
-{
- int wrlen;
- int i = 0;
-
- do {
- wrlen = DS4510_EEPROM_PAGE_SIZE -
- DS4510_EEPROM_PAGE_OFFSET(offset);
- if (count < wrlen)
- wrlen = count;
- if (i2c_write(chip, offset, 1, &buf[i], wrlen))
- return -1;
-
- /*
- * This delay isn't needed for SRAM writes but shouldn't delay
- * things too much, so do it unconditionally for simplicity
- */
- udelay(DS4510_EEPROM_PAGE_WRITE_DELAY_MS * 1000);
- count -= wrlen;
- offset += wrlen;
- i += wrlen;
- } while (count > 0);
-
- return 0;
-}
-
-/*
- * General read from DS4510
- */
-static int ds4510_mem_read(uint8_t chip, int offset, uint8_t *buf, int count)
-{
- return i2c_read(chip, offset, 1, buf, count);
-}
-
-/*
- * Write SEE bit in config register.
- * nv = 0 - Writes to SEEPROM registers behave like EEPROM
- * nv = 1 - Writes to SEEPROM registers behave like SRAM
- */
-static int ds4510_see_write(uint8_t chip, uint8_t nv)
-{
- uint8_t data;
-
- if (i2c_read(chip, DS4510_CFG, 1, &data, 1))
- return -1;
-
- if (nv) /* Treat SEEPROM bits as EEPROM */
- data &= ~DS4510_CFG_SEE;
- else /* Treat SEEPROM bits as SRAM */
- data |= DS4510_CFG_SEE;
-
- return ds4510_mem_write(chip, DS4510_CFG, &data, 1);
-}
-
-/*
- * Write de-assertion of reset signal delay
- */
-static int ds4510_rstdelay_write(uint8_t chip, uint8_t delay)
-{
- uint8_t data;
-
- if (i2c_read(chip, DS4510_RSTDELAY, 1, &data, 1))
- return -1;
-
- data &= ~DS4510_RSTDELAY_MASK;
- data |= delay & DS4510_RSTDELAY_MASK;
-
- return ds4510_mem_write(chip, DS4510_RSTDELAY, &data, 1);
-}
-
-/*
- * Write pullup characteristics of IO pins
- */
-static int ds4510_pullup_write(uint8_t chip, uint8_t val)
-{
- val &= DS4510_IO_MASK;
-
- return ds4510_mem_write(chip, DS4510_PULLUP, (uint8_t *)&val, 1);
-}
-
-/*
- * Read pullup characteristics of IO pins
- */
-static int ds4510_pullup_read(uint8_t chip)
-{
- uint8_t val;
-
- if (i2c_read(chip, DS4510_PULLUP, 1, &val, 1))
- return -1;
-
- return val & DS4510_IO_MASK;
-}
-
-/*
- * Write drive level of IO pins
- */
-static int ds4510_gpio_write(uint8_t chip, uint8_t val)
-{
- uint8_t data;
- int i;
-
- for (i = 0; i < DS4510_NUM_IO; i++) {
- if (i2c_read(chip, DS4510_IO0 - i, 1, &data, 1))
- return -1;
-
- if (val & (0x1 << i))
- data |= 0x1;
- else
- data &= ~0x1;
-
- if (ds4510_mem_write(chip, DS4510_IO0 - i, &data, 1))
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Read drive level of IO pins
- */
-static int ds4510_gpio_read(uint8_t chip)
-{
- uint8_t data;
- int val = 0;
- int i;
-
- for (i = 0; i < DS4510_NUM_IO; i++) {
- if (i2c_read(chip, DS4510_IO0 - i, 1, &data, 1))
- return -1;
-
- if (data & 1)
- val |= (1 << i);
- }
-
- return val;
-}
-
-/*
- * Read physical level of IO pins
- */
-static int ds4510_gpio_read_val(uint8_t chip)
-{
- uint8_t val;
-
- if (i2c_read(chip, DS4510_IO_STATUS, 1, &val, 1))
- return -1;
-
- return val & DS4510_IO_MASK;
-}
-
-/*
- * Display DS4510 information
- */
-static int ds4510_info(uint8_t chip)
-{
- int i;
- int tmp;
- uint8_t data;
-
- printf("DS4510 @ 0x%x:\n\n", chip);
-
- if (i2c_read(chip, DS4510_RSTDELAY, 1, &data, 1))
- return -1;
- printf("rstdelay = 0x%x\n\n", data & DS4510_RSTDELAY_MASK);
-
- if (i2c_read(chip, DS4510_CFG, 1, &data, 1))
- return -1;
- printf("config = 0x%x\n", data);
- printf(" /ready = %d\n", data & DS4510_CFG_READY ? 1 : 0);
- printf(" trip pt = %d\n", data & DS4510_CFG_TRIP_POINT ? 1 : 0);
- printf(" rst sts = %d\n", data & DS4510_CFG_RESET ? 1 : 0);
- printf(" /see = %d\n", data & DS4510_CFG_SEE ? 1 : 0);
- printf(" swrst = %d\n\n", data & DS4510_CFG_SWRST ? 1 : 0);
-
- printf("gpio pins: 3210\n");
- printf("---------------\n");
- printf("pullup ");
-
- tmp = ds4510_pullup_read(chip);
- if (tmp == -1)
- return tmp;
- for (i = DS4510_NUM_IO - 1; i >= 0; i--)
- printf("%d", (tmp & (1 << i)) ? 1 : 0);
- printf("\n");
-
- printf("driven ");
- tmp = ds4510_gpio_read(chip);
- if (tmp == -1)
- return -1;
- for (i = DS4510_NUM_IO - 1; i >= 0; i--)
- printf("%d", (tmp & (1 << i)) ? 1 : 0);
- printf("\n");
-
- printf("read ");
- tmp = ds4510_gpio_read_val(chip);
- if (tmp == -1)
- return -1;
- for (i = DS4510_NUM_IO - 1; i >= 0; i--)
- printf("%d", (tmp & (1 << i)) ? 1 : 0);
- printf("\n");
-
- return 0;
-}
-
-struct cmd_tbl cmd_ds4510[] = {
- U_BOOT_CMD_MKENT(device, 3, 0, (void *)DS4510_CMD_DEVICE, "", ""),
- U_BOOT_CMD_MKENT(nv, 3, 0, (void *)DS4510_CMD_NV, "", ""),
- U_BOOT_CMD_MKENT(output, 4, 0, (void *)DS4510_CMD_OUTPUT, "", ""),
- U_BOOT_CMD_MKENT(input, 3, 0, (void *)DS4510_CMD_INPUT, "", ""),
- U_BOOT_CMD_MKENT(pullup, 4, 0, (void *)DS4510_CMD_PULLUP, "", ""),
- U_BOOT_CMD_MKENT(info, 2, 0, (void *)DS4510_CMD_INFO, "", ""),
- U_BOOT_CMD_MKENT(rstdelay, 3, 0, (void *)DS4510_CMD_RSTDELAY, "", ""),
- U_BOOT_CMD_MKENT(eeprom, 6, 0, (void *)DS4510_CMD_EEPROM, "", ""),
- U_BOOT_CMD_MKENT(seeprom, 6, 0, (void *)DS4510_CMD_SEEPROM, "", ""),
- U_BOOT_CMD_MKENT(sram, 6, 0, (void *)DS4510_CMD_SRAM, "", ""),
-};
-
-int do_ds4510(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
-{
- static uint8_t chip = 0x51;
- struct cmd_tbl *c;
- ulong ul_arg2 = 0;
- ulong ul_arg3 = 0;
- int tmp;
- ulong addr;
- ulong off;
- ulong cnt;
- int end;
- int (*rw_func)(uint8_t, int, uint8_t *, int);
-
- c = find_cmd_tbl(argv[1], cmd_ds4510, ARRAY_SIZE(cmd_ds4510));
-
- /* All commands but "device" require 'maxargs' arguments */
- if (!c || !((argc == (c->maxargs)) ||
- (((int)c->cmd == DS4510_CMD_DEVICE) &&
- (argc == (c->maxargs - 1))))) {
- return cmd_usage(cmdtp);
- }
-
- /* arg2 used as chip addr and pin number */
- if (argc > 2)
- ul_arg2 = hextoul(argv[2], NULL);
-
- /* arg3 used as output/pullup value */
- if (argc > 3)
- ul_arg3 = hextoul(argv[3], NULL);
-
- switch ((int)c->cmd) {
- case DS4510_CMD_DEVICE:
- if (argc == 3)
- chip = ul_arg2;
- printf("Current device address: 0x%x\n", chip);
- return 0;
- case DS4510_CMD_NV:
- return ds4510_see_write(chip, ul_arg2);
- case DS4510_CMD_OUTPUT:
- tmp = ds4510_gpio_read(chip);
- if (tmp == -1)
- return -1;
- if (ul_arg3)
- tmp |= (1 << ul_arg2);
- else
- tmp &= ~(1 << ul_arg2);
- return ds4510_gpio_write(chip, tmp);
- case DS4510_CMD_INPUT:
- tmp = ds4510_gpio_read_val(chip);
- if (tmp == -1)
- return -1;
- return (tmp & (1 << ul_arg2)) != 0;
- case DS4510_CMD_PULLUP:
- tmp = ds4510_pullup_read(chip);
- if (tmp == -1)
- return -1;
- if (ul_arg3)
- tmp |= (1 << ul_arg2);
- else
- tmp &= ~(1 << ul_arg2);
- return ds4510_pullup_write(chip, tmp);
- case DS4510_CMD_INFO:
- return ds4510_info(chip);
- case DS4510_CMD_RSTDELAY:
- return ds4510_rstdelay_write(chip, ul_arg2);
- case DS4510_CMD_EEPROM:
- end = DS4510_EEPROM + DS4510_EEPROM_SIZE;
- off = DS4510_EEPROM;
- break;
- case DS4510_CMD_SEEPROM:
- end = DS4510_SEEPROM + DS4510_SEEPROM_SIZE;
- off = DS4510_SEEPROM;
- break;
- case DS4510_CMD_SRAM:
- end = DS4510_SRAM + DS4510_SRAM_SIZE;
- off = DS4510_SRAM;
- break;
- default:
- /* We should never get here... */
- return 1;
- }
-
- /* Only eeprom, seeprom, and sram commands should make it here */
- if (strcmp(argv[2], "read") == 0)
- rw_func = ds4510_mem_read;
- else if (strcmp(argv[2], "write") == 0)
- rw_func = ds4510_mem_write;
- else
- return cmd_usage(cmdtp);
-
- addr = hextoul(argv[3], NULL);
- off += hextoul(argv[4], NULL);
- cnt = hextoul(argv[5], NULL);
-
- if ((off + cnt) > end) {
- printf("ERROR: invalid len\n");
- return -1;
- }
-
- return rw_func(chip, off, (uint8_t *)addr, cnt);
-}
-
-U_BOOT_CMD(
- ds4510, 6, 1, do_ds4510,
- "ds4510 eeprom/seeprom/sram/gpio access",
- "device [dev]\n"
- " - show or set current device address\n"
- "ds4510 info\n"
- " - display ds4510 info\n"
- "ds4510 output pin 0|1\n"
- " - set pin low or high-Z\n"
- "ds4510 input pin\n"
- " - read value of pin\n"
- "ds4510 pullup pin 0|1\n"
- " - disable/enable pullup on specified pin\n"
- "ds4510 nv 0|1\n"
- " - make gpio and seeprom writes volatile/non-volatile"
- "\n"
- "ds4510 rstdelay 0-3\n"
- " - set reset output delay"
- "\n"
- "ds4510 eeprom read addr off cnt\n"
- "ds4510 eeprom write addr off cnt\n"
- " - read/write 'cnt' bytes at EEPROM offset 'off'\n"
- "ds4510 seeprom read addr off cnt\n"
- "ds4510 seeprom write addr off cnt\n"
- " - read/write 'cnt' bytes at SRAM-shadowed EEPROM offset 'off'\n"
- "ds4510 sram read addr off cnt\n"
- "ds4510 sram write addr off cnt\n"
- " - read/write 'cnt' bytes at SRAM offset 'off'"
-);
diff --git a/drivers/misc/ds4510.h b/drivers/misc/ds4510.h
deleted file mode 100644
index 5c7a1a8c737..00000000000
--- a/drivers/misc/ds4510.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright 2008 Extreme Engineering Solutions, Inc.
- */
-
-#ifndef __DS4510_H_
-#define __DS4510_H_
-
-/* General defines */
-#define DS4510_NUM_IO 0x04
-#define DS4510_IO_MASK ((1 << DS4510_NUM_IO) - 1)
-#define DS4510_EEPROM_PAGE_WRITE_DELAY_MS 20
-
-/* EEPROM from 0x00 - 0x39 */
-#define DS4510_EEPROM 0x00
-#define DS4510_EEPROM_SIZE 0x40
-#define DS4510_EEPROM_PAGE_SIZE 0x08
-#define DS4510_EEPROM_PAGE_OFFSET(x) ((x) & (DS4510_EEPROM_PAGE_SIZE - 1))
-
-/* SEEPROM from 0xf0 - 0xf7 */
-#define DS4510_SEEPROM 0xf0
-#define DS4510_SEEPROM_SIZE 0x08
-
-/* Registers overlapping SEEPROM from 0xf0 - 0xf7 */
-#define DS4510_PULLUP 0xF0
-#define DS4510_PULLUP_DIS 0x00
-#define DS4510_PULLUP_EN 0x01
-#define DS4510_RSTDELAY 0xF1
-#define DS4510_RSTDELAY_MASK 0x03
-#define DS4510_RSTDELAY_125 0x00
-#define DS4510_RSTDELAY_250 0x01
-#define DS4510_RSTDELAY_500 0x02
-#define DS4510_RSTDELAY_1000 0x03
-#define DS4510_IO3 0xF4
-#define DS4510_IO2 0xF5
-#define DS4510_IO1 0xF6
-#define DS4510_IO0 0xF7
-
-/* Status configuration registers from 0xf8 - 0xf9*/
-#define DS4510_IO_STATUS 0xF8
-#define DS4510_CFG 0xF9
-#define DS4510_CFG_READY 0x80
-#define DS4510_CFG_TRIP_POINT 0x40
-#define DS4510_CFG_RESET 0x20
-#define DS4510_CFG_SEE 0x10
-#define DS4510_CFG_SWRST 0x08
-
-/* SRAM from 0xfa - 0xff */
-#define DS4510_SRAM 0xfa
-#define DS4510_SRAM_SIZE 0x06
-
-#endif /* __DS4510_H_ */
diff --git a/drivers/misc/pca9551_led.c b/drivers/misc/pca9551_led.c
deleted file mode 100644
index 040d0d5cf48..00000000000
--- a/drivers/misc/pca9551_led.c
+++ /dev/null
@@ -1,170 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2015 Stefan Roese <sr@denx.de>
- */
-
-#include <errno.h>
-#include <i2c.h>
-#include <status_led.h>
-
-#ifndef CONFIG_PCA9551_I2C_ADDR
-#error "CONFIG_PCA9551_I2C_ADDR not defined!"
-#endif
-
-#define PCA9551_REG_INPUT 0x00 /* Input register (read only) */
-#define PCA9551_REG_PSC0 0x01 /* Frequency prescaler 0 */
-#define PCA9551_REG_PWM0 0x02 /* PWM0 */
-#define PCA9551_REG_PSC1 0x03 /* Frequency prescaler 1 */
-#define PCA9551_REG_PWM1 0x04 /* PWM1 */
-#define PCA9551_REG_LS0 0x05 /* LED0 to LED3 selector */
-#define PCA9551_REG_LS1 0x06 /* LED4 to LED7 selector */
-
-#define PCA9551_CTRL_AI (1 << 4) /* Auto-increment flag */
-
-#define PCA9551_LED_STATE_ON 0x00
-#define PCA9551_LED_STATE_OFF 0x01
-#define PCA9551_LED_STATE_BLINK0 0x02
-#define PCA9551_LED_STATE_BLINK1 0x03
-
-struct pca9551_blink_rate {
- u8 psc; /* Frequency preescaler, see PCA9551_7.pdf p. 6 */
- u8 pwm; /* Pulse width modulation, see PCA9551_7.pdf p. 6 */
-};
-
-static int freq_last = -1;
-static int mask_last = -1;
-static int idx_last = -1;
-static int mode_last;
-
-static int pca9551_led_get_state(int led, int *state)
-{
- unsigned int reg;
- u8 shift, buf;
- int ret;
-
- if (led < 0 || led > 7) {
- return -EINVAL;
- } else if (led < 4) {
- reg = PCA9551_REG_LS0;
- shift = led << 1;
- } else {
- reg = PCA9551_REG_LS1;
- shift = (led - 4) << 1;
- }
-
- ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
- if (ret)
- return ret;
-
- *state = (buf >> shift) & 0x03;
- return 0;
-}
-
-static int pca9551_led_set_state(int led, int state)
-{
- unsigned int reg;
- u8 shift, buf, mask;
- int ret;
-
- if (led < 0 || led > 7) {
- return -EINVAL;
- } else if (led < 4) {
- reg = PCA9551_REG_LS0;
- shift = led << 1;
- } else {
- reg = PCA9551_REG_LS1;
- shift = (led - 4) << 1;
- }
- mask = 0x03 << shift;
-
- ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
- if (ret)
- return ret;
-
- buf = (buf & ~mask) | ((state & 0x03) << shift);
-
- ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int pca9551_led_set_blink_rate(int idx, struct pca9551_blink_rate rate)
-{
- unsigned int reg;
- int ret;
-
- switch (idx) {
- case 0:
- reg = PCA9551_REG_PSC0;
- break;
- case 1:
- reg = PCA9551_REG_PSC1;
- break;
- default:
- return -EINVAL;
- }
- reg |= PCA9551_CTRL_AI;
-
- ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, (u8 *)&rate, 2);
- if (ret)
- return ret;
-
- return 0;
-}
-
-/*
- * Functions referenced by cmd_led.c or status_led.c
- */
-void __led_init(led_id_t id, int state)
-{
-}
-
-void __led_set(led_id_t mask, int state)
-{
- if (state == CONFIG_LED_STATUS_OFF)
- pca9551_led_set_state(mask, PCA9551_LED_STATE_OFF);
- else
- pca9551_led_set_state(mask, PCA9551_LED_STATE_ON);
-}
-
-void __led_toggle(led_id_t mask)
-{
- int state = 0;
-
- pca9551_led_get_state(mask, &state);
- pca9551_led_set_state(mask, !state);
-}
-
-void __led_blink(led_id_t mask, int freq)
-{
- struct pca9551_blink_rate rate;
- int mode;
- int idx;
-
- if ((freq == freq_last) || (mask == mask_last)) {
- idx = idx_last;
- mode = mode_last;
- } else {
- /* Toggle blink index */
- if (idx_last == 0) {
- idx = 1;
- mode = PCA9551_LED_STATE_BLINK1;
- } else {
- idx = 0;
- mode = PCA9551_LED_STATE_BLINK0;
- }
-
- idx_last = idx;
- mode_last = mode;
- }
- freq_last = freq;
- mask_last = mask;
-
- rate.psc = ((freq * 38) / 1000) - 1;
- rate.pwm = 128; /* 50% duty cycle */
-
- pca9551_led_set_blink_rate(idx, rate);
- pca9551_led_set_state(mask, mode);
-}
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 1c9b6898bff..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
@@ -333,15 +333,6 @@ config MMC_MESON_GX
help
Support for EMMC host controller on Meson GX ARM SoCs platform (S905)
-config MMC_MXC
- bool "Freescale i.MX21/27/31 or MPC512x Multimedia Card support"
- help
- This selects the Freescale i.MX21, i.MX27, i.MX31 or MPC512x
- Multimedia Card Interface. If you have an i.MX or MPC512x platform
- with a Multimedia Card slot, say Y here.
-
- If unsure, say N.
-
config MMC_OWL
bool "Actions OWL Multimedia Card Interface support"
depends on ARCH_OWL
@@ -391,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.
@@ -597,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.
@@ -660,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
@@ -718,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.
@@ -740,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.
@@ -794,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
@@ -852,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,
@@ -886,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
@@ -938,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 360706f53d2..a23336d7d8d 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -41,7 +41,6 @@ obj-$(CONFIG_MMC_MESON_GX) += meson_gx_mmc.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
obj-$(CONFIG_MVEBU_MMC) += mvebu_mmc.o
obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o
-obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxsmmc.o
obj-$(CONFIG_MMC_OCTEONTX) += octeontx_hsmmc.o
obj-$(CONFIG_MMC_OWL) += owl_mmc.o
@@ -64,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/mmc/mxcmmc.c b/drivers/mmc/mxcmmc.c
deleted file mode 100644
index 1acea6f820b..00000000000
--- a/drivers/mmc/mxcmmc.c
+++ /dev/null
@@ -1,523 +0,0 @@
-/*
- * This is a driver for the SDHC controller found in Freescale MX2/MX3
- * SoCs. It is basically the same hardware as found on MX1 (imxmmc.c).
- * Unlike the hardware found on MX1, this hardware just works and does
- * not need all the quirks found in imxmmc.c, hence the seperate driver.
- *
- * Copyright (C) 2009 Ilya Yanok, <yanok@emcraft.com>
- * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
- * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
- *
- * derived from pxamci.c by Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <config.h>
-#include <command.h>
-#include <mmc.h>
-#include <part.h>
-#include <malloc.h>
-#include <mmc.h>
-#include <time.h>
-#include <linux/errno.h>
-#include <asm/io.h>
-#include <asm/arch/clock.h>
-
-#define DRIVER_NAME "mxc-mmc"
-
-struct mxcmci_regs {
- u32 str_stp_clk;
- u32 status;
- u32 clk_rate;
- u32 cmd_dat_cont;
- u32 res_to;
- u32 read_to;
- u32 blk_len;
- u32 nob;
- u32 rev_no;
- u32 int_cntr;
- u32 cmd;
- u32 arg;
- u32 pad;
- u32 res_fifo;
- u32 buffer_access;
-};
-
-#define STR_STP_CLK_RESET (1 << 3)
-#define STR_STP_CLK_START_CLK (1 << 1)
-#define STR_STP_CLK_STOP_CLK (1 << 0)
-
-#define STATUS_CARD_INSERTION (1 << 31)
-#define STATUS_CARD_REMOVAL (1 << 30)
-#define STATUS_YBUF_EMPTY (1 << 29)
-#define STATUS_XBUF_EMPTY (1 << 28)
-#define STATUS_YBUF_FULL (1 << 27)
-#define STATUS_XBUF_FULL (1 << 26)
-#define STATUS_BUF_UND_RUN (1 << 25)
-#define STATUS_BUF_OVFL (1 << 24)
-#define STATUS_SDIO_INT_ACTIVE (1 << 14)
-#define STATUS_END_CMD_RESP (1 << 13)
-#define STATUS_WRITE_OP_DONE (1 << 12)
-#define STATUS_DATA_TRANS_DONE (1 << 11)
-#define STATUS_READ_OP_DONE (1 << 11)
-#define STATUS_WR_CRC_ERROR_CODE_MASK (3 << 10)
-#define STATUS_CARD_BUS_CLK_RUN (1 << 8)
-#define STATUS_BUF_READ_RDY (1 << 7)
-#define STATUS_BUF_WRITE_RDY (1 << 6)
-#define STATUS_RESP_CRC_ERR (1 << 5)
-#define STATUS_CRC_READ_ERR (1 << 3)
-#define STATUS_CRC_WRITE_ERR (1 << 2)
-#define STATUS_TIME_OUT_RESP (1 << 1)
-#define STATUS_TIME_OUT_READ (1 << 0)
-#define STATUS_ERR_MASK 0x2f
-
-#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1 << 12)
-#define CMD_DAT_CONT_STOP_READWAIT (1 << 11)
-#define CMD_DAT_CONT_START_READWAIT (1 << 10)
-#define CMD_DAT_CONT_BUS_WIDTH_4 (2 << 8)
-#define CMD_DAT_CONT_INIT (1 << 7)
-#define CMD_DAT_CONT_WRITE (1 << 4)
-#define CMD_DAT_CONT_DATA_ENABLE (1 << 3)
-#define CMD_DAT_CONT_RESPONSE_48BIT_CRC (1 << 0)
-#define CMD_DAT_CONT_RESPONSE_136BIT (2 << 0)
-#define CMD_DAT_CONT_RESPONSE_48BIT (3 << 0)
-
-#define INT_SDIO_INT_WKP_EN (1 << 18)
-#define INT_CARD_INSERTION_WKP_EN (1 << 17)
-#define INT_CARD_REMOVAL_WKP_EN (1 << 16)
-#define INT_CARD_INSERTION_EN (1 << 15)
-#define INT_CARD_REMOVAL_EN (1 << 14)
-#define INT_SDIO_IRQ_EN (1 << 13)
-#define INT_DAT0_EN (1 << 12)
-#define INT_BUF_READ_EN (1 << 4)
-#define INT_BUF_WRITE_EN (1 << 3)
-#define INT_END_CMD_RES_EN (1 << 2)
-#define INT_WRITE_OP_DONE_EN (1 << 1)
-#define INT_READ_OP_EN (1 << 0)
-
-struct mxcmci_host {
- struct mmc *mmc;
- struct mxcmci_regs *base;
- int irq;
- int detect_irq;
- int dma;
- int do_dma;
- unsigned int power_mode;
-
- struct mmc_cmd *cmd;
- struct mmc_data *data;
-
- unsigned int dma_nents;
- unsigned int datasize;
- unsigned int dma_dir;
-
- u16 rev_no;
- unsigned int cmdat;
-
- int clock;
-};
-
-static struct mxcmci_host mxcmci_host;
-
-/* maintainer note: do we really want to have a global host pointer? */
-static struct mxcmci_host *host = &mxcmci_host;
-
-static inline int mxcmci_use_dma(struct mxcmci_host *host)
-{
- return host->do_dma;
-}
-
-static void mxcmci_softreset(struct mxcmci_host *host)
-{
- int i;
-
- /* reset sequence */
- writel(STR_STP_CLK_RESET, &host->base->str_stp_clk);
- writel(STR_STP_CLK_RESET | STR_STP_CLK_START_CLK,
- &host->base->str_stp_clk);
-
- for (i = 0; i < 8; i++)
- writel(STR_STP_CLK_START_CLK, &host->base->str_stp_clk);
-
- writel(0xff, &host->base->res_to);
-}
-
-static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
-{
- unsigned int nob = data->blocks;
- unsigned int blksz = data->blocksize;
- unsigned int datasize = nob * blksz;
-
- host->data = data;
-
- writel(nob, &host->base->nob);
- writel(blksz, &host->base->blk_len);
- host->datasize = datasize;
-}
-
-static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_cmd *cmd,
- unsigned int cmdat)
-{
- if (host->cmd != NULL)
- printf("mxcmci: error!\n");
- host->cmd = cmd;
-
- switch (cmd->resp_type) {
- case MMC_RSP_R1: /* short CRC, OPCODE */
- case MMC_RSP_R1b:/* short CRC, OPCODE, BUSY */
- cmdat |= CMD_DAT_CONT_RESPONSE_48BIT_CRC;
- break;
- case MMC_RSP_R2: /* long 136 bit + CRC */
- cmdat |= CMD_DAT_CONT_RESPONSE_136BIT;
- break;
- case MMC_RSP_R3: /* short */
- cmdat |= CMD_DAT_CONT_RESPONSE_48BIT;
- break;
- case MMC_RSP_NONE:
- break;
- default:
- printf("mxcmci: unhandled response type 0x%x\n",
- cmd->resp_type);
- return -EINVAL;
- }
-
- writel(cmd->cmdidx, &host->base->cmd);
- writel(cmd->cmdarg, &host->base->arg);
- writel(cmdat, &host->base->cmd_dat_cont);
-
- return 0;
-}
-
-static void mxcmci_finish_request(struct mxcmci_host *host,
- struct mmc_cmd *cmd, struct mmc_data *data)
-{
- host->cmd = NULL;
- host->data = NULL;
-}
-
-static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat)
-{
- int data_error = 0;
-
- if (stat & STATUS_ERR_MASK) {
- printf("request failed. status: 0x%08x\n",
- stat);
- if (stat & STATUS_CRC_READ_ERR) {
- data_error = -EILSEQ;
- } else if (stat & STATUS_CRC_WRITE_ERR) {
- u32 err_code = (stat >> 9) & 0x3;
- if (err_code == 2) /* No CRC response */
- data_error = -ETIMEDOUT;
- else
- data_error = -EILSEQ;
- } else if (stat & STATUS_TIME_OUT_READ) {
- data_error = -ETIMEDOUT;
- } else {
- data_error = -EIO;
- }
- }
-
- host->data = NULL;
-
- return data_error;
-}
-
-static int mxcmci_read_response(struct mxcmci_host *host, unsigned int stat)
-{
- struct mmc_cmd *cmd = host->cmd;
- int i;
- u32 a, b, c;
- u32 *resp = (u32 *)cmd->response;
-
- if (!cmd)
- return 0;
-
- if (stat & STATUS_TIME_OUT_RESP) {
- printf("CMD TIMEOUT\n");
- return -ETIMEDOUT;
- } else if (stat & STATUS_RESP_CRC_ERR && cmd->resp_type & MMC_RSP_CRC) {
- printf("cmd crc error\n");
- return -EILSEQ;
- }
-
- if (cmd->resp_type & MMC_RSP_PRESENT) {
- if (cmd->resp_type & MMC_RSP_136) {
- for (i = 0; i < 4; i++) {
- a = readl(&host->base->res_fifo) & 0xFFFF;
- b = readl(&host->base->res_fifo) & 0xFFFF;
- resp[i] = a << 16 | b;
- }
- } else {
- a = readl(&host->base->res_fifo) & 0xFFFF;
- b = readl(&host->base->res_fifo) & 0xFFFF;
- c = readl(&host->base->res_fifo) & 0xFFFF;
- resp[0] = a << 24 | b << 8 | c >> 8;
- }
- }
- return 0;
-}
-
-static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
-{
- u32 stat;
- unsigned long timeout = get_ticks() + CONFIG_SYS_HZ;
-
- do {
- stat = readl(&host->base->status);
- if (stat & STATUS_ERR_MASK)
- return stat;
- if (timeout < get_ticks())
- return STATUS_TIME_OUT_READ;
- if (stat & mask)
- return 0;
- } while (1);
-}
-
-static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
-{
- unsigned int stat;
- u32 *buf = _buf;
-
- while (bytes > 3) {
- stat = mxcmci_poll_status(host,
- STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
- if (stat)
- return stat;
- *buf++ = readl(&host->base->buffer_access);
- bytes -= 4;
- }
-
- if (bytes) {
- u8 *b = (u8 *)buf;
- u32 tmp;
-
- stat = mxcmci_poll_status(host,
- STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
- if (stat)
- return stat;
- tmp = readl(&host->base->buffer_access);
- memcpy(b, &tmp, bytes);
- }
-
- return 0;
-}
-
-static int mxcmci_push(struct mxcmci_host *host, const void *_buf, int bytes)
-{
- unsigned int stat;
- const u32 *buf = _buf;
-
- while (bytes > 3) {
- stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
- if (stat)
- return stat;
- writel(*buf++, &host->base->buffer_access);
- bytes -= 4;
- }
-
- if (bytes) {
- const u8 *b = (u8 *)buf;
- u32 tmp;
-
- stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
- if (stat)
- return stat;
-
- memcpy(&tmp, b, bytes);
- writel(tmp, &host->base->buffer_access);
- }
-
- stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
- if (stat)
- return stat;
-
- return 0;
-}
-
-static int mxcmci_transfer_data(struct mxcmci_host *host)
-{
- struct mmc_data *data = host->data;
- int stat;
- unsigned long length;
-
- length = data->blocks * data->blocksize;
- host->datasize = 0;
-
- if (data->flags & MMC_DATA_READ) {
- stat = mxcmci_pull(host, data->dest, length);
- if (stat)
- return stat;
- host->datasize += length;
- } else {
- stat = mxcmci_push(host, (const void *)(data->src), length);
- if (stat)
- return stat;
- host->datasize += length;
- stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE);
- if (stat)
- return stat;
- }
- return 0;
-}
-
-static int mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat)
-{
- int datastat;
- int ret;
-
- ret = mxcmci_read_response(host, stat);
-
- if (ret) {
- mxcmci_finish_request(host, host->cmd, host->data);
- return ret;
- }
-
- if (!host->data) {
- mxcmci_finish_request(host, host->cmd, host->data);
- return 0;
- }
-
- datastat = mxcmci_transfer_data(host);
- ret = mxcmci_finish_data(host, datastat);
- mxcmci_finish_request(host, host->cmd, host->data);
- return ret;
-}
-
-static int mxcmci_request(struct mmc *mmc, struct mmc_cmd *cmd,
- struct mmc_data *data)
-{
- struct mxcmci_host *host = mmc->priv;
- unsigned int cmdat = host->cmdat;
- u32 stat;
- int ret;
-
- host->cmdat &= ~CMD_DAT_CONT_INIT;
- if (data) {
- mxcmci_setup_data(host, data);
-
- cmdat |= CMD_DAT_CONT_DATA_ENABLE;
-
- if (data->flags & MMC_DATA_WRITE)
- cmdat |= CMD_DAT_CONT_WRITE;
- }
-
- if ((ret = mxcmci_start_cmd(host, cmd, cmdat))) {
- mxcmci_finish_request(host, cmd, data);
- return ret;
- }
-
- do {
- stat = readl(&host->base->status);
- writel(stat, &host->base->status);
- } while (!(stat & STATUS_END_CMD_RESP));
-
- return mxcmci_cmd_done(host, stat);
-}
-
-static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios)
-{
- unsigned int divider;
- int prescaler = 0;
- unsigned long clk_in = mxc_get_clock(MXC_ESDHC_CLK);
-
- while (prescaler <= 0x800) {
- for (divider = 1; divider <= 0xF; divider++) {
- int x;
-
- x = (clk_in / (divider + 1));
-
- if (prescaler)
- x /= (prescaler * 2);
-
- if (x <= clk_ios)
- break;
- }
- if (divider < 0x10)
- break;
-
- if (prescaler == 0)
- prescaler = 1;
- else
- prescaler <<= 1;
- }
-
- writel((prescaler << 4) | divider, &host->base->clk_rate);
-}
-
-static int mxcmci_set_ios(struct mmc *mmc)
-{
- struct mxcmci_host *host = mmc->priv;
- if (mmc->bus_width == 4)
- host->cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
- else
- host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4;
-
- if (mmc->clock) {
- mxcmci_set_clk_rate(host, mmc->clock);
- writel(STR_STP_CLK_START_CLK, &host->base->str_stp_clk);
- } else {
- writel(STR_STP_CLK_STOP_CLK, &host->base->str_stp_clk);
- }
-
- host->clock = mmc->clock;
-
- return 0;
-}
-
-static int mxcmci_init(struct mmc *mmc)
-{
- struct mxcmci_host *host = mmc->priv;
-
- mxcmci_softreset(host);
-
- host->rev_no = readl(&host->base->rev_no);
- if (host->rev_no != 0x400) {
- printf("wrong rev.no. 0x%08x. aborting.\n",
- host->rev_no);
- return -ENODEV;
- }
-
- /* recommended in data sheet */
- writel(0x2db4, &host->base->read_to);
-
- writel(0, &host->base->int_cntr);
-
- return 0;
-}
-
-static const struct mmc_ops mxcmci_ops = {
- .send_cmd = mxcmci_request,
- .set_ios = mxcmci_set_ios,
- .init = mxcmci_init,
-};
-
-static struct mmc_config mxcmci_cfg = {
- .name = "MXC MCI",
- .ops = &mxcmci_ops,
- .host_caps = MMC_MODE_4BIT,
- .voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
- .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
-};
-
-static int mxcmci_initialize(struct bd_info *bis)
-{
- host->base = (struct mxcmci_regs *)CONFIG_MXC_MCI_REGS_BASE;
-
- mxcmci_cfg.f_min = mxc_get_clock(MXC_ESDHC_CLK) >> 7;
- mxcmci_cfg.f_max = mxc_get_clock(MXC_ESDHC_CLK) >> 1;
-
- host->mmc = mmc_create(&mxcmci_cfg, host);
- if (host->mmc == NULL)
- return -1;
-
- return 0;
-}
-
-int mxc_mmc_init(struct bd_info *bis)
-{
- return mxcmci_initialize(bis);
-}
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/airoha_eth.c b/drivers/net/airoha_eth.c
index 7e35e1fd41d..6588eb3a806 100644
--- a/drivers/net/airoha_eth.c
+++ b/drivers/net/airoha_eth.c
@@ -97,6 +97,7 @@
(_n) == 2 ? GDM2_BASE : GDM1_BASE)
#define REG_GDM_FWD_CFG(_n) GDM_BASE(_n)
+#define GDM_PAD_EN BIT(28)
#define GDM_DROP_CRC_ERR BIT(23)
#define GDM_IP4_CKSUM BIT(22)
#define GDM_TCP_CKSUM BIT(21)
@@ -354,13 +355,37 @@ static u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val)
#define airoha_switch_wr(eth, offset, val) \
airoha_wr((eth)->switch_regs, (offset), (val))
+static inline dma_addr_t dma_map_unaligned(void *vaddr, size_t len,
+ enum dma_data_direction dir)
+{
+ uintptr_t start, end;
+
+ start = ALIGN_DOWN((uintptr_t)vaddr, ARCH_DMA_MINALIGN);
+ end = ALIGN((uintptr_t)(vaddr + len), ARCH_DMA_MINALIGN);
+
+ return dma_map_single((void *)start, end - start, dir);
+}
+
+static inline void dma_unmap_unaligned(dma_addr_t addr, size_t len,
+ enum dma_data_direction dir)
+{
+ uintptr_t start, end;
+
+ start = ALIGN_DOWN((uintptr_t)addr, ARCH_DMA_MINALIGN);
+ end = ALIGN((uintptr_t)(addr + len), ARCH_DMA_MINALIGN);
+ dma_unmap_single(start, end - start, dir);
+}
+
static void airoha_fe_maccr_init(struct airoha_eth *eth)
{
int p;
for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) {
- /* Disable any kind of CRC drop or offload */
- airoha_fe_wr(eth, REG_GDM_FWD_CFG(p), 0);
+ /*
+ * Disable any kind of CRC drop or offload.
+ * Enable padding of short TX packets to 60 bytes.
+ */
+ airoha_fe_wr(eth, REG_GDM_FWD_CFG(p), GDM_PAD_EN);
}
}
@@ -371,13 +396,14 @@ static int airoha_fe_init(struct airoha_eth *eth)
return 0;
}
-static void airoha_qdma_reset_rx_desc(struct airoha_queue *q, int index,
- uchar *rx_packet)
+static void airoha_qdma_reset_rx_desc(struct airoha_queue *q, int index)
{
struct airoha_qdma_desc *desc;
+ uchar *rx_packet;
u32 val;
desc = &q->desc[index];
+ rx_packet = net_rx_packets[index];
index = (index + 1) % q->ndesc;
dma_map_single(rx_packet, PKTSIZE_ALIGN, DMA_TO_DEVICE);
@@ -391,7 +417,7 @@ static void airoha_qdma_reset_rx_desc(struct airoha_queue *q, int index,
val = FIELD_PREP(QDMA_DESC_LEN_MASK, PKTSIZE_ALIGN);
WRITE_ONCE(desc->ctrl, cpu_to_le32(val));
- dma_map_single(desc, sizeof(*desc), DMA_TO_DEVICE);
+ dma_map_unaligned(desc, sizeof(*desc), DMA_TO_DEVICE);
}
static void airoha_qdma_init_rx_desc(struct airoha_queue *q)
@@ -399,7 +425,7 @@ static void airoha_qdma_init_rx_desc(struct airoha_queue *q)
int i;
for (i = 0; i < q->ndesc; i++)
- airoha_qdma_reset_rx_desc(q, i, net_rx_packets[i]);
+ airoha_qdma_reset_rx_desc(q, i);
}
static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
@@ -423,10 +449,14 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
RX_RING_SIZE_MASK,
FIELD_PREP(RX_RING_SIZE_MASK, ndesc));
+ /*
+ * See arht_eth_free_pkt() for the reasons used to fill
+ * REG_RX_CPU_IDX(qid) register.
+ */
airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), RX_RING_THR_MASK,
FIELD_PREP(RX_RING_THR_MASK, 0));
airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK,
- FIELD_PREP(RX_RING_CPU_IDX_MASK, q->ndesc - 1));
+ FIELD_PREP(RX_RING_CPU_IDX_MASK, q->ndesc - 3));
airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK,
FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head));
@@ -804,6 +834,11 @@ static int airoha_eth_send(struct udevice *dev, void *packet, int length)
u32 val;
int i;
+ /*
+ * There is no need to pad short TX packets to 60 bytes since the
+ * GDM_PAD_EN bit set in the corresponding REG_GDM_FWD_CFG(n) register.
+ */
+
dma_addr = dma_map_single(packet, length, DMA_TO_DEVICE);
qid = 0;
@@ -826,14 +861,14 @@ static int airoha_eth_send(struct udevice *dev, void *packet, int length)
WRITE_ONCE(desc->msg1, cpu_to_le32(msg1));
WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff));
- dma_map_single(desc, sizeof(*desc), DMA_TO_DEVICE);
+ dma_map_unaligned(desc, sizeof(*desc), DMA_TO_DEVICE);
airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK,
FIELD_PREP(TX_RING_CPU_IDX_MASK, index));
for (i = 0; i < 100; i++) {
- dma_unmap_single(virt_to_phys(desc), sizeof(*desc),
- DMA_FROM_DEVICE);
+ dma_unmap_unaligned(virt_to_phys(desc), sizeof(*desc),
+ DMA_FROM_DEVICE);
if (desc->ctrl & QDMA_DESC_DONE_MASK)
break;
@@ -864,8 +899,8 @@ static int airoha_eth_recv(struct udevice *dev, int flags, uchar **packetp)
q = &qdma->q_rx[qid];
desc = &q->desc[q->head];
- dma_unmap_single(virt_to_phys(desc), sizeof(*desc),
- DMA_FROM_DEVICE);
+ dma_unmap_unaligned(virt_to_phys(desc), sizeof(*desc),
+ DMA_FROM_DEVICE);
if (!(desc->ctrl & QDMA_DESC_DONE_MASK))
return -EAGAIN;
@@ -885,6 +920,7 @@ static int arht_eth_free_pkt(struct udevice *dev, uchar *packet, int length)
struct airoha_qdma *qdma = &eth->qdma[0];
struct airoha_queue *q;
int qid;
+ u16 prev, pprev;
if (!packet)
return 0;
@@ -892,13 +928,24 @@ static int arht_eth_free_pkt(struct udevice *dev, uchar *packet, int length)
qid = 0;
q = &qdma->q_rx[qid];
- dma_map_single(packet, length, DMA_TO_DEVICE);
-
- airoha_qdma_reset_rx_desc(q, q->head, packet);
+ /*
+ * Due to cpu cache issue the airoha_qdma_reset_rx_desc() function
+ * will always touch 2 descriptors:
+ * - if current descriptor is even, then the previous and the one
+ * before previous descriptors will be touched (previous cacheline)
+ * - if current descriptor is odd, then only current and previous
+ * descriptors will be touched (current cacheline)
+ *
+ * Thus, to prevent possible destroying of rx queue, only (q->ndesc - 2)
+ * descriptors might be used for packet receiving.
+ */
+ prev = (q->head + q->ndesc - 1) % q->ndesc;
+ pprev = (q->head + q->ndesc - 2) % q->ndesc;
+ q->head = (q->head + 1) % q->ndesc;
+ airoha_qdma_reset_rx_desc(q, prev);
airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK,
- FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head));
- q->head = (q->head + 1) % q->ndesc;
+ FIELD_PREP(RX_RING_CPU_IDX_MASK, pprev));
return 0;
}
@@ -926,6 +973,7 @@ static int arht_eth_write_hwaddr(struct udevice *dev)
static const struct udevice_id airoha_eth_ids[] = {
{ .compatible = "airoha,en7581-eth" },
+ { }
};
static const struct eth_ops airoha_eth_ops = {
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,