diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/bootcount/Kconfig | 7 | ||||
-rw-r--r-- | drivers/bootcount/Makefile | 1 | ||||
-rw-r--r-- | drivers/bootcount/bootcount_zynqmp.c | 47 | ||||
-rw-r--r-- | drivers/core/root.c | 7 | ||||
-rw-r--r-- | drivers/dfu/Kconfig | 7 | ||||
-rw-r--r-- | drivers/dfu/Makefile | 1 | ||||
-rw-r--r-- | drivers/dfu/dfu.c | 5 | ||||
-rw-r--r-- | drivers/dfu/dfu_scsi.c | 435 | ||||
-rw-r--r-- | drivers/gpio/at91_gpio.c | 97 | ||||
-rw-r--r-- | drivers/gpio/qcom_pmic_gpio.c | 21 | ||||
-rw-r--r-- | drivers/iommu/apple_dart.c | 2 | ||||
-rw-r--r-- | drivers/iommu/qcom-hyp-smmu.c | 9 | ||||
-rw-r--r-- | drivers/misc/imx8/scu_api.c | 20 | ||||
-rw-r--r-- | drivers/mmc/msm_sdhci.c | 12 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/atmel/nand-controller.c | 7 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/atmel/pmecc.c | 1 | ||||
-rw-r--r-- | drivers/mtd/ubispl/ubispl.c | 2 | ||||
-rw-r--r-- | drivers/net/fec_mxc.c | 14 | ||||
-rw-r--r-- | drivers/phy/qcom/phy-qcom-qmp-ufs.c | 210 | ||||
-rw-r--r-- | drivers/spi/cadence_qspi.c | 7 | ||||
-rw-r--r-- | drivers/spi/spi-uclass.c | 4 | ||||
-rw-r--r-- | drivers/usb/eth/asix88179.c | 45 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 7 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/siemens_pmic_wdt.c | 59 |
25 files changed, 984 insertions, 44 deletions
diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig index fa6d8e71281..0080d2a165c 100644 --- a/drivers/bootcount/Kconfig +++ b/drivers/bootcount/Kconfig @@ -164,6 +164,13 @@ config DM_BOOTCOUNT_SYSCON Accessing the backend is done using the regmap interface. +config DM_BOOTCOUNT_ZYNQMP + bool "Support ZynqMP PMUFW as a backing store for bootcount" + depends on ARCH_ZYNQMP + help + Enable support for the bootcount API by utilising the Persistent + Global General Storage Register 2 of the PMU. + endmenu endif diff --git a/drivers/bootcount/Makefile b/drivers/bootcount/Makefile index 245f8796337..0cf79e428d6 100644 --- a/drivers/bootcount/Makefile +++ b/drivers/bootcount/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_DM_BOOTCOUNT_I2C_EEPROM) += i2c-eeprom.o obj-$(CONFIG_DM_BOOTCOUNT_I2C) += bootcount_dm_i2c.o obj-$(CONFIG_DM_BOOTCOUNT_SPI_FLASH) += spi-flash.o obj-$(CONFIG_DM_BOOTCOUNT_SYSCON) += bootcount_syscon.o +obj-$(CONFIG_DM_BOOTCOUNT_ZYNQMP) += bootcount_zynqmp.o diff --git a/drivers/bootcount/bootcount_zynqmp.c b/drivers/bootcount/bootcount_zynqmp.c new file mode 100644 index 00000000000..bc0984e2d26 --- /dev/null +++ b/drivers/bootcount/bootcount_zynqmp.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0+ +// SPDX-FileCopyrightText: 2024 CERN (home.cern) + +#include <bootcount.h> +#include <dm.h> +#include <stdio.h> +#include <zynqmp_firmware.h> +#include <asm/arch/hardware.h> +#include <dm/platdata.h> + +static int bootcount_zynqmp_set(struct udevice *dev, const u32 val) +{ + int ret; + + ret = zynqmp_mmio_write((ulong)&pmu_base->pers_gen_storage2, 0xFF, val); + if (ret) + pr_info("%s write fail\n", __func__); + + return ret; +} + +static int bootcount_zynqmp_get(struct udevice *dev, u32 *val) +{ + int ret; + + *val = 0; + ret = zynqmp_mmio_read((ulong)&pmu_base->pers_gen_storage2, val); + if (ret) + pr_info("%s read fail\n", __func__); + + return ret; +} + +U_BOOT_DRVINFO(bootcount_zynqmp) = { + .name = "bootcount_zynqmp", +}; + +static const struct bootcount_ops bootcount_zynqmp_ops = { + .get = bootcount_zynqmp_get, + .set = bootcount_zynqmp_set, +}; + +U_BOOT_DRIVER(bootcount_zynqmp) = { + .name = "bootcount_zynqmp", + .id = UCLASS_BOOTCOUNT, + .ops = &bootcount_zynqmp_ops, +}; diff --git a/drivers/core/root.c b/drivers/core/root.c index 7a714f5478a..c7fb58285ca 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -147,6 +147,13 @@ int dm_remove_devices_flags(uint flags) return 0; } + +void dm_remove_devices_active(void) +{ + /* Remove non-vital devices first */ + device_remove(dm_root(), DM_REMOVE_ACTIVE_ALL | DM_REMOVE_NON_VITAL); + device_remove(dm_root(), DM_REMOVE_ACTIVE_ALL); +} #endif int dm_scan_plat(bool pre_reloc_only) diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index 604386bb734..e33b0056d0b 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -89,6 +89,13 @@ config DFU_VIRT used at board level to manage specific behavior (OTP update for example). +config DFU_SCSI + bool "SCSI flash back end for DFU" + help + This option enables using DFU to read and write to SCSI devices + used at board level to manage specific behavior + (OTP update for example). + config SET_DFU_ALT_INFO bool "Dynamic set of DFU alternate information" help diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 05d7cc61caa..6e1ab1c2ea5 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_$(XPL_)DFU_RAM) += dfu_ram.o obj-$(CONFIG_$(XPL_)DFU_SF) += dfu_sf.o obj-$(CONFIG_$(XPL_)DFU_WRITE_ALT) += dfu_alt.o obj-$(CONFIG_$(XPL_)DFU_VIRT) += dfu_virt.o +obj-$(CONFIG_$(XPL_)DFU_SCSI) += dfu_scsi.o diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 7a4d7ba2a7f..756569217bb 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -564,6 +564,9 @@ static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt, } else if (strcmp(interface, "virt") == 0) { if (dfu_fill_entity_virt(dfu, devstr, argv, argc)) return -1; + } else if (strcmp(interface, "scsi") == 0) { + if (dfu_fill_entity_scsi(dfu, devstr, argv, argc)) + return -1; } else { printf("%s: Device %s not (yet) supported!\n", __func__, interface); @@ -660,7 +663,7 @@ int dfu_config_entities(char *env, char *interface, char *devstr) const char *dfu_get_dev_type(enum dfu_device_type t) { const char *const dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM", - "SF", "MTD", "VIRT"}; + "SF", "MTD", "VIRT", "SCSI"}; return dev_t[t]; } diff --git a/drivers/dfu/dfu_scsi.c b/drivers/dfu/dfu_scsi.c new file mode 100644 index 00000000000..9f95194784c --- /dev/null +++ b/drivers/dfu/dfu_scsi.c @@ -0,0 +1,435 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DFU SCSI backend (based on MMC backend). + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski <l.majewski@samsung.com> + * Copyright (C) 2024 Linaro Ltd. + */ + +#include <log.h> +#include <malloc.h> +#include <errno.h> +#include <div64.h> +#include <dfu.h> +#include <ext4fs.h> +#include <fat.h> +#include <scsi.h> +#include <part.h> +#include <command.h> +#include <linux/printk.h> + +static unsigned char *dfu_file_buf; +static u64 dfu_file_buf_len; +static u64 dfu_file_buf_offset; + +#define scsi_get_blk_desc(dev) ((struct blk_desc *)dev_get_uclass_plat(dev)) + +#define find_scsi_device(dev_num, scsi) blk_get_device(UCLASS_SCSI, dev_num, scsi) + +static int scsi_block_op(enum dfu_op op, struct dfu_entity *dfu, u64 offset, void *buf, long *len) +{ + struct udevice *scsi; + u32 blk_start, blk_count, n = 0; + int ret; + + ret = find_scsi_device(dfu->data.scsi.lun, &scsi); + if (ret < 0) { + pr_err("Device scsi %d - not found!", dfu->data.scsi.lun); + return -ENODEV; + } + + /* + * We must ensure that we work in lba_blk_size chunks, so ALIGN + * this value. + */ + *len = ALIGN(*len, dfu->data.scsi.lba_blk_size); + + blk_start = dfu->data.scsi.lba_start + (u32)lldiv(offset, dfu->data.scsi.lba_blk_size); + blk_count = *len / dfu->data.scsi.lba_blk_size; + if (blk_start + blk_count > dfu->data.scsi.lba_start + dfu->data.scsi.lba_size) { + puts("Request would exceed designated area!\n"); + return -EINVAL; + } + + debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__, + op == DFU_OP_READ ? "scsi READ" : "scsi WRITE", dfu->data.scsi.lun, blk_start, + blk_count, buf); + switch (op) { + case DFU_OP_READ: + n = blk_dread(scsi_get_blk_desc(scsi), blk_start, blk_count, buf); + break; + case DFU_OP_WRITE: + n = blk_dwrite(scsi_get_blk_desc(scsi), blk_start, blk_count, buf); + break; + default: + pr_err("Operation not supported\n"); + } + + if (n != blk_count) { + pr_err("scsi block operation failed"); + return -EIO; + } + + return 0; +} + +static int scsi_file_op(enum dfu_op op, struct dfu_entity *dfu, u64 offset, void *buf, u64 *len) +{ + char dev_part_str[8]; + int ret; + int fstype; + loff_t size = 0; + + switch (dfu->layout) { + case DFU_FS_FAT: + fstype = FS_TYPE_FAT; + break; + case DFU_FS_EXT4: + fstype = FS_TYPE_EXT; + break; + case DFU_SKIP: + return 0; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + return -1; + } + + snprintf(dev_part_str, sizeof(dev_part_str), "%d:%d", dfu->data.scsi.dev, + dfu->data.scsi.part); + + ret = fs_set_blk_dev("scsi", dev_part_str, fstype); + if (ret) { + puts("dfu: fs_set_blk_dev error!\n"); + return ret; + } + + switch (op) { + case DFU_OP_READ: + ret = fs_read(dfu->name, (size_t)buf, offset, *len, &size); + if (ret) { + puts("dfu: fs_read error!\n"); + return ret; + } + *len = size; + break; + case DFU_OP_WRITE: + ret = fs_write(dfu->name, (size_t)buf, offset, *len, &size); + if (ret) { + puts("dfu: fs_write error!\n"); + return ret; + } + break; + case DFU_OP_SIZE: + ret = fs_size(dfu->name, &size); + if (ret) { + puts("dfu: fs_size error!\n"); + return ret; + } + *len = size; + break; + default: + return -1; + } + + return ret; +} + +static int scsi_file_buf_write(struct dfu_entity *dfu, u64 offset, void *buf, long *len) +{ + int ret = 0; + + if (offset == 0) { + dfu_file_buf_len = 0; + dfu_file_buf_offset = 0; + } + + /* Add to the current buffer. */ + if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) + *len = CONFIG_SYS_DFU_MAX_FILE_SIZE - dfu_file_buf_len; + memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len); + dfu_file_buf_len += *len; + + if (dfu_file_buf_len == CONFIG_SYS_DFU_MAX_FILE_SIZE) { + ret = scsi_file_op(DFU_OP_WRITE, dfu, dfu_file_buf_offset, dfu_file_buf, + &dfu_file_buf_len); + dfu_file_buf_offset += dfu_file_buf_len; + dfu_file_buf_len = 0; + } + + return ret; +} + +static int scsi_file_buf_write_finish(struct dfu_entity *dfu) +{ + int ret = scsi_file_op(DFU_OP_WRITE, dfu, dfu_file_buf_offset, dfu_file_buf, + &dfu_file_buf_len); + + /* Now that we're done */ + dfu_file_buf_len = 0; + dfu_file_buf_offset = 0; + + return ret; +} + +int dfu_write_medium_scsi(struct dfu_entity *dfu, u64 offset, void *buf, long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = scsi_block_op(DFU_OP_WRITE, dfu, offset, buf, len); + break; + case DFU_FS_FAT: + case DFU_FS_EXT4: + ret = scsi_file_buf_write(dfu, offset, buf, len); + break; + case DFU_SCRIPT: + ret = run_command_list(buf, *len, 0); + break; + case DFU_SKIP: + ret = 0; + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +int dfu_flush_medium_scsi(struct dfu_entity *dfu) +{ + int ret = 0; + + switch (dfu->layout) { + case DFU_FS_FAT: + case DFU_FS_EXT4: + ret = scsi_file_buf_write_finish(dfu); + break; + case DFU_SCRIPT: + /* script may have changed the dfu_alt_info */ + dfu_reinit_needed = true; + break; + case DFU_RAW_ADDR: + case DFU_SKIP: + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +int dfu_get_medium_size_scsi(struct dfu_entity *dfu, u64 *size) +{ + int ret; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + *size = dfu->data.scsi.lba_size * dfu->data.scsi.lba_blk_size; + return 0; + case DFU_FS_FAT: + case DFU_FS_EXT4: + ret = scsi_file_op(DFU_OP_SIZE, dfu, 0, NULL, size); + if (ret < 0) + return ret; + return 0; + case DFU_SCRIPT: + case DFU_SKIP: + return 0; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + return -1; + } +} + +static int scsi_file_buf_read(struct dfu_entity *dfu, u64 offset, void *buf, long *len) +{ + int ret; + + if (offset == 0 || offset >= dfu_file_buf_offset + dfu_file_buf_len || + offset + *len < dfu_file_buf_offset) { + u64 file_len = CONFIG_SYS_DFU_MAX_FILE_SIZE; + + ret = scsi_file_op(DFU_OP_READ, dfu, offset, dfu_file_buf, &file_len); + if (ret < 0) + return ret; + dfu_file_buf_len = file_len; + dfu_file_buf_offset = offset; + } + if (offset + *len > dfu_file_buf_offset + dfu_file_buf_len) + return -EINVAL; + + /* Add to the current buffer. */ + memcpy(buf, dfu_file_buf + offset - dfu_file_buf_offset, *len); + + return 0; +} + +int dfu_read_medium_scsi(struct dfu_entity *dfu, u64 offset, void *buf, long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = scsi_block_op(DFU_OP_READ, dfu, offset, buf, len); + break; + case DFU_FS_FAT: + case DFU_FS_EXT4: + ret = scsi_file_buf_read(dfu, offset, buf, len); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +void dfu_free_entity_scsi(struct dfu_entity *dfu) +{ + if (dfu_file_buf) { + free(dfu_file_buf); + dfu_file_buf = NULL; + } +} + +/* + * @param s Parameter string containing space-separated arguments: + * 1st: + * raw (raw read/write) + * fat (files) + * ext4 (^) + * part (partition image) + * 2nd and 3rd: + * lba_start and lba_size, for raw write + * scsi_dev and scsi_part, for filesystems and part + */ +int dfu_fill_entity_scsi(struct dfu_entity *dfu, char *devstr, char **argv, int argc) +{ + const char *entity_type; + ssize_t second_arg; + ssize_t third_arg = -1; + struct udevice *scsi; + struct blk_desc *blk_dev; + int ret; + char *s; + + if (argc < 2) { + pr_err("Need at least one argument\n"); + return -EINVAL; + } + + dfu->data.scsi.lun = dectoul(devstr, &s); + if (*s) + return -EINVAL; + + entity_type = argv[0]; + /* + * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8, + * with default 10. + */ + second_arg = simple_strtol(argv[1], &s, 0); + if (*s) + return -EINVAL; + if (argc >= 3) { + third_arg = simple_strtoul(argv[2], &s, 0); + if (*s) + return -EINVAL; + } + + if (scsi_scan(false)) { + pr_err("Couldn't init scsi device.\n"); + return -ENODEV; + } + + ret = find_scsi_device(dfu->data.scsi.lun, &scsi); + if (ret < 0) { + pr_err("Couldn't find scsi device no. %d.\n", dfu->data.scsi.lun); + return -ENODEV; + } + + blk_dev = scsi_get_blk_desc(scsi); + if (!blk_dev) { + pr_err("Couldn't get block device for scsi device no. %d.\n", dfu->data.scsi.lun); + return -ENODEV; + } + + /* if it's NOT a raw write */ + if (strcmp(entity_type, "raw")) { + dfu->data.scsi.dev = (second_arg != -1) ? second_arg : dfu->data.scsi.lun; + dfu->data.scsi.part = third_arg; + } + + if (!strcmp(entity_type, "raw")) { + dfu->layout = DFU_RAW_ADDR; + dfu->data.scsi.lba_start = second_arg; + if (third_arg < 0) { + pr_err("raw requires two arguments\n"); + return -EINVAL; + } + dfu->data.scsi.lba_size = third_arg; + dfu->data.scsi.lba_blk_size = blk_dev->blksz; + + /* + * In case the size is zero (i.e. scsi raw 0x10 0), + * assume the user intends to use whole device. + */ + if (third_arg == 0) + dfu->data.scsi.lba_size = blk_dev->lba; + + } else if (!strcmp(entity_type, "part")) { + struct disk_partition partinfo; + int scsipart = second_arg; + + if (third_arg >= 0) { + pr_err("part only accepts one argument\n"); + return -EINVAL; + } + + if (part_get_info(blk_dev, scsipart, &partinfo) != 0) { + pr_err("Couldn't find part #%d on scsi device #%d\n", scsipart, + dfu->data.scsi.lun); + return -ENODEV; + } + + dfu->layout = DFU_RAW_ADDR; + dfu->data.scsi.lba_start = partinfo.start; + dfu->data.scsi.lba_size = partinfo.size; + dfu->data.scsi.lba_blk_size = partinfo.blksz; + } else if (!strcmp(entity_type, "fat")) { + dfu->layout = DFU_FS_FAT; + } else if (!strcmp(entity_type, "ext4")) { + dfu->layout = DFU_FS_EXT4; + } else if (!strcmp(entity_type, "skip")) { + dfu->layout = DFU_SKIP; + } else if (!strcmp(entity_type, "script")) { + dfu->layout = DFU_SCRIPT; + } else { + pr_err("Memory layout (%s) not supported!\n", entity_type); + return -ENODEV; + } + + dfu->dev_type = DFU_DEV_SCSI; + dfu->get_medium_size = dfu_get_medium_size_scsi; + dfu->read_medium = dfu_read_medium_scsi; + dfu->write_medium = dfu_write_medium_scsi; + dfu->flush_medium = dfu_flush_medium_scsi; + dfu->inited = 0; + dfu->free_entity = dfu_free_entity_scsi; + + /* Check if file buffer is ready */ + if (!dfu_file_buf) { + dfu_file_buf = memalign(CONFIG_SYS_CACHELINE_SIZE, CONFIG_SYS_DFU_MAX_FILE_SIZE); + if (!dfu_file_buf) { + pr_err("Could not memalign 0x%x bytes\n", CONFIG_SYS_DFU_MAX_FILE_SIZE); + return -ENOMEM; + } + } + + return 0; +} diff --git a/drivers/gpio/at91_gpio.c b/drivers/gpio/at91_gpio.c index 50a69815907..76fcd3fb930 100644 --- a/drivers/gpio/at91_gpio.c +++ b/drivers/gpio/at91_gpio.c @@ -219,6 +219,44 @@ static bool at91_get_port_output(struct at91_port *at91_port, int offset) val = readl(&at91_port->osr); return val & mask; } + +static bool at91_is_port_gpio(struct at91_port *at91_port, int offset) +{ + u32 mask, val; + + mask = 1 << offset; + val = readl(&at91_port->psr); + return !!(val & mask); +} + +static void at91_set_port_multi_drive(struct at91_port *at91_port, int offset, int is_on) +{ + u32 mask; + + mask = 1 << offset; + if (is_on) + writel(mask, &at91_port->mder); + else + writel(mask, &at91_port->mddr); +} + +static bool at91_get_port_multi_drive(struct at91_port *at91_port, int offset) +{ + u32 mask, val; + + mask = 1 << offset; + val = readl(&at91_port->mdsr); + return !!(val & mask); +} + +static bool at91_get_port_pullup(struct at91_port *at91_port, int offset) +{ + u32 mask, val; + + mask = 1 << offset; + val = readl(&at91_port->pusr); + return !(val & mask); +} #endif static void at91_set_port_input(struct at91_port *at91_port, int offset, @@ -549,13 +587,68 @@ static int at91_gpio_get_function(struct udevice *dev, unsigned offset) { struct at91_port_priv *port = dev_get_priv(dev); - /* GPIOF_FUNC is not implemented yet */ + if (!at91_is_port_gpio(port->regs, offset)) + return GPIOF_FUNC; + if (at91_get_port_output(port->regs, offset)) return GPIOF_OUTPUT; else return GPIOF_INPUT; } +static int at91_gpio_set_flags(struct udevice *dev, unsigned int offset, + ulong flags) +{ + struct at91_port_priv *port = dev_get_priv(dev); + ulong supported_mask; + + supported_mask = GPIOD_OPEN_DRAIN | GPIOD_MASK_DIR | GPIOD_PULL_UP; + if (flags & ~supported_mask) + return -ENOTSUPP; + + if (flags & GPIOD_IS_OUT) { + if (flags & GPIOD_OPEN_DRAIN) + at91_set_port_multi_drive(port->regs, offset, true); + else + at91_set_port_multi_drive(port->regs, offset, false); + + at91_set_port_output(port->regs, offset, flags & GPIOD_IS_OUT_ACTIVE); + + } else if (flags & GPIOD_IS_IN) { + at91_set_port_input(port->regs, offset, false); + } + if (flags & GPIOD_PULL_UP) + at91_set_port_pullup(port->regs, offset, true); + + return 0; +} + +static int at91_gpio_get_flags(struct udevice *dev, unsigned int offset, + ulong *flagsp) +{ + struct at91_port_priv *port = dev_get_priv(dev); + ulong dir_flags = 0; + + if (at91_get_port_output(port->regs, offset)) { + dir_flags |= GPIOD_IS_OUT; + + if (at91_get_port_multi_drive(port->regs, offset)) + dir_flags |= GPIOD_OPEN_DRAIN; + + if (at91_get_port_value(port->regs, offset)) + dir_flags |= GPIOD_IS_OUT_ACTIVE; + } else { + dir_flags |= GPIOD_IS_IN; + } + + if (at91_get_port_pullup(port->regs, offset)) + dir_flags |= GPIOD_PULL_UP; + + *flagsp = dir_flags; + + return 0; +} + static const char *at91_get_bank_name(uint32_t base_addr) { switch (base_addr) { @@ -584,6 +677,8 @@ static const struct dm_gpio_ops gpio_at91_ops = { .get_value = at91_gpio_get_value, .set_value = at91_gpio_set_value, .get_function = at91_gpio_get_function, + .set_flags = at91_gpio_set_flags, + .get_flags = at91_gpio_get_flags, }; static int at91_gpio_probe(struct udevice *dev) diff --git a/drivers/gpio/qcom_pmic_gpio.c b/drivers/gpio/qcom_pmic_gpio.c index f2ef4e5ce14..cd9f3926ac4 100644 --- a/drivers/gpio/qcom_pmic_gpio.c +++ b/drivers/gpio/qcom_pmic_gpio.c @@ -69,6 +69,17 @@ #define REG_EN_CTL 0x46 #define REG_EN_CTL_ENABLE (1 << 7) +/** + * pmic_gpio_match_data - platform specific configuration + * + * @PMIC_MATCH_READONLY: treat all GPIOs as readonly, don't attempt to configure them. + * This is a workaround for an unknown bug on some platforms where trying to write the + * GPIO configuration registers causes the board to hang. + */ +enum pmic_gpio_quirks { + QCOM_PMIC_QUIRK_READONLY = (1 << 0), +}; + struct qcom_pmic_gpio_data { uint32_t pid; /* Peripheral ID on SPMI bus */ bool lv_mv_type; /* If subtype is GPIO_LV(0x10) or GPIO_MV(0x11) */ @@ -117,8 +128,13 @@ static int qcom_gpio_set_direction(struct udevice *dev, unsigned int offset, { struct qcom_pmic_gpio_data *plat = dev_get_plat(dev); uint32_t gpio_base = plat->pid + REG_OFFSET(offset); + ulong quirks = dev_get_driver_data(dev); int ret = 0; + /* Some PMICs don't like their GPIOs being configured */ + if (quirks & QCOM_PMIC_QUIRK_READONLY) + return 0; + /* Disable the GPIO */ ret = pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL, REG_EN_CTL_ENABLE, 0); @@ -262,6 +278,7 @@ static int qcom_gpio_bind(struct udevice *dev) { struct qcom_pmic_gpio_data *plat = dev_get_plat(dev); + ulong quirks = dev_get_driver_data(dev); struct udevice *child; struct driver *drv; int ret; @@ -275,7 +292,7 @@ static int qcom_gpio_bind(struct udevice *dev) /* Bind the GPIO driver as a child of the PMIC. */ ret = device_bind_with_driver_data(dev, drv, dev->name, - 0, dev_ofnode(dev), &child); + quirks, dev_ofnode(dev), &child); if (ret) return log_msg_ret("bind", ret); @@ -348,7 +365,7 @@ static const struct udevice_id qcom_gpio_ids[] = { { .compatible = "qcom,pms405-gpio" }, { .compatible = "qcom,pm6125-gpio" }, { .compatible = "qcom,pm8150-gpio" }, - { .compatible = "qcom,pm8550-gpio" }, + { .compatible = "qcom,pm8550-gpio", .data = QCOM_PMIC_QUIRK_READONLY }, { } }; diff --git a/drivers/iommu/apple_dart.c b/drivers/iommu/apple_dart.c index 3e9e7819e51..bfd4ad20105 100644 --- a/drivers/iommu/apple_dart.c +++ b/drivers/iommu/apple_dart.c @@ -322,5 +322,5 @@ U_BOOT_DRIVER(apple_dart) = { .ops = &apple_dart_ops, .probe = apple_dart_probe, .remove = apple_dart_remove, - .flags = DM_FLAG_OS_PREPARE + .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL }; diff --git a/drivers/iommu/qcom-hyp-smmu.c b/drivers/iommu/qcom-hyp-smmu.c index 1b5a09bb7b3..c1b95bc8b8c 100644 --- a/drivers/iommu/qcom-hyp-smmu.c +++ b/drivers/iommu/qcom-hyp-smmu.c @@ -91,6 +91,8 @@ struct qcom_smmu_priv { phys_addr_t base; struct list_head devices; struct udevice *dev; + /* SMMU is not needed when running in EL2 */ + bool disable; /* Read-once config */ int num_cb; @@ -134,7 +136,7 @@ static int get_stream_id(struct udevice *dev) int count = ofnode_parse_phandle_with_args(node, "iommus", "#iommu-cells", 0, 0, &args); - if (count < 0 || args.args[0] == 0) { + if (count < 0) { printf("Error: %s: iommus property not found or wrong number of cells\n", __func__); return -EINVAL; @@ -277,6 +279,9 @@ static int qcom_smmu_connect(struct udevice *dev) if (WARN_ON(!priv)) return -EINVAL; + if (priv->disable) + return 0; + mdev = alloc_dev(dev); if (IS_ERR(mdev) && PTR_ERR(mdev) != -EEXIST) { printf("%s: %s Couldn't create mmu context\n", __func__, @@ -348,6 +353,8 @@ static int qcom_smmu_probe(struct udevice *dev) priv->base = dev_read_addr(dev); INIT_LIST_HEAD(&priv->devices); + priv->disable = current_el() > 1; + /* Read SMMU config */ val = gr0_readl(priv, ARM_SMMU_GR0_ID0); priv->num_smr = FIELD_GET(ARM_SMMU_ID0_NUMSMRG, val); diff --git a/drivers/misc/imx8/scu_api.c b/drivers/misc/imx8/scu_api.c index 591d71b096a..a40c8badf9a 100644 --- a/drivers/misc/imx8/scu_api.c +++ b/drivers/misc/imx8/scu_api.c @@ -951,6 +951,26 @@ int sc_timer_set_wdog_window(sc_ipc_t ipc, sc_timer_wdog_time_t window) return ret; } +int sc_timer_control_siemens_pmic_wdog(sc_ipc_t ipc, u8 cmd) +{ + struct udevice *dev = gd->arch.scu_dev; + struct sc_rpc_msg_s msg; + int size = sizeof(struct sc_rpc_msg_s); + int ret; + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_TIMER; + RPC_FUNC(&msg) = (u8)TIMER_FUNC_CTRL_SIEMENS_PMIC_WDOG; + RPC_U8(&msg, 0U) = (u8)cmd; + RPC_SIZE(&msg) = 2U; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + if (ret) + printf("%s: res:%d\n", __func__, RPC_R8(&msg)); + + return ret; +} + int sc_seco_authenticate(sc_ipc_t ipc, sc_seco_auth_cmd_t cmd, sc_faddr_t addr) { diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c index 4e5c932c071..27bb7052fca 100644 --- a/drivers/mmc/msm_sdhci.c +++ b/drivers/mmc/msm_sdhci.c @@ -15,6 +15,7 @@ #include <asm/global_data.h> #include <asm/io.h> #include <linux/bitops.h> +#include <power/regulator.h> /* Non-standard registers needed for SDHCI startup */ #define SDCC_MCI_POWER 0x0 @@ -43,6 +44,7 @@ struct msm_sdhc { struct sdhci_host host; void *base; struct clk_bulk clks; + struct udevice *vqmmc; }; struct msm_sdhc_variant_info { @@ -163,6 +165,16 @@ static int msm_sdc_probe(struct udevice *dev) if (ret) return ret; + /* Get the vqmmc regulator and enable it if available */ + device_get_supply_regulator(dev, "vqmmc-supply", &prv->vqmmc); + if (prv->vqmmc) { + ret = regulator_set_enable_if_allowed(prv->vqmmc, true); + if (ret) { + printf("Failed to enable the VQMMC regulator\n"); + return ret; + } + } + var_info = (void *)dev_get_driver_data(dev); if (!var_info->mci_removed) { ret = msm_sdc_mci_init(prv); diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 817fab4ca36..25f187a2eec 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -2205,7 +2205,6 @@ static const struct udevice_id atmel_nand_controller_of_ids[] = { static int atmel_nand_controller_probe(struct udevice *dev) { const struct atmel_nand_controller_caps *caps; - struct udevice *pmecc_dev; caps = (struct atmel_nand_controller_caps *)dev_get_driver_data(dev); if (!caps) { @@ -2213,12 +2212,6 @@ static int atmel_nand_controller_probe(struct udevice *dev) return -EINVAL; } - /* Probe pmecc driver */ - if (uclass_get_device(UCLASS_MTD, 1, &pmecc_dev)) { - printf("%s: get device fail\n", __func__); - return -EINVAL; - } - return caps->ops->probe(dev, caps); } diff --git a/drivers/mtd/nand/raw/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c index 51f6bd2e65b..e500a0fe3f8 100644 --- a/drivers/mtd/nand/raw/atmel/pmecc.c +++ b/drivers/mtd/nand/raw/atmel/pmecc.c @@ -913,6 +913,7 @@ struct atmel_pmecc *devm_atmel_pmecc_get(struct udevice *userdev) ret = ofnode_parse_phandle_with_args(userdev->node_, "ecc-engine", NULL, 0, 0, &args); + /* Probe pmecc driver */ ret = uclass_get_device_by_ofnode(UCLASS_MTD, args.node, &pdev); if (ret) return NULL; diff --git a/drivers/mtd/ubispl/ubispl.c b/drivers/mtd/ubispl/ubispl.c index 90a7c4c6f9e..9face5fae15 100644 --- a/drivers/mtd/ubispl/ubispl.c +++ b/drivers/mtd/ubispl/ubispl.c @@ -113,7 +113,7 @@ static int vtbl_check(struct ubi_scan_info *ubi, crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC); if (be32_to_cpu(vtbl[i].crc) != crc) { - ubi_err("bad CRC at record %u: %#08x, not %#08x", + ubi_err("bad CRC at record %u: #%08x, not #%08x", i, crc, be32_to_cpu(vtbl[i].crc)); ubi_dump_vtbl_record(&vtbl[i], i); return 1; diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index d6d5cb52fdd..eca681b16d1 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -160,7 +160,7 @@ static int fec_get_clk_rate(void *udev, int idx) } } -static void fec_mii_setspeed(struct ethernet_regs *eth) +static void fec_mii_setspeed(struct udevice *dev, struct ethernet_regs *eth) { /* * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock @@ -182,7 +182,7 @@ static void fec_mii_setspeed(struct ethernet_regs *eth) u32 hold; int ret; - ret = fec_get_clk_rate(NULL, 0); + ret = fec_get_clk_rate(dev, 0); if (ret < 0) { printf("Can't find FEC0 clk rate: %d\n", ret); return; @@ -581,7 +581,7 @@ static int fecmxc_init(struct udevice *dev) fec_reg_setup(fec); if (fec->xcv_type != SEVENWIRE) - fec_mii_setspeed(fec->bus->priv); + fec_mii_setspeed(dev, fec->bus->priv); /* Set Opcode/Pause Duration Register */ writel(0x00010020, &fec->eth->op_pause); /* FIXME 0xffff0020; */ @@ -996,7 +996,7 @@ static void fec_free_descs(struct fec_priv *fec) free(fec->tbd_base); } -struct mii_dev *fec_get_miibus(ulong base_addr, int dev_id) +struct mii_dev *fec_get_miibus(struct udevice *dev, ulong base_addr, int dev_id) { struct ethernet_regs *eth = (struct ethernet_regs *)base_addr; struct mii_dev *bus; @@ -1018,7 +1018,7 @@ struct mii_dev *fec_get_miibus(ulong base_addr, int dev_id) free(bus); return NULL; } - fec_mii_setspeed(eth); + fec_mii_setspeed(dev, eth); return bus; } @@ -1354,10 +1354,10 @@ static int fecmxc_probe(struct udevice *dev) if (!bus) { dm_mii_bus = false; #ifdef CONFIG_FEC_MXC_MDIO_BASE - bus = fec_get_miibus((ulong)CONFIG_FEC_MXC_MDIO_BASE, + bus = fec_get_miibus(dev, (ulong)CONFIG_FEC_MXC_MDIO_BASE, dev_seq(dev)); #else - bus = fec_get_miibus((ulong)priv->eth, dev_seq(dev)); + bus = fec_get_miibus(dev, (ulong)priv->eth, dev_seq(dev)); #endif } if (!bus) { diff --git a/drivers/phy/qcom/phy-qcom-qmp-ufs.c b/drivers/phy/qcom/phy-qcom-qmp-ufs.c index 8908a34df54..5c90d60e7d1 100644 --- a/drivers/phy/qcom/phy-qcom-qmp-ufs.c +++ b/drivers/phy/qcom/phy-qcom-qmp-ufs.c @@ -84,12 +84,6 @@ enum qphy_reg_layout { QPHY_LAYOUT_SIZE }; -static const unsigned int ufsphy_v2_regs_layout[QPHY_LAYOUT_SIZE] = { - [QPHY_START_CTRL] = QPHY_V2_PCS_UFS_PHY_START, - [QPHY_PCS_READY_STATUS] = QPHY_V2_PCS_UFS_READY_STATUS, - [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V2_PCS_UFS_POWER_DOWN_CONTROL, -}; - static const unsigned int ufsphy_v3_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_START_CTRL] = QPHY_V3_PCS_UFS_PHY_START, [QPHY_PCS_READY_STATUS] = QPHY_V3_PCS_UFS_READY_STATUS, @@ -189,6 +183,29 @@ static const struct qmp_ufs_init_tbl sdm845_ufsphy_pcs[] = { QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_MULTI_LANE_CTRL1, 0x02), }; +static const struct qmp_ufs_init_tbl sm8150_ufsphy_hs_g4_tx[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x75), +}; + +static const struct qmp_ufs_init_tbl sm8150_ufsphy_hs_g4_rx[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CTRL2, 0x81), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_TERM_BW, 0x6f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_MEASURE_TIME, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x6c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x6d), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0x6d), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xed), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0x3c), +}; + static const struct qmp_ufs_init_tbl sm8150_ufsphy_serdes[] = { QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0xd9), QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x11), @@ -461,6 +478,112 @@ static const struct qmp_ufs_init_tbl sm8650_ufsphy_pcs[] = { QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSG5_SYNC_WAIT_TIME, 0x9e), }; + +static const struct qmp_ufs_init_tbl sc7280_ufsphy_tx[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x35), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0c), +}; + +static const struct qmp_ufs_init_tbl sc7280_ufsphy_rx[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_LVL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_BAND, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0xf1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_TERM_BW, 0x1b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1d), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_MEASURE_TIME, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x6d), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x6d), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xed), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x3c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xe0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xe0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), +}; + +static const struct qmp_ufs_init_tbl sc7280_ufsphy_pcs[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2, 0x6d), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_MID_TERM_CTRL1, 0x43), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_DEBUG_BUS_CLKSEL, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_MIN_HIBERN8_TIME, 0xff), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_PLL_CNTL, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB, 0x16), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB, 0xd8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_PWM_GEAR_BAND, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_HS_GEAR_BAND, 0x06), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x03), +}; + +static const struct qmp_ufs_init_tbl sc7280_ufsphy_hs_g4_rx[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_LVL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_BAND, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0xf1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CTRL2, 0x81), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_TERM_BW, 0x6f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_MEASURE_TIME, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x2c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x6d), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0x6d), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xed), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0x3c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xe0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x0f), +}; + struct qmp_ufs_offsets { u16 serdes; u16 pcs; @@ -623,6 +746,44 @@ static const struct qmp_ufs_cfg sdm845_ufsphy_cfg = { .no_pcs_sw_reset = true, }; +static const struct qmp_ufs_cfg sm8150_ufsphy_cfg = { + .lanes = 2, + + .offsets = &qmp_ufs_offsets, + + .tbls = { + .serdes = sm8150_ufsphy_serdes, + .serdes_num = ARRAY_SIZE(sm8150_ufsphy_serdes), + .tx = sm8150_ufsphy_tx, + .tx_num = ARRAY_SIZE(sm8150_ufsphy_tx), + .rx = sm8150_ufsphy_rx, + .rx_num = ARRAY_SIZE(sm8150_ufsphy_rx), + .pcs = sm8150_ufsphy_pcs, + .pcs_num = ARRAY_SIZE(sm8150_ufsphy_pcs), + }, + .tbls_hs_b = { + .serdes = sm8150_ufsphy_hs_b_serdes, + .serdes_num = ARRAY_SIZE(sm8150_ufsphy_hs_b_serdes), + }, + .tbls_hs_g4 = { + .tx = sm8150_ufsphy_hs_g4_tx, + .tx_num = ARRAY_SIZE(sm8150_ufsphy_hs_g4_tx), + .rx = sm8150_ufsphy_hs_g4_rx, + .rx_num = ARRAY_SIZE(sm8150_ufsphy_hs_g4_rx), + .pcs = sm8150_ufsphy_hs_g4_pcs, + .pcs_num = ARRAY_SIZE(sm8150_ufsphy_hs_g4_pcs), + }, + .clk_list = sdm845_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), + .vreg_list = qmp_ufs_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_ufs_vreg_l), + .reset_list = qmp_ufs_reset_l, + .num_resets = ARRAY_SIZE(qmp_ufs_reset_l), + .regs = ufsphy_v4_regs_layout, + + .no_pcs_sw_reset = false, +}; + static const struct qmp_ufs_cfg sm8250_ufsphy_cfg = { .lanes = 2, @@ -713,6 +874,41 @@ static const struct qmp_ufs_cfg sm8650_ufsphy_cfg = { .no_pcs_sw_reset = false, }; + +static const struct qmp_ufs_cfg sc7280_ufsphy_cfg = { + .lanes = 2, + + .offsets = &qmp_ufs_offsets, + + .tbls = { + .serdes = sm8150_ufsphy_serdes, + .serdes_num = ARRAY_SIZE(sm8150_ufsphy_serdes), + .tx = sc7280_ufsphy_tx, + .tx_num = ARRAY_SIZE(sc7280_ufsphy_tx), + .rx = sc7280_ufsphy_rx, + .rx_num = ARRAY_SIZE(sc7280_ufsphy_rx), + .pcs = sc7280_ufsphy_pcs, + .pcs_num = ARRAY_SIZE(sc7280_ufsphy_pcs), + }, + .tbls_hs_b = { + .serdes = sm8150_ufsphy_hs_b_serdes, + .serdes_num = ARRAY_SIZE(sm8150_ufsphy_hs_b_serdes), + }, + .tbls_hs_g4 = { + .tx = sm8250_ufsphy_hs_g4_tx, + .tx_num = ARRAY_SIZE(sm8250_ufsphy_hs_g4_tx), + .rx = sc7280_ufsphy_hs_g4_rx, + .rx_num = ARRAY_SIZE(sc7280_ufsphy_hs_g4_rx), + .pcs = sm8150_ufsphy_hs_g4_pcs, + .pcs_num = ARRAY_SIZE(sm8150_ufsphy_hs_g4_pcs), + }, + .clk_list = sdm845_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), + .vreg_list = qmp_ufs_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_ufs_vreg_l), + .regs = ufsphy_v4_regs_layout, +}; + static void qmp_ufs_configure_lane(void __iomem *base, const struct qmp_ufs_init_tbl tbl[], int num, @@ -1100,9 +1296,11 @@ static struct phy_ops qmp_ufs_ops = { static const struct udevice_id qmp_ufs_ids[] = { { .compatible = "qcom,sdm845-qmp-ufs-phy", .data = (ulong)&sdm845_ufsphy_cfg }, + { .compatible = "qcom,sm8150-qmp-ufs-phy", .data = (ulong)&sm8150_ufsphy_cfg }, { .compatible = "qcom,sm8250-qmp-ufs-phy", .data = (ulong)&sm8250_ufsphy_cfg }, { .compatible = "qcom,sm8550-qmp-ufs-phy", .data = (ulong)&sm8550_ufsphy_cfg }, { .compatible = "qcom,sm8650-qmp-ufs-phy", .data = (ulong)&sm8650_ufsphy_cfg }, + { .compatible = "qcom,sc7280-qmp-ufs-phy", .data = (ulong)&sc7280_ufsphy_cfg, }, { } }; diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index 9c466f8695e..331a46d88f7 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -251,13 +251,6 @@ static int cadence_spi_probe(struct udevice *bus) priv->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, priv->ref_clk_hz); - /* Versal and Versal-NET use spi calibration to set read delay */ - if (CONFIG_IS_ENABLED(ARCH_VERSAL) || - CONFIG_IS_ENABLED(ARCH_VERSAL_NET) || - CONFIG_IS_ENABLED(ARCH_VERSAL2)) - if (priv->read_delay >= 0) - priv->read_delay = -1; - /* Reset ospi flash device */ return cadence_qspi_versal_flash_reset(bus); } diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 36b7d383aa9..d6049753740 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -446,7 +446,7 @@ int _spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, slave = dev_get_parent_priv(dev); bus_data = dev_get_uclass_priv(bus); -#if CONFIG_IS_ENABLED(SPI_ADVANCE) +#if CONFIG_IS_ENABLED(SPI_STACKED_PARALLEL) if ((dev_read_bool(dev, "parallel-memories")) && !slave->multi_cs_cap) { dev_err(dev, "controller doesn't support multi CS\n"); return -EINVAL; @@ -515,7 +515,7 @@ int spi_slave_of_to_plat(struct udevice *dev, struct dm_spi_slave_plat *plat) int mode = 0; int value; -#if CONFIG_IS_ENABLED(SPI_ADVANCE) +#if CONFIG_IS_ENABLED(SPI_STACKED_PARALLEL) int ret; ret = dev_read_u32_array(dev, "reg", plat->cs, SPI_CS_CNT_MAX); diff --git a/drivers/usb/eth/asix88179.c b/drivers/usb/eth/asix88179.c index 4bd3b9d10dc..69d3073b669 100644 --- a/drivers/usb/eth/asix88179.c +++ b/drivers/usb/eth/asix88179.c @@ -173,9 +173,10 @@ #define USB_BULK_SEND_TIMEOUT 5000 #define USB_BULK_RECV_TIMEOUT 5000 -#define AX_RX_URB_SIZE 1024 * 0x12 +#define AX_RX_URB_SIZE 1024 * 0x1a #define BLK_FRAME_SIZE 0x200 #define PHY_CONNECT_TIMEOUT 5000 +#define PHY_RESET_TIMEOUT 500 #define TIMEOUT_RESOLUTION 50 /* ms */ @@ -192,10 +193,10 @@ static const struct { unsigned char ctrl, timer_l, timer_h, size, ifg; } AX88179_BULKIN_SIZE[] = { - {7, 0x4f, 0, 0x02, 0xff}, - {7, 0x20, 3, 0x03, 0xff}, - {7, 0xae, 7, 0x04, 0xff}, - {7, 0xcc, 0x4c, 0x04, 8}, + {7, 0x4f, 0, 0x12, 0xff}, + {7, 0x20, 3, 0x16, 0xff}, + {7, 0xae, 7, 0x18, 0xff}, + {7, 0xcc, 0x4c, 0x18, 8}, }; /* driver private */ @@ -285,6 +286,26 @@ static int asix_write_mac(struct ueth_data *dev, uint8_t *enetaddr) return ret; } +static int asix_reset_phy(struct ueth_data *dev) +{ + u16 bmcr; + u32 t; + + /* Reset the PHY */ + bmcr = BMCR_RESET; + asix_write_cmd(dev, AX_ACCESS_PHY, 0x03, MII_BMCR, 2, &bmcr); + + for (t = 0; t < PHY_RESET_TIMEOUT; t += TIMEOUT_RESOLUTION) { + asix_read_cmd(dev, AX_ACCESS_PHY, 0x03, MII_BMCR, 2, &bmcr); + if (!(bmcr & BMCR_RESET)) + return 0; + mdelay(TIMEOUT_RESOLUTION); + } + + debug("Reset PHY timeout\n"); + return -ETIMEDOUT; +} + static int asix_basic_reset(struct ueth_data *dev, struct asix_private *dev_priv) { @@ -311,7 +332,7 @@ static int asix_basic_reset(struct ueth_data *dev, memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); - dev_priv->rx_urb_size = 128 * 20; + dev_priv->rx_urb_size = 1024 * 20; /* Water Level configuration */ *tmp = 0x34; @@ -344,14 +365,22 @@ static int asix_basic_reset(struct ueth_data *dev, AX_MEDIUM_GIGAMODE | AX_MEDIUM_JUMBO_EN; asix_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, tmp16); + asix_reset_phy(dev); + u16 adv = 0; - adv = ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_LPACK | - ADVERTISE_NPAGE | ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP; + adv = ADVERTISE_ALL | ADVERTISE_CSMA | + ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP; asix_write_cmd(dev, AX_ACCESS_PHY, 0x03, MII_ADVERTISE, 2, &adv); adv = ADVERTISE_1000FULL; asix_write_cmd(dev, AX_ACCESS_PHY, 0x03, MII_CTRL1000, 2, &adv); + /* Restart auto-negotiation */ + u16 bmcr = 0; + asix_read_cmd(dev, AX_ACCESS_PHY, 0x03, MII_BMCR, 2, &bmcr); + bmcr |= BMCR_ANENABLE | BMCR_ANRESTART; + asix_write_cmd(dev, AX_ACCESS_PHY, 0x03, MII_BMCR, 2, &bmcr); + return 0; } diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 0e45f0a0922..b39b2546e5c 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -351,6 +351,13 @@ config WDT_SBSA In the single stage mode, when the timeout is reached, your system will be reset by WS1. The first signal (WS0) is ignored. +config WDT_SIEMENS_PMIC + bool "Enable PMIC Watchdog Timer support for Siemens platforms" + depends on ARCH_IMX8 && WDT + help + Select this to enable the PMIC watchdog driver controlled via + IMX8 SCU API found on Siemens platforms. + config WDT_SL28CPLD bool "sl28cpld watchdog timer support" depends on WDT && SL28CPLD diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 0b107c008f7..9b6b1a8e8ad 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_WDT_OCTEONTX) += octeontx_wdt.o obj-$(CONFIG_WDT_OMAP3) += omap_wdt.o obj-$(CONFIG_WDT_SBSA) += sbsa_gwdt.o obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o +obj-$(CONFIG_WDT_SIEMENS_PMIC) += siemens_pmic_wdt.o obj-$(CONFIG_WDT_SL28CPLD) += sl28cpld-wdt.o obj-$(CONFIG_WDT_SP805) += sp805_wdt.o obj-$(CONFIG_WDT_STARFIVE) += starfive_wdt.o diff --git a/drivers/watchdog/siemens_pmic_wdt.c b/drivers/watchdog/siemens_pmic_wdt.c new file mode 100644 index 00000000000..87e817bb5b2 --- /dev/null +++ b/drivers/watchdog/siemens_pmic_wdt.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for a PMIC watchdog timer controlled via Siemens SCU firmware + * extensions. Only useful on some Siemens i.MX8-based platforms as + * special NXP SCFW is needed which provides the needed SCU API. + * + * Copyright (C) 2024 Siemens AG + */ + +#include <dm.h> +#include <wdt.h> +#include <firmware/imx/sci/sci.h> + +/* watchdog commands */ +#define CMD_START_WDT 0x55 +#define CMD_STOP_WDT 0x45 +#define CMD_PING_WDT 0x35 + +static int scu_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) +{ + /* start external watchdog via Timer API */ + return sc_timer_control_siemens_pmic_wdog(-1, CMD_START_WDT); +} + +static int scu_wdt_stop(struct udevice *dev) +{ + /* stop external watchdog via Timer API */ + return sc_timer_control_siemens_pmic_wdog(-1, CMD_STOP_WDT); +} + +static int scu_wdt_reset(struct udevice *dev) +{ + return sc_timer_control_siemens_pmic_wdog(-1, CMD_PING_WDT); +} + +static int scu_wdt_probe(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + return 0; +} + +static const struct wdt_ops scu_wdt_ops = { + .reset = scu_wdt_reset, + .start = scu_wdt_start, + .stop = scu_wdt_stop, +}; + +static const struct udevice_id scu_wdt_ids[] = { + { .compatible = "siemens,scu-wdt" }, + { } +}; + +U_BOOT_DRIVER(scu_wdt) = { + .name = "scu_wdt", + .id = UCLASS_WDT, + .of_match = scu_wdt_ids, + .probe = scu_wdt_probe, + .ops = &scu_wdt_ops, +}; |