diff options
Diffstat (limited to 'drivers')
420 files changed, 18284 insertions, 7290 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index a25f6ae02fd..a073230c26d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -118,6 +118,8 @@ source "drivers/scsi/Kconfig" source "drivers/serial/Kconfig" +source "drivers/sm/Kconfig" + source "drivers/smem/Kconfig" source "drivers/sound/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index efc2a4afb24..bf73b7718ce 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_$(SPL_TPL_)VIRTIO) += virtio/ obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox/ obj-$(CONFIG_$(SPL_)REMOTEPROC) += remoteproc/ obj-$(CONFIG_$(SPL_)SYSINFO) += sysinfo/ +obj-$(CONFIG_$(SPL_TPL_)SM) += sm/ obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm/ obj-$(CONFIG_$(SPL_)NVME) += nvme/ obj-$(CONFIG_XEN) += xen/ @@ -56,6 +57,7 @@ obj-$(CONFIG_$(SPL_)ALTERA_SDRAM) += ddr/altera/ obj-$(CONFIG_ARCH_IMX8M) += ddr/imx/imx8m/ obj-$(CONFIG_IMX8ULP_DRAM) += ddr/imx/imx8ulp/ obj-$(CONFIG_ARCH_IMX9) += ddr/imx/imx9/ +obj-$(CONFIG_DRAM_SUN20I_D1) += ram/ obj-$(CONFIG_SPL_DM_RESET) += reset/ obj-$(CONFIG_SPL_MUSB_NEW) += usb/musb-new/ obj-$(CONFIG_SPL_USB_GADGET) += usb/gadget/ diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index 4336732dee5..a01d73846b7 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -66,6 +66,7 @@ config STM32_ADC config ADC_IMX93 bool "Enable NXP IMX93 ADC driver" + depends on ADC help This enables basic driver for NXP IMX93 ADC. It provides: diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 049f7efd10b..9bc5283c268 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -20,14 +20,6 @@ config SATA See also CMD_SATA which provides command-line support. -config SYS_SATA_MAX_PORTS - int "Maximum supported SATA ports" - depends on SCSI_AHCI && !DM_SCSI - default 1 - help - Sets the maximum number of ports to scan when looking for devices. - Ports from 0 to (this value - 1) are scanned. - config LIBATA bool help @@ -44,7 +36,7 @@ menu "SATA/SCSI device support" config AHCI_PCI bool "Support for PCI-based AHCI controller" depends on PCI - depends on DM_SCSI + depends on SCSI depends on SCSI_AHCI help Enables support for the PCI-based AHCI controller. @@ -55,13 +47,13 @@ config SPL_AHCI_PCI bool "Support for PCI-based AHCI controller for SPL" depends on SPL depends on SPL_PCI - depends on SPL_SATA && DM_SCSI + depends on SPL_SATA && SCSI config DWC_AHCI bool "Enable Synopsys DWC AHCI driver support" select SCSI_AHCI select PHY - depends on DM_SCSI + depends on SCSI help Enable this driver to support Sata devices through Synopsys DWC AHCI module. @@ -91,7 +83,7 @@ config AHCI_MVEBU bool "Marvell EBU AHCI SATA support" depends on ARCH_MVEBU || ARCH_OCTEON select SCSI_AHCI - select DM_SCSI + select SCSI help This option enables support for the Marvell EBU SoC's onboard AHCI SATA. @@ -112,7 +104,7 @@ if SATA config SATA_CEVA bool "Ceva Sata controller" depends on AHCI - depends on DM_SCSI + depends on SCSI help This option enables Ceva Sata controller hard IP available on Xilinx ZynqMP. Support up to 2 external devices. Compliant with SATA 3.1 and diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 6e30180b8b4..af6f0bf2780 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -10,11 +10,10 @@ obj-$(CONFIG_SCSI_AHCI) += ahci.o obj-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o obj-$(CONFIG_FSL_SATA) += fsl_sata.o obj-$(CONFIG_LIBATA) += libata.o -obj-$(CONFIG_SATA) += sata.o +obj-$(CONFIG_SATA) += sata.o sata_bootdev.o obj-$(CONFIG_SATA_CEVA) += sata_ceva.o obj-$(CONFIG_SATA_MV) += sata_mv.o obj-$(CONFIG_SATA_SIL) += sata_sil.o -obj-$(CONFIG_SANDBOX) += sata_sandbox.o obj-$(CONFIG_AHCI_MVEBU) += ahci_mvebu.o obj-$(CONFIG_SUNXI_AHCI) += ahci_sunxi.o obj-$(CONFIG_MTK_AHCI) += mtk_ahci.o diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index cb2c648a91f..04ddc339464 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -33,10 +33,6 @@ static int ata_io_flush(struct ahci_uc_priv *uc_priv, u8 port); -#ifndef CONFIG_DM_SCSI -struct ahci_uc_priv *probe_ent = NULL; -#endif - #define writel_with_flush(a,b) do { writel(a,b); readl(b); } while (0) /* @@ -169,11 +165,6 @@ int ahci_reset(void __iomem *base) static int ahci_host_init(struct ahci_uc_priv *uc_priv) { -#if !defined(CONFIG_SCSI_AHCI_PLAT) && !defined(CONFIG_DM_SCSI) - struct udevice *dev = uc_priv->dev; - struct pci_child_plat *pplat = dev_get_parent_plat(dev); - u16 tmp16; -#endif void __iomem *mmio = uc_priv->mmio_base; u32 tmp, cap_save, cmd; int i, j, ret; @@ -194,14 +185,6 @@ static int ahci_host_init(struct ahci_uc_priv *uc_priv) writel(cap_save, mmio + HOST_CAP); writel_with_flush(0xf, mmio + HOST_PORTS_IMPL); -#if !defined(CONFIG_SCSI_AHCI_PLAT) && !defined(CONFIG_DM_SCSI) - if (pplat->vendor == PCI_VENDOR_ID_INTEL) { - u16 tmp16; - - dm_pci_read_config16(dev, 0x92, &tmp16); - dm_pci_write_config16(dev, 0x92, tmp16 | 0xf); - } -#endif uc_priv->cap = readl(mmio + HOST_CAP); uc_priv->port_map = readl(mmio + HOST_PORTS_IMPL); port_map = uc_priv->port_map; @@ -210,11 +193,6 @@ static int ahci_host_init(struct ahci_uc_priv *uc_priv) debug("cap 0x%x port_map 0x%x n_ports %d\n", uc_priv->cap, uc_priv->port_map, uc_priv->n_ports); -#if !defined(CONFIG_DM_SCSI) - if (uc_priv->n_ports > CONFIG_SYS_SATA_MAX_PORTS) - uc_priv->n_ports = CONFIG_SYS_SATA_MAX_PORTS; -#endif - for (i = 0; i < uc_priv->n_ports; i++) { if (!(port_map & (1 << i))) continue; @@ -313,23 +291,12 @@ static int ahci_host_init(struct ahci_uc_priv *uc_priv) writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); tmp = readl(mmio + HOST_CTL); debug("HOST_CTL 0x%x\n", tmp); -#if !defined(CONFIG_DM_SCSI) -#ifndef CONFIG_SCSI_AHCI_PLAT - dm_pci_read_config16(dev, PCI_COMMAND, &tmp16); - tmp |= PCI_COMMAND_MASTER; - dm_pci_write_config16(dev, PCI_COMMAND, tmp16); -#endif -#endif return 0; } static void ahci_print_info(struct ahci_uc_priv *uc_priv) { -#if !defined(CONFIG_SCSI_AHCI_PLAT) && !defined(CONFIG_DM_SCSI) - struct udevice *dev = uc_priv->dev; - u16 cc; -#endif void __iomem *mmio = uc_priv->mmio_base; u32 vers, cap, cap2, impl, speed; const char *speed_s; @@ -350,19 +317,7 @@ static void ahci_print_info(struct ahci_uc_priv *uc_priv) else speed_s = "?"; -#if defined(CONFIG_SCSI_AHCI_PLAT) || defined(CONFIG_DM_SCSI) scc_s = "SATA"; -#else - dm_pci_read_config16(dev, 0x0a, &cc); - if (cc == 0x0101) - scc_s = "IDE"; - else if (cc == 0x0106) - scc_s = "SATA"; - else if (cc == 0x0104) - scc_s = "RAID"; - else - scc_s = "unknown"; -#endif printf("AHCI %02x%02x.%02x%02x " "%u slots %u ports %s Gbps 0x%x impl %s mode\n", (vers >> 24) & 0xff, @@ -397,12 +352,8 @@ static void ahci_print_info(struct ahci_uc_priv *uc_priv) cap2 & (1 << 0) ? "boh " : ""); } -#if defined(CONFIG_DM_SCSI) || !defined(CONFIG_SCSI_AHCI_PLAT) static int ahci_init_one(struct ahci_uc_priv *uc_priv, struct udevice *dev) { -#if !defined(CONFIG_DM_SCSI) - u16 vendor; -#endif int rc; uc_priv->dev = dev; @@ -415,21 +366,8 @@ static int ahci_init_one(struct ahci_uc_priv *uc_priv, struct udevice *dev) uc_priv->pio_mask = 0x1f; uc_priv->udma_mask = 0x7f; /*Fixme,assume to support UDMA6 */ -#if !defined(CONFIG_DM_SCSI) - uc_priv->mmio_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_5, 0, 0, - PCI_REGION_TYPE, PCI_REGION_MEM); - - /* Take from kernel: - * JMicron-specific fixup: - * make sure we're in AHCI mode - */ - dm_pci_read_config16(dev, PCI_VENDOR_ID, &vendor); - if (vendor == 0x197b) - dm_pci_write_config8(dev, 0x41, 0xa1); -#else struct scsi_plat *plat = dev_get_uclass_plat(dev); uc_priv->mmio_base = (void *)plat->base; -#endif debug("ahci mmio_base=0x%p\n", uc_priv->mmio_base); /* initialize adapter */ @@ -444,7 +382,6 @@ static int ahci_init_one(struct ahci_uc_priv *uc_priv, struct udevice *dev) err_out: return rc; } -#endif #define MAX_DATA_BYTE_COUNT (4*1024*1024) @@ -893,12 +830,7 @@ static int ata_scsiop_test_unit_ready(struct ahci_uc_priv *uc_priv, static int ahci_scsi_exec(struct udevice *dev, struct scsi_cmd *pccb) { - struct ahci_uc_priv *uc_priv; -#ifdef CONFIG_DM_SCSI - uc_priv = dev_get_uclass_priv(dev->parent); -#else - uc_priv = probe_ent; -#endif + struct ahci_uc_priv *uc_priv = dev_get_uclass_priv(dev->parent); int ret; switch (pccb->cmd[0]) { @@ -953,41 +885,12 @@ static int ahci_start_ports(struct ahci_uc_priv *uc_priv) return 0; } -#ifndef CONFIG_DM_SCSI -void scsi_low_level_init(int busdevfunc) -{ - struct ahci_uc_priv *uc_priv; - -#ifndef CONFIG_SCSI_AHCI_PLAT - probe_ent = calloc(1, sizeof(struct ahci_uc_priv)); - if (!probe_ent) { - printf("%s: No memory for uc_priv\n", __func__); - return; - } - uc_priv = probe_ent; - struct udevice *dev; - int ret; - - ret = dm_pci_bus_find_bdf(busdevfunc, &dev); - if (ret) - return; - ahci_init_one(uc_priv, dev); -#else - uc_priv = probe_ent; -#endif - - ahci_start_ports(uc_priv); -} -#endif - -#ifndef CONFIG_SCSI_AHCI_PLAT int ahci_init_one_dm(struct udevice *dev) { struct ahci_uc_priv *uc_priv = dev_get_uclass_priv(dev); return ahci_init_one(uc_priv, dev); } -#endif int ahci_start_ports_dm(struct udevice *dev) { @@ -996,65 +899,6 @@ int ahci_start_ports_dm(struct udevice *dev) return ahci_start_ports(uc_priv); } -#ifdef CONFIG_SCSI_AHCI_PLAT -static int ahci_init_common(struct ahci_uc_priv *uc_priv, void __iomem *base) -{ - int rc; - - uc_priv->host_flags = ATA_FLAG_SATA - | ATA_FLAG_NO_LEGACY - | ATA_FLAG_MMIO - | ATA_FLAG_PIO_DMA - | ATA_FLAG_NO_ATAPI; - uc_priv->pio_mask = 0x1f; - uc_priv->udma_mask = 0x7f; /*Fixme,assume to support UDMA6 */ - - uc_priv->mmio_base = base; - - /* initialize adapter */ - rc = ahci_host_init(uc_priv); - if (rc) - goto err_out; - - ahci_print_info(uc_priv); - - rc = ahci_start_ports(uc_priv); - -err_out: - return rc; -} - -#ifndef CONFIG_DM_SCSI -int ahci_init(void __iomem *base) -{ - struct ahci_uc_priv *uc_priv; - - probe_ent = malloc(sizeof(struct ahci_uc_priv)); - if (!probe_ent) { - printf("%s: No memory for uc_priv\n", __func__); - return -ENOMEM; - } - - uc_priv = probe_ent; - memset(uc_priv, 0, sizeof(struct ahci_uc_priv)); - - return ahci_init_common(uc_priv, base); -} -#endif - -int ahci_init_dm(struct udevice *dev, void __iomem *base) -{ - struct ahci_uc_priv *uc_priv = dev_get_uclass_priv(dev); - - return ahci_init_common(uc_priv, base); -} - -void __weak scsi_init(void) -{ -} - -#endif /* CONFIG_SCSI_AHCI_PLAT */ - /* * In the general case of generic rotating media it makes sense to have a * flush capability. It probably even makes sense in the case of SSDs because @@ -1098,7 +942,6 @@ static int ahci_scsi_bus_reset(struct udevice *dev) return 0; } -#ifdef CONFIG_DM_SCSI int ahci_bind_scsi(struct udevice *ahci_dev, struct udevice **devp) { struct udevice *dev; @@ -1190,16 +1033,3 @@ U_BOOT_DRIVER(ahci_scsi) = { .id = UCLASS_SCSI, .ops = &scsi_ops, }; -#else -int scsi_exec(struct udevice *dev, struct scsi_cmd *pccb) -{ - return ahci_scsi_exec(dev, pccb); -} - -__weak int scsi_bus_reset(struct udevice *dev) -{ - return ahci_scsi_bus_reset(dev); - - return 0; -} -#endif diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c index 94a3379c532..9064774e661 100644 --- a/drivers/ata/ahci_sunxi.c +++ b/drivers/ata/ahci_sunxi.c @@ -7,6 +7,7 @@ #include <asm/io.h> #include <asm/gpio.h> #include <linux/delay.h> +#include <power/regulator.h> #define AHCI_PHYCS0R 0x00c0 #define AHCI_PHYCS1R 0x00c4 @@ -74,6 +75,7 @@ static int sunxi_ahci_phy_init(u8 *reg_base) static int sunxi_sata_probe(struct udevice *dev) { + struct udevice *reg_dev; ulong base; u8 *reg; int ret; @@ -89,6 +91,13 @@ static int sunxi_sata_probe(struct udevice *dev) debug("%s: Failed to init phy (err=%d)\n", __func__, ret); return ret; } + + ret = device_get_supply_regulator(dev, "target-supply", ®_dev); + if (ret == 0) { + regulator_set_enable(reg_dev, true); + mdelay(500); + } + ret = ahci_probe_scsi(dev, base); if (ret) { debug("%s: Failed to probe (err=%d)\n", __func__, ret); diff --git a/drivers/ata/dwc_ahsata.c b/drivers/ata/dwc_ahsata.c index 6a4d861bf1e..b4d4e39c9b3 100644 --- a/drivers/ata/dwc_ahsata.c +++ b/drivers/ata/dwc_ahsata.c @@ -880,7 +880,8 @@ int dwc_ahsata_scan(struct udevice *dev) device_find_first_child(dev, &blk); if (!blk) { ret = blk_create_devicef(dev, "dwc_ahsata_blk", "blk", - UCLASS_AHCI, -1, 512, 0, &blk); + UCLASS_AHCI, -1, DEFAULT_BLKSZ, + 0, &blk); if (ret) { debug("Can't create device\n"); return ret; diff --git a/drivers/ata/fsl_sata.c b/drivers/ata/fsl_sata.c index 972101b29ce..969bc191f8e 100644 --- a/drivers/ata/fsl_sata.c +++ b/drivers/ata/fsl_sata.c @@ -888,7 +888,8 @@ static int fsl_ata_probe(struct udevice *dev) for (i = 0; i < nr_ports; i++) { snprintf(sata_name, sizeof(sata_name), "fsl_sata%d", i); ret = blk_create_devicef(dev, "sata_fsl_blk", sata_name, - UCLASS_AHCI, -1, 512, 0, &blk); + UCLASS_AHCI, -1, DEFAULT_BLKSZ, + 0, &blk); if (ret) { debug("Can't create device\n"); return ret; diff --git a/drivers/ata/sata.c b/drivers/ata/sata.c index ce3e9b5a400..784d9bbeacb 100644 --- a/drivers/ata/sata.c +++ b/drivers/ata/sata.c @@ -15,10 +15,8 @@ #include <dm.h> #include <part.h> #include <sata.h> - -#ifndef CONFIG_AHCI -struct blk_desc sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE]; -#endif +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> int sata_reset(struct udevice *dev) { @@ -50,14 +48,41 @@ int sata_scan(struct udevice *dev) return ops->scan(dev); } -#ifndef CONFIG_AHCI -#ifdef CONFIG_PARTITIONS -struct blk_desc *sata_get_dev(int dev) +int sata_rescan(bool verbose) { - return (dev < CONFIG_SYS_SATA_MAX_DEVICE) ? &sata_dev_desc[dev] : NULL; + int ret; + struct udevice *dev; + + if (verbose) + printf("Removing devices on SATA bus...\n"); + + blk_unbind_all(UCLASS_AHCI); + + ret = uclass_find_first_device(UCLASS_AHCI, &dev); + if (ret || !dev) { + printf("Cannot find SATA device (err=%d)\n", ret); + return -ENOENT; + } + + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) { + printf("Cannot remove SATA device '%s' (err=%d)\n", dev->name, ret); + return -ENOSYS; + } + + if (verbose) + printf("Rescanning SATA bus for devices...\n"); + + ret = uclass_probe_all(UCLASS_AHCI); + + if (ret == -ENODEV) { + if (verbose) + printf("No SATA block device found\n"); + return 0; + } + + return ret; } -#endif -#endif static unsigned long sata_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst) @@ -71,51 +96,6 @@ static unsigned long sata_bwrite(struct udevice *dev, lbaint_t start, return -ENOSYS; } -#ifndef CONFIG_AHCI -int __sata_initialize(void) -{ - int rc, ret = -1; - int i; - - for (i = 0; i < CONFIG_SYS_SATA_MAX_DEVICE; i++) { - memset(&sata_dev_desc[i], 0, sizeof(struct blk_desc)); - sata_dev_desc[i].uclass_id = UCLASS_AHCI; - sata_dev_desc[i].devnum = i; - sata_dev_desc[i].part_type = PART_TYPE_UNKNOWN; - sata_dev_desc[i].type = DEV_TYPE_HARDDISK; - sata_dev_desc[i].lba = 0; - sata_dev_desc[i].blksz = 512; - sata_dev_desc[i].log2blksz = LOG2(sata_dev_desc[i].blksz); - rc = init_sata(i); - if (!rc) { - rc = scan_sata(i); - if (!rc && sata_dev_desc[i].lba > 0 && - sata_dev_desc[i].blksz > 0) { - part_init(&sata_dev_desc[i]); - ret = i; - } - } - } - - return ret; -} -int sata_initialize(void) __attribute__((weak, alias("__sata_initialize"))); - -__weak int __sata_stop(void) -{ - int i, err = 0; - - for (i = 0; i < CONFIG_SYS_SATA_MAX_DEVICE; i++) - err |= reset_sata(i); - - if (err) - printf("Could not reset some SATA devices\n"); - - return err; -} -int sata_stop(void) __attribute__((weak, alias("__sata_stop"))); -#endif - static const struct blk_ops sata_blk_ops = { .read = sata_bread, .write = sata_bwrite, diff --git a/drivers/ata/sata_bootdev.c b/drivers/ata/sata_bootdev.c new file mode 100644 index 00000000000..f638493ce04 --- /dev/null +++ b/drivers/ata/sata_bootdev.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Bootdev for sata + * + * Copyright 2023 Tony Dinh <mibodhi@gmail.com> + */ + +#include <common.h> +#include <ahci.h> +#include <bootdev.h> +#include <dm.h> +#include <init.h> +#include <sata.h> + +static int sata_bootdev_bind(struct udevice *dev) +{ + struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); + + ucp->prio = BOOTDEVP_4_SCAN_FAST; + + return 0; +} + +static int sata_bootdev_hunt(struct bootdev_hunter *info, bool show) +{ + int ret; + + if (IS_ENABLED(CONFIG_PCI)) { + ret = pci_init(); + if (ret) + return ret; + } + + ret = sata_rescan(true); + if (ret) + return ret; + + return 0; +} + +struct bootdev_ops sata_bootdev_ops = { +}; + +static const struct udevice_id sata_bootdev_ids[] = { + { .compatible = "u-boot,bootdev-sata" }, + { } +}; + +U_BOOT_DRIVER(sata_bootdev) = { + .name = "sata_bootdev", + .id = UCLASS_BOOTDEV, + .ops = &sata_bootdev_ops, + .bind = sata_bootdev_bind, + .of_match = sata_bootdev_ids, +}; + +BOOTDEV_HUNTER(sata_bootdev_hunter) = { + .prio = BOOTDEVP_4_SCAN_FAST, + .uclass = UCLASS_AHCI, + .hunt = sata_bootdev_hunt, + .drv = DM_DRIVER_REF(sata_bootdev), +}; diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 18c7a66db1b..94d7369351a 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -34,6 +34,7 @@ #include <common.h> #include <ahci.h> #include <blk.h> +#include <bootdev.h> #include <cpu_func.h> #include <dm.h> #include <log.h> @@ -1076,7 +1077,8 @@ static int sata_mv_probe(struct udevice *dev) for (i = 0; i < nr_ports; i++) { ret = blk_create_devicef(dev, "sata_mv_blk", "blk", - UCLASS_AHCI, -1, 512, 0, &blk); + UCLASS_AHCI, -1, DEFAULT_BLKSZ, + 0, &blk); if (ret) { debug("Can't create device\n"); continue; @@ -1104,6 +1106,12 @@ static int sata_mv_probe(struct udevice *dev) /* TODO: undo create */ continue; + ret = bootdev_setup_for_sibling_blk(blk, "sata_bootdev"); + if (ret) { + printf("%s: Failed to create bootdev\n", __func__); + continue; + } + /* If we got here, the current SATA port was probed * successfully, so set the probe status to successful. */ @@ -1116,7 +1124,6 @@ static int sata_mv_probe(struct udevice *dev) static int sata_mv_scan(struct udevice *dev) { /* Nothing to do here */ - return 0; } diff --git a/drivers/ata/sata_sandbox.c b/drivers/ata/sata_sandbox.c deleted file mode 100644 index e64cc4a5c10..00000000000 --- a/drivers/ata/sata_sandbox.c +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2015 Google, Inc - * Written by Simon Glass <sjg@chromium.org> - */ - -#include <common.h> -#include <blk.h> - -int init_sata(int dev) -{ - return 0; -} - -int reset_sata(int dev) -{ - return 0; -} - -int scan_sata(int dev) -{ - return 0; -} - -ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer) -{ - return 0; -} - -ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, const void *buffer) -{ - return 0; -} diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index b5e150d568b..43a91a79120 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -730,7 +730,8 @@ static int sil_pci_probe(struct udevice *dev) for (i = sata_info.portbase; i < sata_info.maxport; i++) { snprintf(sata_name, sizeof(sata_name), "sil_sata%d", i); ret = blk_create_devicef(dev, "sata_sil_blk", sata_name, - UCLASS_AHCI, -1, 512, 0, &blk); + UCLASS_AHCI, -1, DEFAULT_BLKSZ, + 0, &blk); if (ret) { debug("Can't create device\n"); return ret; diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 1abea3f10db..6ad18889f61 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -13,7 +13,7 @@ config BLK config SPL_LEGACY_BLOCK bool # "Enable Legacy Block Device" - depends on SPL && !DM_SPL + depends on SPL default y if SPL_MMC || SPL_USB_STORAGE || SCSI || NVME || IDE default y if SPL_AHCI_PCI help @@ -262,3 +262,11 @@ config SYS_64BIT_LBA help Make the block subsystem use 64bit sector addresses, rather than the default of 32bit. + +config RKMTD + bool "Rockchip rkmtd virtual block device" + select RANDOM_UUID + help + Enable "rkmtd" class and driver to create a virtual block device + to transfer Rockchip boot block data to and from NAND with block + orientate tools like "ums" and "rockusb". diff --git a/drivers/block/Makefile b/drivers/block/Makefile index a161d145fd3..fe6a1fcf486 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -11,11 +11,14 @@ endif ifndef CONFIG_SPL_BUILD obj-$(CONFIG_IDE) += ide.o +obj-$(CONFIG_RKMTD) += rkmtd.o endif obj-$(CONFIG_SANDBOX) += sandbox.o host-uclass.o host_dev.o obj-$(CONFIG_$(SPL_TPL_)BLOCK_CACHE) += blkcache.o -obj-$(CONFIG_BLKMAP) += blkmap.o +obj-$(CONFIG_$(SPL_TPL_)BLKMAP) += blkmap.o +obj-$(CONFIG_$(SPL_TPL_)BLKMAP) += blkmap_helper.o obj-$(CONFIG_EFI_MEDIA) += efi-media-uclass.o obj-$(CONFIG_EFI_MEDIA_SANDBOX) += sb_efi_media.o obj-$(CONFIG_EFI_MEDIA_BLK) += efi_blk.o + diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index 885513893f6..77066da352a 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -36,6 +36,7 @@ static struct { { UCLASS_VIRTIO, "virtio" }, { UCLASS_PVBLOCK, "pvblock" }, { UCLASS_BLKMAP, "blkmap" }, + { UCLASS_RKMTD, "rkmtd" }, }; static enum uclass_id uclass_name_to_iftype(const char *uclass_idname) @@ -178,17 +179,7 @@ struct blk_desc *blk_get_by_device(struct udevice *dev) return NULL; } -/** - * get_desc() - Get the block device descriptor for the given device number - * - * @uclass_id: Interface type - * @devnum: Device number (0 = first) - * @descp: Returns block device descriptor on success - * Return: 0 on success, -ENODEV if there is no such device and no device - * with a higher device number, -ENOENT if there is no such device but there - * is one with a higher number, or other -ve on other error. - */ -static int get_desc(enum uclass_id uclass_id, int devnum, struct blk_desc **descp) +int blk_get_desc(enum uclass_id uclass_id, int devnum, struct blk_desc **descp) { bool found_more = false; struct udevice *dev; @@ -240,7 +231,7 @@ int blk_list_part(enum uclass_id uclass_id) int ret; for (ok = 0, devnum = 0;; ++devnum) { - ret = get_desc(uclass_id, devnum, &desc); + ret = blk_get_desc(uclass_id, devnum, &desc); if (ret == -ENODEV) break; else if (ret) @@ -263,7 +254,7 @@ int blk_print_part_devnum(enum uclass_id uclass_id, int devnum) struct blk_desc *desc; int ret; - ret = get_desc(uclass_id, devnum, &desc); + ret = blk_get_desc(uclass_id, devnum, &desc); if (ret) return ret; if (desc->type == DEV_TYPE_UNKNOWN) @@ -280,7 +271,7 @@ void blk_list_devices(enum uclass_id uclass_id) int i; for (i = 0;; ++i) { - ret = get_desc(uclass_id, i, &desc); + ret = blk_get_desc(uclass_id, i, &desc); if (ret == -ENODEV) break; else if (ret) @@ -297,7 +288,7 @@ int blk_print_device_num(enum uclass_id uclass_id, int devnum) struct blk_desc *desc; int ret; - ret = get_desc(uclass_id, devnum, &desc); + ret = blk_get_desc(uclass_id, devnum, &desc); if (ret) return ret; printf("\nIDE device %d: ", devnum); @@ -312,7 +303,7 @@ int blk_show_device(enum uclass_id uclass_id, int devnum) int ret; printf("\nDevice %d: ", devnum); - ret = get_desc(uclass_id, devnum, &desc); + ret = blk_get_desc(uclass_id, devnum, &desc); if (ret == -ENODEV || ret == -ENOENT) { printf("unknown device\n"); return -ENODEV; @@ -327,35 +318,6 @@ int blk_show_device(enum uclass_id uclass_id, int devnum) return 0; } -ulong blk_read_devnum(enum uclass_id uclass_id, int devnum, lbaint_t start, - lbaint_t blkcnt, void *buffer) -{ - struct blk_desc *desc; - ulong n; - int ret; - - ret = get_desc(uclass_id, devnum, &desc); - if (ret) - return ret; - n = blk_dread(desc, start, blkcnt, buffer); - if (IS_ERR_VALUE(n)) - return n; - - return n; -} - -ulong blk_write_devnum(enum uclass_id uclass_id, int devnum, lbaint_t start, - lbaint_t blkcnt, const void *buffer) -{ - struct blk_desc *desc; - int ret; - - ret = get_desc(uclass_id, devnum, &desc); - if (ret) - return ret; - return blk_dwrite(desc, start, blkcnt, buffer); -} - int blk_select_hwpart(struct udevice *dev, int hwpart) { const struct blk_ops *ops = blk_get_ops(dev); @@ -479,7 +441,7 @@ long blk_read(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *buf) start, blkcnt, desc->blksz, buf)) return blkcnt; - if (IS_ENABLED(CONFIG_BOUNCE_BUFFER)) { + if (IS_ENABLED(CONFIG_BOUNCE_BUFFER) && desc->bb) { struct blk_bounce_buffer bbstate = { .dev = dev }; int ret; @@ -516,7 +478,7 @@ long blk_write(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, blkcache_invalidate(desc->uclass_id, desc->devnum); - if (IS_ENABLED(CONFIG_BOUNCE_BUFFER)) { + if (IS_ENABLED(CONFIG_BOUNCE_BUFFER) && desc->bb) { struct blk_bounce_buffer bbstate = { .dev = dev }; int ret; diff --git a/drivers/block/blkmap.c b/drivers/block/blkmap.c index 2bb0acc20f2..21201409ed4 100644 --- a/drivers/block/blkmap.c +++ b/drivers/block/blkmap.c @@ -66,21 +66,6 @@ struct blkmap_slice { void (*destroy)(struct blkmap *bm, struct blkmap_slice *bms); }; -/** - * struct blkmap - Block map - * - * Data associated with a blkmap. - * - * @label: Human readable name of this blkmap - * @blk: Underlying block device - * @slices: List of slices associated with this blkmap - */ -struct blkmap { - char *label; - struct udevice *blk; - struct list_head slices; -}; - static bool blkmap_slice_contains(struct blkmap_slice *bms, lbaint_t blknr) { return (blknr >= bms->blknr) && (blknr < (bms->blknr + bms->blkcnt)); @@ -171,11 +156,11 @@ int blkmap_map_linear(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, bd = dev_get_uclass_plat(bm->blk); lbd = dev_get_uclass_plat(lblk); - if (lbd->blksz != bd->blksz) - /* We could support block size translation, but we - * don't yet. - */ - return -EINVAL; + if (lbd->blksz != bd->blksz) { + /* update to match the mapped device */ + bd->blksz = lbd->blksz; + bd->log2blksz = LOG2(bd->blksz); + } linear = malloc(sizeof(*linear)); if (!linear) @@ -383,14 +368,14 @@ U_BOOT_DRIVER(blkmap_blk) = { .ops = &blkmap_blk_ops, }; -int blkmap_dev_bind(struct udevice *dev) +static int blkmap_dev_bind(struct udevice *dev) { struct blkmap *bm = dev_get_plat(dev); struct blk_desc *bd; int err; err = blk_create_devicef(dev, "blkmap_blk", "blk", UCLASS_BLKMAP, - dev_seq(dev), 512, 0, &bm->blk); + dev_seq(dev), DEFAULT_BLKSZ, 0, &bm->blk); if (err) return log_msg_ret("blk", err); @@ -410,7 +395,7 @@ int blkmap_dev_bind(struct udevice *dev) return 0; } -int blkmap_dev_unbind(struct udevice *dev) +static int blkmap_dev_unbind(struct udevice *dev) { struct blkmap *bm = dev_get_plat(dev); struct blkmap_slice *bms, *tmp; diff --git a/drivers/block/blkmap_helper.c b/drivers/block/blkmap_helper.c new file mode 100644 index 00000000000..bfba14110d2 --- /dev/null +++ b/drivers/block/blkmap_helper.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * blkmap helper function + * + * Copyright (c) 2023, Linaro Limited + */ + +#include <blk.h> +#include <blkmap.h> +#include <dm/device.h> +#include <dm/device-internal.h> + +int blkmap_create_ramdisk(const char *label, ulong image_addr, ulong image_size, + struct udevice **devp) +{ + int ret; + lbaint_t blknum; + struct blkmap *bm; + struct blk_desc *desc; + struct udevice *bm_dev; + + ret = blkmap_create(label, &bm_dev); + if (ret) { + log_err("failed to create blkmap\n"); + return ret; + } + + bm = dev_get_plat(bm_dev); + desc = dev_get_uclass_plat(bm->blk); + blknum = image_size >> desc->log2blksz; + ret = blkmap_map_pmem(bm_dev, 0, blknum, image_addr); + if (ret) { + log_err("Unable to map %#llx at block %d : %d\n", + (unsigned long long)image_addr, 0, ret); + goto err; + } + log_info("Block %d+0x" LBAF " mapped to %#llx\n", 0, blknum, + (unsigned long long)image_addr); + + ret = device_probe(bm->blk); + if (ret) + goto err; + + if (devp) + *devp = bm_dev; + + return 0; + +err: + blkmap_destroy(bm_dev); + + return ret; +} diff --git a/drivers/block/host-uclass.c b/drivers/block/host-uclass.c index 6460d968c23..b3647e3ce33 100644 --- a/drivers/block/host-uclass.c +++ b/drivers/block/host-uclass.c @@ -13,6 +13,7 @@ #include <blk.h> #include <dm.h> #include <malloc.h> +#include <part.h> #include <sandbox_host.h> #include <dm/device-internal.h> #include <dm/lists.h> @@ -29,7 +30,8 @@ struct host_priv { struct udevice *cur_dev; }; -int host_create_device(const char *label, bool removable, struct udevice **devp) +int host_create_device(const char *label, bool removable, unsigned long blksz, + struct udevice **devp) { char dev_name[30], *str, *label_new; struct host_sb_plat *plat; @@ -68,6 +70,12 @@ int host_create_device(const char *label, bool removable, struct udevice **devp) struct blk_desc *desc = dev_get_uclass_plat(blk); desc->removable = removable; + + /* update blk device's block size with the provided one */ + if (blksz != desc->blksz) { + desc->blksz = blksz; + desc->log2blksz = LOG2(desc->blksz); + } } plat = dev_get_plat(dev); @@ -95,12 +103,13 @@ int host_attach_file(struct udevice *dev, const char *filename) } int host_create_attach_file(const char *label, const char *filename, - bool removable, struct udevice **devp) + bool removable, unsigned long blksz, + struct udevice **devp) { struct udevice *dev; int ret; - ret = host_create_device(label, removable, &dev); + ret = host_create_device(label, removable, blksz, &dev); if (ret) return log_msg_ret("cre", ret); diff --git a/drivers/block/host_dev.c b/drivers/block/host_dev.c index 64422417b74..30c74157934 100644 --- a/drivers/block/host_dev.c +++ b/drivers/block/host_dev.c @@ -58,6 +58,11 @@ static int host_sb_attach_file(struct udevice *dev, const char *filename) size = os_filesize(fd); desc = dev_get_uclass_plat(blk); + if (size % desc->blksz) { + printf("The size of host backing file '%s' is not multiple of " + "the device block size\n", filename); + goto err_fname; + } desc->lba = size / desc->blksz; /* write this in last, when nothing can go wrong */ @@ -73,7 +78,7 @@ err_fname: return ret; } -int host_sb_detach_file(struct udevice *dev) +static int host_sb_detach_file(struct udevice *dev) { struct host_sb_plat *plat = dev_get_plat(dev); int ret; @@ -105,7 +110,7 @@ static int host_sb_bind(struct udevice *dev) int ret; ret = blk_create_devicef(dev, "sandbox_host_blk", "blk", UCLASS_HOST, - dev_seq(dev), 512, 0, &blk); + dev_seq(dev), DEFAULT_BLKSZ, 0, &blk); if (ret) return log_msg_ret("blk", ret); @@ -123,7 +128,7 @@ static int host_sb_bind(struct udevice *dev) return 0; } -struct host_ops host_sb_ops = { +static struct host_ops host_sb_ops = { .attach_file = host_sb_attach_file, .detach_file = host_sb_detach_file, }; diff --git a/drivers/block/rkmtd.c b/drivers/block/rkmtd.c new file mode 100644 index 00000000000..c55f052e51b --- /dev/null +++ b/drivers/block/rkmtd.c @@ -0,0 +1,1152 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Some functions are derived from: + * https://github.com/rockchip-linux/u-boot/blob/next-dev/drivers/rknand/rk_ftl_arm_v7.S + * Copyright (c) 2016-2018, Fuzhou Rockchip Electronics Co., Ltd + * + * Driver interface derived from: + * /drivers/block/host_dev.c + * /drivers/block/host-uclass.c + * Copyright 2022 Google LLC + * Written by Simon Glass <sjg@chromium.org> + * + * Copyright (C) 2023 Johan Jonker <jbx6244@gmail.com> + */ + +#include <blk.h> +#include <dm.h> +#include <nand.h> +#include <part.h> +#include <rkmtd.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <dm/uclass-internal.h> +#include <linux/mtd/mtd.h> +#if !IS_ENABLED(CONFIG_SANDBOX) +#include <linux/mtd/rawnand.h> +#endif +#include <u-boot/crc.h> + +struct nand_para_info nand_para_tbl[] = { + {6, {0x2c, 0x64, 0x44, 0x4b, 0xa9, 0x00}, 4, 1, 16, 256, 2, 2, 2048, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0x44, 0x44, 0x4b, 0xa9, 0x00}, 4, 1, 16, 256, 2, 2, 1064, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0x68, 0x04, 0x4a, 0xa9, 0x00}, 4, 1, 8, 256, 2, 2, 2048, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x88, 0x04, 0x4b, 0xa9, 0x00}, 4, 1, 16, 256, 2, 2, 2048, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0xa8, 0x05, 0xcb, 0xa9, 0x00}, 4, 2, 16, 256, 2, 2, 2048, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0x68, 0x04, 0x46, 0x89, 0x00}, 4, 1, 8, 256, 2, 2, 2048, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0x48, 0x04, 0x4a, 0xa5, 0x00}, 4, 1, 8, 256, 2, 2, 1024, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0x84, 0x64, 0x3c, 0xa5, 0x00}, 4, 1, 32, 512, 2, 2, 1024, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x84, 0x64, 0x54, 0xa9, 0x00}, 4, 1, 32, 512, 2, 2, 1024, 0x01df, 4, 18, 60, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0xd7, 0x94, 0x3e, 0x84, 0x00}, 4, 1, 8, 128, 2, 2, 4096, 0x0117, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0x48, 0x04, 0x46, 0x85, 0x00}, 4, 1, 8, 256, 2, 2, 1024, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0x88, 0x05, 0xc6, 0x89, 0x00}, 4, 2, 8, 256, 2, 2, 2048, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x88, 0x24, 0x4b, 0xa9, 0x00}, 4, 1, 16, 256, 2, 2, 2048, 0x011f, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0x68, 0x00, 0x27, 0xa9, 0x00}, 4, 1, 16, 128, 1, 2, 2048, 0x011f, 0, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x64, 0x64, 0x56, 0xa5, 0x00}, 4, 1, 24, 512, 2, 2, 700, 0x01df, 4, 18, 60, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0x84, 0xc5, 0x4b, 0xa9, 0x00}, 4, 2, 16, 256, 2, 2, 2048, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0xd5, 0xd1, 0xa6, 0x68, 0x00}, 4, 2, 8, 64, 1, 2, 2048, 0x0117, 0, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0xdc, 0x90, 0xa6, 0x54, 0x00}, 4, 1, 8, 64, 1, 2, 1024, 0x0117, 0, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x64, 0x64, 0x54, 0xa4, 0x00}, 4, 1, 32, 512, 2, 1, 1024, 0x01df, 4, 18, 60, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x84, 0x44, 0x32, 0xaa, 0x00}, 4, 1, 32, 512, 2, 1, 2184, 0x05c7, 5, 19, 60, 32, 1, 0, 1, 0, 1, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x64, 0x44, 0x32, 0xa5, 0x00}, 4, 1, 32, 512, 2, 1, 1048, 0x05c7, 5, 19, 60, 32, 1, 0, 1, 0, 1, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x64, 0x64, 0x3c, 0xa5, 0x00}, 4, 1, 32, 512, 2, 1, 1044, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x84, 0x44, 0x32, 0xaa, 0x00}, 4, 1, 32, 512, 2, 1, 2184, 0x05c7, 5, 19, 60, 32, 1, 0, 4, 0, 1, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x84, 0x44, 0x34, 0xaa, 0x00}, 4, 1, 32, 512, 2, 1, 2184, 0x05c7, 5, 19, 60, 32, 1, 0, 4, 0, 1, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x84, 0xc4, 0x34, 0xaa, 0x00}, 4, 1, 32, 512, 2, 1, 2184, 0x05c7, 5, 19, 60, 32, 1, 0, 1, 0, 1, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x84, 0x44, 0x34, 0xa4, 0x00}, 4, 1, 32, 512, 2, 1, 2184, 0x05c7, 5, 19, 60, 32, 1, 0, 1, 0, 1, {0, 0, 0, 0, 0}}, + {5, {0x2c, 0x84, 0x64, 0x3c, 0xa9, 0x00}, 4, 1, 32, 512, 2, 2, 1024, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x2c, 0xa4, 0x64, 0x32, 0xaa, 0x04}, 4, 1, 32, 1024, 2, 1, 2192, 0x05c7, 10, 19, 60, 32, 1, 0, 4, 0, 1, {0, 0, 0, 0, 0}}, + {6, {0xad, 0xde, 0x94, 0xd2, 0x04, 0x43}, 2, 1, 16, 256, 2, 2, 2048, 0x01d9, 1, 1, 24, 32, 4, 0, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0xd7, 0x94, 0xda, 0x74, 0xc3}, 2, 1, 16, 256, 2, 2, 1024, 0x01d9, 1, 2, 40, 32, 4, 0, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0xd7, 0x94, 0x91, 0x60, 0x44}, 2, 1, 16, 256, 2, 2, 1046, 0x01d9, 1, 3, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4}, 2, 1, 16, 256, 2, 2, 2090, 0x01d9, 1, 4, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0xde, 0x94, 0xeb, 0x74, 0x44}, 2, 1, 32, 256, 2, 2, 1066, 0x01d9, 1, 7, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0xd5, 0x94, 0xda, 0x74, 0xc4}, 2, 1, 16, 256, 2, 2, 530, 0x01d9, 1, 3, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0xd7, 0x94, 0x9a, 0x74, 0x42}, 2, 1, 16, 256, 2, 2, 1024, 0x0119, 1, 0, 24, 32, 4, 0, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a}, 2, 1, 32, 256, 2, 2, 1060, 0x01d9, 2, 5, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0xd7, 0x14, 0x9e, 0x34, 0x4a}, 2, 1, 16, 256, 2, 2, 1056, 0x01d9, 2, 5, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0xde, 0x94, 0xa7, 0x42, 0x48}, 2, 1, 32, 256, 2, 2, 1060, 0x01d9, 2, 5, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0xde, 0x14, 0xab, 0x42, 0x4a}, 2, 1, 32, 256, 2, 2, 1056, 0x01d9, 2, 6, 40, 32, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0x3a, 0x14, 0xab, 0x42, 0x4a}, 2, 1, 32, 256, 2, 2, 2092, 0x01d9, 2, 5, 40, 32, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0xd5, 0x94, 0x9a, 0x74, 0x42}, 2, 1, 16, 256, 2, 1, 1024, 0x0111, 1, 0, 24, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xad, 0x3a, 0x14, 0x03, 0x08, 0x50}, 2, 1, 32, 388, 2, 2, 1362, 0x01d9, 9, 8, 40, 32, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x89, 0x64, 0x44, 0x4b, 0xa9, 0x00}, 7, 1, 16, 256, 2, 2, 2048, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x89, 0x88, 0x24, 0x4b, 0xa9, 0x84}, 7, 1, 16, 256, 2, 2, 2048, 0x01df, 3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x89, 0x88, 0x24, 0x4b, 0xa9, 0x00}, 7, 1, 16, 256, 2, 2, 2048, 0x0117, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x89, 0x68, 0x24, 0x4a, 0xa9, 0x00}, 7, 1, 8, 256, 2, 2, 2048, 0x0117, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x89, 0x68, 0x04, 0x4a, 0xa9, 0x00}, 7, 1, 8, 256, 2, 2, 2048, 0x0117, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x89, 0xd7, 0x94, 0x3e, 0x84, 0x00}, 7, 1, 8, 256, 2, 2, 2048, 0x0117, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x89, 0x68, 0x04, 0x46, 0xa9, 0x00}, 7, 1, 8, 256, 2, 2, 2048, 0x0117, 1, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {5, {0x89, 0x64, 0x64, 0x3c, 0xa1, 0x00}, 7, 1, 32, 512, 2, 1, 1024, 0x01c7, 4, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {5, {0x89, 0x84, 0x64, 0x3c, 0xa5, 0x00}, 7, 1, 32, 512, 2, 2, 1024, 0x01c7, 4, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x89, 0x88, 0x24, 0x3b, 0xa9, 0x00}, 7, 1, 16, 192, 2, 2, 2048, 0x0117, 12, 0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0xd7, 0x84, 0x93, 0x72, 0x57}, 1, 1, 32, 256, 2, 1, 1060, 0x05c1, 2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0xde, 0x84, 0x93, 0x72, 0x57}, 1, 1, 32, 256, 2, 1, 2092, 0x05c1, 2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0x3a, 0x85, 0x93, 0x76, 0x57}, 1, 2, 32, 256, 2, 1, 2092, 0x05e1, 2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0xd5, 0x84, 0x32, 0x72, 0x56}, 1, 1, 16, 128, 2, 1, 2056, 0x05c1, 2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56}, 1, 1, 16, 128, 2, 2, 2058, 0x05d1, 2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0xde, 0x94, 0x82, 0x76, 0x56}, 1, 1, 16, 256, 2, 2, 2062, 0x05d1, 1, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0xde, 0x94, 0x93, 0x76, 0x50}, 1, 1, 32, 256, 2, 2, 1066, 0x05d9, 2, 34, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0x3a, 0x95, 0x93, 0x7a, 0x50}, 1, 2, 32, 256, 2, 2, 1066, 0x05d9, 2, 34, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0xd7, 0x94, 0x32, 0x76, 0x55}, 1, 1, 16, 128, 2, 2, 2050, 0x0191, 2, 0, 24, 32, 1, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0xde, 0x94, 0x93, 0x76, 0x57}, 1, 1, 32, 256, 2, 2, 1058, 0x05d9, 2, 33, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0xd7, 0x84, 0x93, 0x72, 0x50}, 1, 1, 32, 256, 2, 1, 1060, 0x05c1, 2, 34, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0xde, 0x94, 0x93, 0x76, 0x51}, 1, 1, 32, 256, 2, 2, 1074, 0x05d9, 2, 35, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0x3a, 0x94, 0x93, 0x76, 0x51}, 1, 1, 32, 256, 2, 2, 2106, 0x05d9, 2, 35, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0xd7, 0x84, 0x93, 0x72, 0x51}, 1, 1, 32, 256, 2, 1, 1056, 0x05d9, 2, 35, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x98, 0xde, 0x94, 0x93, 0x76, 0xd1}, 1, 1, 32, 256, 2, 2, 1074, 0x05d9, 2, 35, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x45, 0xde, 0x94, 0x93, 0x76, 0x57}, 8, 1, 32, 256, 2, 2, 1058, 0x05d9, 2, 66, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x45, 0xd7, 0x84, 0x93, 0x72, 0x57}, 8, 1, 32, 256, 2, 1, 1060, 0x05c1, 2, 66, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x45, 0xde, 0xa4, 0x82, 0x76, 0x56}, 8, 1, 16, 256, 2, 2, 2082, 0x01d9, 1, 65, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x45, 0xde, 0x94, 0x93, 0x76, 0x50}, 8, 1, 32, 256, 2, 2, 1066, 0x05d9, 2, 67, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x45, 0xd7, 0x84, 0x93, 0x72, 0x50}, 8, 1, 32, 256, 2, 1, 1060, 0x05c1, 2, 67, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x45, 0xde, 0xa4, 0x82, 0x76, 0xd7}, 8, 1, 16, 256, 2, 2, 2090, 0x04d9, 1, 66, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x45, 0xde, 0x84, 0x93, 0x72, 0x57}, 8, 1, 32, 256, 2, 1, 2092, 0x05c1, 2, 66, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x45, 0x3a, 0x94, 0x93, 0x76, 0x51}, 8, 1, 32, 256, 2, 2, 2106, 0x01d9, 2, 68, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x45, 0xde, 0x94, 0x93, 0x76, 0x51}, 8, 1, 32, 256, 2, 2, 1074, 0x01d9, 2, 68, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x45, 0x3a, 0xa4, 0x93, 0x7a, 0x50}, 8, 1, 32, 256, 2, 2, 2138, 0x05d9, 2, 0, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x45, 0xde, 0x94, 0x82, 0x76, 0x56}, 8, 1, 16, 256, 2, 2, 2062, 0x01d9, 1, 0, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0x45, 0xde, 0x94, 0x93, 0x76, 0xd7}, 8, 1, 32, 256, 2, 2, 1058, 0x05d9, 2, 66, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xec, 0xd7, 0x94, 0x7e, 0x64, 0x44}, 0, 1, 16, 128, 2, 2, 2048, 0x01d9, 2, 49, 60, 36, 3, 0, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xec, 0xde, 0xd5, 0x7e, 0x68, 0x44}, 0, 2, 16, 128, 2, 2, 2048, 0x01f9, 2, 49, 60, 36, 3, 0, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xec, 0xd7, 0x94, 0x7a, 0x54, 0x43}, 0, 1, 16, 128, 2, 2, 2076, 0x0199, 2, 0, 40, 36, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xec, 0xde, 0xd5, 0x7a, 0x58, 0x43}, 0, 2, 16, 128, 2, 2, 2076, 0x01b9, 2, 0, 40, 36, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xec, 0xd5, 0x94, 0x76, 0x54, 0x43}, 0, 1, 16, 128, 2, 2, 1038, 0x0119, 2, 0, 24, 36, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xec, 0xd7, 0x14, 0x76, 0x54, 0xc2}, 0, 1, 16, 128, 2, 2, 2076, 0x0491, 2, 0, 24, 40, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}}, + {6, {0xec, 0xde, 0x94, 0xc3, 0xa4, 0xca}, 0, 1, 32, 792, 2, 1, 688, 0x04c1, 11, 50, 40, 32, 3, 1, 1, 0, 1, {0, 0, 0, 0, 0}}, +}; + +#if !IS_ENABLED(CONFIG_SANDBOX) +static int rkmtd_write_oob(struct rkmtd_dev *plat, ulong off, u_char *datbuf, u_char *oobbuf) +{ + struct mtd_info *mtd = plat->mtd; + struct mtd_oob_ops ops; + loff_t addr; + int ret; + + off &= ~(mtd->writesize - 1); + addr = (loff_t)off; + + memset(&ops, 0, sizeof(ops)); + ops.datbuf = datbuf; + ops.oobbuf = oobbuf; + ops.len = mtd->writesize; + ops.ooblen = mtd->oobsize; + ops.mode = MTD_OPS_PLACE_OOB; + ret = mtd_write_oob(mtd, addr, &ops); + if (ret < 0) { + debug("Error (%d) writing page %08lx\n", ret, off); + return 1; + } + + return 0; +} + +static int rkmtd_read_oob(struct rkmtd_dev *plat, ulong off, u_char *datbuf, u_char *oobbuf) +{ + struct mtd_info *mtd = plat->mtd; + struct mtd_oob_ops ops; + loff_t addr; + int ret; + + off &= ~(mtd->writesize - 1); + addr = (loff_t)off; + + memset(&ops, 0, sizeof(ops)); + ops.datbuf = datbuf; + ops.oobbuf = oobbuf; + ops.len = mtd->writesize; + ops.ooblen = mtd->oobsize; + ops.mode = MTD_OPS_PLACE_OOB; + ret = mtd_read_oob(mtd, addr, &ops); + if (ret < 0) { + debug("Error (%d) reading page %08lx\n", ret, off); + return 1; + } + + return 0; +} + +static int rkmtd_erase(struct rkmtd_dev *plat, ulong off) +{ + struct mtd_info *mtd = plat->mtd; + struct erase_info info; + loff_t addr; + int ret; + + off &= ~(mtd->writesize - 1); + addr = (loff_t)off; + + memset(&info, 0, sizeof(info)); + info.mtd = mtd; + info.addr = addr; + info.len = mtd->erasesize; + info.scrub = 1; + ret = mtd_erase(mtd, &info); + if (ret) { + debug("Error (%d) erasing page %08lx\n", ret, off); + return 1; + } + + return 0; +} +#endif +void rkmtd_scan_block(struct rkmtd_dev *plat) +{ + plat->blk_counter = 0; + +#if !IS_ENABLED(CONFIG_SANDBOX) + u32 blk; + + for (blk = 0; blk < plat->boot_blks; blk++) { + rkmtd_read_oob(plat, blk * plat->mtd->erasesize, plat->datbuf, plat->oobbuf); + if (*(u32 *)plat->datbuf == RK_TAG) { + struct sector0 *sec0 = (struct sector0 *)plat->datbuf; + + rkmtd_rc4(plat->datbuf, 512); + + plat->idblock[plat->blk_counter].blk = blk; + plat->idblock[plat->blk_counter].offset = sec0->boot_code1_offset; + plat->idblock[plat->blk_counter].boot_size = sec0->flash_boot_size; + + debug("\nblk : %d\n", plat->idblock[plat->blk_counter].blk); + debug("offset : %d\n", plat->idblock[plat->blk_counter].offset); + debug("boot_size : %d\n", plat->idblock[plat->blk_counter].boot_size); + + plat->blk_counter += 1; + + if (plat->blk_counter >= ARRAY_SIZE(plat->idblock)) + return; + } + } +#endif +} + +void rkmtd_read_block(struct rkmtd_dev *plat, u32 idx, u8 *buf) +{ +#if !IS_ENABLED(CONFIG_SANDBOX) + ulong off = plat->idblock[idx].blk * plat->mtd->erasesize; + struct nand_chip *chip = mtd_to_nand(plat->mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int counter = 0; + u32 spare0 = 0; + u32 *p_spare; + int sector; + int page; + + rkmtd_read_oob(plat, off, + plat->datbuf, plat->oobbuf); + + memcpy(buf, plat->datbuf, BLK_SIZE); + + while (counter < plat->idblock[idx].boot_size) { + if (spare0) + page = (plat->idblock[idx].offset + spare0) / 4; + else + page = (plat->idblock[idx].offset + counter) / 4; + + rkmtd_read_oob(plat, + off + page * plat->mtd->writesize, + plat->datbuf, plat->oobbuf); + + sector = plat->idblock[idx].offset + counter; + + memcpy(&buf[(sector / 4) * BLK_SIZE], plat->datbuf, BLK_SIZE); + + p_spare = (u32 *)&plat->oobbuf[(ecc->steps - 1) * NFC_SYS_DATA_SIZE]; + + spare0 = *p_spare; + if (spare0 == -1) + break; + + counter += 4; + } +#endif +} + +void rkmtd_write_block(struct rkmtd_dev *plat, u32 idx, u8 *buf) +{ +#if !IS_ENABLED(CONFIG_SANDBOX) + ulong off = plat->idblock[idx].blk * plat->mtd->erasesize; + struct nand_chip *chip = mtd_to_nand(plat->mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int counter = 0; + u32 *p_spare; + int sector; + int page; + int j, w, r; + + rkmtd_erase(plat, off); + + memset(plat->datbuf, 0xff, plat->mtd->writesize); + memcpy(plat->datbuf, buf, BLK_SIZE); + memset(plat->oobbuf, 0xff, plat->mtd->oobsize); + + rkmtd_write_oob(plat, off, + plat->datbuf, plat->oobbuf); + + while (counter < plat->idblock[idx].boot_size) { + sector = plat->idblock[idx].offset + counter; + + memset(plat->datbuf, 0xff, plat->mtd->writesize); + memcpy(plat->datbuf, &buf[(sector / 4) * BLK_SIZE], BLK_SIZE); + memset(plat->oobbuf, 0xff, plat->mtd->oobsize); + + p_spare = (u32 *)&plat->oobbuf[(ecc->steps - 1) * NFC_SYS_DATA_SIZE]; + + *p_spare = (plat->page_table[sector / 4 + 1] - 1) * 4; + + page = plat->page_table[sector / 4]; + + rkmtd_write_oob(plat, + off + page * plat->mtd->writesize, + plat->datbuf, plat->oobbuf); + + counter += 4; + } + + memset(plat->check, 0, BUF_SIZE); + rkmtd_read_block(plat, idx, plat->check); + + for (j = 0; j < BLK_SIZE; j++) { + w = *(buf + j); + r = *(plat->check + j); + + if (r != w) + goto dumpblock; + } + + for (j = 0; j < (plat->idblock[idx].boot_size * 512); j++) { + w = *(buf + plat->idblock[idx].offset * 512 + j); + r = *(plat->check + plat->idblock[idx].offset * 512 + j); + + if (r != w) + goto dumpblock; + } + + debug("write OK\n"); + return; + +dumpblock: + debug("write and check error:%x r=%x w=%x\n", j, r, w); + + plat->idblock[idx].offset = 0; + plat->idblock[idx].boot_size = 0; + + memset(plat->datbuf, 0xff, plat->mtd->writesize); + memset(plat->datbuf, 0, BLK_SIZE); + memset(plat->oobbuf, 0xff, plat->mtd->oobsize); + + rkmtd_write_oob(plat, off, plat->datbuf, plat->oobbuf); +#endif +} + +ulong rkmtd_bread(struct udevice *udev, lbaint_t start, + lbaint_t blkcnt, void *dst) +{ + struct blk_desc *block_dev = dev_get_uclass_plat(udev); + struct udevice *parent_dev = dev_get_parent(udev); + struct rkmtd_dev *plat = dev_get_plat(parent_dev); + char *buf = dst; + int i; + + if (blkcnt == 0) + return 0; + + if (start > (block_dev->lba - 1) || + (start + blkcnt) > block_dev->lba) + return 0; + + memset(dst, 0, blkcnt * block_dev->blksz); + + for (i = start; i < (start + blkcnt); i++) { + if (i == 0) { + debug("mbr : %d\n", i); + + memcpy(&buf[(i - start) * block_dev->blksz], + plat->mbr, sizeof(legacy_mbr)); + } else if (i == 1) { + debug("gpt_h : %d\n", i); + + memcpy(&buf[(i - start) * block_dev->blksz], + plat->gpt_h, sizeof(gpt_header)); + } else if (i == (block_dev->lba - 1)) { + debug("gpt_h2 : %d\n", i); + + memcpy(&buf[(i - start) * block_dev->blksz], + plat->gpt_h2, sizeof(gpt_header)); + } else if (i == 2 || i == (block_dev->lba - 33)) { + debug("gpt_e : %d\n", i); + + memcpy(&buf[(i - start) * block_dev->blksz], + plat->gpt_e, sizeof(gpt_entry)); + } else if (i >= 64 && i < (block_dev->lba - 33)) { + debug("rd : %d\n", i); + + memcpy(&buf[(i - start) * block_dev->blksz], + &plat->idb[(i - 64) * block_dev->blksz], block_dev->blksz); + } + } + + return blkcnt; +} + +ulong rkmtd_bwrite(struct udevice *udev, lbaint_t start, + lbaint_t blkcnt, const void *src) +{ + struct blk_desc *block_dev = dev_get_uclass_plat(udev); + struct udevice *parent_dev = dev_get_parent(udev); + struct rkmtd_dev *plat = dev_get_plat(parent_dev); + struct sector0 *sec0; + int i, j; + + if (blkcnt == 0) + return 0; + + if (start > (block_dev->lba - 1) || + (start + blkcnt) > block_dev->lba) + return 0; + + for (i = start; i < (start + blkcnt); i++) { + debug("wr : %d\n", i); + + if (i >= 64 && i < (block_dev->lba - 33)) { + if (i == 64) { + debug("first block\n"); + + plat->idb_need_write_back = 1; + memset(plat->idb, 0, BUF_SIZE); + } + + if (plat->idb_need_write_back) { + char *buf = (char *)src; + + memcpy(&plat->idb[(i - 64) * block_dev->blksz], + &buf[(i - start) * block_dev->blksz], + block_dev->blksz); + + if (i == 64) { + memcpy(plat->check, plat->idb, 512); + + if (*(u32 *)plat->check == RK_TAG) { + rkmtd_rc4(plat->check, 512); + + sec0 = (struct sector0 *)plat->check; + plat->offset = sec0->boot_code1_offset; + plat->boot_size = sec0->flash_boot_size; + + if (plat->offset + plat->boot_size > 512) { + debug("max size limit\n"); + plat->idb_need_write_back = 0; + } + } else { + debug("no IDB block found\n"); + plat->idb_need_write_back = 0; + } + } + + if (i == (64 + plat->offset + plat->boot_size - 1)) { + debug("last block\n"); + + plat->idb_need_write_back = 0; + + if (!plat->blk_counter) { + plat->idblock[0].blk = 2; + plat->idblock[1].blk = 3; + plat->idblock[2].blk = 4; + plat->idblock[3].blk = 5; + plat->idblock[4].blk = 6; + plat->blk_counter = 5; + } + + for (j = 0; j < plat->blk_counter; j++) { + if (plat->idblock[j].blk < plat->boot_blks) { + plat->idblock[j].offset = plat->offset; + plat->idblock[j].boot_size = plat->boot_size; + rkmtd_write_block(plat, j, plat->idb); + } + } + + rkmtd_scan_block(plat); + + if (!IS_ENABLED(CONFIG_SANDBOX)) + memset(plat->idb, 0, BUF_SIZE); + + if (plat->blk_counter) + rkmtd_read_block(plat, 0, plat->idb); + } + } + } else if (plat->idb_need_write_back) { + plat->idb_need_write_back = 0; + + memset(plat->idb, 0, BUF_SIZE); + + if (plat->blk_counter) + rkmtd_read_block(plat, 0, plat->idb); + } + } + + return blkcnt; +} + +static const struct blk_ops rkmtd_blk_ops = { + .read = rkmtd_bread, + .write = rkmtd_bwrite, +}; + +U_BOOT_DRIVER(rkmtd_blk) = { + .name = "rkmtd_blk", + .id = UCLASS_BLK, + .ops = &rkmtd_blk_ops, +}; + +void rkmtd_build_page_table(struct rkmtd_dev *plat) +{ + u32 counter; + u32 counter2; + + switch (plat->lsb_mode) { + case 0: + counter = 0; + do { + u16 val = counter; + + plat->page_table[counter++] = val; + } while (counter != 512); + break; + case 1: + counter = 0; + do { + u16 val = counter; + + if (counter > 3) { + u16 offset; + + if (counter & 1) + offset = 3; + else + offset = 2; + val = 2 * counter - offset; + } + plat->page_table[counter++] = val; + } while (counter != 512); + break; + case 2: + counter = 0; + do { + u16 val = counter; + + if (counter > 1) + val = 2 * counter - 1; + plat->page_table[counter++] = val; + } while (counter != 512); + break; + case 3: + counter = 0; + do { + u16 val = counter; + + if (counter > 5) { + u16 offset; + + if (counter & 1) + offset = 5; + else + offset = 4; + val = 2 * counter - offset; + } + plat->page_table[counter++] = val; + } while (counter != 512); + break; + case 4: + counter = 8; + plat->page_table[0] = 0; + plat->page_table[1] = 1; + plat->page_table[2] = 2; + plat->page_table[3] = 3; + plat->page_table[4] = 4; + plat->page_table[5] = 5; + plat->page_table[6] = 7; + plat->page_table[7] = 8; + do { + u32 offset; + u32 val; + + if (counter & 1) + offset = 7; + else + offset = 6; + val = 2 * counter - offset; + plat->page_table[counter++] = val; + } while (counter != 512); + break; + case 5: + counter = 0; + counter2 = 16; + do { + u16 val = counter; + + plat->page_table[counter++] = val; + } while (counter != 16); + do { + plat->page_table[counter++] = counter2; + counter2 = counter2 + 2; + } while (counter != 512); + break; + case 6: + counter = 0; + counter2 = 0; + do { + u16 val = counter; + + if (counter > 5) { + u16 offset; + + if (counter & 1) + offset = 12; + else + offset = 10; + val = counter2 - offset; + } + plat->page_table[counter++] = val; + counter2 = counter2 + 3; + } while (counter != 512); + break; + case 9: + counter = 3; + counter2 = 3; + plat->page_table[0] = 0; + plat->page_table[1] = 1; + plat->page_table[2] = 2; + do { + plat->page_table[counter++] = counter2; + counter2 = counter2 + 2; + } while (counter != 512); + break; + case 10: + counter = 0; + counter2 = 63; + do { + u16 val = counter; + + plat->page_table[counter++] = val; + } while (counter != 63); + do { + plat->page_table[counter++] = counter2; + counter2 = counter2 + 2; + } while (counter != 512); + break; + case 11: + counter = 0; + do { + u16 val = counter; + + plat->page_table[counter++] = val; + } while (counter != 8); + do { + u32 offset; + u32 val; + + if (counter & 1) + offset = 7; + else + offset = 6; + val = 2 * counter - offset; + plat->page_table[counter++] = val; + } while (counter != 512); + break; + case 12: + counter = 4; + plat->page_table[0] = 0; + plat->page_table[1] = 1; + plat->page_table[2] = 2; + plat->page_table[3] = 3; + do { + u32 val = counter - 1 + (counter >> 1); + + plat->page_table[counter++] = val; + } while (counter != 512); + break; + } +} + +static inline u32 efi_crc32(const void *buf, u32 len) +{ + return crc32(0, buf, len); +} + +int rkmtd_init_plat(struct udevice *dev) +{ + static const efi_guid_t partition_basic_data_guid = PARTITION_BASIC_DATA_GUID; + struct rkmtd_dev *plat = dev_get_plat(dev); + size_t efiname_len, dosname_len; + uchar name[] = "loader1"; + u32 calc_crc32; + int k; + + gen_rand_uuid_str(plat->uuid_disk_str, UUID_STR_FORMAT_GUID); + gen_rand_uuid_str(plat->uuid_part_str, UUID_STR_FORMAT_GUID); + + debug("uuid_part_str : %s\n", plat->uuid_part_str); + debug("uuid_disk_str : %s\n", plat->uuid_disk_str); + + plat->idb = devm_kzalloc(plat->dev, BUF_SIZE, GFP_KERNEL); + if (!plat->idb) + return -ENOMEM; + + plat->check = devm_kzalloc(plat->dev, BUF_SIZE, GFP_KERNEL); + if (!plat->check) + return -ENOMEM; + + plat->mbr = devm_kzalloc(plat->dev, sizeof(legacy_mbr), GFP_KERNEL); + if (!plat->mbr) + return -ENOMEM; + + plat->gpt_e = devm_kzalloc(plat->dev, sizeof(gpt_entry), GFP_KERNEL); + if (!plat->gpt_e) + return -ENOMEM; + + plat->gpt_h = devm_kzalloc(plat->dev, sizeof(gpt_header), GFP_KERNEL); + if (!plat->gpt_h) + return -ENOMEM; + + plat->gpt_h2 = devm_kzalloc(plat->dev, sizeof(gpt_header), GFP_KERNEL); + if (!plat->gpt_h2) + return -ENOMEM; + + /* Init mbr */ + plat->mbr->signature = MSDOS_MBR_SIGNATURE; + plat->mbr->partition_record[0].sys_ind = EFI_PMBR_OSTYPE_EFI_GPT; + plat->mbr->partition_record[0].start_sect = 1; + plat->mbr->partition_record[0].nr_sects = LBA - 1; + + /* Init gpt_e */ + plat->gpt_e->starting_lba = cpu_to_le64(64); + plat->gpt_e->ending_lba = cpu_to_le64(LBA - 34); + + debug("starting_lba : %llu\n", le64_to_cpu(plat->gpt_e->starting_lba)); + debug("ending_lba : %llu\n", le64_to_cpu(plat->gpt_e->ending_lba)); + + memcpy(plat->gpt_e->partition_type_guid.b, &partition_basic_data_guid, 16); + + uuid_str_to_bin(plat->uuid_part_str, plat->gpt_e->unique_partition_guid.b, + UUID_STR_FORMAT_GUID); + + efiname_len = sizeof(plat->gpt_e->partition_name) / sizeof(efi_char16_t); + dosname_len = sizeof(name); + + for (k = 0; k < min(dosname_len, efiname_len); k++) + plat->gpt_e->partition_name[k] = (efi_char16_t)(name[k]); + + /* Init gpt_h */ + plat->gpt_h->signature = cpu_to_le64(GPT_HEADER_SIGNATURE_UBOOT); + plat->gpt_h->revision = cpu_to_le32(GPT_HEADER_REVISION_V1); + plat->gpt_h->header_size = cpu_to_le32(sizeof(gpt_header)); + plat->gpt_h->first_usable_lba = cpu_to_le64(64); + plat->gpt_h->last_usable_lba = cpu_to_le64(LBA - 34); + plat->gpt_h->num_partition_entries = cpu_to_le32(1); + plat->gpt_h->sizeof_partition_entry = cpu_to_le32(sizeof(gpt_entry)); + + uuid_str_to_bin(plat->uuid_disk_str, plat->gpt_h->disk_guid.b, + UUID_STR_FORMAT_GUID); + + plat->gpt_h->partition_entry_array_crc32 = 0; + calc_crc32 = efi_crc32((const unsigned char *)plat->gpt_e, + le32_to_cpu(plat->gpt_h->num_partition_entries) * + le32_to_cpu(plat->gpt_h->sizeof_partition_entry)); + plat->gpt_h->partition_entry_array_crc32 = cpu_to_le32(calc_crc32); + + debug("partition crc32 : 0x%08x\n", calc_crc32); + + plat->gpt_h->my_lba = cpu_to_le64(1); + plat->gpt_h->partition_entry_lba = cpu_to_le64(2); + plat->gpt_h->alternate_lba = cpu_to_le64(LBA - 1); + + plat->gpt_h->header_crc32 = 0; + calc_crc32 = efi_crc32((const unsigned char *)plat->gpt_h, + le32_to_cpu(plat->gpt_h->header_size)); + plat->gpt_h->header_crc32 = cpu_to_le32(calc_crc32); + + debug("header h1 crc32 : 0x%08x\n", calc_crc32); + + /* Init gpt_h2 */ + memcpy(plat->gpt_h2, plat->gpt_h, sizeof(gpt_header)); + + plat->gpt_h2->my_lba = cpu_to_le64(LBA - 1); + plat->gpt_h2->partition_entry_lba = + cpu_to_le64(le64_to_cpu(plat->gpt_h2->last_usable_lba) + 1); + plat->gpt_h2->alternate_lba = cpu_to_le64(1); + + plat->gpt_h2->header_crc32 = 0; + calc_crc32 = efi_crc32((const unsigned char *)plat->gpt_h2, + le32_to_cpu(plat->gpt_h2->header_size)); + plat->gpt_h2->header_crc32 = cpu_to_le32(calc_crc32); + + debug("header h2 crc32 : 0x%08x\n", calc_crc32); + + part_init(plat->desc); + + return 0; +} + +static void rkmtd_blk_kmalloc_release(struct udevice *dev, void *res) +{ + /* noop */ +} + +static int rkmtd_bind(struct udevice *dev) +{ + struct rkmtd_dev *plat = dev_get_plat(dev); + char dev_name[30], *str; + struct blk_desc *desc; + struct udevice *bdev; + int ret; + + snprintf(dev_name, sizeof(dev_name), "%s.%s", dev->name, "blk"); + + str = devres_alloc(rkmtd_blk_kmalloc_release, strlen(dev_name) + 1, GFP_KERNEL); + if (unlikely(!str)) + return -ENOMEM; + + strcpy(str, dev_name); + + ret = blk_create_device(dev, "rkmtd_blk", str, UCLASS_RKMTD, + -1, 512, LBA, &bdev); + if (ret) { + free(str); + return log_msg_ret("blk", ret); + } + + devres_add(dev, str); + + desc = dev_get_uclass_plat(bdev); + sprintf(desc->vendor, "0x%.4x", 0x2207); + memcpy(desc->product, "RKMTD", sizeof("RKMTD")); + memcpy(desc->revision, "V1.00", sizeof("V1.00")); + plat->desc = desc; + + return 0; +} + +static int rkmtd_attach_mtd(struct udevice *dev) +{ + struct rkmtd_dev *plat = dev_get_plat(dev); + struct mtd_info *mtd; + struct udevice *blk; + int ret; + + plat->dev = dev; + + /* Sanity check that rkmtd_bind() has been used */ + ret = blk_find_from_parent(dev, &blk); + if (ret) + return ret; + +#if IS_ENABLED(CONFIG_SANDBOX) + plat->mtd = devm_kzalloc(dev, sizeof(struct mtd_info), GFP_KERNEL); + if (!plat->mtd) + return -ENOMEM; + + mtd = plat->mtd; + mtd->erasesize = 2 ^ 3 * BLK_SIZE; + mtd->writesize = BLK_SIZE; + mtd->oobsize = BLK_SIZE / STEP_SIZE * NFC_SYS_DATA_SIZE; + plat->boot_blks = 0; + plat->lsb_mode = 0; +#else + struct nand_chip *chip; + u8 id[6]; + int i, j; + u32 tmp; + + mtd = get_nand_dev_by_index(0); + if (!mtd) + return -ENOSYS; + + chip = mtd_to_nand(mtd); + + ret = ofnode_read_u32(chip->flash_node, "rockchip,boot-blks", &tmp); + plat->boot_blks = ret ? 0 : tmp; + plat->mtd = mtd; + + if (chip->select_chip) + chip->select_chip(mtd, 0); + + nand_readid_op(chip, 0, id, 6); + + if (chip->select_chip) + chip->select_chip(mtd, -1); + + for (i = 0; i < ARRAY_SIZE(nand_para_tbl); i++) { + plat->info = (struct nand_para_info *)&nand_para_tbl[i]; + for (j = 0; j < plat->info->id_bytes; j++) { + if (plat->info->nand_id[j] != id[j]) + break; + if (j == plat->info->id_bytes - 1) + goto valid; + } + } + + debug("no nand_para_info found\n"); + return -ENODEV; +valid: + plat->lsb_mode = plat->info->lsb_mode; + + debug("FLASH ID :"); + + for (j = 0; j < plat->info->id_bytes; j++) + debug(" %x", id[j]); + + debug("\n"); +#endif + + rkmtd_build_page_table(plat); + + plat->datbuf = devm_kzalloc(dev, mtd->writesize, GFP_KERNEL); + if (!plat->datbuf) + return -ENOMEM; + + plat->oobbuf = devm_kzalloc(dev, mtd->oobsize, GFP_KERNEL); + if (!plat->oobbuf) + return -ENOMEM; + + debug("erasesize %8d\n", mtd->erasesize); + debug("writesize %8d\n", mtd->writesize); + debug("oobsize %8d\n", mtd->oobsize); + debug("boot_blks %8d\n", plat->boot_blks); + debug("lsb_mode %8d\n", plat->lsb_mode); + + ret = rkmtd_init_plat(dev); + if (ret) { + debug("rkmtd_init_plat failed\n"); + return -ENOENT; + } + + rkmtd_scan_block(plat); + + memset(plat->idb, 0, BUF_SIZE); + + if (plat->blk_counter) + rkmtd_read_block(plat, 0, plat->idb); + + return 0; +} + +int rkmtd_detach_mtd(struct udevice *dev) +{ + int ret; + + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) + return log_msg_ret("rem", ret); + + ret = device_chld_unbind(dev, NULL); + if (ret) + return log_msg_ret("unb", ret); + + return 0; +} + +struct rkmtd_ops rkmtd_ops = { + .attach_mtd = rkmtd_attach_mtd, + .detach_mtd = rkmtd_detach_mtd, +}; + +U_BOOT_DRIVER(rkmtd_drv) = { + .name = "rkmtd_drv", + .id = UCLASS_RKMTD, + .ops = &rkmtd_ops, + .bind = rkmtd_bind, + .plat_auto = sizeof(struct rkmtd_dev), +}; + +struct rkmtd_priv { + struct udevice *cur_dev; +}; + +void rkmtd_rc4(u8 *buf, u32 len) +{ + u8 S[256], K[256], temp; + u32 i, j, t, x; + u8 key[16] = { 124, 78, 3, 4, 85, 5, 9, 7, 45, 44, 123, 56, 23, 13, 23, 17}; + + j = 0; + for (i = 0; i < 256; i++) { + S[i] = (u8)i; + j &= 0x0f; + K[i] = key[j]; + j++; + } + + j = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + K[i]) % 256; + temp = S[i]; + S[i] = S[j]; + S[j] = temp; + } + + i = 0; + j = 0; + for (x = 0; x < len; x++) { + i = (i + 1) % 256; + j = (j + S[i]) % 256; + temp = S[i]; + S[i] = S[j]; + S[j] = temp; + t = (S[i] + (S[j] % 256)) % 256; + buf[x] = buf[x] ^ S[t]; + } +} + +struct udevice *rkmtd_get_cur_dev(void) +{ + struct uclass *uc = uclass_find(UCLASS_RKMTD); + + if (uc) { + struct rkmtd_priv *priv = uclass_get_priv(uc); + + return priv->cur_dev; + } + + return NULL; +} + +void rkmtd_set_cur_dev(struct udevice *dev) +{ + struct uclass *uc = uclass_find(UCLASS_RKMTD); + + if (uc) { + struct rkmtd_priv *priv = uclass_get_priv(uc); + + priv->cur_dev = dev; + } +} + +struct udevice *rkmtd_find_by_label(const char *label) +{ + struct udevice *dev; + struct uclass *uc; + + uclass_id_foreach_dev(UCLASS_RKMTD, dev, uc) { + struct rkmtd_dev *plat = dev_get_plat(dev); + + if (plat->label && !strcmp(label, plat->label)) + return dev; + } + + return NULL; +} + +int rkmtd_attach(struct udevice *dev) +{ + struct rkmtd_ops *ops = rkmtd_get_ops(dev); + + if (!ops->attach_mtd) + return -ENOSYS; + + return ops->attach_mtd(dev); +} + +int rkmtd_detach(struct udevice *dev) +{ + struct rkmtd_ops *ops = rkmtd_get_ops(dev); + + if (!ops->detach_mtd) + return -ENOSYS; + + if (dev == rkmtd_get_cur_dev()) + rkmtd_set_cur_dev(NULL); + + return ops->detach_mtd(dev); +} + +static void rkmtd_drv_kmalloc_release(struct udevice *dev, void *res) +{ + /* noop */ +} + +int rkmtd_create_device(const char *label, struct udevice **devp) +{ + char dev_name[30], *str, *label_new; + struct udevice *dev, *blk; + struct rkmtd_dev *plat; + int ret; + + /* unbind any existing device with this label */ + dev = rkmtd_find_by_label(label); + if (dev) { + ret = rkmtd_detach(dev); + if (ret) + return log_msg_ret("det", ret); + + ret = device_unbind(dev); + if (ret) + return log_msg_ret("unb", ret); + } + + snprintf(dev_name, sizeof(dev_name), "rkmtd-%s", label); + + str = devres_alloc(rkmtd_drv_kmalloc_release, strlen(dev_name) + 1, GFP_KERNEL); + if (unlikely(!str)) + return -ENOMEM; + + strcpy(str, dev_name); + + ret = device_bind_driver(dm_root(), "rkmtd_drv", str, &dev); + if (ret) { + free(str); + return log_msg_ret("drv", ret); + } + + devres_add(dev, str); + + if (!blk_find_from_parent(dev, &blk)) { + struct blk_desc *desc = dev_get_uclass_plat(blk); + + desc->removable = true; + } + + label_new = devm_kzalloc(dev, strlen(label) + 1, GFP_KERNEL); + if (!label_new) + return -ENOMEM; + + strcpy(label_new, label); + + plat = dev_get_plat(dev); + plat->label = label_new; + *devp = dev; + + return 0; +} + +int rkmtd_create_attach_mtd(const char *label, struct udevice **devp) +{ + struct udevice *dev; + int ret; + + ret = rkmtd_create_device(label, &dev); + if (ret) + return log_msg_ret("cre", ret); + + ret = rkmtd_attach(dev); + if (ret) { + device_unbind(dev); + return log_msg_ret("att", ret); + } + *devp = dev; + + return 0; +} + +UCLASS_DRIVER(rkmtd) = { + .name = "rkmtd", + .id = UCLASS_RKMTD, + .post_bind = dm_scan_fdt_dev, + .priv_auto = sizeof(struct rkmtd_priv), +}; diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig index 570252d186a..3c56253b1ea 100644 --- a/drivers/bootcount/Kconfig +++ b/drivers/bootcount/Kconfig @@ -79,14 +79,6 @@ config BOOTCOUNT_RAM Store the bootcount in DRAM protected against bit errors due to short power loss or holding a system in RESET. -config BOOTCOUNT_I2C - bool "Boot counter on I2C device" - help - Enable support for the bootcounter on an i2c (like RTC) device. - CFG_SYS_I2C_RTC_ADDR = i2c chip address - CONFIG_SYS_BOOTCOUNT_ADDR = i2c addr which is used for - the bootcounter. - config BOOTCOUNT_AT91 bool "Boot counter for Atmel AT91SAM9XE" depends on AT91SAM9XE @@ -117,6 +109,16 @@ config DM_BOOTCOUNT_RTC Accesses to the backing store are performed using the write16 and read16 ops of DM RTC devices. +config DM_BOOTCOUNT_I2C + bool "Driver Model boot counter on I2C device" + depends on DM_I2C + help + Enable support for the bootcounter on a generic i2c device, like a RTC + or PMIC. The bootcounter is configured in the device tree using the + "u-boot,bootcount-i2c" compatible string. It requires a phandle + 'i2cbcdev' for the i2c device and an 'offset' property used within the + device. + config DM_BOOTCOUNT_I2C_EEPROM bool "Support i2c eeprom devices as a backing store for bootcount" depends on I2C_EEPROM @@ -175,14 +177,6 @@ config BOOTCOUNT_BOOTLIMIT counter being cleared. If set to 0, do not set a boot limit in the environment. -config BOOTCOUNT_ALEN - int "I2C address length" - default 1 - depends on BOOTCOUNT_I2C - help - Length of the the I2C address at SYS_BOOTCOUNT_ADDR for storing - the boot counter. - config SYS_BOOTCOUNT_SINGLEWORD bool "Use single word to pack boot count and magic value" depends on BOOTCOUNT_GENERIC @@ -218,7 +212,7 @@ config SYS_BOOTCOUNT_ADDR default 0x44E3E000 if BOOTCOUNT_AM33XX || BOOTCOUNT_AM33XX_NVMEM default 0xE0115FF8 if ARCH_LS1043A || ARCH_LS1021A depends on BOOTCOUNT_AM33XX || BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \ - BOOTCOUNT_I2C || BOOTCOUNT_AM33XX_NVMEM + BOOTCOUNT_AM33XX_NVMEM help Set the address used for reading and writing the boot counter. @@ -226,13 +220,11 @@ config SYS_BOOTCOUNT_MAGIC hex "Magic value for the boot counter" default 0xB001C041 if BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \ BOOTCOUNT_AM33XX || BOOTCOUNT_ENV || \ - BOOTCOUNT_RAM || BOOTCOUNT_I2C || \ - BOOTCOUNT_AT91 || DM_BOOTCOUNT + BOOTCOUNT_RAM || BOOTCOUNT_AT91 || DM_BOOTCOUNT default 0xB0 if BOOTCOUNT_AM33XX_NVMEM depends on BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \ BOOTCOUNT_AM33XX || BOOTCOUNT_ENV || \ - BOOTCOUNT_RAM || BOOTCOUNT_I2C || \ - BOOTCOUNT_AT91 || DM_BOOTCOUNT || \ + BOOTCOUNT_RAM || BOOTCOUNT_AT91 || DM_BOOTCOUNT || \ BOOTCOUNT_AM33XX_NVMEM help Set the magic value used for the boot counter. diff --git a/drivers/bootcount/Makefile b/drivers/bootcount/Makefile index b65959a384b..e7771f5b36d 100644 --- a/drivers/bootcount/Makefile +++ b/drivers/bootcount/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_BOOTCOUNT_AT91) += bootcount_at91.o obj-$(CONFIG_BOOTCOUNT_AM33XX) += bootcount_davinci.o obj-$(CONFIG_BOOTCOUNT_RAM) += bootcount_ram.o obj-$(CONFIG_BOOTCOUNT_ENV) += bootcount_env.o -obj-$(CONFIG_BOOTCOUNT_I2C) += bootcount_i2c.o obj-$(CONFIG_BOOTCOUNT_EXT) += bootcount_ext.o obj-$(CONFIG_BOOTCOUNT_AM33XX_NVMEM) += bootcount_nvmem.o @@ -14,5 +13,6 @@ obj-$(CONFIG_DM_BOOTCOUNT) += bootcount-uclass.o obj-$(CONFIG_DM_BOOTCOUNT_PMIC_PFUZE100) += pmic_pfuze100.o obj-$(CONFIG_DM_BOOTCOUNT_RTC) += rtc.o 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 diff --git a/drivers/bootcount/bootcount_dm_i2c.c b/drivers/bootcount/bootcount_dm_i2c.c new file mode 100644 index 00000000000..e27034cbeb0 --- /dev/null +++ b/drivers/bootcount/bootcount_dm_i2c.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2023 + * Philip Richard Oberfichtner <pro@denx.de> + * + * Based on previous work from Heiko Schocher (legacy bootcount_i2c.c driver) + */ + +#include <bootcount.h> +#include <dm.h> +#include <i2c.h> + +#define BC_MAGIC 0x55 + +struct bootcount_i2c_priv { + struct udevice *bcdev; + unsigned int offset; +}; + +static int bootcount_i2c_set(struct udevice *dev, const u32 val) +{ + int ret; + struct bootcount_i2c_priv *priv = dev_get_priv(dev); + + ret = dm_i2c_reg_write(priv->bcdev, priv->offset, BC_MAGIC); + if (ret < 0) + goto err_exit; + + ret = dm_i2c_reg_write(priv->bcdev, priv->offset + 1, val & 0xff); + if (ret < 0) + goto err_exit; + + return 0; + +err_exit: + log_debug("%s: Error writing to I2C device (%d)\n", __func__, ret); + return ret; +} + +static int bootcount_i2c_get(struct udevice *dev, u32 *val) +{ + int ret; + struct bootcount_i2c_priv *priv = dev_get_priv(dev); + + ret = dm_i2c_reg_read(priv->bcdev, priv->offset); + if (ret < 0) + goto err_exit; + + if ((ret & 0xff) != BC_MAGIC) { + log_debug("%s: Invalid Magic, reset bootcounter.\n", __func__); + *val = 0; + return bootcount_i2c_set(dev, 0); + } + + ret = dm_i2c_reg_read(priv->bcdev, priv->offset + 1); + if (ret < 0) + goto err_exit; + + *val = ret; + return 0; + +err_exit: + log_debug("%s: Error reading from I2C device (%d)\n", __func__, ret); + return ret; +} + +static int bootcount_i2c_probe(struct udevice *dev) +{ + struct bootcount_i2c_priv *priv = dev_get_priv(dev); + int ret; + + ret = dev_read_u32(dev, "offset", &priv->offset); + if (ret) + goto exit; + + ret = i2c_get_chip_by_phandle(dev, "i2cbcdev", &priv->bcdev); + +exit: + if (ret) + log_debug("%s failed, ret = %d\n", __func__, ret); + + return ret; +} + +static const struct bootcount_ops bootcount_i2c_ops = { + .get = bootcount_i2c_get, + .set = bootcount_i2c_set, +}; + +static const struct udevice_id bootcount_i2c_ids[] = { + { .compatible = "u-boot,bootcount-i2c" }, + { } +}; + +U_BOOT_DRIVER(bootcount_i2c) = { + .name = "bootcount-i2c", + .id = UCLASS_BOOTCOUNT, + .priv_auto = sizeof(struct bootcount_i2c_priv), + .probe = bootcount_i2c_probe, + .of_match = bootcount_i2c_ids, + .ops = &bootcount_i2c_ops, +}; diff --git a/drivers/bootcount/bootcount_i2c.c b/drivers/bootcount/bootcount_i2c.c deleted file mode 100644 index b3ac67ea35d..00000000000 --- a/drivers/bootcount/bootcount_i2c.c +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2013 - * Heiko Schocher, DENX Software Engineering, hs@denx.de. - */ - -#include <bootcount.h> -#include <linux/compiler.h> -#include <i2c.h> - -#define BC_MAGIC 0xbc - -void bootcount_store(ulong a) -{ - unsigned char buf[3]; - int ret; - - buf[0] = BC_MAGIC; - buf[1] = (a & 0xff); - ret = i2c_write(CFG_SYS_I2C_RTC_ADDR, CONFIG_SYS_BOOTCOUNT_ADDR, - CONFIG_BOOTCOUNT_ALEN, buf, 2); - if (ret != 0) - puts("Error writing bootcount\n"); -} - -ulong bootcount_load(void) -{ - unsigned char buf[3]; - int ret; - - ret = i2c_read(CFG_SYS_I2C_RTC_ADDR, CONFIG_SYS_BOOTCOUNT_ADDR, - CONFIG_BOOTCOUNT_ALEN, buf, 2); - if (ret != 0) { - puts("Error loading bootcount\n"); - return 0; - } - if (buf[0] == BC_MAGIC) - return buf[1]; - - bootcount_store(0); - - return 0; -} diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 26bf429acbc..af27ceb27da 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -27,7 +27,6 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_ARCH_SOCFPGA) += altera/ obj-$(CONFIG_ARCH_STM32) += stm32/ obj-$(CONFIG_ARCH_STM32MP) += stm32/ -obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_CLK_AT91) += at91/ obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o obj-$(CONFIG_CLK_BOSTON) += clk_boston.o @@ -43,6 +42,7 @@ obj-$(CONFIG_CLK_OWL) += owl/ obj-$(CONFIG_CLK_RENESAS) += renesas/ obj-$(CONFIG_$(SPL_TPL_)CLK_SCMI) += clk_scmi.o obj-$(CONFIG_CLK_SIFIVE) += sifive/ +obj-$(CONFIG_CLK_SUNXI) += sunxi/ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ obj-$(CONFIG_CLK_VERSACLOCK) += clk_versaclock.o obj-$(CONFIG_CLK_VERSAL) += clk_versal.o diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c index b52d926f339..025c7a7aa26 100644 --- a/drivers/clk/at91/clk-main.c +++ b/drivers/clk/at91/clk-main.c @@ -17,6 +17,7 @@ #include <linux/clk/at91_pmc.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/time.h> #include "pmc.h" #define UBOOT_DM_CLK_AT91_MAIN_RC "at91-main-rc-clk" @@ -25,7 +26,6 @@ #define UBOOT_DM_CLK_AT91_SAM9X5_MAIN "at91-sam9x5-main-clk" #define MOR_KEY_MASK GENMASK(23, 16) -#define USEC_PER_SEC 1000000UL #define SLOW_CLOCK_FREQ 32768 #define clk_main_parent_select(s) (((s) & \ diff --git a/drivers/clk/clk-cdce9xx.c b/drivers/clk/clk-cdce9xx.c index f23465d7e1f..b8700f517fc 100644 --- a/drivers/clk/clk-cdce9xx.c +++ b/drivers/clk/clk-cdce9xx.c @@ -2,7 +2,7 @@ /* * Texas Instruments CDCE913/925/937/949 clock synthesizer driver * - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ * Tero Kristo <t-kristo@ti.com> * * Based on Linux kernel clk-cdce925.c. diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index f186fcbcdb8..3b5e3f9c86b 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -422,12 +422,13 @@ int clk_get_by_name_nodev(ofnode node, const char *name, struct clk *clk) return clk_get_by_index_nodev(node, index, clk); } -int clk_release_all(struct clk *clk, int count) +int clk_release_all(struct clk *clk, unsigned int count) { - int i, ret; + unsigned int i; + int ret; for (i = 0; i < count; i++) { - debug("%s(clk[%d]=%p)\n", __func__, i, &clk[i]); + debug("%s(clk[%u]=%p)\n", __func__, i, &clk[i]); /* check if clock has been previously requested */ if (!clk[i].dev) @@ -477,7 +478,7 @@ void clk_free(struct clk *clk) ulong clk_get_rate(struct clk *clk) { const struct clk_ops *ops; - int ret; + ulong ret; debug("%s(clk=%p)\n", __func__, clk); if (!clk_valid(clk)) @@ -655,7 +656,7 @@ int clk_enable(struct clk *clk) } if (ops->enable) { - ret = ops->enable(clk); + ret = ops->enable(clkp ? clkp : clk); if (ret) { printf("Enable %s failed\n", clk->dev->name); return ret; @@ -712,7 +713,7 @@ int clk_disable(struct clk *clk) } if (ops->disable) { - ret = ops->disable(clk); + ret = ops->disable(clkp ? clkp : clk); if (ret) return ret; } diff --git a/drivers/clk/clk_k210.c b/drivers/clk/clk_k210.c index c534cc07e09..b9469b93853 100644 --- a/drivers/clk/clk_k210.c +++ b/drivers/clk/clk_k210.c @@ -16,6 +16,7 @@ #include <dt-bindings/mfd/k210-sysctl.h> #include <k210/pll.h> #include <linux/bitfield.h> +#include <asm/barrier.h> DECLARE_GLOBAL_DATA_PTR; diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c index d172fed24c9..34a49363a51 100644 --- a/drivers/clk/clk_scmi.c +++ b/drivers/clk/clk_scmi.c @@ -13,17 +13,8 @@ #include <asm/types.h> #include <linux/clk-provider.h> -/** - * struct scmi_clk_priv - Private data for SCMI clocks - * @channel: Reference to the SCMI channel to use - */ -struct scmi_clk_priv { - struct scmi_channel *channel; -}; - static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks) { - struct scmi_clk_priv *priv = dev_get_priv(dev); struct scmi_clk_protocol_attr_out out; struct scmi_msg msg = { .protocol_id = SCMI_PROTOCOL_ID_CLOCK, @@ -33,7 +24,7 @@ static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks) }; int ret; - ret = devm_scmi_process_msg(dev, priv->channel, &msg); + ret = devm_scmi_process_msg(dev, &msg); if (ret) return ret; @@ -44,7 +35,6 @@ static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks) static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name) { - struct scmi_clk_priv *priv = dev_get_priv(dev); struct scmi_clk_attribute_in in = { .clock_id = clkid, }; @@ -59,7 +49,7 @@ static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name) }; int ret; - ret = devm_scmi_process_msg(dev, priv->channel, &msg); + ret = devm_scmi_process_msg(dev, &msg); if (ret) return ret; @@ -70,7 +60,6 @@ static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name) static int scmi_clk_gate(struct clk *clk, int enable) { - struct scmi_clk_priv *priv = dev_get_priv(clk->dev); struct scmi_clk_state_in in = { .clock_id = clk->id, .attributes = enable, @@ -81,7 +70,7 @@ static int scmi_clk_gate(struct clk *clk, int enable) in, out); int ret; - ret = devm_scmi_process_msg(clk->dev, priv->channel, &msg); + ret = devm_scmi_process_msg(clk->dev, &msg); if (ret) return ret; @@ -100,7 +89,6 @@ static int scmi_clk_disable(struct clk *clk) static ulong scmi_clk_get_rate(struct clk *clk) { - struct scmi_clk_priv *priv = dev_get_priv(clk->dev); struct scmi_clk_rate_get_in in = { .clock_id = clk->id, }; @@ -110,7 +98,7 @@ static ulong scmi_clk_get_rate(struct clk *clk) in, out); int ret; - ret = devm_scmi_process_msg(clk->dev, priv->channel, &msg); + ret = devm_scmi_process_msg(clk->dev, &msg); if (ret < 0) return ret; @@ -123,7 +111,6 @@ static ulong scmi_clk_get_rate(struct clk *clk) static ulong scmi_clk_set_rate(struct clk *clk, ulong rate) { - struct scmi_clk_priv *priv = dev_get_priv(clk->dev); struct scmi_clk_rate_set_in in = { .clock_id = clk->id, .flags = SCMI_CLK_RATE_ROUND_CLOSEST, @@ -136,7 +123,7 @@ static ulong scmi_clk_set_rate(struct clk *clk, ulong rate) in, out); int ret; - ret = devm_scmi_process_msg(clk->dev, priv->channel, &msg); + ret = devm_scmi_process_msg(clk->dev, &msg); if (ret < 0) return ret; @@ -149,12 +136,11 @@ static ulong scmi_clk_set_rate(struct clk *clk, ulong rate) static int scmi_clk_probe(struct udevice *dev) { - struct scmi_clk_priv *priv = dev_get_priv(dev); struct clk *clk; size_t num_clocks, i; int ret; - ret = devm_scmi_of_get_channel(dev, &priv->channel); + ret = devm_scmi_of_get_channel(dev); if (ret) return ret; @@ -205,5 +191,4 @@ U_BOOT_DRIVER(scmi_clock) = { .id = UCLASS_CLK, .ops = &scmi_clk_ops, .probe = scmi_clk_probe, - .priv_auto = sizeof(struct scmi_clk_priv *), }; diff --git a/drivers/clk/clk_versal.c b/drivers/clk/clk_versal.c index 2e004beca2f..c473643603a 100644 --- a/drivers/clk/clk_versal.c +++ b/drivers/clk/clk_versal.c @@ -773,7 +773,6 @@ static struct clk_ops versal_clk_ops = { static const struct udevice_id versal_clk_ids[] = { { .compatible = "xlnx,versal-clk" }, - { .compatible = "xlnx,versal-net-clk" }, { } }; diff --git a/drivers/clk/exynos/clk-pll.h b/drivers/clk/exynos/clk-pll.h index c79aac44258..7b7af5e6761 100644 --- a/drivers/clk/exynos/clk-pll.h +++ b/drivers/clk/exynos/clk-pll.h @@ -5,4 +5,9 @@ * Thomas Abraham <thomas.ab@samsung.com> */ +#ifndef __EXYNOS_CLK_PLL_H +#define __EXYNOS_CLK_PLL_H + unsigned long pll145x_get_rate(unsigned int *con1, unsigned long fin_freq); + +#endif /* __EXYNOS_CLK_PLL_H */ diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig index abcb19ce6d5..56d893e0579 100644 --- a/drivers/clk/imx/Kconfig +++ b/drivers/clk/imx/Kconfig @@ -89,6 +89,24 @@ config CLK_IMX8MQ help This enables support clock driver for i.MX8MQ platforms. +config SPL_CLK_IMX93 + bool "SPL clock support for i.MX93" + depends on ARCH_IMX9 && SPL + select SPL_CLK + select SPL_CLK_CCF + select SPL_CLK_COMPOSITE_CCF + help + This enables SPL DM/DTS support for clock driver in i.MX93 + +config CLK_IMX93 + bool "Clock support for i.MX93" + depends on ARCH_IMX9 + select CLK + select CLK_CCF + select CLK_COMPOSITE_CCF + help + This enables support for clock driver in i.MX93 + config SPL_CLK_IMXRT1020 bool "SPL clock support for i.MXRT1020" depends on ARCH_IMXRT && SPL diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index b9c197f952e..6d4bcd35714 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -18,6 +18,8 @@ obj-$(CONFIG_$(SPL_TPL_)CLK_IMX8MP) += clk-imx8mp.o clk-pll14xx.o \ clk-composite-8m.o obj-$(CONFIG_$(SPL_TPL_)CLK_IMX8MQ) += clk-imx8mq.o clk-pll14xx.o \ clk-composite-8m.o +obj-$(CONFIG_$(SPL_TPL_)CLK_IMX93) += clk-imx93.o clk-fracn-gppll.o \ + clk-gate-93.o clk-composite-93.o obj-$(CONFIG_$(SPL_TPL_)CLK_IMXRT1020) += clk-imxrt1020.o obj-$(CONFIG_$(SPL_TPL_)CLK_IMXRT1050) += clk-imxrt1050.o diff --git a/drivers/clk/imx/clk-composite-93.c b/drivers/clk/imx/clk-composite-93.c new file mode 100644 index 00000000000..6d71c0c03ff --- /dev/null +++ b/drivers/clk/imx/clk-composite-93.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ +#include <common.h> +#include <log.h> +#include <asm/io.h> +#include <malloc.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <dm/devres.h> +#include <linux/iopoll.h> +#include <linux/clk-provider.h> +#include <clk.h> +#include "clk.h" +#include <linux/err.h> + +#define TIMEOUT_US 500U + +#define CCM_DIV_SHIFT 0 +#define CCM_DIV_WIDTH 8 +#define CCM_MUX_SHIFT 8 +#define CCM_MUX_MASK 3 +#define CCM_OFF_SHIFT 24 +#define CCM_BUSY_SHIFT 28 + +#define STAT_OFFSET 0x4 +#define AUTHEN_OFFSET 0x30 +#define TZ_NS_SHIFT 9 +#define TZ_NS_MASK BIT(9) + +#define WHITE_LIST_SHIFT 16 + +#define readl_poll_timeout_atomic readl_poll_timeout + +static int imx93_clk_composite_wait_ready(struct clk *clk, void __iomem *reg) +{ + int ret; + u32 val; + + ret = readl_poll_timeout_atomic(reg + STAT_OFFSET, val, !(val & BIT(CCM_BUSY_SHIFT)), + TIMEOUT_US); + if (ret) + pr_err("Slice[%s] busy timeout\n", "TODO"); + + return ret; +} + +static void imx93_clk_composite_gate_endisable(struct clk *clk, int enable) +{ + struct clk_gate *gate = to_clk_gate(clk); + u32 reg; + + reg = readl(gate->reg); + + if (enable) + reg &= ~BIT(gate->bit_idx); + else + reg |= BIT(gate->bit_idx); + + writel(reg, gate->reg); + + imx93_clk_composite_wait_ready(clk, gate->reg); +} + +static int imx93_clk_composite_gate_enable(struct clk *clk) +{ + imx93_clk_composite_gate_endisable(clk, 1); + + return 0; +} + +static int imx93_clk_composite_gate_disable(struct clk *clk) +{ + imx93_clk_composite_gate_endisable(clk, 0); + + return 0; +} + +static const struct clk_ops imx93_clk_composite_gate_ops = { + .enable = imx93_clk_composite_gate_enable, + .disable = imx93_clk_composite_gate_disable, +}; + +struct clk *imx93_clk_composite_flags(const char *name, + const char * const *parent_names, + int num_parents, void __iomem *reg, u32 domain_id, + unsigned long flags) +{ + struct clk *clk = ERR_PTR(-ENOMEM); + struct clk_divider *div = NULL; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + goto fail; + + mux->reg = reg; + mux->shift = CCM_MUX_SHIFT; + mux->mask = CCM_MUX_MASK; + mux->num_parents = num_parents; + mux->parent_names = parent_names; + mux->flags = flags; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + goto fail; + + div->reg = reg; + div->shift = CCM_DIV_SHIFT; + div->width = CCM_DIV_WIDTH; + div->flags = CLK_DIVIDER_ROUND_CLOSEST | flags; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + goto fail; + + gate->reg = reg; + gate->bit_idx = CCM_OFF_SHIFT; + gate->flags = flags; + + clk = clk_register_composite(NULL, name, + parent_names, num_parents, + &mux->clk, &clk_mux_ops, + &div->clk, &clk_divider_ops, + &gate->clk, &imx93_clk_composite_gate_ops, + flags); + + if (IS_ERR(clk)) + goto fail; + + return clk; + +fail: + kfree(gate); + kfree(div); + kfree(mux); + return ERR_CAST(clk); +} diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c new file mode 100644 index 00000000000..9228f279e27 --- /dev/null +++ b/drivers/clk/imx/clk-fracn-gppll.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2021 NXP + */ + +#include <common.h> +#include <asm/io.h> +#include <malloc.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <dm/devres.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/iopoll.h> +#include <clk.h> +#include <div64.h> + +#include "clk.h" + +#define UBOOT_DM_CLK_IMX_FRACN_GPPLL "imx_clk_fracn_gppll" + +#define PLL_CTRL 0x0 +#define HW_CTRL_SEL BIT(16) +#define CLKMUX_BYPASS BIT(2) +#define CLKMUX_EN BIT(1) +#define POWERUP_MASK BIT(0) + +#define PLL_ANA_PRG 0x10 +#define PLL_SPREAD_SPECTRUM 0x30 + +#define PLL_NUMERATOR 0x40 +#define PLL_MFN_MASK GENMASK(31, 2) + +#define PLL_DENOMINATOR 0x50 +#define PLL_MFD_MASK GENMASK(29, 0) + +#define PLL_DIV 0x60 +#define PLL_MFI_MASK GENMASK(24, 16) +#define PLL_RDIV_MASK GENMASK(15, 13) +#define PLL_ODIV_MASK GENMASK(7, 0) + +#define PLL_DFS_CTRL(x) (0x70 + (x) * 0x10) + +#define PLL_STATUS 0xF0 +#define LOCK_STATUS BIT(0) + +#define DFS_STATUS 0xF4 + +#define LOCK_TIMEOUT_US 200 + +#define PLL_FRACN_GP(_rate, _mfi, _mfn, _mfd, _rdiv, _odiv) \ + { \ + .rate = (_rate), \ + .mfi = (_mfi), \ + .mfn = (_mfn), \ + .mfd = (_mfd), \ + .rdiv = (_rdiv), \ + .odiv = (_odiv), \ + } + +#define PLL_FRACN_GP_INTEGER(_rate, _mfi, _rdiv, _odiv) \ + { \ + .rate = (_rate), \ + .mfi = (_mfi), \ + .mfn = 0, \ + .mfd = 0, \ + .rdiv = (_rdiv), \ + .odiv = (_odiv), \ + } + +struct clk_fracn_gppll { + struct clk clk; + void __iomem *base; + const struct imx_fracn_gppll_rate_table *rate_table; + int rate_count; + u32 flags; +}; + +/* + * Fvco = (Fref / rdiv) * (MFI + MFN / MFD) + * Fout = Fvco / odiv + * The (Fref / rdiv) should be in range 20MHz to 40MHz + * The Fvco should be in range 2.5Ghz to 5Ghz + */ +static const struct imx_fracn_gppll_rate_table fracn_tbl[] = { + PLL_FRACN_GP(650000000U, 162, 50, 100, 0, 6), + PLL_FRACN_GP(594000000U, 198, 0, 1, 0, 8), + PLL_FRACN_GP(560000000U, 140, 0, 1, 0, 6), + PLL_FRACN_GP(498000000U, 166, 0, 1, 0, 8), + PLL_FRACN_GP(484000000U, 121, 0, 1, 0, 6), + PLL_FRACN_GP(445333333U, 167, 0, 1, 0, 9), + PLL_FRACN_GP(400000000U, 200, 0, 1, 0, 12), + PLL_FRACN_GP(393216000U, 163, 84, 100, 0, 10), + PLL_FRACN_GP(300000000U, 150, 0, 1, 0, 12) +}; + +struct imx_fracn_gppll_clk imx_fracn_gppll = { + .rate_table = fracn_tbl, + .rate_count = ARRAY_SIZE(fracn_tbl), +}; + +/* + * Fvco = (Fref / rdiv) * MFI + * Fout = Fvco / odiv + * The (Fref / rdiv) should be in range 20MHz to 40MHz + * The Fvco should be in range 2.5Ghz to 5Ghz + */ +static const struct imx_fracn_gppll_rate_table int_tbl[] = { + PLL_FRACN_GP_INTEGER(1700000000U, 141, 1, 2), + PLL_FRACN_GP_INTEGER(1400000000U, 175, 1, 3), + PLL_FRACN_GP_INTEGER(900000000U, 150, 1, 4), +}; + +struct imx_fracn_gppll_clk imx_fracn_gppll_integer = { + .rate_table = int_tbl, + .rate_count = ARRAY_SIZE(int_tbl), +}; + +#define to_clk_fracn_gppll(_clk) container_of(_clk, struct clk_fracn_gppll, clk) + +static const struct imx_fracn_gppll_rate_table * +imx_get_pll_settings(struct clk_fracn_gppll *pll, unsigned long rate) +{ + const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) + if (rate == rate_table[i].rate) + return &rate_table[i]; + + return NULL; +} + +static unsigned long clk_fracn_gppll_round_rate(struct clk *clk, unsigned long rate) +{ + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(clk); + const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; + int i; + + /* Assuming rate_table is in descending order */ + for (i = 0; i < pll->rate_count; i++) + if (rate >= rate_table[i].rate) + return rate_table[i].rate; + + /* return minimum supported value */ + return rate_table[pll->rate_count - 1].rate; +} + +static unsigned long clk_fracn_gppll_recalc_rate(struct clk *clk) +{ + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(clk); + const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; + u32 pll_numerator, pll_denominator, pll_div; + u32 mfi, mfn, mfd, rdiv, odiv; + u64 fvco = clk_get_parent_rate(clk); + long rate = 0; + int i; + + pll_numerator = readl_relaxed(pll->base + PLL_NUMERATOR); + mfn = FIELD_GET(PLL_MFN_MASK, pll_numerator); + + pll_denominator = readl_relaxed(pll->base + PLL_DENOMINATOR); + mfd = FIELD_GET(PLL_MFD_MASK, pll_denominator); + + pll_div = readl_relaxed(pll->base + PLL_DIV); + mfi = FIELD_GET(PLL_MFI_MASK, pll_div); + + rdiv = FIELD_GET(PLL_RDIV_MASK, pll_div); + odiv = FIELD_GET(PLL_ODIV_MASK, pll_div); + + /* + * Sometimes, the recalculated rate has deviation due to + * the frac part. So find the accurate pll rate from the table + * first, if no match rate in the table, use the rate calculated + * from the equation below. + */ + for (i = 0; i < pll->rate_count; i++) { + if (rate_table[i].mfn == mfn && rate_table[i].mfi == mfi && + rate_table[i].mfd == mfd && rate_table[i].rdiv == rdiv && + rate_table[i].odiv == odiv) + rate = rate_table[i].rate; + } + + if (rate) + return (unsigned long)rate; + + if (!rdiv) + rdiv = rdiv + 1; + + switch (odiv) { + case 0: + odiv = 2; + break; + case 1: + odiv = 3; + break; + default: + break; + } + + if (pll->flags & CLK_FRACN_GPPLL_INTEGER) { + /* Fvco = (Fref / rdiv) * MFI */ + fvco = fvco * mfi; + do_div(fvco, rdiv * odiv); + } else { + /* Fvco = (Fref / rdiv) * (MFI + MFN / MFD) */ + fvco = fvco * mfi * mfd + fvco * mfn; + do_div(fvco, mfd * rdiv * odiv); + } + + return (unsigned long)fvco; +} + +static int clk_fracn_gppll_wait_lock(struct clk_fracn_gppll *pll) +{ + u32 val; + + return readl_poll_timeout(pll->base + PLL_STATUS, val, + val & LOCK_STATUS, LOCK_TIMEOUT_US); +} + +static ulong clk_fracn_gppll_set_rate(struct clk *clk, unsigned long drate) +{ + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(clk); + const struct imx_fracn_gppll_rate_table *rate; + u32 tmp, pll_div, ana_mfn; + int ret; + + rate = imx_get_pll_settings(pll, drate); + + /* Hardware control select disable. PLL is control by register */ + tmp = readl_relaxed(pll->base + PLL_CTRL); + tmp &= ~HW_CTRL_SEL; + writel_relaxed(tmp, pll->base + PLL_CTRL); + + /* Disable output */ + tmp = readl_relaxed(pll->base + PLL_CTRL); + tmp &= ~CLKMUX_EN; + writel_relaxed(tmp, pll->base + PLL_CTRL); + + /* Power Down */ + tmp &= ~POWERUP_MASK; + writel_relaxed(tmp, pll->base + PLL_CTRL); + + /* Disable BYPASS */ + tmp &= ~CLKMUX_BYPASS; + writel_relaxed(tmp, pll->base + PLL_CTRL); + + pll_div = FIELD_PREP(PLL_RDIV_MASK, rate->rdiv) | rate->odiv | + FIELD_PREP(PLL_MFI_MASK, rate->mfi); + writel_relaxed(pll_div, pll->base + PLL_DIV); + if (pll->flags & CLK_FRACN_GPPLL_FRACN) { + writel_relaxed(rate->mfd, pll->base + PLL_DENOMINATOR); + writel_relaxed(FIELD_PREP(PLL_MFN_MASK, rate->mfn), pll->base + PLL_NUMERATOR); + } + + /* Wait for 5us according to fracn mode pll doc */ + udelay(5); + + /* Enable Powerup */ + tmp |= POWERUP_MASK; + writel_relaxed(tmp, pll->base + PLL_CTRL); + + /* Wait Lock */ + ret = clk_fracn_gppll_wait_lock(pll); + if (ret) + return ret; + + /* Enable output */ + tmp |= CLKMUX_EN; + writel_relaxed(tmp, pll->base + PLL_CTRL); + + ana_mfn = readl_relaxed(pll->base + PLL_STATUS); + ana_mfn = FIELD_GET(PLL_MFN_MASK, ana_mfn); + + WARN(ana_mfn != rate->mfn, "ana_mfn != rate->mfn\n"); + + return 0; +} + +static int clk_fracn_gppll_prepare(struct clk *clk) +{ + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(clk); + u32 val; + int ret; + + val = readl_relaxed(pll->base + PLL_CTRL); + if (val & POWERUP_MASK) + return 0; + + val |= CLKMUX_BYPASS; + writel_relaxed(val, pll->base + PLL_CTRL); + + val |= POWERUP_MASK; + writel_relaxed(val, pll->base + PLL_CTRL); + + val |= CLKMUX_EN; + writel_relaxed(val, pll->base + PLL_CTRL); + + ret = clk_fracn_gppll_wait_lock(pll); + if (ret) + return ret; + + val &= ~CLKMUX_BYPASS; + writel_relaxed(val, pll->base + PLL_CTRL); + + return 0; +} + +static int clk_fracn_gppll_unprepare(struct clk *clk) +{ + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(dev_get_clk_ptr(clk->dev)); + u32 val; + + val = readl_relaxed(pll->base + PLL_CTRL); + val &= ~POWERUP_MASK; + writel_relaxed(val, pll->base + PLL_CTRL); + + return 0; +} + +static const struct clk_ops clk_fracn_gppll_ops = { + .enable = clk_fracn_gppll_prepare, + .disable = clk_fracn_gppll_unprepare, + .get_rate = clk_fracn_gppll_recalc_rate, + .set_rate = clk_fracn_gppll_set_rate, + .round_rate = clk_fracn_gppll_round_rate, +}; + +static struct clk *_imx_clk_fracn_gppll(const char *name, const char *parent_name, + void __iomem *base, + const struct imx_fracn_gppll_clk *pll_clk, + u32 pll_flags) +{ + struct clk_fracn_gppll *pll; + struct clk *clk; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base = base; + pll->rate_table = pll_clk->rate_table; + pll->rate_count = pll_clk->rate_count; + pll->flags = pll_flags; + + clk = &pll->clk; + + ret = clk_register(clk, UBOOT_DM_CLK_IMX_FRACN_GPPLL, + name, parent_name); + if (ret) { + pr_err("%s: failed to register pll %s %d\n", __func__, name, ret); + kfree(pll); + return ERR_PTR(ret); + } + + return clk; +} + +struct clk *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base, + const struct imx_fracn_gppll_clk *pll_clk) +{ + return _imx_clk_fracn_gppll(name, parent_name, base, pll_clk, CLK_FRACN_GPPLL_FRACN); +} + +struct clk *imx_clk_fracn_gppll_integer(const char *name, const char *parent_name, + void __iomem *base, + const struct imx_fracn_gppll_clk *pll_clk) +{ + return _imx_clk_fracn_gppll(name, parent_name, base, pll_clk, CLK_FRACN_GPPLL_INTEGER); +} + +U_BOOT_DRIVER(clk_fracn_gppll) = { + .name = UBOOT_DM_CLK_IMX_FRACN_GPPLL, + .id = UCLASS_CLK, + .ops = &clk_fracn_gppll_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/imx/clk-gate-93.c b/drivers/clk/imx/clk-gate-93.c new file mode 100644 index 00000000000..bc857413713 --- /dev/null +++ b/drivers/clk/imx/clk-gate-93.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <malloc.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <dm/devres.h> +#include <linux/bug.h> +#include <linux/clk-provider.h> +#include <clk.h> +#include "clk.h" +#include <linux/err.h> + +#define UBOOT_DM_CLK_IMX_GATE93 "imx_clk_gate93" + +#define DIRECT_OFFSET 0x0 + +/* + * 0b000 - LPCG will be OFF in any CPU mode. + * 0b100 - LPCG will be ON in any CPU mode. + */ +#define LPM_SETTING_OFF 0x0 +#define LPM_SETTING_ON 0x4 + +#define LPM_CUR_OFFSET 0x1c + +#define AUTHEN_OFFSET 0x30 +#define CPULPM_EN BIT(2) +#define TZ_NS_SHIFT 9 +#define TZ_NS_MASK BIT(9) + +#define WHITE_LIST_SHIFT 16 + +struct imx93_clk_gate { + struct clk clk; + void __iomem *reg; + u32 bit_idx; + u32 val; + u32 mask; + unsigned int *share_count; +}; + +#define to_imx93_clk_gate(_clk) container_of(_clk, struct imx93_clk_gate, clk) + +static void imx93_clk_gate_do_hardware(struct clk *clk, bool enable) +{ + struct imx93_clk_gate *gate = to_imx93_clk_gate(clk); + u32 val; + + val = readl(gate->reg + AUTHEN_OFFSET); + if (val & CPULPM_EN) { + val = enable ? LPM_SETTING_ON : LPM_SETTING_OFF; + writel(val, gate->reg + LPM_CUR_OFFSET); + } else { + val = readl(gate->reg + DIRECT_OFFSET); + val &= ~(gate->mask << gate->bit_idx); + if (enable) + val |= (gate->val & gate->mask) << gate->bit_idx; + writel(val, gate->reg + DIRECT_OFFSET); + } +} + +static int imx93_clk_gate_enable(struct clk *clk) +{ + struct imx93_clk_gate *gate = to_imx93_clk_gate(clk); + + if (gate->share_count && (*gate->share_count)++ > 0) + return 0; + + imx93_clk_gate_do_hardware(clk, true); + + return 0; +} + +static int imx93_clk_gate_disable(struct clk *clk) +{ + struct imx93_clk_gate *gate = to_imx93_clk_gate(clk); + + if (gate->share_count) { + if (WARN_ON(*gate->share_count == 0)) + return 0; + else if (--(*gate->share_count) > 0) + return 0; + } + + imx93_clk_gate_do_hardware(clk, false); + + return 0; +} + +static ulong imx93_clk_set_rate(struct clk *clk, ulong rate) +{ + struct clk *parent = clk_get_parent(clk); + + if (parent) + return clk_set_rate(parent, rate); + + return -ENODEV; +} + +static const struct clk_ops imx93_clk_gate_ops = { + .enable = imx93_clk_gate_enable, + .disable = imx93_clk_gate_disable, + .get_rate = clk_generic_get_rate, + .set_rate = imx93_clk_set_rate, +}; + +struct clk *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val, + u32 mask, u32 domain_id, unsigned int *share_count) +{ + struct imx93_clk_gate *gate; + struct clk *clk; + int ret; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->reg = reg; + gate->bit_idx = bit_idx; + gate->val = val; + gate->mask = mask; + gate->share_count = share_count; + + clk = &gate->clk; + + ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE93, name, parent_name); + if (ret) { + kfree(gate); + return ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(clk_gate93) = { + .name = UBOOT_DM_CLK_IMX_GATE93, + .id = UCLASS_CLK, + .ops = &imx93_clk_gate_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c new file mode 100644 index 00000000000..ce10d795316 --- /dev/null +++ b/drivers/clk/imx/clk-imx93.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2021 NXP. + */ + +#include <common.h> +#include <clk.h> +#include <clk-uclass.h> +#include <dm.h> +#include <log.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <dt-bindings/clock/imx93-clock.h> + +#include "clk.h" + +enum clk_sel { + LOW_SPEED_IO_SEL, + NON_IO_SEL, + FAST_SEL, + AUDIO_SEL, + VIDEO_SEL, + TPM_SEL, + CKO1_SEL, + CKO2_SEL, + MISC_SEL, + MAX_SEL +}; + +static u32 share_count_sai1; +static u32 share_count_sai2; +static u32 share_count_sai3; +static u32 share_count_mub; + +static const char * const a55_core_sels[] = {"a55_alt", "arm_pll"}; +static const char *parent_names[MAX_SEL][4] = { + {"clock-osc-24m", "sys_pll_pfd0_div2", "sys_pll_pfd1_div2", "video_pll"}, + {"clock-osc-24m", "sys_pll_pfd0_div2", "sys_pll_pfd1_div2", "sys_pll_pfd2_div2"}, + {"clock-osc-24m", "sys_pll_pfd0", "sys_pll_pfd1", "sys_pll_pfd2"}, + {"clock-osc-24m", "audio_pll", "video_pll", "clk_ext1"}, + {"clock-osc-24m", "audio_pll", "video_pll", "sys_pll_pfd0"}, + {"clock-osc-24m", "sys_pll_pfd0", "audio_pll", "clk_ext1"}, + {"clock-osc-24m", "sys_pll_pfd0", "sys_pll_pfd1", "audio_pll"}, + {"clock-osc-24m", "sys_pll_pfd0", "sys_pll_pfd1", "video_pll"}, + {"clock-osc-24m", "audio_pll", "video_pll", "sys_pll_pfd2"}, +}; + +static const struct imx93_clk_root { + u32 clk; + char *name; + u32 off; + enum clk_sel sel; + unsigned long flags; +} root_array[] = { + /* a55/m33/bus critical clk for system run */ + { IMX93_CLK_A55_PERIPH, "a55_periph_root", 0x0000, FAST_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_A55_MTR_BUS, "a55_mtr_bus_root", 0x0080, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_A55, "a55_alt_root", 0x0100, FAST_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_M33, "m33_root", 0x0180, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_BUS_WAKEUP, "bus_wakeup_root", 0x0280, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_BUS_AON, "bus_aon_root", 0x0300, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_WAKEUP_AXI, "wakeup_axi_root", 0x0380, FAST_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_SWO_TRACE, "swo_trace_root", 0x0400, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_M33_SYSTICK, "m33_systick_root", 0x0480, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_FLEXIO1, "flexio1_root", 0x0500, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_FLEXIO2, "flexio2_root", 0x0580, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPTMR1, "lptmr1_root", 0x0700, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPTMR2, "lptmr2_root", 0x0780, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_TPM2, "tpm2_root", 0x0880, TPM_SEL, }, + { IMX93_CLK_TPM4, "tpm4_root", 0x0980, TPM_SEL, }, + { IMX93_CLK_TPM5, "tpm5_root", 0x0a00, TPM_SEL, }, + { IMX93_CLK_TPM6, "tpm6_root", 0x0a80, TPM_SEL, }, + { IMX93_CLK_FLEXSPI1, "flexspi1_root", 0x0b00, FAST_SEL, }, + { IMX93_CLK_CAN1, "can1_root", 0x0b80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_CAN2, "can2_root", 0x0c00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART1, "lpuart1_root", 0x0c80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART2, "lpuart2_root", 0x0d00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART3, "lpuart3_root", 0x0d80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART4, "lpuart4_root", 0x0e00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART5, "lpuart5_root", 0x0e80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART6, "lpuart6_root", 0x0f00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART7, "lpuart7_root", 0x0f80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART8, "lpuart8_root", 0x1000, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C1, "lpi2c1_root", 0x1080, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C2, "lpi2c2_root", 0x1100, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C3, "lpi2c3_root", 0x1180, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C4, "lpi2c4_root", 0x1200, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C5, "lpi2c5_root", 0x1280, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C6, "lpi2c6_root", 0x1300, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C7, "lpi2c7_root", 0x1380, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C8, "lpi2c8_root", 0x1400, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI1, "lpspi1_root", 0x1480, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI2, "lpspi2_root", 0x1500, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI3, "lpspi3_root", 0x1580, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI4, "lpspi4_root", 0x1600, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI5, "lpspi5_root", 0x1680, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI6, "lpspi6_root", 0x1700, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI7, "lpspi7_root", 0x1780, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI8, "lpspi8_root", 0x1800, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_I3C1, "i3c1_root", 0x1880, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_I3C2, "i3c2_root", 0x1900, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_USDHC1, "usdhc1_root", 0x1980, FAST_SEL, }, + { IMX93_CLK_USDHC2, "usdhc2_root", 0x1a00, FAST_SEL, }, + { IMX93_CLK_USDHC3, "usdhc3_root", 0x1a80, FAST_SEL, }, + { IMX93_CLK_SAI1, "sai1_root", 0x1b00, AUDIO_SEL, }, + { IMX93_CLK_SAI2, "sai2_root", 0x1b80, AUDIO_SEL, }, + { IMX93_CLK_SAI3, "sai3_root", 0x1c00, AUDIO_SEL, }, + { IMX93_CLK_CCM_CKO1, "ccm_cko1_root", 0x1c80, CKO1_SEL, }, + { IMX93_CLK_CCM_CKO2, "ccm_cko2_root", 0x1d00, CKO2_SEL, }, + { IMX93_CLK_CCM_CKO3, "ccm_cko3_root", 0x1d80, CKO1_SEL, }, + { IMX93_CLK_CCM_CKO4, "ccm_cko4_root", 0x1e00, CKO2_SEL, }, + /* + * Critical because clk is used for handshake between HSIOMIX and NICMIX when + * NICMIX power down/on during system suspend/resume + */ + { IMX93_CLK_HSIO, "hsio_root", 0x1e80, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL}, + { IMX93_CLK_HSIO_USB_TEST_60M, "hsio_usb_test_60m_root", 0x1f00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_HSIO_ACSCAN_80M, "hsio_acscan_80m_root", 0x1f80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_HSIO_ACSCAN_480M, "hsio_acscan_480m_root", 0x2000, MISC_SEL, }, + { IMX93_CLK_NIC_AXI, "nic_axi_root", 0x2080, FAST_SEL, CLK_IS_CRITICAL, }, + { IMX93_CLK_ML_APB, "ml_apb_root", 0x2180, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_ML, "ml_root", 0x2200, FAST_SEL, }, + { IMX93_CLK_MEDIA_AXI, "media_axi_root", 0x2280, FAST_SEL, }, + { IMX93_CLK_MEDIA_APB, "media_apb_root", 0x2300, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_MEDIA_LDB, "media_ldb_root", 0x2380, VIDEO_SEL, }, + { IMX93_CLK_MEDIA_DISP_PIX, "media_disp_pix_root", 0x2400, VIDEO_SEL, }, + { IMX93_CLK_CAM_PIX, "cam_pix_root", 0x2480, VIDEO_SEL, }, + { IMX93_CLK_MIPI_TEST_BYTE, "mipi_test_byte_root", 0x2500, VIDEO_SEL, }, + { IMX93_CLK_MIPI_PHY_CFG, "mipi_phy_cfg_root", 0x2580, VIDEO_SEL, }, + { IMX93_CLK_ADC, "adc_root", 0x2700, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_PDM, "pdm_root", 0x2780, AUDIO_SEL, }, + { IMX93_CLK_TSTMR1, "tstmr1_root", 0x2800, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_TSTMR2, "tstmr2_root", 0x2880, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_MQS1, "mqs1_root", 0x2900, AUDIO_SEL, }, + { IMX93_CLK_MQS2, "mqs2_root", 0x2980, AUDIO_SEL, }, + { IMX93_CLK_AUDIO_XCVR, "audio_xcvr_root", 0x2a00, NON_IO_SEL, }, + { IMX93_CLK_SPDIF, "spdif_root", 0x2a80, AUDIO_SEL, }, + { IMX93_CLK_ENET, "enet_root", 0x2b00, NON_IO_SEL, }, + { IMX93_CLK_ENET_TIMER1, "enet_timer1_root", 0x2b80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_ENET_TIMER2, "enet_timer2_root", 0x2c00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_ENET_REF, "enet_ref_root", 0x2c80, NON_IO_SEL, }, + { IMX93_CLK_ENET_REF_PHY, "enet_ref_phy_root", 0x2d00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_I3C1_SLOW, "i3c1_slow_root", 0x2d80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_I3C2_SLOW, "i3c2_slow_root", 0x2e00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_USB_PHY_BURUNIN, "usb_phy_root", 0x2e80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_PAL_CAME_SCAN, "pal_came_scan_root", 0x2f00, MISC_SEL, } +}; + +static const struct imx93_clk_ccgr { + u32 clk; + char *name; + char *parent_name; + u32 off; + unsigned long flags; + u32 *shared_count; +} ccgr_array[] = { + { IMX93_CLK_A55_GATE, "a55_alt", "a55_alt_root", 0x8000, }, + /* M33 critical clk for system run */ + { IMX93_CLK_CM33_GATE, "cm33", "m33_root", 0x8040, CLK_IS_CRITICAL }, + { IMX93_CLK_ADC1_GATE, "adc1", "adc_root", 0x82c0, }, + { IMX93_CLK_WDOG1_GATE, "wdog1", "clock-osc-24m", 0x8300, }, + { IMX93_CLK_WDOG2_GATE, "wdog2", "clock-osc-24m", 0x8340, }, + { IMX93_CLK_WDOG3_GATE, "wdog3", "clock-osc-24m", 0x8380, }, + { IMX93_CLK_WDOG4_GATE, "wdog4", "clock-osc-24m", 0x83c0, }, + { IMX93_CLK_WDOG5_GATE, "wdog5", "clock-osc-24m", 0x8400, }, + { IMX93_CLK_SEMA1_GATE, "sema1", "bus_aon_root", 0x8440, }, + { IMX93_CLK_SEMA2_GATE, "sema2", "bus_wakeup_root", 0x8480, }, + { IMX93_CLK_MU1_A_GATE, "mu1_a", "bus_aon_root", 0x84c0, CLK_IGNORE_UNUSED }, + { IMX93_CLK_MU2_A_GATE, "mu2_a", "bus_wakeup_root", 0x84c0, CLK_IGNORE_UNUSED }, + { IMX93_CLK_MU1_B_GATE, "mu1_b", "bus_aon_root", 0x8500, 0, &share_count_mub }, + { IMX93_CLK_MU2_B_GATE, "mu2_b", "bus_wakeup_root", 0x8500, 0, &share_count_mub }, + { IMX93_CLK_EDMA1_GATE, "edma1", "m33_root", 0x8540, }, + { IMX93_CLK_EDMA2_GATE, "edma2", "wakeup_axi_root", 0x8580, }, + { IMX93_CLK_FLEXSPI1_GATE, "flexspi1", "flexspi1_root", 0x8640, }, + { IMX93_CLK_GPIO1_GATE, "gpio1", "m33_root", 0x8880, }, + { IMX93_CLK_GPIO2_GATE, "gpio2", "bus_wakeup_root", 0x88c0, }, + { IMX93_CLK_GPIO3_GATE, "gpio3", "bus_wakeup_root", 0x8900, }, + { IMX93_CLK_GPIO4_GATE, "gpio4", "bus_wakeup_root", 0x8940, }, + { IMX93_CLK_FLEXIO1_GATE, "flexio1", "flexio1_root", 0x8980, }, + { IMX93_CLK_FLEXIO2_GATE, "flexio2", "flexio2_root", 0x89c0, }, + { IMX93_CLK_LPIT1_GATE, "lpit1", "bus_aon_root", 0x8a00, }, + { IMX93_CLK_LPIT2_GATE, "lpit2", "bus_wakeup_root", 0x8a40, }, + { IMX93_CLK_LPTMR1_GATE, "lptmr1", "lptmr1_root", 0x8a80, }, + { IMX93_CLK_LPTMR2_GATE, "lptmr2", "lptmr2_root", 0x8ac0, }, + { IMX93_CLK_TPM1_GATE, "tpm1", "bus_aon_root", 0x8b00, }, + { IMX93_CLK_TPM2_GATE, "tpm2", "tpm2_root", 0x8b40, }, + { IMX93_CLK_TPM3_GATE, "tpm3", "bus_wakeup_root", 0x8b80, }, + { IMX93_CLK_TPM4_GATE, "tpm4", "tpm4_root", 0x8bc0, }, + { IMX93_CLK_TPM5_GATE, "tpm5", "tpm5_root", 0x8c00, }, + { IMX93_CLK_TPM6_GATE, "tpm6", "tpm6_root", 0x8c40, }, + { IMX93_CLK_CAN1_GATE, "can1", "can1_root", 0x8c80, }, + { IMX93_CLK_CAN2_GATE, "can2", "can2_root", 0x8cc0, }, + { IMX93_CLK_LPUART1_GATE, "lpuart1", "lpuart1_root", 0x8d00, }, + { IMX93_CLK_LPUART2_GATE, "lpuart2", "lpuart2_root", 0x8d40, }, + { IMX93_CLK_LPUART3_GATE, "lpuart3", "lpuart3_root", 0x8d80, }, + { IMX93_CLK_LPUART4_GATE, "lpuart4", "lpuart4_root", 0x8dc0, }, + { IMX93_CLK_LPUART5_GATE, "lpuart5", "lpuart5_root", 0x8e00, }, + { IMX93_CLK_LPUART6_GATE, "lpuart6", "lpuart6_root", 0x8e40, }, + { IMX93_CLK_LPUART7_GATE, "lpuart7", "lpuart7_root", 0x8e80, }, + { IMX93_CLK_LPUART8_GATE, "lpuart8", "lpuart8_root", 0x8ec0, }, + { IMX93_CLK_LPI2C1_GATE, "lpi2c1", "lpi2c1_root", 0x8f00, }, + { IMX93_CLK_LPI2C2_GATE, "lpi2c2", "lpi2c2_root", 0x8f40, }, + { IMX93_CLK_LPI2C3_GATE, "lpi2c3", "lpi2c3_root", 0x8f80, }, + { IMX93_CLK_LPI2C4_GATE, "lpi2c4", "lpi2c4_root", 0x8fc0, }, + { IMX93_CLK_LPI2C5_GATE, "lpi2c5", "lpi2c5_root", 0x9000, }, + { IMX93_CLK_LPI2C6_GATE, "lpi2c6", "lpi2c6_root", 0x9040, }, + { IMX93_CLK_LPI2C7_GATE, "lpi2c7", "lpi2c7_root", 0x9080, }, + { IMX93_CLK_LPI2C8_GATE, "lpi2c8", "lpi2c8_root", 0x90c0, }, + { IMX93_CLK_LPSPI1_GATE, "lpspi1", "lpspi1_root", 0x9100, }, + { IMX93_CLK_LPSPI2_GATE, "lpspi2", "lpspi2_root", 0x9140, }, + { IMX93_CLK_LPSPI3_GATE, "lpspi3", "lpspi3_root", 0x9180, }, + { IMX93_CLK_LPSPI4_GATE, "lpspi4", "lpspi4_root", 0x91c0, }, + { IMX93_CLK_LPSPI5_GATE, "lpspi5", "lpspi5_root", 0x9200, }, + { IMX93_CLK_LPSPI6_GATE, "lpspi6", "lpspi6_root", 0x9240, }, + { IMX93_CLK_LPSPI7_GATE, "lpspi7", "lpspi7_root", 0x9280, }, + { IMX93_CLK_LPSPI8_GATE, "lpspi8", "lpspi8_root", 0x92c0, }, + { IMX93_CLK_I3C1_GATE, "i3c1", "i3c1_root", 0x9300, }, + { IMX93_CLK_I3C2_GATE, "i3c2", "i3c2_root", 0x9340, }, + { IMX93_CLK_USDHC1_GATE, "usdhc1", "usdhc1_root", 0x9380, }, + { IMX93_CLK_USDHC2_GATE, "usdhc2", "usdhc2_root", 0x93c0, }, + { IMX93_CLK_USDHC3_GATE, "usdhc3", "usdhc3_root", 0x9400, }, + { IMX93_CLK_SAI1_GATE, "sai1", "sai1_root", 0x9440, 0, &share_count_sai1}, + { IMX93_CLK_SAI1_IPG, "sai1_ipg_clk", "bus_aon_root", 0x9440, 0, &share_count_sai1}, + { IMX93_CLK_SAI2_GATE, "sai2", "sai2_root", 0x9480, 0, &share_count_sai2}, + { IMX93_CLK_SAI2_IPG, "sai2_ipg_clk", "bus_wakeup_root", 0x9480, 0, &share_count_sai2}, + { IMX93_CLK_SAI3_GATE, "sai3", "sai3_root", 0x94c0, 0, &share_count_sai3}, + { IMX93_CLK_SAI3_IPG, "sai3_ipg_clk", "bus_wakeup_root", 0x94c0, 0, &share_count_sai3}, + { IMX93_CLK_MIPI_CSI_GATE, "mipi_csi", "media_apb_root", 0x9580, }, + { IMX93_CLK_MIPI_DSI_GATE, "mipi_dsi", "media_apb_root", 0x95c0, }, + { IMX93_CLK_LVDS_GATE, "lvds", "media_ldb_root", 0x9600, }, + { IMX93_CLK_LCDIF_GATE, "lcdif", "media_apb_root", 0x9640, }, + { IMX93_CLK_PXP_GATE, "pxp", "media_apb_root", 0x9680, }, + { IMX93_CLK_ISI_GATE, "isi", "media_apb_root", 0x96c0, }, + { IMX93_CLK_NIC_MEDIA_GATE, "nic_media", "media_axi_root", 0x9700, }, + { IMX93_CLK_USB_CONTROLLER_GATE, "usb_controller", "hsio_root", 0x9a00, }, + { IMX93_CLK_USB_TEST_60M_GATE, "usb_test_60m", "hsio_usb_test_60m_root", 0x9a40, }, + { IMX93_CLK_HSIO_TROUT_24M_GATE, "hsio_trout_24m", "clock-osc-24m", 0x9a80, }, + { IMX93_CLK_PDM_GATE, "pdm", "pdm_root", 0x9ac0, }, + { IMX93_CLK_MQS1_GATE, "mqs1", "sai1_root", 0x9b00, }, + { IMX93_CLK_MQS2_GATE, "mqs2", "sai3_root", 0x9b40, }, + { IMX93_CLK_AUD_XCVR_GATE, "aud_xcvr", "audio_xcvr_root", 0x9b80, }, + { IMX93_CLK_SPDIF_GATE, "spdif", "spdif_root", 0x9c00, }, + { IMX93_CLK_HSIO_32K_GATE, "hsio_32k", "clock-osc-24m", 0x9dc0, }, + { IMX93_CLK_ENET1_GATE, "enet1", "wakeup_axi_root", 0x9e00, }, + { IMX93_CLK_ENET_QOS_GATE, "enet_qos", "wakeup_axi_root", 0x9e40, }, + /* Critical because clk accessed during CPU idle */ + { IMX93_CLK_SYS_CNT_GATE, "sys_cnt", "clock-osc-24m", 0x9e80, CLK_IS_CRITICAL}, + { IMX93_CLK_TSTMR1_GATE, "tstmr1", "bus_aon_root", 0x9ec0, }, + { IMX93_CLK_TSTMR2_GATE, "tstmr2", "bus_wakeup_root", 0x9f00, }, + { IMX93_CLK_TMC_GATE, "tmc", "clock-osc-24m", 0x9f40, }, + { IMX93_CLK_PMRO_GATE, "pmro", "clock-osc-24m", 0x9f80, } +}; + +static int imx93_clk_probe(struct udevice *dev) +{ + const struct imx93_clk_root *root; + const struct imx93_clk_ccgr *ccgr; + struct clk osc_24m_clk, osc_32k_clk, ext1_clk; + void __iomem *base, *anatop_base; + int i, ret; + + clk_dm(IMX93_CLK_DUMMY, clk_register_fixed_rate(NULL, "dummy", 0UL)); + + ret = clk_get_by_name(dev, "osc_24m", &osc_24m_clk); + if (ret) + return ret; + clk_dm(IMX93_CLK_24M, dev_get_clk_ptr(osc_24m_clk.dev)); + + ret = clk_get_by_name(dev, "osc_32k", &osc_32k_clk); + if (ret) + return ret; + clk_dm(IMX93_CLK_32K, dev_get_clk_ptr(osc_32k_clk.dev)); + + ret = clk_get_by_name(dev, "clk_ext1", &ext1_clk); + if (ret) + return ret; + clk_dm(IMX93_CLK_EXT1, dev_get_clk_ptr(ext1_clk.dev)); + + clk_dm(IMX93_CLK_SYS_PLL_PFD0, + clk_register_fixed_rate(NULL, "sys_pll_pfd0", 1000000000)); + clk_dm(IMX93_CLK_SYS_PLL_PFD0_DIV2, + imx_clk_fixed_factor("sys_pll_pfd0_div2", "sys_pll_pfd0", 1, 2)); + clk_dm(IMX93_CLK_SYS_PLL_PFD1, + clk_register_fixed_rate(NULL, "sys_pll_pfd1", 800000000)); + clk_dm(IMX93_CLK_SYS_PLL_PFD1_DIV2, + imx_clk_fixed_factor("sys_pll_pfd1_div2", "sys_pll_pfd1", 1, 2)); + clk_dm(IMX93_CLK_SYS_PLL_PFD2, + clk_register_fixed_rate(NULL, "sys_pll_pfd2", 625000000)); + clk_dm(IMX93_CLK_SYS_PLL_PFD2_DIV2, + imx_clk_fixed_factor("sys_pll_pfd2_div2", "sys_pll_pfd2", 1, 2)); + + base = (void *)ANATOP_BASE_ADDR; + + clk_dm(IMX93_CLK_ARM_PLL, + imx_clk_fracn_gppll_integer("arm_pll", "clock-osc-24m", + anatop_base + 0x1000, + &imx_fracn_gppll_integer)); + clk_dm(IMX93_CLK_AUDIO_PLL, + imx_clk_fracn_gppll("audio_pll", "clock-osc-24m", + anatop_base + 0x1200, &imx_fracn_gppll)); + clk_dm(IMX93_CLK_VIDEO_PLL, + imx_clk_fracn_gppll("video_pll", "clock-osc-24m", + anatop_base + 0x1400, &imx_fracn_gppll)); + + base = dev_read_addr_ptr(dev); + if (!base) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(root_array); i++) { + root = &root_array[i]; + clk_dm(root->clk, imx93_clk_composite_flags(root->name, + parent_names[root->sel], + 4, base + root->off, 3, + root->flags)); + } + + for (i = 0; i < ARRAY_SIZE(ccgr_array); i++) { + ccgr = &ccgr_array[i]; + clk_dm(ccgr->clk, imx93_clk_gate(NULL, ccgr->name, ccgr->parent_name, + ccgr->flags, base + ccgr->off, 0, 1, 1, 3, + ccgr->shared_count)); + } + + clk_dm(IMX93_CLK_A55_SEL, + imx_clk_mux2("a55_sel", base + 0x4820, 0, 1, + a55_core_sels, ARRAY_SIZE(a55_core_sels))); + + return 0; +} + +static const struct udevice_id imx93_clk_ids[] = { + { .compatible = "fsl,imx93-ccm" }, + { /* Sentinel */ }, +}; + +U_BOOT_DRIVER(imx93_clk) = { + .name = "clk_imx93", + .id = UCLASS_CLK, + .of_match = imx93_clk_ids, + .ops = &ccf_clk_ops, + .probe = imx93_clk_probe, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 11f5dca1175..27a53ae5583 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -46,6 +46,34 @@ extern struct imx_pll14xx_clk imx_1416x_pll; extern struct imx_pll14xx_clk imx_1443x_pll; extern struct imx_pll14xx_clk imx_1443x_dram_pll; +#define CLK_FRACN_GPPLL_INTEGER BIT(0) +#define CLK_FRACN_GPPLL_FRACN BIT(1) + +/* NOTE: Rate table should be kept sorted in descending order. */ +struct imx_fracn_gppll_rate_table { + unsigned int rate; + unsigned int mfi; + unsigned int mfn; + unsigned int mfd; + unsigned int rdiv; + unsigned int odiv; +}; + +struct imx_fracn_gppll_clk { + const struct imx_fracn_gppll_rate_table *rate_table; + int rate_count; + int flags; +}; + +struct clk *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base, + const struct imx_fracn_gppll_clk *pll_clk); +struct clk *imx_clk_fracn_gppll_integer(const char *name, const char *parent_name, + void __iomem *base, + const struct imx_fracn_gppll_clk *pll_clk); + +extern struct imx_fracn_gppll_clk imx_fracn_gppll; +extern struct imx_fracn_gppll_clk imx_fracn_gppll_integer; + struct clk *imx_clk_pll14xx(const char *name, const char *parent_name, void __iomem *base, const struct imx_pll14xx_clk *pll_clk); @@ -224,4 +252,18 @@ struct clk *imx8m_clk_composite_flags(const char *name, #define imx8m_clk_composite_critical(name, parent_names, reg) \ __imx8m_clk_composite(name, parent_names, reg, CLK_IS_CRITICAL) +struct clk *imx93_clk_composite_flags(const char *name, + const char * const *parent_names, + int num_parents, + void __iomem *reg, + u32 domain_id, + unsigned long flags); +#define imx93_clk_composite(name, parent_names, num_parents, reg, domain_id) \ + imx93_clk_composite_flags(name, parent_names, num_parents, reg, domain_id \ + CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) + +struct clk *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val, + u32 mask, u32 domain_id, unsigned int *share_count); + #endif /* __MACH_IMX_CLK_H */ diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index 994b44ad7ab..cdc9d6f76ce 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -21,3 +21,11 @@ config CLK_MESON_G12A help Enable clock support for the Amlogic G12A SoC family, such as the S905X/D2 + +config CLK_MESON_A1 + bool "Enable clock support for Amlogic A1" + depends on CLK && ARCH_MESON + default MESON_A1 + help + Enable clock support for the Amlogic A1 SoC family, such as + the A113L diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index a486b13e9ce..d975f07aab0 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_CLK_MESON_AXG) += axg.o obj-$(CONFIG_CLK_MESON_AXG) += axg-ao.o obj-$(CONFIG_CLK_MESON_G12A) += g12a.o obj-$(CONFIG_CLK_MESON_G12A) += g12a-ao.o +obj-$(CONFIG_CLK_MESON_A1) += a1.o diff --git a/drivers/clk/meson/a1.c b/drivers/clk/meson/a1.c new file mode 100644 index 00000000000..d0f5bb37530 --- /dev/null +++ b/drivers/clk/meson/a1.c @@ -0,0 +1,735 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2023 SberDevices, Inc. + * Author: Igor Prusov <ivprusov@salutedevices.com> + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <regmap.h> +#include <asm/arch/clock-a1.h> +#include <dt-bindings/clock/amlogic,a1-pll-clkc.h> +#include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h> +#include "clk_meson.h" + +/* + * This driver supports both PLL and peripherals clock sources. + * Following operations are supported: + * - calculating clock frequency on a limited tree + * - reading muxes and dividers + * - enabling/disabling gates without propagation + * - reparenting without rate propagation, only on muxes + * - setting rates with limited reparenting, only on dividers with mux parent + */ + +#define NR_CLKS 154 +#define NR_PLL_CLKS 11 + +/* External clock IDs. Those should not overlap with regular IDs */ +#define EXTERNAL_XTAL (NR_CLKS + 0) +#define EXTERNAL_FCLK_DIV2 (NR_CLKS + 1) +#define EXTERNAL_FCLK_DIV3 (NR_CLKS + 2) +#define EXTERNAL_FCLK_DIV5 (NR_CLKS + 3) +#define EXTERNAL_FCLK_DIV7 (NR_CLKS + 4) + +#define EXTERNAL_FIXPLL_IN (NR_PLL_CLKS + 1) + +#define SET_PARM_VALUE(_priv, _parm, _val) \ + regmap_update_bits((_priv)->map, (_parm)->reg_off, \ + SETPMASK((_parm)->width, (_parm)->shift), \ + (_val) << (_parm)->shift) + +#define GET_PARM_VALUE(_priv, _parm) \ +({ \ + uint _reg; \ + regmap_read((_priv)->map, (_parm)->reg_off, &_reg); \ + PARM_GET((_parm)->width, (_parm)->shift, _reg); \ +}) + +struct meson_clk { + struct regmap *map; +}; + +/** + * enum meson_clk_type - The type of clock + * @MESON_CLK_ANY: Special value that matches any clock type + * @MESON_CLK_GATE: This clock is a gate + * @MESON_CLK_MUX: This clock is a multiplexer + * @MESON_CLK_DIV: This clock is a configurable divider + * @MESON_CLK_FIXED_DIV: This clock is a configurable divider + * @MESON_CLK_EXTERNAL: This is an external clock from different clock provider + * @MESON_CLK_PLL: This is a PLL + */ +enum meson_clk_type { + MESON_CLK_ANY = 0, + MESON_CLK_GATE, + MESON_CLK_MUX, + MESON_CLK_DIV, + MESON_CLK_FIXED_DIV, + MESON_CLK_EXTERNAL, + MESON_CLK_PLL, +}; + +/** + * struct meson_clk_info - The parameters defining a clock + * @name: Name of the clock + * @parm: Register bits description for muxes and dividers + * @div: Fixed divider value + * @parents: List of parent clock IDs + * @type: Clock type + */ +struct meson_clk_info { + const char *name; + union { + const struct parm *parm; + u8 div; + }; + const unsigned int *parents; + const enum meson_clk_type type; +}; + +/** + * struct meson_clk_data - Clocks supported by clock provider + * @num_clocks: Number of clocks + * @clocks: Array of clock descriptions + * + */ +struct meson_clk_data { + const u8 num_clocks; + const struct meson_clk_info **clocks; +}; + +/* Clock description initialization macros */ + +/* A multiplexer */ +#define CLK_MUX(_name, _reg, _shift, _width, ...) \ + (&(struct meson_clk_info){ \ + .parents = (const unsigned int[])__VA_ARGS__, \ + .parm = &(struct parm) { \ + .reg_off = (_reg), \ + .shift = (_shift), \ + .width = (_width), \ + }, \ + .name = (_name), \ + .type = MESON_CLK_MUX, \ + }) + +/* A divider with an integral divisor */ +#define CLK_DIV(_name, _reg, _shift, _width, _parent) \ + (&(struct meson_clk_info){ \ + .parents = (const unsigned int[]) { (_parent) }, \ + .parm = &(struct parm) { \ + .reg_off = (_reg), \ + .shift = (_shift), \ + .width = (_width), \ + }, \ + .name = (_name), \ + .type = MESON_CLK_DIV, \ + }) + +/* A fixed divider */ +#define CLK_DIV_FIXED(_name, _div, _parent) \ + (&(struct meson_clk_info){ \ + .parents = (const unsigned int[]) { (_parent) }, \ + .div = (_div), \ + .name = (_name), \ + .type = MESON_CLK_FIXED_DIV, \ + }) + +/* An external clock */ +#define CLK_EXTERNAL(_name) \ + (&(struct meson_clk_info){ \ + .name = (_name), \ + .parents = (const unsigned int[]) { -ENOENT }, \ + .type = MESON_CLK_EXTERNAL, \ + }) + +/* A clock gate */ +#define CLK_GATE(_name, _reg, _shift, _parent) \ + (&(struct meson_clk_info){ \ + .parents = (const unsigned int[]) { (_parent) }, \ + .parm = &(struct parm) { \ + .reg_off = (_reg), \ + .shift = (_shift), \ + .width = 1, \ + }, \ + .name = (_name), \ + .type = MESON_CLK_GATE, \ + }) + +/* A PLL clock */ +#define CLK_PLL(_name, _parent, ...) \ + (&(struct meson_clk_info){ \ + .name = (_name), \ + .parents = (const unsigned int[]) { (_parent) }, \ + .parm = (const struct parm[])__VA_ARGS__, \ + .type = MESON_CLK_PLL, \ + }) + +/* A1 peripherals clocks */ +static const struct meson_clk_info *meson_clocks[] = { + [CLKID_SPIFC_SEL] = CLK_MUX("spifc_sel", A1_SPIFC_CLK_CTRL, 9, 2, { + EXTERNAL_FCLK_DIV2, + EXTERNAL_FCLK_DIV3, + EXTERNAL_FCLK_DIV5, + -ENOENT, + }), + [CLKID_SPIFC_SEL2] = CLK_MUX("spifc_sel2", A1_SPIFC_CLK_CTRL, 15, 1, { + CLKID_SPIFC_DIV, + EXTERNAL_XTAL, + }), + [CLKID_USB_BUS_SEL] = CLK_MUX("usb_bus_sel", A1_USB_BUSCLK_CTRL, 9, 2, { + -ENOENT, + CLKID_SYS, + EXTERNAL_FCLK_DIV3, + EXTERNAL_FCLK_DIV5, + }), + [CLKID_SYS] = CLK_MUX("sys", A1_SYS_CLK_CTRL0, 31, 1, { + CLKID_SYS_A, + CLKID_SYS_B, + }), + [CLKID_SYS_A_SEL] = CLK_MUX("sys_a_sel", A1_SYS_CLK_CTRL0, 10, 3, { + -ENOENT, + EXTERNAL_FCLK_DIV2, + EXTERNAL_FCLK_DIV3, + EXTERNAL_FCLK_DIV5, + -ENOENT, + -ENOENT, + -ENOENT, + -ENOENT, + }), + [CLKID_SYS_B_SEL] = CLK_MUX("sys_b_sel", A1_SYS_CLK_CTRL0, 26, 3, { + -ENOENT, + EXTERNAL_FCLK_DIV2, + EXTERNAL_FCLK_DIV3, + EXTERNAL_FCLK_DIV5, + -ENOENT, + -ENOENT, + -ENOENT, + -ENOENT, + }), + + [CLKID_SPIFC_DIV] = CLK_DIV("spifc_div", A1_SPIFC_CLK_CTRL, 0, 8, + CLKID_SPIFC_SEL + ), + [CLKID_USB_BUS_DIV] = CLK_DIV("usb_bus_div", A1_USB_BUSCLK_CTRL, 0, 8, + CLKID_USB_BUS_SEL + ), + [CLKID_SYS_A_DIV] = CLK_DIV("sys_a_div", A1_SYS_CLK_CTRL0, 0, 10, + CLKID_SYS_A_SEL + ), + [CLKID_SYS_B_DIV] = CLK_DIV("sys_b_div", A1_SYS_CLK_CTRL0, 16, 10, + CLKID_SYS_B_SEL + ), + + [CLKID_SPIFC] = CLK_GATE("spifc", A1_SPIFC_CLK_CTRL, 8, + CLKID_SPIFC_SEL2 + ), + [CLKID_USB_BUS] = CLK_GATE("usb_bus", A1_USB_BUSCLK_CTRL, 8, + CLKID_USB_BUS_DIV + ), + [CLKID_SYS_A] = CLK_GATE("sys_a", A1_SYS_CLK_CTRL0, 13, + CLKID_SYS_A_DIV + ), + [CLKID_SYS_B] = CLK_GATE("sys_b", A1_SYS_CLK_CTRL0, 29, + CLKID_SYS_B_DIV + ), + [CLKID_FIXPLL_IN] = CLK_GATE("fixpll_in", A1_SYS_OSCIN_CTRL, 1, + EXTERNAL_XTAL + ), + [CLKID_USB_PHY_IN] = CLK_GATE("usb_phy_in", A1_SYS_OSCIN_CTRL, 2, + EXTERNAL_XTAL + ), + [CLKID_USB_CTRL_IN] = CLK_GATE("usb_ctrl_in", A1_SYS_OSCIN_CTRL, 3, + EXTERNAL_XTAL + ), + [CLKID_USB_CTRL] = CLK_GATE("usb_ctrl", A1_SYS_CLK_EN0, 28, + CLKID_SYS + ), + [CLKID_USB_PHY] = CLK_GATE("usb_phy", A1_SYS_CLK_EN0, 27, + CLKID_SYS + ), + [CLKID_SARADC] = CLK_GATE("saradc", A1_SAR_ADC_CLK_CTR, 8, + -ENOENT + ), + [CLKID_SARADC_EN] = CLK_GATE("saradc_en", A1_SYS_CLK_EN0, 13, + CLKID_SYS + ), + + [EXTERNAL_XTAL] = CLK_EXTERNAL("xtal"), + [EXTERNAL_FCLK_DIV2] = CLK_EXTERNAL("fclk_div2"), + [EXTERNAL_FCLK_DIV3] = CLK_EXTERNAL("fclk_div3"), + [EXTERNAL_FCLK_DIV5] = CLK_EXTERNAL("fclk_div5"), + [EXTERNAL_FCLK_DIV7] = CLK_EXTERNAL("fclk_div7"), +}; + +/* A1 PLL clocks */ +static const struct meson_clk_info *meson_pll_clocks[] = { + [EXTERNAL_FIXPLL_IN] = CLK_EXTERNAL("fixpll_in"), + + [CLKID_FIXED_PLL_DCO] = CLK_PLL("fixed_pll_dco", EXTERNAL_FIXPLL_IN, { + {A1_ANACTRL_FIXPLL_CTRL0, 0, 8}, + {A1_ANACTRL_FIXPLL_CTRL0, 10, 5}, + }), + + [CLKID_FCLK_DIV2_DIV] = CLK_DIV_FIXED("fclk_div2_div", 2, + CLKID_FIXED_PLL + ), + [CLKID_FCLK_DIV3_DIV] = CLK_DIV_FIXED("fclk_div3_div", 3, + CLKID_FIXED_PLL + ), + [CLKID_FCLK_DIV5_DIV] = CLK_DIV_FIXED("fclk_div5_div", 5, + CLKID_FIXED_PLL + ), + [CLKID_FCLK_DIV7_DIV] = CLK_DIV_FIXED("fclk_div7_div", 7, + CLKID_FIXED_PLL + ), + + [CLKID_FIXED_PLL] = CLK_GATE("fixed_pll", A1_ANACTRL_FIXPLL_CTRL0, 20, + CLKID_FIXED_PLL_DCO + ), + [CLKID_FCLK_DIV2] = CLK_GATE("fclk_div2", A1_ANACTRL_FIXPLL_CTRL0, 21, + CLKID_FCLK_DIV2_DIV + ), + [CLKID_FCLK_DIV3] = CLK_GATE("fclk_div3", A1_ANACTRL_FIXPLL_CTRL0, 22, + CLKID_FCLK_DIV3_DIV + ), + [CLKID_FCLK_DIV5] = CLK_GATE("fclk_div5", A1_ANACTRL_FIXPLL_CTRL0, 23, + CLKID_FCLK_DIV5_DIV + ), + [CLKID_FCLK_DIV7] = CLK_GATE("fclk_div7", A1_ANACTRL_FIXPLL_CTRL0, 24, + CLKID_FCLK_DIV7_DIV + ), +}; + +static const struct meson_clk_info *meson_clk_get_info(struct clk *clk, ulong id, + enum meson_clk_type type) +{ + struct meson_clk_data *data; + const struct meson_clk_info *info; + + data = (struct meson_clk_data *)dev_get_driver_data(clk->dev); + if (id >= data->num_clocks) + return ERR_PTR(-EINVAL); + + info = data->clocks[id]; + if (!info) + return ERR_PTR(-ENOENT); + + if (type != MESON_CLK_ANY && type != info->type) + return ERR_PTR(-EINVAL); + + return info; +} + +static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id); + +static int meson_set_gate(struct clk *clk, bool on) +{ + struct meson_clk *priv = dev_get_priv(clk->dev); + const struct meson_clk_info *info; + + debug("%s: %sabling %lu\n", __func__, on ? "en" : "dis", clk->id); + + info = meson_clk_get_info(clk, clk->id, MESON_CLK_ANY); + if (IS_ERR(info)) + return PTR_ERR(info); + + SET_PARM_VALUE(priv, info->parm, on); + + return 0; +} + +static int meson_clk_enable(struct clk *clk) +{ + return meson_set_gate(clk, true); +} + +static int meson_clk_disable(struct clk *clk) +{ + return meson_set_gate(clk, false); +} + +static ulong meson_div_get_rate(struct clk *clk, unsigned long id) +{ + struct meson_clk *priv = dev_get_priv(clk->dev); + u16 n; + ulong rate; + const struct meson_clk_info *info; + + info = meson_clk_get_info(clk, id, MESON_CLK_DIV); + if (IS_ERR(info)) + return PTR_ERR(info); + + /* Actual divider value is (field value + 1), hence the increment */ + n = GET_PARM_VALUE(priv, info->parm) + 1; + + rate = meson_clk_get_rate_by_id(clk, info->parents[0]); + + return rate / n; +} + +static int meson_clk_get_parent(struct clk *clk, unsigned long id) +{ + uint reg = 0; + struct meson_clk *priv = dev_get_priv(clk->dev); + const struct meson_clk_info *info; + + info = meson_clk_get_info(clk, id, MESON_CLK_ANY); + if (IS_ERR(info)) + return PTR_ERR(info); + + /* For muxes we read currently selected parent from register, + * for other types there is always only one element in parents array. + */ + if (info->type == MESON_CLK_MUX) { + reg = GET_PARM_VALUE(priv, info->parm); + if (IS_ERR_VALUE(reg)) + return reg; + } + + return info->parents[reg]; +} + +static ulong meson_pll_get_rate(struct clk *clk, unsigned long id) +{ + struct meson_clk *priv = dev_get_priv(clk->dev); + const struct meson_clk_info *info; + const struct parm *pm, *pn; + ulong parent_rate_mhz; + unsigned int parent; + u16 n, m; + + info = meson_clk_get_info(clk, id, MESON_CLK_ANY); + if (IS_ERR(info)) + return PTR_ERR(info); + + pm = &info->parm[0]; + pn = &info->parm[1]; + + n = GET_PARM_VALUE(priv, pn); + m = GET_PARM_VALUE(priv, pm); + + if (n == 0) + return -EINVAL; + + parent = info->parents[0]; + parent_rate_mhz = meson_clk_get_rate_by_id(clk, parent) / 1000000; + + return parent_rate_mhz * m / n * 1000000; +} + +static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id) +{ + ulong rate, parent; + const struct meson_clk_info *info; + + if (IS_ERR_VALUE(id)) + return id; + + info = meson_clk_get_info(clk, id, MESON_CLK_ANY); + if (IS_ERR(info)) + return PTR_ERR(info); + + switch (info->type) { + case MESON_CLK_PLL: + rate = meson_pll_get_rate(clk, id); + break; + case MESON_CLK_GATE: + case MESON_CLK_MUX: + parent = meson_clk_get_parent(clk, id); + rate = meson_clk_get_rate_by_id(clk, parent); + break; + case MESON_CLK_DIV: + rate = meson_div_get_rate(clk, id); + break; + case MESON_CLK_FIXED_DIV: + parent = meson_clk_get_parent(clk, id); + rate = meson_clk_get_rate_by_id(clk, parent) / info->div; + break; + case MESON_CLK_EXTERNAL: { + int ret; + struct clk external_clk; + + ret = clk_get_by_name(clk->dev, info->name, &external_clk); + if (ret) + return ret; + + rate = clk_get_rate(&external_clk); + break; + } + default: + rate = -EINVAL; + break; + } + + return rate; +} + +static ulong meson_clk_get_rate(struct clk *clk) +{ + return meson_clk_get_rate_by_id(clk, clk->id); +} + +/* This implements rate propagation for dividers placed after multiplexer: + * ---------|\ + * ..... | |---DIV-- + * ---------|/ + */ +static ulong meson_composite_set_rate(struct clk *clk, ulong id, ulong rate) +{ + unsigned int i, best_div_val; + unsigned long best_delta, best_parent; + const struct meson_clk_info *div; + const struct meson_clk_info *mux; + struct meson_clk *priv = dev_get_priv(clk->dev); + + div = meson_clk_get_info(clk, id, MESON_CLK_DIV); + if (IS_ERR(div)) + return PTR_ERR(div); + + mux = meson_clk_get_info(clk, div->parents[0], MESON_CLK_MUX); + if (IS_ERR(mux)) + return PTR_ERR(mux); + + best_parent = -EINVAL; + best_delta = ULONG_MAX; + for (i = 0; i < (1 << mux->parm->width); i++) { + unsigned long parent_rate, delta; + unsigned int div_val; + + parent_rate = meson_clk_get_rate_by_id(clk, mux->parents[i]); + if (IS_ERR_VALUE(parent_rate)) + continue; + + /* If overflow, try to use max divider value */ + div_val = min(DIV_ROUND_CLOSEST(parent_rate, rate), + (1UL << div->parm->width)); + + delta = abs(rate - (parent_rate / div_val)); + if (delta < best_delta) { + best_delta = delta; + best_div_val = div_val; + best_parent = i; + } + } + + if (IS_ERR_VALUE(best_parent)) + return best_parent; + + SET_PARM_VALUE(priv, mux->parm, best_parent); + /* Divider is set to (field value + 1), hence the decrement */ + SET_PARM_VALUE(priv, div->parm, best_div_val - 1); + + return 0; +} + +static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned int id, ulong rate); + +static ulong meson_mux_set_rate(struct clk *clk, unsigned long id, ulong rate) +{ + int i; + ulong ret = -EINVAL; + struct meson_clk *priv = dev_get_priv(clk->dev); + const struct meson_clk_info *info; + + info = meson_clk_get_info(clk, id, MESON_CLK_MUX); + if (IS_ERR(info)) + return PTR_ERR(info); + + for (i = 0; i < (1 << info->parm->width); i++) { + ret = meson_clk_set_rate_by_id(clk, info->parents[i], rate); + if (!ret) { + SET_PARM_VALUE(priv, info->parm, i); + break; + } + } + + return ret; +} + +/* Rate propagation is implemented for a subcection of a clock tree, that is + * required at boot stage. + */ +static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned int id, ulong rate) +{ + switch (id) { + case CLKID_SPIFC_DIV: + case CLKID_USB_BUS_DIV: + return meson_composite_set_rate(clk, id, rate); + case CLKID_SPIFC: + case CLKID_USB_BUS: { + unsigned long parent = meson_clk_get_parent(clk, id); + + return meson_clk_set_rate_by_id(clk, parent, rate); + } + case CLKID_SPIFC_SEL2: + return meson_mux_set_rate(clk, id, rate); + } + + return -EINVAL; +} + +static ulong meson_clk_set_rate(struct clk *clk, ulong rate) +{ + return meson_clk_set_rate_by_id(clk, clk->id, rate); +} + +static int meson_mux_set_parent_by_id(struct clk *clk, unsigned int parent_id) +{ + unsigned int i, parent_index; + struct meson_clk *priv = dev_get_priv(clk->dev); + const struct meson_clk_info *info; + + info = meson_clk_get_info(clk, clk->id, MESON_CLK_MUX); + if (IS_ERR(info)) + return PTR_ERR(info); + + parent_index = -EINVAL; + for (i = 0; i < (1 << info->parm->width); i++) { + if (parent_id == info->parents[i]) { + parent_index = i; + break; + } + } + + if (IS_ERR_VALUE(parent_index)) + return parent_index; + + SET_PARM_VALUE(priv, info->parm, parent_index); + + return 0; +} + +static int meson_clk_set_parent(struct clk *clk, struct clk *parent_clk) +{ + return meson_mux_set_parent_by_id(clk, parent_clk->id); +} + +static struct clk_ops meson_clk_ops = { + .disable = meson_clk_disable, + .enable = meson_clk_enable, + .get_rate = meson_clk_get_rate, + .set_rate = meson_clk_set_rate, + .set_parent = meson_clk_set_parent, +}; + +static int meson_clk_probe(struct udevice *dev) +{ + struct meson_clk *priv = dev_get_priv(dev); + + return regmap_init_mem(dev_ofnode(dev), &priv->map); +} + +struct meson_clk_data meson_a1_peripherals_info = { + .clocks = meson_clocks, + .num_clocks = ARRAY_SIZE(meson_clocks), +}; + +struct meson_clk_data meson_a1_pll_info = { + .clocks = meson_pll_clocks, + .num_clocks = ARRAY_SIZE(meson_pll_clocks), +}; + +static const struct udevice_id meson_clk_ids[] = { + { + .compatible = "amlogic,a1-peripherals-clkc", + .data = (ulong)&meson_a1_peripherals_info, + }, + { + .compatible = "amlogic,a1-pll-clkc", + .data = (ulong)&meson_a1_pll_info, + }, + { } +}; + +U_BOOT_DRIVER(meson_clk) = { + .name = "meson-clk-a1", + .id = UCLASS_CLK, + .of_match = meson_clk_ids, + .priv_auto = sizeof(struct meson_clk), + .ops = &meson_clk_ops, + .probe = meson_clk_probe, +}; + +static const char *meson_clk_get_name(struct clk *clk, int id) +{ + const struct meson_clk_info *info; + + info = meson_clk_get_info(clk, id, MESON_CLK_ANY); + + return IS_ERR(info) ? "unknown" : info->name; +} + +static int meson_clk_dump(struct clk *clk) +{ + const struct meson_clk_info *info; + struct meson_clk *priv; + unsigned long rate; + char *state, frequency[80]; + int parent; + + priv = dev_get_priv(clk->dev); + + info = meson_clk_get_info(clk, clk->id, MESON_CLK_ANY); + if (IS_ERR(info) || !info->name) + return -EINVAL; + + rate = clk_get_rate(clk); + if (IS_ERR_VALUE(rate)) + sprintf(frequency, "unknown"); + else + sprintf(frequency, "%lu", rate); + + if (info->type == MESON_CLK_GATE) + state = GET_PARM_VALUE(priv, info->parm) ? "enabled" : "disabled"; + else + state = "N/A"; + + parent = meson_clk_get_parent(clk, clk->id); + printf("%15s%20s%20s%15s\n", + info->name, + frequency, + meson_clk_get_name(clk, parent), + state); + + return 0; +} + +static int meson_clk_dump_dev(struct udevice *dev) +{ + int i; + struct meson_clk_data *data; + const char *sep = "--------------------"; + + printf("%s:\n", dev->name); + printf("%.15s%s%s%.15s\n", sep, sep, sep, sep); + printf("%15s%20s%20s%15s\n", "clk", "frequency", "parent", "state"); + printf("%.15s%s%s%.15s\n", sep, sep, sep, sep); + + data = (struct meson_clk_data *)dev_get_driver_data(dev); + for (i = 0; i < data->num_clocks; i++) { + meson_clk_dump(&(struct clk){ + .dev = dev, + .id = i + }); + } + + return 0; +} + +int soc_clk_dump(void) +{ + struct udevice *dev; + int i = 0; + + while (!uclass_get_device(UCLASS_CLK, i++, &dev)) { + if (dev->driver == DM_DRIVER_GET(meson_clk)) { + meson_clk_dump_dev(dev); + printf("\n"); + } + } + + return 0; +} diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index 437a82cd48b..927d62cf99a 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -156,3 +156,12 @@ config CLK_R9A06G032 depends on CLK_RENESAS help Enable this to support the clocks on Renesas R9A06G032 SoC. + +config CLK_RZG2L + bool "Renesas RZ/G2L family clock support" + depends on CLK_RENESAS + select DM_RESET + +config CLK_R9A07G044 + bool "RZ/G2L (R9A07G044L) clock support" + depends on CLK_RZG2L diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index 48373e61b90..df7e225e9ca 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -23,3 +23,5 @@ obj-$(CONFIG_CLK_R8A779A0) += r8a779a0-cpg-mssr.o obj-$(CONFIG_CLK_R8A779F0) += r8a779f0-cpg-mssr.o obj-$(CONFIG_CLK_R8A779G0) += r8a779g0-cpg-mssr.o obj-$(CONFIG_CLK_R9A06G032) += r9a06g032-clocks.o +obj-$(CONFIG_CLK_RZG2L) += rzg2l-cpg.o +obj-$(CONFIG_CLK_R9A07G044) += r9a07g044-cpg.o diff --git a/drivers/clk/renesas/r9a07g044-cpg.c b/drivers/clk/renesas/r9a07g044-cpg.c new file mode 100644 index 00000000000..c8baad1a6c8 --- /dev/null +++ b/drivers/clk/renesas/r9a07g044-cpg.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G2L CPG driver + * + * Copyright (C) 2021-2023 Renesas Electronics Corp. + */ + +#include <dm/device.h> +#include <dt-bindings/clock/r9a07g044-cpg.h> +#include <linux/clk-provider.h> + +#include "rzg2l-cpg.h" + +/* Divider tables */ +static const struct clk_div_table dtable_1_8[] = { + {0, 1}, + {1, 2}, + {2, 4}, + {3, 8}, + {0, 0}, +}; + +static const struct clk_div_table dtable_1_32[] = { + {0, 1}, + {1, 2}, + {2, 4}, + {3, 8}, + {4, 32}, + {0, 0}, +}; + +static const struct clk_div_table dtable_16_128[] = { + {0, 16}, + {1, 32}, + {2, 64}, + {3, 128}, + {0, 0}, +}; + +/* Mux clock tables */ +static const char * const sel_pll3_3[] = { ".pll3_533", ".pll3_400" }; +static const char * const sel_pll5_4[] = { ".pll5_foutpostdiv", ".pll5_fout1ph0" }; +static const char * const sel_pll6_2[] = { ".pll6_250", ".pll5_250" }; +static const char * const sel_shdi[] = { ".clk_533", ".clk_400", ".clk_266" }; +static const char * const sel_gpu2[] = { ".pll6", ".pll3_div2_2" }; + +static const struct { + struct cpg_core_clk common[56]; +} core_clks = { + .common = { + /* External Clock Inputs */ + DEF_INPUT("extal", CLK_EXTAL), + + /* Internal Core Clocks */ + DEF_FIXED(".osc", R9A07G044_OSCCLK, CLK_EXTAL, 1, 1), + DEF_FIXED(".osc_div1000", CLK_OSC_DIV1000, CLK_EXTAL, 1, 1000), + DEF_SAMPLL(".pll1", CLK_PLL1, CLK_EXTAL, PLL146_CONF(0)), + DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3), + DEF_FIXED(".pll2_533", CLK_PLL2_533, CLK_PLL2, 1, 3), + DEF_FIXED(".pll3", CLK_PLL3, CLK_EXTAL, 200, 3), + DEF_FIXED(".pll3_400", CLK_PLL3_400, CLK_PLL3, 1, 4), + DEF_FIXED(".pll3_533", CLK_PLL3_533, CLK_PLL3, 1, 3), + + DEF_FIXED(".pll5", CLK_PLL5, CLK_EXTAL, 125, 1), + DEF_FIXED(".pll5_fout3", CLK_PLL5_FOUT3, CLK_PLL5, 1, 6), + + DEF_FIXED(".pll6", CLK_PLL6, CLK_EXTAL, 125, 6), + + DEF_FIXED(".pll2_div2", CLK_PLL2_DIV2, CLK_PLL2, 1, 2), + DEF_FIXED(".clk_800", CLK_PLL2_800, CLK_PLL2, 1, 2), + DEF_FIXED(".clk_533", CLK_PLL2_SDHI_533, CLK_PLL2, 1, 3), + DEF_FIXED(".clk_400", CLK_PLL2_SDHI_400, CLK_PLL2_800, 1, 2), + DEF_FIXED(".clk_266", CLK_PLL2_SDHI_266, CLK_PLL2_SDHI_533, 1, 2), + + DEF_FIXED(".pll2_div2_8", CLK_PLL2_DIV2_8, CLK_PLL2_DIV2, 1, 8), + DEF_FIXED(".pll2_div2_10", CLK_PLL2_DIV2_10, CLK_PLL2_DIV2, 1, 10), + + DEF_FIXED(".pll2_533_div2", CLK_PLL2_533_DIV2, CLK_PLL2_533, 1, 2), + + DEF_FIXED(".pll3_div2", CLK_PLL3_DIV2, CLK_PLL3, 1, 2), + DEF_FIXED(".pll3_div2_2", CLK_PLL3_DIV2_2, CLK_PLL3_DIV2, 1, 2), + DEF_FIXED(".pll3_div2_4", CLK_PLL3_DIV2_4, CLK_PLL3_DIV2, 1, 4), + DEF_FIXED(".pll3_div2_4_2", CLK_PLL3_DIV2_4_2, CLK_PLL3_DIV2_4, 1, 2), + DEF_MUX_RO(".sel_pll3_3", CLK_SEL_PLL3_3, SEL_PLL3_3, sel_pll3_3), + DEF_DIV("divpl3c", CLK_DIV_PLL3_C, CLK_SEL_PLL3_3, DIVPL3C, dtable_1_32), + + DEF_FIXED(".pll5_250", CLK_PLL5_250, CLK_PLL5_FOUT3, 1, 2), + DEF_FIXED(".pll6_250", CLK_PLL6_250, CLK_PLL6, 1, 2), + DEF_MUX_RO(".sel_gpu2", CLK_SEL_GPU2, SEL_GPU2, sel_gpu2), + DEF_PLL5_FOUTPOSTDIV(".pll5_foutpostdiv", CLK_PLL5_FOUTPOSTDIV, CLK_EXTAL), + DEF_FIXED(".pll5_fout1ph0", CLK_PLL5_FOUT1PH0, CLK_PLL5_FOUTPOSTDIV, 1, 2), + DEF_PLL5_4_MUX(".sel_pll5_4", CLK_SEL_PLL5_4, SEL_PLL5_4, sel_pll5_4), + DEF_DIV(".div_dsi_lpclk", CLK_DIV_DSI_LPCLK, CLK_PLL2_533_DIV2, + DIVDSILPCLK, dtable_16_128), + + /* Core output clk */ + DEF_DIV("I", R9A07G044_CLK_I, CLK_PLL1, DIVPL1A, dtable_1_8), + DEF_DIV("P0", R9A07G044_CLK_P0, CLK_PLL2_DIV2_8, DIVPL2A, dtable_1_32), + DEF_FIXED("P0_DIV2", R9A07G044_CLK_P0_DIV2, R9A07G044_CLK_P0, 1, 2), + DEF_FIXED("TSU", R9A07G044_CLK_TSU, CLK_PLL2_DIV2_10, 1, 1), + DEF_DIV("P1", R9A07G044_CLK_P1, CLK_PLL3_DIV2_4, DIVPL3B, dtable_1_32), + DEF_FIXED("P1_DIV2", CLK_P1_DIV2, R9A07G044_CLK_P1, 1, 2), + DEF_DIV("P2", R9A07G044_CLK_P2, CLK_PLL3_DIV2_4_2, DIVPL3A, dtable_1_32), + DEF_FIXED("M0", R9A07G044_CLK_M0, CLK_PLL3_DIV2_4, 1, 1), + DEF_FIXED("ZT", R9A07G044_CLK_ZT, CLK_PLL3_DIV2_4_2, 1, 1), + DEF_MUX("HP", R9A07G044_CLK_HP, SEL_PLL6_2, sel_pll6_2), + DEF_FIXED("SPI0", R9A07G044_CLK_SPI0, CLK_DIV_PLL3_C, 1, 2), + DEF_FIXED("SPI1", R9A07G044_CLK_SPI1, CLK_DIV_PLL3_C, 1, 4), + DEF_SD_MUX("SD0", R9A07G044_CLK_SD0, SEL_SDHI0, sel_shdi), + DEF_SD_MUX("SD1", R9A07G044_CLK_SD1, SEL_SDHI1, sel_shdi), + DEF_FIXED("SD0_DIV4", CLK_SD0_DIV4, R9A07G044_CLK_SD0, 1, 4), + DEF_FIXED("SD1_DIV4", CLK_SD1_DIV4, R9A07G044_CLK_SD1, 1, 4), + DEF_DIV("G", R9A07G044_CLK_G, CLK_SEL_GPU2, DIVGPU, dtable_1_8), + DEF_FIXED("M1", R9A07G044_CLK_M1, CLK_PLL5_FOUTPOSTDIV, 1, 1), + DEF_FIXED("M2", R9A07G044_CLK_M2, CLK_PLL3_533, 1, 2), + DEF_FIXED("M2_DIV2", CLK_M2_DIV2, R9A07G044_CLK_M2, 1, 2), + DEF_DSI_DIV("DSI_DIV", CLK_DSI_DIV, CLK_SEL_PLL5_4, CLK_SET_RATE_PARENT), + DEF_FIXED("M3", R9A07G044_CLK_M3, CLK_DSI_DIV, 1, 1), + DEF_FIXED("M4", R9A07G044_CLK_M4, CLK_DIV_DSI_LPCLK, 1, 1), + }, +}; + +static const struct { + struct rzg2l_mod_clk common[79]; +} mod_clks = { + .common = { + DEF_MOD("gic", R9A07G044_GIC600_GICCLK, R9A07G044_CLK_P1, + 0x514, 0), + DEF_MOD("ia55_pclk", R9A07G044_IA55_PCLK, R9A07G044_CLK_P2, + 0x518, 0), + DEF_MOD("ia55_clk", R9A07G044_IA55_CLK, R9A07G044_CLK_P1, + 0x518, 1), + DEF_MOD("dmac_aclk", R9A07G044_DMAC_ACLK, R9A07G044_CLK_P1, + 0x52c, 0), + DEF_MOD("dmac_pclk", R9A07G044_DMAC_PCLK, CLK_P1_DIV2, + 0x52c, 1), + DEF_MOD("ostm0_pclk", R9A07G044_OSTM0_PCLK, R9A07G044_CLK_P0, + 0x534, 0), + DEF_MOD("ostm1_pclk", R9A07G044_OSTM1_PCLK, R9A07G044_CLK_P0, + 0x534, 1), + DEF_MOD("ostm2_pclk", R9A07G044_OSTM2_PCLK, R9A07G044_CLK_P0, + 0x534, 2), + DEF_MOD("mtu_x_mck", R9A07G044_MTU_X_MCK_MTU3, R9A07G044_CLK_P0, + 0x538, 0), + DEF_MOD("gpt_pclk", R9A07G044_GPT_PCLK, R9A07G044_CLK_P0, + 0x540, 0), + DEF_MOD("poeg_a_clkp", R9A07G044_POEG_A_CLKP, R9A07G044_CLK_P0, + 0x544, 0), + DEF_MOD("poeg_b_clkp", R9A07G044_POEG_B_CLKP, R9A07G044_CLK_P0, + 0x544, 1), + DEF_MOD("poeg_c_clkp", R9A07G044_POEG_C_CLKP, R9A07G044_CLK_P0, + 0x544, 2), + DEF_MOD("poeg_d_clkp", R9A07G044_POEG_D_CLKP, R9A07G044_CLK_P0, + 0x544, 3), + DEF_MOD("wdt0_pclk", R9A07G044_WDT0_PCLK, R9A07G044_CLK_P0, + 0x548, 0), + DEF_MOD("wdt0_clk", R9A07G044_WDT0_CLK, R9A07G044_OSCCLK, + 0x548, 1), + DEF_MOD("wdt1_pclk", R9A07G044_WDT1_PCLK, R9A07G044_CLK_P0, + 0x548, 2), + DEF_MOD("wdt1_clk", R9A07G044_WDT1_CLK, R9A07G044_OSCCLK, + 0x548, 3), + DEF_MOD("spi_clk2", R9A07G044_SPI_CLK2, R9A07G044_CLK_SPI1, + 0x550, 0), + DEF_MOD("spi_clk", R9A07G044_SPI_CLK, R9A07G044_CLK_SPI0, + 0x550, 1), + DEF_MOD("sdhi0_imclk", R9A07G044_SDHI0_IMCLK, CLK_SD0_DIV4, + 0x554, 0), + DEF_MOD("sdhi0_imclk2", R9A07G044_SDHI0_IMCLK2, CLK_SD0_DIV4, + 0x554, 1), + DEF_MOD("sdhi0_clk_hs", R9A07G044_SDHI0_CLK_HS, R9A07G044_CLK_SD0, + 0x554, 2), + DEF_MOD("sdhi0_aclk", R9A07G044_SDHI0_ACLK, R9A07G044_CLK_P1, + 0x554, 3), + DEF_MOD("sdhi1_imclk", R9A07G044_SDHI1_IMCLK, CLK_SD1_DIV4, + 0x554, 4), + DEF_MOD("sdhi1_imclk2", R9A07G044_SDHI1_IMCLK2, CLK_SD1_DIV4, + 0x554, 5), + DEF_MOD("sdhi1_clk_hs", R9A07G044_SDHI1_CLK_HS, R9A07G044_CLK_SD1, + 0x554, 6), + DEF_MOD("sdhi1_aclk", R9A07G044_SDHI1_ACLK, R9A07G044_CLK_P1, + 0x554, 7), + DEF_MOD("gpu_clk", R9A07G044_GPU_CLK, R9A07G044_CLK_G, + 0x558, 0), + DEF_MOD("gpu_axi_clk", R9A07G044_GPU_AXI_CLK, R9A07G044_CLK_P1, + 0x558, 1), + DEF_MOD("gpu_ace_clk", R9A07G044_GPU_ACE_CLK, R9A07G044_CLK_P1, + 0x558, 2), + DEF_MOD("cru_sysclk", R9A07G044_CRU_SYSCLK, CLK_M2_DIV2, + 0x564, 0), + DEF_MOD("cru_vclk", R9A07G044_CRU_VCLK, R9A07G044_CLK_M2, + 0x564, 1), + DEF_MOD("cru_pclk", R9A07G044_CRU_PCLK, R9A07G044_CLK_ZT, + 0x564, 2), + DEF_MOD("cru_aclk", R9A07G044_CRU_ACLK, R9A07G044_CLK_M0, + 0x564, 3), + DEF_MOD("dsi_pll_clk", R9A07G044_MIPI_DSI_PLLCLK, R9A07G044_CLK_M1, + 0x568, 0), + DEF_MOD("dsi_sys_clk", R9A07G044_MIPI_DSI_SYSCLK, CLK_M2_DIV2, + 0x568, 1), + DEF_MOD("dsi_aclk", R9A07G044_MIPI_DSI_ACLK, R9A07G044_CLK_P1, + 0x568, 2), + DEF_MOD("dsi_pclk", R9A07G044_MIPI_DSI_PCLK, R9A07G044_CLK_P2, + 0x568, 3), + DEF_MOD("dsi_vclk", R9A07G044_MIPI_DSI_VCLK, R9A07G044_CLK_M3, + 0x568, 4), + DEF_MOD("dsi_lpclk", R9A07G044_MIPI_DSI_LPCLK, R9A07G044_CLK_M4, + 0x568, 5), + DEF_COUPLED("lcdc_a", R9A07G044_LCDC_CLK_A, R9A07G044_CLK_M0, + 0x56c, 0), + DEF_COUPLED("lcdc_p", R9A07G044_LCDC_CLK_P, R9A07G044_CLK_ZT, + 0x56c, 0), + DEF_MOD("lcdc_clk_d", R9A07G044_LCDC_CLK_D, R9A07G044_CLK_M3, + 0x56c, 1), + DEF_MOD("ssi0_pclk", R9A07G044_SSI0_PCLK2, R9A07G044_CLK_P0, + 0x570, 0), + DEF_MOD("ssi0_sfr", R9A07G044_SSI0_PCLK_SFR, R9A07G044_CLK_P0, + 0x570, 1), + DEF_MOD("ssi1_pclk", R9A07G044_SSI1_PCLK2, R9A07G044_CLK_P0, + 0x570, 2), + DEF_MOD("ssi1_sfr", R9A07G044_SSI1_PCLK_SFR, R9A07G044_CLK_P0, + 0x570, 3), + DEF_MOD("ssi2_pclk", R9A07G044_SSI2_PCLK2, R9A07G044_CLK_P0, + 0x570, 4), + DEF_MOD("ssi2_sfr", R9A07G044_SSI2_PCLK_SFR, R9A07G044_CLK_P0, + 0x570, 5), + DEF_MOD("ssi3_pclk", R9A07G044_SSI3_PCLK2, R9A07G044_CLK_P0, + 0x570, 6), + DEF_MOD("ssi3_sfr", R9A07G044_SSI3_PCLK_SFR, R9A07G044_CLK_P0, + 0x570, 7), + DEF_MOD("usb0_host", R9A07G044_USB_U2H0_HCLK, R9A07G044_CLK_P1, + 0x578, 0), + DEF_MOD("usb1_host", R9A07G044_USB_U2H1_HCLK, R9A07G044_CLK_P1, + 0x578, 1), + DEF_MOD("usb0_func", R9A07G044_USB_U2P_EXR_CPUCLK, R9A07G044_CLK_P1, + 0x578, 2), + DEF_MOD("usb_pclk", R9A07G044_USB_PCLK, R9A07G044_CLK_P1, + 0x578, 3), + DEF_COUPLED("eth0_axi", R9A07G044_ETH0_CLK_AXI, R9A07G044_CLK_M0, + 0x57c, 0), + DEF_COUPLED("eth0_chi", R9A07G044_ETH0_CLK_CHI, R9A07G044_CLK_ZT, + 0x57c, 0), + DEF_COUPLED("eth1_axi", R9A07G044_ETH1_CLK_AXI, R9A07G044_CLK_M0, + 0x57c, 1), + DEF_COUPLED("eth1_chi", R9A07G044_ETH1_CLK_CHI, R9A07G044_CLK_ZT, + 0x57c, 1), + DEF_MOD("i2c0", R9A07G044_I2C0_PCLK, R9A07G044_CLK_P0, + 0x580, 0), + DEF_MOD("i2c1", R9A07G044_I2C1_PCLK, R9A07G044_CLK_P0, + 0x580, 1), + DEF_MOD("i2c2", R9A07G044_I2C2_PCLK, R9A07G044_CLK_P0, + 0x580, 2), + DEF_MOD("i2c3", R9A07G044_I2C3_PCLK, R9A07G044_CLK_P0, + 0x580, 3), + DEF_MOD("scif0", R9A07G044_SCIF0_CLK_PCK, R9A07G044_CLK_P0, + 0x584, 0), + DEF_MOD("scif1", R9A07G044_SCIF1_CLK_PCK, R9A07G044_CLK_P0, + 0x584, 1), + DEF_MOD("scif2", R9A07G044_SCIF2_CLK_PCK, R9A07G044_CLK_P0, + 0x584, 2), + DEF_MOD("scif3", R9A07G044_SCIF3_CLK_PCK, R9A07G044_CLK_P0, + 0x584, 3), + DEF_MOD("scif4", R9A07G044_SCIF4_CLK_PCK, R9A07G044_CLK_P0, + 0x584, 4), + DEF_MOD("sci0", R9A07G044_SCI0_CLKP, R9A07G044_CLK_P0, + 0x588, 0), + DEF_MOD("sci1", R9A07G044_SCI1_CLKP, R9A07G044_CLK_P0, + 0x588, 1), + DEF_MOD("rspi0", R9A07G044_RSPI0_CLKB, R9A07G044_CLK_P0, + 0x590, 0), + DEF_MOD("rspi1", R9A07G044_RSPI1_CLKB, R9A07G044_CLK_P0, + 0x590, 1), + DEF_MOD("rspi2", R9A07G044_RSPI2_CLKB, R9A07G044_CLK_P0, + 0x590, 2), + DEF_MOD("canfd", R9A07G044_CANFD_PCLK, R9A07G044_CLK_P0, + 0x594, 0), + DEF_MOD("gpio", R9A07G044_GPIO_HCLK, R9A07G044_OSCCLK, + 0x598, 0), + DEF_MOD("adc_adclk", R9A07G044_ADC_ADCLK, R9A07G044_CLK_TSU, + 0x5a8, 0), + DEF_MOD("adc_pclk", R9A07G044_ADC_PCLK, R9A07G044_CLK_P0, + 0x5a8, 1), + DEF_MOD("tsu_pclk", R9A07G044_TSU_PCLK, R9A07G044_CLK_TSU, + 0x5ac, 0), + }, +}; + +static const struct rzg2l_reset r9a07g044_resets[] = { + DEF_RST(R9A07G044_GIC600_GICRESET_N, 0x814, 0), + DEF_RST(R9A07G044_GIC600_DBG_GICRESET_N, 0x814, 1), + DEF_RST(R9A07G044_IA55_RESETN, 0x818, 0), + DEF_RST(R9A07G044_DMAC_ARESETN, 0x82c, 0), + DEF_RST(R9A07G044_DMAC_RST_ASYNC, 0x82c, 1), + DEF_RST(R9A07G044_OSTM0_PRESETZ, 0x834, 0), + DEF_RST(R9A07G044_OSTM1_PRESETZ, 0x834, 1), + DEF_RST(R9A07G044_OSTM2_PRESETZ, 0x834, 2), + DEF_RST(R9A07G044_MTU_X_PRESET_MTU3, 0x838, 0), + DEF_RST(R9A07G044_GPT_RST_C, 0x840, 0), + DEF_RST(R9A07G044_POEG_A_RST, 0x844, 0), + DEF_RST(R9A07G044_POEG_B_RST, 0x844, 1), + DEF_RST(R9A07G044_POEG_C_RST, 0x844, 2), + DEF_RST(R9A07G044_POEG_D_RST, 0x844, 3), + DEF_RST(R9A07G044_WDT0_PRESETN, 0x848, 0), + DEF_RST(R9A07G044_WDT1_PRESETN, 0x848, 1), + DEF_RST(R9A07G044_SPI_RST, 0x850, 0), + DEF_RST(R9A07G044_SDHI0_IXRST, 0x854, 0), + DEF_RST(R9A07G044_SDHI1_IXRST, 0x854, 1), + DEF_RST(R9A07G044_GPU_RESETN, 0x858, 0), + DEF_RST(R9A07G044_GPU_AXI_RESETN, 0x858, 1), + DEF_RST(R9A07G044_GPU_ACE_RESETN, 0x858, 2), + DEF_RST(R9A07G044_CRU_CMN_RSTB, 0x864, 0), + DEF_RST(R9A07G044_CRU_PRESETN, 0x864, 1), + DEF_RST(R9A07G044_CRU_ARESETN, 0x864, 2), + DEF_RST(R9A07G044_MIPI_DSI_CMN_RSTB, 0x868, 0), + DEF_RST(R9A07G044_MIPI_DSI_ARESET_N, 0x868, 1), + DEF_RST(R9A07G044_MIPI_DSI_PRESET_N, 0x868, 2), + DEF_RST(R9A07G044_LCDC_RESET_N, 0x86c, 0), + DEF_RST(R9A07G044_SSI0_RST_M2_REG, 0x870, 0), + DEF_RST(R9A07G044_SSI1_RST_M2_REG, 0x870, 1), + DEF_RST(R9A07G044_SSI2_RST_M2_REG, 0x870, 2), + DEF_RST(R9A07G044_SSI3_RST_M2_REG, 0x870, 3), + DEF_RST(R9A07G044_USB_U2H0_HRESETN, 0x878, 0), + DEF_RST(R9A07G044_USB_U2H1_HRESETN, 0x878, 1), + DEF_RST(R9A07G044_USB_U2P_EXL_SYSRST, 0x878, 2), + DEF_RST(R9A07G044_USB_PRESETN, 0x878, 3), + DEF_RST(R9A07G044_ETH0_RST_HW_N, 0x87c, 0), + DEF_RST(R9A07G044_ETH1_RST_HW_N, 0x87c, 1), + DEF_RST(R9A07G044_I2C0_MRST, 0x880, 0), + DEF_RST(R9A07G044_I2C1_MRST, 0x880, 1), + DEF_RST(R9A07G044_I2C2_MRST, 0x880, 2), + DEF_RST(R9A07G044_I2C3_MRST, 0x880, 3), + DEF_RST(R9A07G044_SCIF0_RST_SYSTEM_N, 0x884, 0), + DEF_RST(R9A07G044_SCIF1_RST_SYSTEM_N, 0x884, 1), + DEF_RST(R9A07G044_SCIF2_RST_SYSTEM_N, 0x884, 2), + DEF_RST(R9A07G044_SCIF3_RST_SYSTEM_N, 0x884, 3), + DEF_RST(R9A07G044_SCIF4_RST_SYSTEM_N, 0x884, 4), + DEF_RST(R9A07G044_SCI0_RST, 0x888, 0), + DEF_RST(R9A07G044_SCI1_RST, 0x888, 1), + DEF_RST(R9A07G044_RSPI0_RST, 0x890, 0), + DEF_RST(R9A07G044_RSPI1_RST, 0x890, 1), + DEF_RST(R9A07G044_RSPI2_RST, 0x890, 2), + DEF_RST(R9A07G044_CANFD_RSTP_N, 0x894, 0), + DEF_RST(R9A07G044_CANFD_RSTC_N, 0x894, 1), + DEF_RST(R9A07G044_GPIO_RSTN, 0x898, 0), + DEF_RST(R9A07G044_GPIO_PORT_RESETN, 0x898, 1), + DEF_RST(R9A07G044_GPIO_SPARE_RESETN, 0x898, 2), + DEF_RST(R9A07G044_ADC_PRESETN, 0x8a8, 0), + DEF_RST(R9A07G044_ADC_ADRST_N, 0x8a8, 1), + DEF_RST(R9A07G044_TSU_PRESETN, 0x8ac, 0), +}; + +const struct rzg2l_cpg_info r9a07g044_cpg_info = { + /* Core Clocks */ + .core_clks = core_clks.common, + .num_core_clks = ARRAY_SIZE(core_clks.common), + + /* Module Clocks */ + .mod_clks = mod_clks.common, + .num_mod_clks = ARRAY_SIZE(mod_clks.common), + .num_hw_mod_clks = R9A07G044_TSU_PCLK + 1, + + /* Resets */ + .resets = r9a07g044_resets, + .num_resets = R9A07G044_TSU_PRESETN + 1, /* Last reset ID + 1 */ + + .has_clk_mon_regs = true, +}; + +static const struct udevice_id r9a07g044_cpg_ids[] = { + { + .compatible = "renesas,r9a07g044-cpg", + .data = (unsigned long)&r9a07g044_cpg_info, + }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(r9a07g044_cpg) = { + .name = "r9a07g044-cpg", + .id = UCLASS_NOP, + .of_match = r9a07g044_cpg_ids, + .bind = rzg2l_cpg_bind, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c new file mode 100644 index 00000000000..e54508c35ce --- /dev/null +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -0,0 +1,504 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G2L Clock Pulse Generator + * + * Copyright (C) 2021-2023 Renesas Electronics Corp. + * + * Based on renesas-cpg-mssr.c + * + * Copyright (C) 2015 Glider bvba + * Copyright (C) 2013 Ideas On Board SPRL + * Copyright (C) 2015 Renesas Electronics Corp. + */ + +#include <asm/io.h> +#include <clk-uclass.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <dm/lists.h> +#include <dt-bindings/clock/renesas-cpg-mssr.h> +#include <linux/clk-provider.h> +#include <linux/iopoll.h> +#include <reset-uclass.h> +#include <reset.h> + +#include "rzg2l-cpg.h" + +#define CLK_MON_R(reg) (0x180 + (reg)) + +static ulong rzg2l_cpg_clk_get_rate_by_id(struct udevice *dev, unsigned int id); +static ulong rzg2l_cpg_clk_get_rate_by_name(struct udevice *dev, const char *name); + +struct rzg2l_cpg_data { + void __iomem *base; + struct rzg2l_cpg_info *info; +}; + +/* + * The top 16 bits of the clock ID are used to identify if it is a core clock or + * a module clock. + */ +#define CPG_CLK_TYPE_SHIFT 16 +#define CPG_CLK_ID_MASK 0xffff +#define CPG_CLK_ID(x) ((x) & CPG_CLK_ID_MASK) +#define CPG_CLK_PACK(type, id) (((type) << CPG_CLK_TYPE_SHIFT) | CPG_CLK_ID(id)) + +static inline bool is_mod_clk(unsigned int id) +{ + return (id >> CPG_CLK_TYPE_SHIFT) == CPG_MOD; +} + +static int rzg2l_cpg_clk_set(struct clk *clk, bool enable) +{ + struct rzg2l_cpg_data *data = + (struct rzg2l_cpg_data *)dev_get_driver_data(clk->dev); + const unsigned int cpg_clk_id = CPG_CLK_ID(clk->id); + const struct rzg2l_mod_clk *mod_clk = NULL; + u32 value; + unsigned int i; + + dev_dbg(clk->dev, "%s %s clock %u\n", enable ? "enable" : "disable", + is_mod_clk(clk->id) ? "module" : "core", cpg_clk_id); + if (!is_mod_clk(clk->id)) { + dev_err(clk->dev, "ID %lu is not a module clock\n", clk->id); + return -EINVAL; + } + + for (i = 0; i < data->info->num_mod_clks; i++) { + if (data->info->mod_clks[i].id == cpg_clk_id) { + mod_clk = &data->info->mod_clks[i]; + break; + } + } + + if (!mod_clk) { + dev_err(clk->dev, "Module clock %u not found\n", cpg_clk_id); + return -ENODEV; + } + + value = BIT(mod_clk->bit) << 16; + if (enable) + value |= BIT(mod_clk->bit); + writel(value, data->base + mod_clk->off); + + if (enable && readl_poll_timeout(data->base + CLK_MON_R(mod_clk->off), + value, (value & BIT(mod_clk->bit)), + 10)) { + dev_err(clk->dev, "Timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int rzg2l_cpg_clk_enable(struct clk *clk) +{ + return rzg2l_cpg_clk_set(clk, true); +} + +static int rzg2l_cpg_clk_disable(struct clk *clk) +{ + return rzg2l_cpg_clk_set(clk, false); +} + +static int rzg2l_cpg_clk_of_xlate(struct clk *clk, + struct ofnode_phandle_args *args) +{ + struct rzg2l_cpg_data *data = + (struct rzg2l_cpg_data *)dev_get_driver_data(clk->dev); + u32 cpg_clk_type, cpg_clk_id; + bool found = false; + unsigned int i; + + if (args->args_count != 2) { + dev_dbg(clk->dev, "Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + cpg_clk_type = args->args[0]; + cpg_clk_id = args->args[1]; + + switch (cpg_clk_type) { + case CPG_CORE: + for (i = 0; i < data->info->num_core_clks; i++) { + if (data->info->core_clks[i].id == cpg_clk_id) { + found = true; + break; + } + } + if (!found) { + dev_dbg(clk->dev, + "Invalid second argument %u: Must be a valid core clock ID\n", + cpg_clk_id); + return -EINVAL; + } + break; + case CPG_MOD: + for (i = 0; i < data->info->num_mod_clks; i++) { + if (data->info->mod_clks[i].id == cpg_clk_id) { + found = true; + break; + } + } + if (!found) { + dev_dbg(clk->dev, + "Invalid second argument %u: Must be a valid module clock ID\n", + cpg_clk_id); + return -EINVAL; + } + break; + default: + dev_dbg(clk->dev, + "Invalid first argument %u: Must be CPG_CORE or CPG_MOD\n", + cpg_clk_type); + return -EINVAL; + } + + clk->id = CPG_CLK_PACK(cpg_clk_type, cpg_clk_id); + + return 0; +} + +static ulong rzg2l_sdhi_clk_get_rate(struct udevice *dev, const struct cpg_core_clk *cc) +{ + struct rzg2l_cpg_data *data = + (struct rzg2l_cpg_data *)dev_get_driver_data(dev); + const ulong offset = CPG_CONF_OFFSET(cc->conf); + const int shift = CPG_CONF_BITPOS(cc->conf); + const u32 mask = CPG_CONF_BITMASK(cc->conf); + unsigned int sel; + + sel = (readl(data->base + offset) >> shift) & mask; + + if (!sel || sel > cc->num_parents) { + dev_err(dev, "Invalid SEL_SDHI%d_SET value %u\n", shift / 4, sel); + return -EIO; + } + return rzg2l_cpg_clk_get_rate_by_name(dev, cc->parent_names[sel - 1]); +} + +static ulong rzg2l_div_clk_get_rate(struct udevice *dev, const struct cpg_core_clk *cc) +{ + struct rzg2l_cpg_data *data = + (struct rzg2l_cpg_data *)dev_get_driver_data(dev); + const ulong offset = CPG_CONF_OFFSET(cc->conf); + const int shift = CPG_CONF_BITPOS(cc->conf); + const u32 mask = CPG_CONF_BITMASK(cc->conf); + unsigned int sel, i; + + sel = (readl(data->base + offset) >> shift) & mask; + + for (i = 0; cc->dtable[i].div; i++) { + if (cc->dtable[i].val == sel) + return rzg2l_cpg_clk_get_rate_by_id(dev, cc->parent) / cc->dtable[i].div; + } + dev_err(dev, "Invalid selector value %u for clock %s\n", sel, cc->name); + return -EINVAL; +} + +static ulong rzg2l_core_clk_get_rate(struct udevice *dev, const struct cpg_core_clk *cc) +{ + switch (cc->type) { + case CLK_TYPE_FF: + const ulong parent_rate = rzg2l_cpg_clk_get_rate_by_id(dev, cc->parent); + return parent_rate * cc->mult / cc->div; + case CLK_TYPE_IN: + struct clk clk_in; + clk_get_by_name(dev, cc->name, &clk_in); + return clk_get_rate(&clk_in); + case CLK_TYPE_SD_MUX: + return rzg2l_sdhi_clk_get_rate(dev, cc); + case CLK_TYPE_DIV: + return rzg2l_div_clk_get_rate(dev, cc); + default: + dev_err(dev, "get_rate needed for clock %u, type %d\n", cc->id, cc->type); + return -ENOSYS; + } +} + +static ulong rzg2l_cpg_clk_get_rate_by_id(struct udevice *dev, unsigned int id) +{ + struct rzg2l_cpg_data *data = + (struct rzg2l_cpg_data *)dev_get_driver_data(dev); + const unsigned int cpg_clk_id = CPG_CLK_ID(id); + unsigned int i; + + if (is_mod_clk(id)) { + for (i = 0; i < data->info->num_mod_clks; i++) { + if (data->info->mod_clks[i].id == cpg_clk_id) + return rzg2l_cpg_clk_get_rate_by_id(dev, + data->info->mod_clks[i].parent); + } + + dev_err(dev, "Module clock ID %u not found\n", cpg_clk_id); + return -ENODEV; + } + + for (i = 0; i < data->info->num_core_clks; i++) { + if (data->info->core_clks[i].id == cpg_clk_id) + return rzg2l_core_clk_get_rate(dev, &data->info->core_clks[i]); + } + + dev_err(dev, "Core clock ID %u not found\n", cpg_clk_id); + return -ENODEV; +} + +static ulong rzg2l_cpg_clk_get_rate_by_name(struct udevice *dev, const char *name) +{ + struct rzg2l_cpg_data *data = + (struct rzg2l_cpg_data *)dev_get_driver_data(dev); + unsigned int i; + + for (i = 0; i < data->info->num_mod_clks; i++) { + if (!strcmp(name, data->info->mod_clks[i].name)) + return rzg2l_cpg_clk_get_rate_by_id(dev, data->info->mod_clks[i].parent); + } + for (i = 0; i < data->info->num_core_clks; i++) { + if (!strcmp(name, data->info->core_clks[i].name)) + return rzg2l_core_clk_get_rate(dev, &data->info->core_clks[i]); + } + + dev_err(dev, "Clock name %s not found\n", name); + return -EINVAL; +} + +static ulong rzg2l_cpg_clk_get_rate(struct clk *clk) +{ + return rzg2l_cpg_clk_get_rate_by_id(clk->dev, clk->id); +} + +static ulong rzg2l_sdhi_clk_set_rate(struct udevice *dev, const struct cpg_core_clk *cc, ulong rate) +{ + struct rzg2l_cpg_data *data = + (struct rzg2l_cpg_data *)dev_get_driver_data(dev); + const ulong offset = CPG_CONF_OFFSET(cc->conf); + const int shift = CPG_CONF_BITPOS(cc->conf); + int channel, new_sel, prev_sel; + ulong target_rate; + unsigned int i; + u32 value; + + prev_sel = (readl(data->base + offset) >> shift) & 0x3; + channel = shift / 4; + + /* + * Round the requested rate down, unless it is below the minimum + * supported rate. Assume that the parent clock names are listed in + * order of descending rate. + */ + for (i = 0; i < cc->num_parents; i++) { + target_rate = rzg2l_cpg_clk_get_rate_by_name(dev, cc->parent_names[i]); + if (rate >= target_rate) { + new_sel = i + 1; + break; + } + } + if (!new_sel) + new_sel = cc->num_parents - 1; + + if (new_sel == prev_sel) + return target_rate; + dev_dbg(dev, "sdhi set_rate rate=%lu target_rate=%lu sel=%d\n", + rate, target_rate, new_sel); + + /* + * As per the HW manual, we should not directly switch from 533 MHz to + * 400 MHz and vice versa. To change the setting from 2’b01 (533 MHz) + * to 2’b10 (400 MHz) or vice versa, Switch to 2’b11 (266 MHz) first, + * and then switch to the target setting (2’b01 (533 MHz) or 2’b10 + * (400 MHz)). + */ + if (new_sel != SEL_SDHI_266MHz && prev_sel != SEL_SDHI_266MHz) { + u32 waitbit; + int ret; + + dev_dbg(dev, "sdhi set_rate via 266MHz\n"); + value = (SEL_SDHI_WRITE_ENABLE | SEL_SDHI_266MHz) << shift; + writel(value, data->base + offset); + + /* Wait for the switch to complete. */ + waitbit = channel ? CPG_CLKSTATUS_SELSDHI1_STS : CPG_CLKSTATUS_SELSDHI0_STS; + ret = readl_poll_timeout(data->base + CPG_CLKSTATUS, value, + !(value & waitbit), + CPG_SDHI_CLK_SWITCH_STATUS_TIMEOUT_US); + if (ret) { + dev_err(dev, "Failed to switch SDHI%d clock source\n", channel); + return -EIO; + } + } + + value = (SEL_SDHI_WRITE_ENABLE | new_sel) << shift; + writel(value, data->base + offset); + + return target_rate; +} + +static ulong rzg2l_core_clk_set_rate(struct udevice *dev, const struct cpg_core_clk *cc, ulong rate) +{ + if (cc->type == CLK_TYPE_SD_MUX) + return rzg2l_sdhi_clk_set_rate(dev, cc, rate); + + /* + * The sdhi driver calls clk_set_rate for SD0_DIV4 and SD1_DIV4, even + * though they're in a fixed relationship with SD0 and SD1 respectively. + * To allow the driver to proceed, simply return the current rates + * without making any change. + */ + if (cc->id == CLK_SD0_DIV4 || cc->id == CLK_SD1_DIV4) + return rzg2l_core_clk_get_rate(dev, cc); + + dev_err(dev, "set_rate needed for clock %u, type %d\n", cc->id, cc->type); + return -ENOSYS; +} + +static ulong rzg2l_cpg_clk_set_rate_by_id(struct udevice *dev, unsigned int id, ulong rate) +{ + struct rzg2l_cpg_data *data = + (struct rzg2l_cpg_data *)dev_get_driver_data(dev); + const unsigned int cpg_clk_id = CPG_CLK_ID(id); + unsigned int i; + + if (is_mod_clk(id)) { + for (i = 0; i < data->info->num_mod_clks; i++) { + if (data->info->mod_clks[i].id == cpg_clk_id) + return rzg2l_cpg_clk_set_rate_by_id(dev, + data->info->mod_clks[i].parent, + rate); + } + + dev_err(dev, "Module clock ID %u not found\n", cpg_clk_id); + return -ENODEV; + } + + for (i = 0; i < data->info->num_core_clks; i++) { + if (data->info->core_clks[i].id == cpg_clk_id) + return rzg2l_core_clk_set_rate(dev, &data->info->core_clks[i], rate); + } + + dev_err(dev, "Core clock ID %u not found\n", cpg_clk_id); + return -ENODEV; +} + +static ulong rzg2l_cpg_clk_set_rate(struct clk *clk, ulong rate) +{ + return rzg2l_cpg_clk_set_rate_by_id(clk->dev, clk->id, rate); +} + +static const struct clk_ops rzg2l_cpg_clk_ops = { + .enable = rzg2l_cpg_clk_enable, + .disable = rzg2l_cpg_clk_disable, + .of_xlate = rzg2l_cpg_clk_of_xlate, + .get_rate = rzg2l_cpg_clk_get_rate, + .set_rate = rzg2l_cpg_clk_set_rate, +}; + +U_BOOT_DRIVER(rzg2l_cpg_clk) = { + .name = "rzg2l-cpg-clk", + .id = UCLASS_CLK, + .ops = &rzg2l_cpg_clk_ops, + .flags = DM_FLAG_VITAL, +}; + +static int rzg2l_cpg_rst_set(struct reset_ctl *reset_ctl, bool asserted) +{ + struct rzg2l_cpg_data *data = + (struct rzg2l_cpg_data *)dev_get_driver_data(reset_ctl->dev); + const struct rzg2l_reset *rst; + u32 value; + + dev_dbg(reset_ctl->dev, "%s %lu\n", asserted ? "assert" : "deassert", reset_ctl->id); + if (reset_ctl->id >= data->info->num_resets) { + dev_err(reset_ctl->dev, "Invalid reset id %lu\n", reset_ctl->id); + return -EINVAL; + } + rst = &data->info->resets[reset_ctl->id]; + + value = BIT(rst->bit) << 16; + if (!asserted) + value |= BIT(rst->bit); + writel(value, data->base + rst->off); + + return 0; +} + +static int rzg2l_cpg_rst_assert(struct reset_ctl *reset_ctl) +{ + return rzg2l_cpg_rst_set(reset_ctl, true); +} + +static int rzg2l_cpg_rst_deassert(struct reset_ctl *reset_ctl) +{ + return rzg2l_cpg_rst_set(reset_ctl, false); +} + +static int rzg2l_cpg_rst_of_xlate(struct reset_ctl *reset_ctl, + struct ofnode_phandle_args *args) +{ + struct rzg2l_cpg_data *data = + (struct rzg2l_cpg_data *)dev_get_driver_data(reset_ctl->dev); + + if (args->args[0] >= data->info->num_resets) + return -EINVAL; + + reset_ctl->id = args->args[0]; + return 0; +} + +static const struct reset_ops rzg2l_cpg_rst_ops = { + .rst_assert = rzg2l_cpg_rst_assert, + .rst_deassert = rzg2l_cpg_rst_deassert, + .of_xlate = rzg2l_cpg_rst_of_xlate, +}; + +U_BOOT_DRIVER(rzg2l_cpg_rst) = { + .name = "rzg2l-cpg-rst", + .id = UCLASS_RESET, + .ops = &rzg2l_cpg_rst_ops, + .flags = DM_FLAG_VITAL, +}; + +int rzg2l_cpg_bind(struct udevice *parent) +{ + struct udevice *cdev, *rdev; + struct rzg2l_cpg_data *data; + struct driver *drv; + int ret; + + data = devm_kmalloc(parent, sizeof(*data), 0); + if (!data) + return -ENOMEM; + + data->base = dev_read_addr_ptr(parent); + if (!data->base) + return -EINVAL; + + data->info = (struct rzg2l_cpg_info *)dev_get_driver_data(parent); + if (!data->info) + return -EINVAL; + + drv = lists_driver_lookup_name("rzg2l-cpg-clk"); + if (!drv) + return -ENOENT; + + ret = device_bind_with_driver_data(parent, drv, parent->name, + (ulong)data, dev_ofnode(parent), + &cdev); + if (ret) + return ret; + + drv = lists_driver_lookup_name("rzg2l-cpg-rst"); + if (!drv) { + device_unbind(cdev); + return -ENOENT; + } + + ret = device_bind_with_driver_data(parent, drv, parent->name, + (ulong)data, dev_ofnode(parent), + &rdev); + if (ret) + device_unbind(cdev); + + return ret; +} diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h new file mode 100644 index 00000000000..7cb6c481a62 --- /dev/null +++ b/drivers/clk/renesas/rzg2l-cpg.h @@ -0,0 +1,319 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * RZ/G2L Clock Pulse Generator + * + * Copyright (C) 2021-2023 Renesas Electronics Corp. + * + */ + +#ifndef __RENESAS_RZG2L_CPG_H__ +#define __RENESAS_RZG2L_CPG_H__ + +#define CPG_SIPLL5_STBY 0x140 +#define CPG_SIPLL5_CLK1 0x144 +#define CPG_SIPLL5_CLK3 0x14C +#define CPG_SIPLL5_CLK4 0x150 +#define CPG_SIPLL5_CLK5 0x154 +#define CPG_SIPLL5_MON 0x15C +#define CPG_PL1_DDIV 0x200 +#define CPG_PL2_DDIV 0x204 +#define CPG_PL3A_DDIV 0x208 +#define CPG_PL6_DDIV 0x210 +#define CPG_PL2SDHI_DSEL 0x218 +#define CPG_CLKSTATUS 0x280 +#define CPG_PL3_SSEL 0x408 +#define CPG_PL6_SSEL 0x414 +#define CPG_PL6_ETH_SSEL 0x418 +#define CPG_PL5_SDIV 0x420 +#define CPG_RST_MON 0x680 +#define CPG_OTHERFUNC1_REG 0xBE8 + +#define CPG_SIPLL5_STBY_RESETB BIT(0) +#define CPG_SIPLL5_STBY_RESETB_WEN BIT(16) +#define CPG_SIPLL5_STBY_SSCG_EN_WEN BIT(18) +#define CPG_SIPLL5_STBY_DOWNSPREAD_WEN BIT(20) +#define CPG_SIPLL5_CLK4_RESV_LSB 0xFF +#define CPG_SIPLL5_MON_PLL5_LOCK BIT(4) + +#define CPG_OTHERFUNC1_REG_RES0_ON_WEN BIT(16) + +#define CPG_PL5_SDIV_DIV_DSI_A_WEN BIT(16) +#define CPG_PL5_SDIV_DIV_DSI_B_WEN BIT(24) + +#define CPG_CLKSTATUS_SELSDHI0_STS BIT(28) +#define CPG_CLKSTATUS_SELSDHI1_STS BIT(29) + +#define CPG_SDHI_CLK_SWITCH_STATUS_TIMEOUT_US 20000 + +/* n = 0/1/2 for PLL1/4/6 */ +#define CPG_SAMPLL_CLK1(n) (0x04 + (16 * (n))) +#define CPG_SAMPLL_CLK2(n) (0x08 + (16 * (n))) + +#define PLL146_CONF(n) (CPG_SAMPLL_CLK1(n) << 22 | CPG_SAMPLL_CLK2(n) << 12) + +#define DDIV_PACK(offset, bitpos, size) \ + (((offset) << 20) | ((bitpos) << 12) | ((size) << 8)) +#define DIVPL1A DDIV_PACK(CPG_PL1_DDIV, 0, 2) +#define DIVPL2A DDIV_PACK(CPG_PL2_DDIV, 0, 3) +#define DIVDSILPCLK DDIV_PACK(CPG_PL2_DDIV, 12, 2) +#define DIVPL3A DDIV_PACK(CPG_PL3A_DDIV, 0, 3) +#define DIVPL3B DDIV_PACK(CPG_PL3A_DDIV, 4, 3) +#define DIVPL3C DDIV_PACK(CPG_PL3A_DDIV, 8, 3) +#define DIVGPU DDIV_PACK(CPG_PL6_DDIV, 0, 2) + +#define SEL_PLL_PACK(offset, bitpos, size) \ + (((offset) << 20) | ((bitpos) << 12) | ((size) << 8)) + +#define SEL_PLL3_3 SEL_PLL_PACK(CPG_PL3_SSEL, 8, 1) +#define SEL_PLL5_4 SEL_PLL_PACK(CPG_OTHERFUNC1_REG, 0, 1) +#define SEL_PLL6_2 SEL_PLL_PACK(CPG_PL6_ETH_SSEL, 0, 1) +#define SEL_GPU2 SEL_PLL_PACK(CPG_PL6_SSEL, 12, 1) + +#define SEL_SDHI0 DDIV_PACK(CPG_PL2SDHI_DSEL, 0, 2) +#define SEL_SDHI1 DDIV_PACK(CPG_PL2SDHI_DSEL, 4, 2) + +#define SEL_SDHI_533MHz 1 +#define SEL_SDHI_400MHz 2 +#define SEL_SDHI_266MHz 3 +#define SEL_SDHI_WRITE_ENABLE 0x10000 + +/* Unpack CPG conf value create by DDIV_PACK() or SEL_PLL_PACK(). */ +#define CPG_CONF_OFFSET(x) ((x) >> 20) +#define CPG_CONF_BITPOS(x) (((x) >> 12) & 0xff) +#define CPG_CONF_SIZE(x) (((x) >> 8) & 0xf) +#define CPG_CONF_BITMASK(x) GENMASK(CPG_CONF_SIZE(x) - 1, 0) + +#define EXTAL_FREQ_IN_MEGA_HZ 24 + +/** + * Definitions of CPG Core Clocks + * + * These include: + * - Clock outputs exported to DT + * - External input clocks + * - Internal CPG clocks + */ +struct cpg_core_clk { + const char *name; + unsigned int id; + unsigned int parent; + unsigned int div; + unsigned int mult; + unsigned int type; + unsigned int conf; + const struct clk_div_table *dtable; + const char * const *parent_names; + int flag; + int mux_flags; + int num_parents; +}; + +enum clk_types { + /* Generic */ + CLK_TYPE_IN, /* External Clock Input */ + CLK_TYPE_FF, /* Fixed Factor Clock */ + CLK_TYPE_SAM_PLL, + + /* Clock with divider */ + CLK_TYPE_DIV, + + /* Clock with clock source selector */ + CLK_TYPE_MUX, + + /* Clock with SD clock source selector */ + CLK_TYPE_SD_MUX, + + /* Clock for SIPLL5 */ + CLK_TYPE_SIPLL5, + + /* Clock for PLL5_4 clock source selector */ + CLK_TYPE_PLL5_4_MUX, + + /* Clock for DSI divider */ + CLK_TYPE_DSI_DIV, + +}; + +#define DEF_TYPE(_name, _id, _type...) \ + { .name = _name, .id = _id, .type = _type } +#define DEF_BASE(_name, _id, _type, _parent...) \ + DEF_TYPE(_name, _id, _type, .parent = _parent) +#define DEF_SAMPLL(_name, _id, _parent, _conf) \ + DEF_TYPE(_name, _id, CLK_TYPE_SAM_PLL, .parent = _parent, .conf = _conf) +#define DEF_INPUT(_name, _id) \ + DEF_TYPE(_name, _id, CLK_TYPE_IN) +#define DEF_FIXED(_name, _id, _parent, _mult, _div) \ + DEF_BASE(_name, _id, CLK_TYPE_FF, _parent, .div = _div, .mult = _mult) +#define DEF_DIV(_name, _id, _parent, _conf, _dtable) \ + DEF_TYPE(_name, _id, CLK_TYPE_DIV, .conf = _conf, \ + .parent = _parent, .dtable = _dtable, \ + .flag = CLK_DIVIDER_HIWORD_MASK) +#define DEF_DIV_RO(_name, _id, _parent, _conf, _dtable) \ + DEF_TYPE(_name, _id, CLK_TYPE_DIV, .conf = _conf, \ + .parent = _parent, .dtable = _dtable, \ + .flag = CLK_DIVIDER_READ_ONLY) +#define DEF_MUX(_name, _id, _conf, _parent_names) \ + DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = _conf, \ + .parent_names = _parent_names, \ + .num_parents = ARRAY_SIZE(_parent_names), \ + .mux_flags = CLK_MUX_HIWORD_MASK) +#define DEF_MUX_RO(_name, _id, _conf, _parent_names) \ + DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = _conf, \ + .parent_names = _parent_names, \ + .num_parents = ARRAY_SIZE(_parent_names), \ + .mux_flags = CLK_MUX_READ_ONLY) +#define DEF_SD_MUX(_name, _id, _conf, _parent_names) \ + DEF_TYPE(_name, _id, CLK_TYPE_SD_MUX, .conf = _conf, \ + .parent_names = _parent_names, \ + .num_parents = ARRAY_SIZE(_parent_names)) +#define DEF_PLL5_FOUTPOSTDIV(_name, _id, _parent) \ + DEF_TYPE(_name, _id, CLK_TYPE_SIPLL5, .parent = _parent) +#define DEF_PLL5_4_MUX(_name, _id, _conf, _parent_names) \ + DEF_TYPE(_name, _id, CLK_TYPE_PLL5_4_MUX, .conf = _conf, \ + .parent_names = _parent_names, \ + .num_parents = ARRAY_SIZE(_parent_names)) +#define DEF_DSI_DIV(_name, _id, _parent, _flag) \ + DEF_TYPE(_name, _id, CLK_TYPE_DSI_DIV, .parent = _parent, .flag = _flag) + +/** + * struct rzg2l_mod_clk - Module Clocks definitions + * + * @name: handle between common and hardware-specific interfaces + * @id: clock index in array containing all Core and Module Clocks + * @parent: id of parent clock + * @off: register offset + * @bit: ON/MON bit + * @is_coupled: flag to indicate coupled clock + */ +struct rzg2l_mod_clk { + const char *name; + unsigned int id; + unsigned int parent; + u16 off; + u8 bit; + bool is_coupled; +}; + +#define DEF_MOD_BASE(_name, _id, _parent, _off, _bit, _is_coupled) \ + { \ + .name = (_name), \ + .id = (_id), \ + .parent = (_parent), \ + .off = (_off), \ + .bit = (_bit), \ + .is_coupled = (_is_coupled), \ + } + +#define DEF_MOD(_name, _id, _parent, _off, _bit) \ + DEF_MOD_BASE(_name, _id, _parent, _off, _bit, false) + +#define DEF_COUPLED(_name, _id, _parent, _off, _bit) \ + DEF_MOD_BASE(_name, _id, _parent, _off, _bit, true) + +/** + * struct rzg2l_reset - Reset definitions + * + * @off: register offset + * @bit: reset bit + * @monbit: monitor bit in CPG_RST_MON register, -1 if none + */ +struct rzg2l_reset { + u16 off; + u8 bit; + s8 monbit; +}; + +#define DEF_RST_MON(_id, _off, _bit, _monbit) \ + [_id] = { \ + .off = (_off), \ + .bit = (_bit), \ + .monbit = (_monbit) \ + } +#define DEF_RST(_id, _off, _bit) \ + DEF_RST_MON(_id, _off, _bit, -1) + +/** + * struct rzg2l_cpg_info - SoC-specific CPG Description + * + * @core_clks: Array of Core Clock definitions + * @num_core_clks: Number of entries in core_clks[] + * + * @mod_clks: Array of Module Clock definitions + * @num_mod_clks: Number of entries in mod_clks[] + * @num_hw_mod_clks: Number of Module Clocks supported by the hardware + * + * @resets: Array of Module Reset definitions + * @num_resets: Number of entries in resets[] + * + * @has_clk_mon_regs: Flag indicating whether the SoC has CLK_MON registers + */ +struct rzg2l_cpg_info { + /* Core Clocks */ + const struct cpg_core_clk *core_clks; + unsigned int num_core_clks; + + /* Module Clocks */ + const struct rzg2l_mod_clk *mod_clks; + unsigned int num_mod_clks; + unsigned int num_hw_mod_clks; + + /* Resets */ + const struct rzg2l_reset *resets; + unsigned int num_resets; + + bool has_clk_mon_regs; +}; + +extern const struct rzg2l_cpg_info r9a07g044_cpg_info; + +int rzg2l_cpg_bind(struct udevice *parent); + +/* + * Clock IDs start at an offset to avoid overlapping with core & module clock + * IDs defined in the dt-bindings headers. + */ +enum clk_ids { + /* External Input Clocks */ + CLK_EXTAL = 0x100, + + /* Internal Core Clocks */ + CLK_OSC_DIV1000 = 0x200, + CLK_PLL1, + CLK_PLL2, + CLK_PLL2_DIV2, + CLK_PLL2_DIV2_8, + CLK_PLL2_DIV2_10, + CLK_PLL3, + CLK_PLL3_400, + CLK_PLL3_533, + CLK_M2_DIV2, + CLK_PLL3_DIV2, + CLK_PLL3_DIV2_2, + CLK_PLL3_DIV2_4, + CLK_PLL3_DIV2_4_2, + CLK_SEL_PLL3_3, + CLK_DIV_PLL3_C, + CLK_PLL4, + CLK_PLL5, + CLK_PLL5_FOUTPOSTDIV, + CLK_PLL5_FOUT1PH0, + CLK_PLL5_FOUT3, + CLK_PLL5_250, + CLK_PLL6, + CLK_PLL6_250, + CLK_P1_DIV2, + CLK_PLL2_800, + CLK_PLL2_SDHI_533, + CLK_PLL2_SDHI_400, + CLK_PLL2_SDHI_266, + CLK_SD0_DIV4, + CLK_SD1_DIV4, + CLK_SEL_GPU2, + CLK_SEL_PLL5_4, + CLK_DSI_DIV, + CLK_PLL2_533, + CLK_PLL2_533_DIV2, + CLK_DIV_DSI_LPCLK, +}; + +#endif diff --git a/drivers/clk/rockchip/clk_pll.c b/drivers/clk/rockchip/clk_pll.c index d657ef38f3c..1bb31b3313b 100644 --- a/drivers/clk/rockchip/clk_pll.c +++ b/drivers/clk/rockchip/clk_pll.c @@ -168,13 +168,71 @@ rockchip_pll_clk_set_by_auto(ulong fin_hz, return rate_table; } +static u32 +rockchip_rk3588_pll_k_get(u32 m, u32 p, u32 s, u64 fin_hz, u64 fvco) +{ + u64 fref, fout, ffrac; + u32 k = 0; + + fref = fin_hz / p; + ffrac = fvco - (m * fref); + fout = ffrac * 65536; + k = fout / fref; + if (k > 32767) { + fref = fin_hz / p; + ffrac = ((m + 1) * fref) - fvco; + fout = ffrac * 65536; + k = ((fout * 10 / fref) + 7) / 10; + if (k > 32767) + k = 0; + else + k = ~k + 1; + } + return k; +} + +static struct rockchip_pll_rate_table * +rockchip_rk3588_pll_frac_by_auto(unsigned long fin_hz, unsigned long fout_hz) +{ + struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table; + u32 p, m, s, k; + u64 fvco; + + for (s = 0; s <= 6; s++) { + fvco = (u64)fout_hz << s; + if (fvco < RK3588_VCO_MIN_HZ || fvco > RK3588_VCO_MAX_HZ) + continue; + for (p = 1; p <= 4; p++) { + for (m = 64; m <= 1023; m++) { + if ((fvco >= m * fin_hz / p) && + (fvco < (m + 1) * fin_hz / p)) { + k = rockchip_rk3588_pll_k_get(m, p, s, + fin_hz, + fvco); + if (!k) + continue; + rate_table->p = p; + rate_table->s = s; + rate_table->k = k; + if (k > 32767) + rate_table->m = m + 1; + else + rate_table->m = m; + return rate_table; + } + } + } + } + return NULL; +} + static struct rockchip_pll_rate_table * rk3588_pll_clk_set_by_auto(unsigned long fin_hz, unsigned long fout_hz) { struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table; u32 p, m, s; - ulong fvco, fref, fout, ffrac; + ulong fvco; if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz) return NULL; @@ -202,27 +260,12 @@ rk3588_pll_clk_set_by_auto(unsigned long fin_hz, } pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz); } else { - for (s = 0; s <= 6; s++) { - fvco = fout_hz << s; - if (fvco < RK3588_VCO_MIN_HZ || - fvco > RK3588_VCO_MAX_HZ) - continue; - for (p = 1; p <= 4; p++) { - for (m = 64; m <= 1023; m++) { - if ((fvco >= m * fin_hz / p) && (fvco < (m + 1) * fin_hz / p)) { - rate_table->p = p; - rate_table->m = m; - rate_table->s = s; - fref = fin_hz / p; - ffrac = fvco - (m * fref); - fout = ffrac * 65536; - rate_table->k = fout / fref; - return rate_table; - } - } - } - } - pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz); + rate_table = rockchip_rk3588_pll_frac_by_auto(fin_hz, fout_hz); + if (!rate_table) + pr_err("CANNOT FIND Fout by auto,fout = %lu\n", + fout_hz); + else + return rate_table; } return NULL; } @@ -533,11 +576,22 @@ static ulong rk3588_pll_get_rate(struct rockchip_pll_clock *pll, rate = OSC_HZ / p; rate *= m; - if (k) { + if (k & BIT(15)) { + /* fractional mode */ + u64 frac_rate64; + + k = (~(k - 1)) & RK3588_PLLCON2_K_MASK; + frac_rate64 = OSC_HZ * k; + postdiv = p; + postdiv *= 65536; + do_div(frac_rate64, postdiv); + rate -= frac_rate64; + } else { /* fractional mode */ u64 frac_rate64 = OSC_HZ * k; - postdiv = p * 65536; + postdiv = p; + postdiv *= 65536; do_div(frac_rate64, postdiv); rate += frac_rate64; } diff --git a/drivers/clk/rockchip/clk_rk3568.c b/drivers/clk/rockchip/clk_rk3568.c index 599b7b130eb..68f5bbbb9e5 100644 --- a/drivers/clk/rockchip/clk_rk3568.c +++ b/drivers/clk/rockchip/clk_rk3568.c @@ -1838,7 +1838,7 @@ static ulong rk3568_dclk_vop_set_clk(struct rk3568_clk_priv *priv, rockchip_pll_set_rate(&rk3568_pll_clks[VPLL], priv->cru, VPLL, div * rate); } else { - for (i = 0; i <= DCLK_VOP_SEL_CPLL; i++) { + for (i = sel; i <= DCLK_VOP_SEL_CPLL; i++) { switch (i) { case DCLK_VOP_SEL_GPLL: pll_rate = priv->gpll_hz; @@ -2785,9 +2785,15 @@ static int rk3568_dclk_vop_set_parent(struct clk *clk, struct clk *parent) if (parent->id == PLL_VPLL) { rk_clrsetreg(&cru->clksel_con[con_id], DCLK0_VOP_SEL_MASK, DCLK_VOP_SEL_VPLL << DCLK0_VOP_SEL_SHIFT); - } else { + } else if (parent->id == PLL_HPLL) { rk_clrsetreg(&cru->clksel_con[con_id], DCLK0_VOP_SEL_MASK, DCLK_VOP_SEL_HPLL << DCLK0_VOP_SEL_SHIFT); + } else if (parent->id == PLL_CPLL) { + rk_clrsetreg(&cru->clksel_con[con_id], DCLK0_VOP_SEL_MASK, + DCLK_VOP_SEL_CPLL << DCLK0_VOP_SEL_SHIFT); + } else { + rk_clrsetreg(&cru->clksel_con[con_id], DCLK0_VOP_SEL_MASK, + DCLK_VOP_SEL_GPLL << DCLK0_VOP_SEL_SHIFT); } return 0; diff --git a/drivers/clk/rockchip/clk_rk3588.c b/drivers/clk/rockchip/clk_rk3588.c index 119b1337bdf..a995dd5591d 100644 --- a/drivers/clk/rockchip/clk_rk3588.c +++ b/drivers/clk/rockchip/clk_rk3588.c @@ -36,6 +36,7 @@ static struct rockchip_pll_rate_table rk3588_pll_rates[] = { RK3588_PLL_RATE(816000000, 2, 272, 2, 0), RK3588_PLL_RATE(786432000, 2, 262, 2, 9437), RK3588_PLL_RATE(786000000, 1, 131, 2, 0), + RK3588_PLL_RATE(742500000, 4, 495, 2, 0), RK3588_PLL_RATE(722534400, 8, 963, 2, 24850), RK3588_PLL_RATE(600000000, 2, 200, 2, 0), RK3588_PLL_RATE(594000000, 2, 198, 2, 0), @@ -305,12 +306,18 @@ static ulong rk3588_top_set_clk(struct rk3588_clk_priv *priv, switch (clk_id) { case ACLK_TOP_ROOT: - src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate); + if (!(priv->cpll_hz % rate)) { + src_clk = ACLK_TOP_ROOT_SRC_SEL_CPLL; + src_clk_div = DIV_ROUND_UP(priv->cpll_hz, rate); + } else { + src_clk = ACLK_TOP_ROOT_SRC_SEL_GPLL; + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate); + } assert(src_clk_div - 1 <= 31); rk_clrsetreg(&cru->clksel_con[8], ACLK_TOP_ROOT_DIV_MASK | ACLK_TOP_ROOT_SRC_SEL_MASK, - (ACLK_TOP_ROOT_SRC_SEL_GPLL << + (src_clk << ACLK_TOP_ROOT_SRC_SEL_SHIFT) | (src_clk_div - 1) << ACLK_TOP_ROOT_DIV_SHIFT); break; @@ -1123,13 +1130,23 @@ static ulong rk3588_dclk_vop_set_clk(struct rk3588_clk_priv *priv, } if (sel == DCLK_VOP_SRC_SEL_V0PLL) { - div = DIV_ROUND_UP(RK3588_VOP_PLL_LIMIT_FREQ, rate); - rk_clrsetreg(&cru->clksel_con[conid], - mask, - DCLK_VOP_SRC_SEL_V0PLL << sel_shift | - ((div - 1) << div_shift)); - rockchip_pll_set_rate(&rk3588_pll_clks[V0PLL], - priv->cru, V0PLL, div * rate); + pll_rate = rockchip_pll_get_rate(&rk3588_pll_clks[V0PLL], + priv->cru, V0PLL); + if (pll_rate >= RK3588_VOP_PLL_LIMIT_FREQ && pll_rate % rate == 0) { + div = DIV_ROUND_UP(pll_rate, rate); + rk_clrsetreg(&cru->clksel_con[conid], + mask, + DCLK_VOP_SRC_SEL_V0PLL << sel_shift | + ((div - 1) << div_shift)); + } else { + div = DIV_ROUND_UP(RK3588_VOP_PLL_LIMIT_FREQ, rate); + rk_clrsetreg(&cru->clksel_con[conid], + mask, + DCLK_VOP_SRC_SEL_V0PLL << sel_shift | + ((div - 1) << div_shift)); + rockchip_pll_set_rate(&rk3588_pll_clks[V0PLL], + priv->cru, V0PLL, div * rate); + } } else { for (i = 0; i <= DCLK_VOP_SRC_SEL_AUPLL; i++) { switch (i) { diff --git a/drivers/clk/starfive/clk-jh7110.c b/drivers/clk/starfive/clk-jh7110.c index 31aaf3340f9..a835541e48e 100644 --- a/drivers/clk/starfive/clk-jh7110.c +++ b/drivers/clk/starfive/clk-jh7110.c @@ -539,6 +539,16 @@ static int jh7110_stgcrg_init(struct udevice *dev) "pcie1_tl", "stg_axiahb", OFFSET(JH7110_STGCLK_PCIE1_TL))); + /* Security clocks */ + clk_dm(JH7110_STG_ID_TRANS(JH7110_STGCLK_SEC_HCLK), + starfive_clk_gate(priv->reg, + "sec_ahb", "stg_axiahb", + OFFSET(JH7110_STGCLK_SEC_HCLK))); + clk_dm(JH7110_STG_ID_TRANS(JH7110_STGCLK_SEC_MISCAHB), + starfive_clk_gate(priv->reg, + "sec_misc_ahb", "stg_axiahb", + OFFSET(JH7110_STGCLK_SEC_MISCAHB))); + return 0; } diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig index bf11fad6eef..8bdc0944896 100644 --- a/drivers/clk/sunxi/Kconfig +++ b/drivers/clk/sunxi/Kconfig @@ -87,6 +87,13 @@ config CLK_SUN8I_H3 This enables common clock driver support for platforms based on Allwinner H3/H5 SoC. +config CLK_SUN20I_D1 + bool "Clock driver for Allwinner D1" + default MACH_SUN8I_R528 + help + This enables common clock driver support for platforms based + on Allwinner D1 SoC. + config CLK_SUN50I_H6 bool "Clock driver for Allwinner H6" default MACH_SUN50I_H6 diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 895da02ebea..90a277489dc 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_CLK_SUN8I_R40) += clk_r40.o obj-$(CONFIG_CLK_SUN8I_V3S) += clk_v3s.o obj-$(CONFIG_CLK_SUN9I_A80) += clk_a80.o obj-$(CONFIG_CLK_SUN8I_H3) += clk_h3.o +obj-$(CONFIG_CLK_SUN20I_D1) += clk_d1.o obj-$(CONFIG_CLK_SUN50I_H6) += clk_h6.o obj-$(CONFIG_CLK_SUN50I_H6_R) += clk_h6_r.o obj-$(CONFIG_CLK_SUN50I_H616) += clk_h616.o diff --git a/drivers/clk/sunxi/clk_d1.c b/drivers/clk/sunxi/clk_d1.c new file mode 100644 index 00000000000..9dae761de83 --- /dev/null +++ b/drivers/clk/sunxi/clk_d1.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2021 Samuel Holland <samuel@sholland.org> + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <errno.h> +#include <clk/sunxi.h> +#include <dt-bindings/clock/sun20i-d1-ccu.h> +#include <dt-bindings/reset/sun20i-d1-ccu.h> +#include <linux/bitops.h> + +static struct ccu_clk_gate d1_gates[] = { + [CLK_APB0] = GATE_DUMMY, + + [CLK_BUS_MMC0] = GATE(0x84c, BIT(0)), + [CLK_BUS_MMC1] = GATE(0x84c, BIT(1)), + [CLK_BUS_MMC2] = GATE(0x84c, BIT(2)), + [CLK_BUS_UART0] = GATE(0x90c, BIT(0)), + [CLK_BUS_UART1] = GATE(0x90c, BIT(1)), + [CLK_BUS_UART2] = GATE(0x90c, BIT(2)), + [CLK_BUS_UART3] = GATE(0x90c, BIT(3)), + [CLK_BUS_UART4] = GATE(0x90c, BIT(4)), + [CLK_BUS_UART5] = GATE(0x90c, BIT(5)), + [CLK_BUS_I2C0] = GATE(0x91c, BIT(0)), + [CLK_BUS_I2C1] = GATE(0x91c, BIT(1)), + [CLK_BUS_I2C2] = GATE(0x91c, BIT(2)), + [CLK_BUS_I2C3] = GATE(0x91c, BIT(3)), + [CLK_SPI0] = GATE(0x940, BIT(31)), + [CLK_SPI1] = GATE(0x944, BIT(31)), + [CLK_BUS_SPI0] = GATE(0x96c, BIT(0)), + [CLK_BUS_SPI1] = GATE(0x96c, BIT(1)), + + [CLK_BUS_EMAC] = GATE(0x97c, BIT(0)), + + [CLK_USB_OHCI0] = GATE(0xa70, BIT(31)), + [CLK_USB_OHCI1] = GATE(0xa74, BIT(31)), + [CLK_BUS_OHCI0] = GATE(0xa8c, BIT(0)), + [CLK_BUS_OHCI1] = GATE(0xa8c, BIT(1)), + [CLK_BUS_EHCI0] = GATE(0xa8c, BIT(4)), + [CLK_BUS_EHCI1] = GATE(0xa8c, BIT(5)), + [CLK_BUS_OTG] = GATE(0xa8c, BIT(8)), + [CLK_BUS_LRADC] = GATE(0xa9c, BIT(0)), + + [CLK_RISCV] = GATE(0xd04, BIT(31)), +}; + +static struct ccu_reset d1_resets[] = { + [RST_BUS_MMC0] = RESET(0x84c, BIT(16)), + [RST_BUS_MMC1] = RESET(0x84c, BIT(17)), + [RST_BUS_MMC2] = RESET(0x84c, BIT(18)), + [RST_BUS_UART0] = RESET(0x90c, BIT(16)), + [RST_BUS_UART1] = RESET(0x90c, BIT(17)), + [RST_BUS_UART2] = RESET(0x90c, BIT(18)), + [RST_BUS_UART3] = RESET(0x90c, BIT(19)), + [RST_BUS_UART4] = RESET(0x90c, BIT(20)), + [RST_BUS_UART5] = RESET(0x90c, BIT(21)), + [RST_BUS_I2C0] = RESET(0x91c, BIT(16)), + [RST_BUS_I2C1] = RESET(0x91c, BIT(17)), + [RST_BUS_I2C2] = RESET(0x91c, BIT(18)), + [RST_BUS_I2C3] = RESET(0x91c, BIT(19)), + [RST_BUS_SPI0] = RESET(0x96c, BIT(16)), + [RST_BUS_SPI1] = RESET(0x96c, BIT(17)), + + [RST_BUS_EMAC] = RESET(0x97c, BIT(16)), + + [RST_USB_PHY0] = RESET(0xa70, BIT(30)), + [RST_USB_PHY1] = RESET(0xa74, BIT(30)), + [RST_BUS_OHCI0] = RESET(0xa8c, BIT(16)), + [RST_BUS_OHCI1] = RESET(0xa8c, BIT(17)), + [RST_BUS_EHCI0] = RESET(0xa8c, BIT(20)), + [RST_BUS_EHCI1] = RESET(0xa8c, BIT(21)), + [RST_BUS_OTG] = RESET(0xa8c, BIT(24)), + [RST_BUS_LRADC] = RESET(0xa9c, BIT(16)), +}; + +const struct ccu_desc d1_ccu_desc = { + .gates = d1_gates, + .resets = d1_resets, + .num_gates = ARRAY_SIZE(d1_gates), + .num_resets = ARRAY_SIZE(d1_resets), +}; diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c index ec02a2d0370..1782cffc404 100644 --- a/drivers/clk/sunxi/clk_sunxi.c +++ b/drivers/clk/sunxi/clk_sunxi.c @@ -118,6 +118,7 @@ extern const struct ccu_desc a64_ccu_desc; extern const struct ccu_desc a80_ccu_desc; extern const struct ccu_desc a80_mmc_clk_desc; extern const struct ccu_desc a83t_ccu_desc; +extern const struct ccu_desc d1_ccu_desc; extern const struct ccu_desc f1c100s_ccu_desc; extern const struct ccu_desc h3_ccu_desc; extern const struct ccu_desc h6_ccu_desc; @@ -195,6 +196,10 @@ static const struct udevice_id sunxi_clk_ids[] = { { .compatible = "allwinner,sun50i-h5-ccu", .data = (ulong)&h3_ccu_desc }, #endif +#ifdef CONFIG_CLK_SUN20I_D1 + { .compatible = "allwinner,sun20i-d1-ccu", + .data = (ulong)&d1_ccu_desc }, +#endif #ifdef CONFIG_CLK_SUN50I_H6 { .compatible = "allwinner,sun50i-h6-ccu", .data = (ulong)&h6_ccu_desc }, diff --git a/drivers/clk/ti/clk-k3-pll.c b/drivers/clk/ti/clk-k3-pll.c index bf762c558ef..8323e6e6919 100644 --- a/drivers/clk/ti/clk-k3-pll.c +++ b/drivers/clk/ti/clk-k3-pll.c @@ -2,7 +2,7 @@ /* * Texas Instruments K3 SoC PLL clock driver * - * Copyright (C) 2020-2021 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ * Tero Kristo <t-kristo@ti.com> */ @@ -25,6 +25,23 @@ #define PLL_16FFT_FREQ_CTRL0 0x30 #define PLL_16FFT_FREQ_CTRL1 0x34 #define PLL_16FFT_DIV_CTRL 0x38 +#define PLL_16FFT_CAL_CTRL 0x60 +#define PLL_16FFT_CAL_STAT 0x64 + +/* CAL STAT register bits */ +#define PLL_16FFT_CAL_STAT_CAL_LOCK BIT(31) + +/* CFG register bits */ +#define PLL_16FFT_CFG_PLL_TYPE_SHIFT (0) +#define PLL_16FFT_CFG_PLL_TYPE_MASK (0x3 << 0) +#define PLL_16FFT_CFG_PLL_TYPE_FRACF 1 + +/* CAL CTRL register bits */ +#define PLL_16FFT_CAL_CTRL_CAL_EN BIT(31) +#define PLL_16FFT_CAL_CTRL_FAST_CAL BIT(20) +#define PLL_16FFT_CAL_CTRL_CAL_BYP BIT(15) +#define PLL_16FFT_CAL_CTRL_CAL_CNT_SHIFT 16 +#define PLL_16FFT_CAL_CTRL_CAL_CNT_MASK (0x7 << 16) /* CTRL register bits */ #define PLL_16FFT_CTRL_BYPASS_EN BIT(31) @@ -40,9 +57,14 @@ /* DIV CTRL register bits */ #define PLL_16FFT_DIV_CTRL_REF_DIV_MASK 0x3f -#define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS 24 +/* HSDIV register bits*/ #define PLL_16FFT_HSDIV_CTRL_CLKOUT_EN BIT(15) +/* FREQ_CTRL1 bits */ +#define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS 24 +#define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_MASK 0xffffff +#define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_SHIFT 0 + /* KICK register magic values */ #define PLL_KICK0_VALUE 0x68ef3490 #define PLL_KICK1_VALUE 0xd172bc5a @@ -63,18 +85,65 @@ static int ti_pll_wait_for_lock(struct clk *clk) { struct ti_pll_clk *pll = to_clk_pll(clk); u32 stat; + u32 cfg; + u32 cal; + u32 freq_ctrl1; int i; + u32 pllfm; + u32 pll_type; + int success; for (i = 0; i < 100000; i++) { stat = readl(pll->reg + PLL_16FFT_STAT); - if (stat & PLL_16FFT_STAT_LOCK) - return 0; + if (stat & PLL_16FFT_STAT_LOCK) { + success = 1; + break; + } } - printf("%s: pll (%s) failed to lock\n", __func__, - clk->dev->name); + /* Enable calibration if not in fractional mode of the FRACF PLL */ + freq_ctrl1 = readl(pll->reg + PLL_16FFT_FREQ_CTRL1); + pllfm = freq_ctrl1 & PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_MASK; + pllfm >>= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_SHIFT; + cfg = readl(pll->reg + PLL_16FFT_CFG); + pll_type = (cfg & PLL_16FFT_CFG_PLL_TYPE_MASK) >> PLL_16FFT_CFG_PLL_TYPE_SHIFT; + + if (success && pll_type == PLL_16FFT_CFG_PLL_TYPE_FRACF && pllfm == 0) { + cal = readl(pll->reg + PLL_16FFT_CAL_CTRL); - return -EBUSY; + /* Enable calibration for FRACF */ + cal |= PLL_16FFT_CAL_CTRL_CAL_EN; + + /* Enable fast cal mode */ + cal |= PLL_16FFT_CAL_CTRL_FAST_CAL; + + /* Disable calibration bypass */ + cal &= ~PLL_16FFT_CAL_CTRL_CAL_BYP; + + /* Set CALCNT to 2 */ + cal &= ~PLL_16FFT_CAL_CTRL_CAL_CNT_MASK; + cal |= 2 << PLL_16FFT_CAL_CTRL_CAL_CNT_SHIFT; + + /* Note this register does not readback the written value. */ + writel(cal, pll->reg + PLL_16FFT_CAL_CTRL); + + success = 0; + for (i = 0; i < 100000; i++) { + stat = readl(pll->reg + PLL_16FFT_CAL_STAT); + if (stat & PLL_16FFT_CAL_STAT_CAL_LOCK) { + success = 1; + break; + } + } + } + + if (success == 0) { + printf("%s: pll (%s) failed to lock\n", __func__, + clk->dev->name); + return -EBUSY; + } else { + return 0; + } } static ulong ti_pll_clk_get_rate(struct clk *clk) diff --git a/drivers/clk/ti/clk-k3.c b/drivers/clk/ti/clk-k3.c index ba925fa3c48..eb76195bd75 100644 --- a/drivers/clk/ti/clk-k3.c +++ b/drivers/clk/ti/clk-k3.c @@ -2,7 +2,7 @@ /* * Texas Instruments K3 clock driver * - * Copyright (C) 2020-2021 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ * Tero Kristo <t-kristo@ti.com> */ @@ -11,6 +11,7 @@ #include <errno.h> #include <soc.h> #include <clk-uclass.h> +#include <k3-avs.h> #include "k3-clk.h" #define PLL_MIN_FREQ 800000000 @@ -242,7 +243,11 @@ static ulong ti_clk_set_rate(struct clk *clk, ulong rate) const struct clk_ops *ops; ulong new_rate, rem; ulong diff, new_diff; + int freq_scale_up = rate >= ti_clk_get_rate(clk) ? 1 : 0; + if (IS_ENABLED(CONFIG_K3_AVS0) && freq_scale_up) + k3_avs_notify_freq(data->map[clk->id].dev_id, + data->map[clk->id].clk_id, rate); /* * We must propagate rate change to parent if current clock type * does not allow setting it. @@ -339,6 +344,10 @@ static ulong ti_clk_set_rate(struct clk *clk, ulong rate) } } + if (IS_ENABLED(CONFIG_K3_AVS0) && !freq_scale_up) + k3_avs_notify_freq(data->map[clk->id].dev_id, + data->map[clk->id].clk_id, rate); + return new_rate; } diff --git a/drivers/clk/ti/clk-sci.c b/drivers/clk/ti/clk-sci.c index 74df5a397bf..9e5760d3354 100644 --- a/drivers/clk/ti/clk-sci.c +++ b/drivers/clk/ti/clk-sci.c @@ -2,7 +2,7 @@ /* * Texas Instruments System Control Interface (TI SCI) clock driver * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Andreas Dannenberg <dannenberg@ti.com> * * Loosely based on Linux kernel sci-clk.c... @@ -91,12 +91,12 @@ static ulong ti_sci_clk_set_rate(struct clk *clk, ulong rate) const struct ti_sci_handle *sci = data->sci; const struct ti_sci_clk_ops *cops = &sci->ops.clk_ops; int ret; + int freq_scale_up = rate >= ti_sci_clk_get_rate(clk) ? 1 : 0; debug("%s(clk=%p, rate=%lu)\n", __func__, clk, rate); -#ifdef CONFIG_K3_AVS0 - k3_avs_notify_freq(clk->id, clk->data, rate); -#endif + if (IS_ENABLED(CONFIG_K3_AVS0) && freq_scale_up) + k3_avs_notify_freq(clk->id, clk->data, rate); ret = cops->set_freq(sci, clk->id, clk->data, 0, rate, ULONG_MAX); if (ret) { @@ -104,6 +104,9 @@ static ulong ti_sci_clk_set_rate(struct clk *clk, ulong rate) return ret; } + if (IS_ENABLED(CONFIG_K3_AVS0) && !freq_scale_up) + k3_avs_notify_freq(clk->id, clk->data, rate); + return rate; } diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index f0d848f45d8..1081d61fcf0 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -14,7 +14,7 @@ config SPL_DM help Enable driver model in SPL. You will need to provide a suitable malloc() implementation. If you are not using the - full malloc() enabled by CFG_SYS_SPL_MALLOC_START, + full malloc() enabled by CFG_SPL_SYS_MALLOC_START, consider using CONFIG_SPL_SYS_MALLOC_SIMPLE. In that case you must provide CONFIG_SPL_SYS_MALLOC_F_LEN to set the size. In most cases driver model will only allocate a few uclasses @@ -27,12 +27,12 @@ config TPL_DM help Enable driver model in TPL. You will need to provide a suitable malloc() implementation. If you are not using the - full malloc() enabled by CFG_SYS_SPL_MALLOC_START, + full malloc() enabled by CFG_TPL_SYS_MALLOC_START, consider using CONFIG_TPL_SYS_MALLOC_SIMPLE. In that case you - must provide CONFIG_SPL_SYS_MALLOC_F_LEN to set the size. + must provide CONFIG_TPL_SYS_MALLOC_F_LEN to set the size. In most cases driver model will only allocate a few uclasses - and devices in SPL, so 1KB should be enough. See - CONFIG_SPL_SYS_MALLOC_F_LEN for more details on how to enable it. + and devices in TPL, so 1KB should be enough. See + CONFIG_TPL_SYS_MALLOC_F_LEN for more details on how to enable it. Disable this for very small implementations. config VPL_DM @@ -42,13 +42,12 @@ config VPL_DM help Enable driver model in VPL. You will need to provide a suitable malloc() implementation. If you are not using the - full malloc() enabled by CFG_SYS_SPL_MALLOC_START, - consider using CONFIG_SPL_SYS_MALLOC_SIMPLE. + full malloc() enabled by CFG_TPL_SYS_MALLOC_START, + consider using CONFIG_TPL_SYS_MALLOC_SIMPLE. config DM_WARN bool "Enable warnings in driver model" depends on DM - default y help Enable this to see warnings related to driver model. @@ -88,7 +87,7 @@ config DM_STATS config SPL_DM_STATS bool "Collect and show driver model stats in SPL" - depends on DM_SPL + depends on SPL_DM help Enable this to collect and display memory statistics about driver model. This can help to figure out where all the memory is going and diff --git a/drivers/core/Makefile b/drivers/core/Makefile index bce0a3f65cb..acbd2bf2cef 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_$(SPL_)OF_LIVE) += of_access.o of_addr.o ifndef CONFIG_DM_DEV_READ_INLINE obj-$(CONFIG_OF_CONTROL) += read.o endif +obj-$(CONFIG_$(SPL_)OF_PLATDATA) += read.o obj-$(CONFIG_OF_CONTROL) += of_extra.o ofnode.o read_extra.o ccflags-$(CONFIG_DM_DEBUG) += -DDEBUG diff --git a/drivers/core/fdtaddr.c b/drivers/core/fdtaddr.c index 546db675aaf..8e774d49ce6 100644 --- a/drivers/core/fdtaddr.c +++ b/drivers/core/fdtaddr.c @@ -145,7 +145,7 @@ fdt_addr_t devfdt_get_addr_name(const struct udevice *dev, const char *name) index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev), "reg-names", name); if (index < 0) - return index; + return FDT_ADDR_T_NONE; return devfdt_get_addr_index(dev, index); #else @@ -153,6 +153,16 @@ fdt_addr_t devfdt_get_addr_name(const struct udevice *dev, const char *name) #endif } +void *devfdt_get_addr_name_ptr(const struct udevice *dev, const char *name) +{ + fdt_addr_t addr = devfdt_get_addr_name(dev, name); + + if (addr == FDT_ADDR_T_NONE) + return NULL; + + return map_sysmem(addr, 0); +} + fdt_addr_t devfdt_get_addr_size_name(const struct udevice *dev, const char *name, fdt_size_t *size) { @@ -162,7 +172,7 @@ fdt_addr_t devfdt_get_addr_size_name(const struct udevice *dev, index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev), "reg-names", name); if (index < 0) - return index; + return FDT_ADDR_T_NONE; return devfdt_get_addr_size_index(dev, index, size); #else @@ -170,6 +180,17 @@ fdt_addr_t devfdt_get_addr_size_name(const struct udevice *dev, #endif } +void *devfdt_get_addr_size_name_ptr(const struct udevice *dev, + const char *name, fdt_size_t *size) +{ + fdt_addr_t addr = devfdt_get_addr_size_name(dev, name, size); + + if (addr == FDT_ADDR_T_NONE) + return NULL; + + return map_sysmem(addr, 0); +} + fdt_addr_t devfdt_get_addr(const struct udevice *dev) { return devfdt_get_addr_index(dev, 0); @@ -215,7 +236,7 @@ void *devfdt_map_physmem(const struct udevice *dev, unsigned long size) return map_physmem(addr, size, MAP_NOCACHE); } -fdt_addr_t devfdt_get_addr_pci(const struct udevice *dev) +fdt_addr_t devfdt_get_addr_pci(const struct udevice *dev, fdt_size_t *sizep) { ulong addr; @@ -226,12 +247,12 @@ fdt_addr_t devfdt_get_addr_pci(const struct udevice *dev) int ret; ret = ofnode_read_pci_addr(dev_ofnode(dev), FDT_PCI_SPACE_MEM32, - "reg", &pci_addr); + "reg", &pci_addr, sizep); if (ret) { /* try if there is any i/o-mapped register */ ret = ofnode_read_pci_addr(dev_ofnode(dev), FDT_PCI_SPACE_IO, "reg", - &pci_addr); + &pci_addr, sizep); if (ret) return FDT_ADDR_T_NONE; } diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c index 1bb4d8eab70..c8db743f529 100644 --- a/drivers/core/of_access.c +++ b/drivers/core/of_access.c @@ -1040,3 +1040,68 @@ int of_add_subnode(struct device_node *parent, const char *name, int len, return 0; } + +int __of_remove_property(struct device_node *np, struct property *prop) +{ + struct property **next; + + for (next = &np->properties; *next; next = &(*next)->next) { + if (*next == prop) + break; + } + if (!*next) + return -ENODEV; + + /* found the node */ + *next = prop->next; + + return 0; +} + +int of_remove_property(struct device_node *np, struct property *prop) +{ + int rc; + + mutex_lock(&of_mutex); + + rc = __of_remove_property(np, prop); + + mutex_unlock(&of_mutex); + + return rc; +} + +int of_remove_node(struct device_node *to_remove) +{ + struct device_node *parent = to_remove->parent; + struct device_node *np, *prev; + + if (!parent) + return -EPERM; + prev = NULL; + __for_each_child_of_node(parent, np) { + if (np == to_remove) + break; + prev = np; + } + if (!np) + return -EFAULT; + + /* if there is a previous node, link it to this one's sibling */ + if (prev) + prev->sibling = np->sibling; + else + parent->child = np->sibling; + + /* + * don't free it, since if this is an unflattened tree, all the memory + * was alloced in one block; this pointer will be somewhere in the + * middle of that + * + * TODO(sjg@chromium.org): Consider marking nodes as 'allocated'? + * + * free(np); + */ + + return 0; +} diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 2cafa7bca5b..f72ea416cf1 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -47,6 +47,17 @@ static int oftree_find(const void *fdt) return -1; } +static int check_tree_count(void) +{ + if (oftree_count == CONFIG_OFNODE_MULTI_TREE_MAX) { + log_warning("Too many registered device trees (max %d)\n", + CONFIG_OFNODE_MULTI_TREE_MAX); + return -E2BIG; + } + + return 0; +} + static oftree oftree_ensure(void *fdt) { oftree tree; @@ -69,11 +80,8 @@ static oftree oftree_ensure(void *fdt) if (gd->flags & GD_FLG_RELOC) { i = oftree_find(fdt); if (i == -1) { - if (oftree_count == CONFIG_OFNODE_MULTI_TREE_MAX) { - log_warning("Too many registered device trees (max %d)\n", - CONFIG_OFNODE_MULTI_TREE_MAX); + if (check_tree_count()) return oftree_null(); - } /* register the new tree */ i = oftree_count++; @@ -92,6 +100,41 @@ static oftree oftree_ensure(void *fdt) return tree; } +int oftree_new(oftree *treep) +{ + oftree tree = oftree_null(); + int ret; + + if (of_live_active()) { + struct device_node *root; + + ret = of_live_create_empty(&root); + if (ret) + return log_msg_ret("liv", ret); + tree = oftree_from_np(root); + } else { + const int size = 1024; + void *fdt; + + ret = check_tree_count(); + if (ret) + return log_msg_ret("fla", ret); + + /* register the new tree with a small size */ + fdt = malloc(size); + if (!fdt) + return log_msg_ret("fla", -ENOMEM); + ret = fdt_create_empty_tree(fdt, size); + if (ret) + return log_msg_ret("fla", -EINVAL); + oftree_list[oftree_count++] = fdt; + tree.fdt = fdt; + } + *treep = tree; + + return 0; +} + void oftree_dispose(oftree tree) { if (of_live_active()) @@ -193,8 +236,31 @@ static inline int oftree_find(const void *fdt) return 0; } +int oftree_new(oftree *treep) +{ + return -ENOSYS; +} + #endif /* OFNODE_MULTI_TREE */ +int oftree_to_fdt(oftree tree, struct abuf *buf) +{ + int ret; + + if (of_live_active()) { + ret = of_live_flatten(ofnode_to_np(oftree_root(tree)), buf); + if (ret) + return log_msg_ret("flt", ret); + } else { + void *fdt = oftree_lookup_fdt(tree); + + abuf_init(buf); + abuf_set(buf, fdt, fdt_totalsize(fdt)); + } + + return 0; +} + /** * ofnode_from_tree_offset() - get an ofnode from a tree offset (flat tree) * @@ -425,12 +491,12 @@ u64 ofnode_read_u64_default(ofnode node, const char *propname, u64 def) bool ofnode_read_bool(ofnode node, const char *propname) { - const void *prop; + bool prop; assert(ofnode_valid(node)); debug("%s: %s: ", __func__, propname); - prop = ofnode_get_property(node, propname, NULL); + prop = ofnode_has_property(node, propname); debug("%s\n", prop ? "true" : "false"); @@ -925,6 +991,24 @@ ofnode ofnode_get_chosen_node(const char *name) return ofnode_path(prop); } +int ofnode_read_baud(void) +{ + const char *str, *p; + u32 baud; + + str = ofnode_read_chosen_string("stdout-path"); + if (!str) + return -EINVAL; + + /* Parse string serial0:115200n8 */ + p = strchr(str, ':'); + if (!p) + return -EINVAL; + + baud = dectoul(p + 1, NULL); + return baud; +} + const void *ofnode_read_aliases_prop(const char *propname, int *sizep) { ofnode node; @@ -1102,6 +1186,14 @@ const void *ofnode_get_property(ofnode node, const char *propname, int *lenp) propname, lenp); } +bool ofnode_has_property(ofnode node, const char *propname) +{ + if (ofnode_is_np(node)) + return of_find_property(ofnode_to_np(node), propname, NULL); + else + return ofnode_get_property(node, propname, NULL); +} + int ofnode_first_property(ofnode node, struct ofprop *prop) { prop->node = node; @@ -1196,7 +1288,8 @@ const uint8_t *ofnode_read_u8_array_ptr(ofnode node, const char *propname, } int ofnode_read_pci_addr(ofnode node, enum fdt_pci_space type, - const char *propname, struct fdt_pci_addr *addr) + const char *propname, struct fdt_pci_addr *addr, + fdt_size_t *size) { const fdt32_t *cell; int len; @@ -1224,14 +1317,18 @@ int ofnode_read_pci_addr(ofnode node, enum fdt_pci_space type, (ulong)fdt32_to_cpu(cell[1]), (ulong)fdt32_to_cpu(cell[2])); if ((fdt32_to_cpu(*cell) & type) == type) { + const unaligned_fdt64_t *ptr; + addr->phys_hi = fdt32_to_cpu(cell[0]); addr->phys_mid = fdt32_to_cpu(cell[1]); addr->phys_lo = fdt32_to_cpu(cell[2]); + ptr = (const unaligned_fdt64_t *)(cell + 3); + if (size) + *size = fdt64_to_cpu(*ptr); break; } - cell += (FDT_PCI_ADDR_CELLS + - FDT_PCI_SIZE_CELLS); + cell += FDT_PCI_ADDR_CELLS + FDT_PCI_SIZE_CELLS; } if (i == num) { @@ -1547,7 +1644,46 @@ int ofnode_write_u32(ofnode node, const char *propname, u32 value) return -ENOMEM; *val = cpu_to_fdt32(value); - return ofnode_write_prop(node, propname, val, sizeof(value), false); + return ofnode_write_prop(node, propname, val, sizeof(value), true); +} + +int ofnode_write_u64(ofnode node, const char *propname, u64 value) +{ + fdt64_t *val; + + assert(ofnode_valid(node)); + + log_debug("%s = %llx", propname, (unsigned long long)value); + val = malloc(sizeof(*val)); + if (!val) + return -ENOMEM; + *val = cpu_to_fdt64(value); + + return ofnode_write_prop(node, propname, val, sizeof(value), true); +} + +int ofnode_write_bool(ofnode node, const char *propname, bool value) +{ + if (value) + return ofnode_write_prop(node, propname, NULL, 0, false); + else + return ofnode_delete_prop(node, propname); +} + +int ofnode_delete_prop(ofnode node, const char *propname) +{ + if (ofnode_is_np(node)) { + struct property *prop; + int len; + + prop = of_find_property(ofnode_to_np(node), propname, &len); + if (prop) + return of_remove_property(ofnode_to_np(node), prop); + return 0; + } else { + return fdt_delprop(ofnode_to_fdt(node), ofnode_to_offset(node), + propname); + } } int ofnode_set_enabled(ofnode node, bool value) @@ -1731,7 +1867,30 @@ int ofnode_add_subnode(ofnode node, const char *name, ofnode *subnodep) return ret; /* 0 or -EEXIST */ } -int ofnode_copy_props(ofnode src, ofnode dst) +int ofnode_delete(ofnode *nodep) +{ + ofnode node = *nodep; + int ret; + + assert(ofnode_valid(node)); + if (ofnode_is_np(node)) { + ret = of_remove_node(ofnode_to_np(node)); + } else { + void *fdt = ofnode_to_fdt(node); + int offset = ofnode_to_offset(node); + + ret = fdt_del_node(fdt, offset); + if (ret) + ret = -EFAULT; + } + if (ret) + return ret; + *nodep = ofnode_null(); + + return 0; +} + +int ofnode_copy_props(ofnode dst, ofnode src) { struct ofprop prop; @@ -1754,3 +1913,23 @@ int ofnode_copy_props(ofnode src, ofnode dst) return 0; } + +int ofnode_copy_node(ofnode dst_parent, const char *name, ofnode src, + ofnode *nodep) +{ + ofnode node; + int ret; + + ret = ofnode_add_subnode(dst_parent, name, &node); + if (ret) { + if (ret == -EEXIST) + *nodep = node; + return log_msg_ret("add", ret); + } + ret = ofnode_copy_props(node, src); + if (ret) + return log_msg_ret("cpy", ret); + *nodep = node; + + return 0; +} diff --git a/drivers/core/read.c b/drivers/core/read.c index 49066b59cda..1a4a95cddea 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -181,6 +181,16 @@ fdt_addr_t dev_read_addr_name(const struct udevice *dev, const char *name) return dev_read_addr_index(dev, index); } +void *dev_read_addr_name_ptr(const struct udevice *dev, const char *name) +{ + fdt_addr_t addr = dev_read_addr_name(dev, name); + + if (addr == FDT_ADDR_T_NONE) + return NULL; + + return map_sysmem(addr, 0); +} + fdt_addr_t dev_read_addr_size_name(const struct udevice *dev, const char *name, fdt_size_t *size) { @@ -192,6 +202,17 @@ fdt_addr_t dev_read_addr_size_name(const struct udevice *dev, const char *name, return dev_read_addr_size_index(dev, index, size); } +void *dev_read_addr_size_name_ptr(const struct udevice *dev, const char *name, + fdt_size_t *size) +{ + fdt_addr_t addr = dev_read_addr_size_name(dev, name, size); + + if (addr == FDT_ADDR_T_NONE) + return NULL; + + return map_sysmem(addr, 0); +} + void *dev_remap_addr_name(const struct udevice *dev, const char *name) { fdt_addr_t addr = dev_read_addr_name(dev, name); @@ -405,13 +426,15 @@ int dev_read_alias_highest_id(const char *stem) return fdtdec_get_alias_highest_id(gd->fdt_blob, stem); } -fdt_addr_t dev_read_addr_pci(const struct udevice *dev) +fdt_addr_t dev_read_addr_pci(const struct udevice *dev, fdt_size_t *sizep) { ulong addr; addr = dev_read_addr(dev); + if (sizep) + *sizep = 0; if (addr == FDT_ADDR_T_NONE && !of_live_active()) - addr = devfdt_get_addr_pci(dev); + addr = devfdt_get_addr_pci(dev, sizep); return addr; } diff --git a/drivers/core/root.c b/drivers/core/root.c index 126b3140666..d4ae652bcfb 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -426,7 +426,7 @@ void dm_get_mem(struct dm_stats *stats) stats->tag_size; } -#ifdef CONFIG_ACPIGEN +#if CONFIG_IS_ENABLED(ACPIGEN) static int root_acpi_get_name(const struct udevice *dev, char *out_name) { return acpi_copy_name(out_name, "\\_SB"); diff --git a/drivers/core/util.c b/drivers/core/util.c index aa60fdd15bc..81497df85ff 100644 --- a/drivers/core/util.c +++ b/drivers/core/util.c @@ -30,7 +30,7 @@ int pci_get_devfn(struct udevice *dev) /* Extract the devfn from fdt_pci_addr */ ret = ofnode_read_pci_addr(dev_ofnode(dev), FDT_PCI_SPACE_CONFIG, - "reg", &addr); + "reg", &addr, NULL); if (ret) { if (ret != -ENOENT) return -EINVAL; diff --git a/drivers/crypto/fsl/Kconfig b/drivers/crypto/fsl/Kconfig index 91a51cc5fe7..eaad19633f1 100644 --- a/drivers/crypto/fsl/Kconfig +++ b/drivers/crypto/fsl/Kconfig @@ -77,7 +77,6 @@ endif config FSL_DCP_RNG bool "Enable Random Number Generator support" depends on DM_RNG - default n help Enable support for the hardware based random number generator module of the DCP. It uses the True Random Number Generator (TRNG) diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index 4e80e85d10d..0360d9da142 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -19,6 +19,7 @@ config DFU_WRITE_ALT config DFU_TFTP bool "DFU via TFTP" + depends on NETDEVICES select UPDATE_COMMON select DFU_OVER_TFTP help @@ -111,5 +112,14 @@ config SYS_DFU_MAX_FILE_SIZE the buffer once we've been given the whole file. Define this to the maximum filesize (in bytes) for the buffer. If undefined it defaults to the CONFIG_SYS_DFU_DATA_BUF_SIZE. + +config DFU_NAME_MAX_SIZE + int "Size of the name to be added in dfu entity" + default 32 + depends on DFU + help + This value is used to maximum size. If name is longer than default size, + we need to change the proper maximum size. + endif endmenu diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index cdb3c18b01d..12c54e90ef7 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -387,6 +387,16 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char **argv, int a dfu->data.mmc.lba_blk_size = mmc->read_bl_len; /* + * In case the size is zero (i.e. mmc raw 0x10 0), + * assume the user intends to use whole device. + */ + if (third_arg == 0) { + struct blk_desc *blk_dev = mmc_get_blk_desc(mmc); + + dfu->data.mmc.lba_size = blk_dev->lba; + } + + /* * Check for an extra entry at dfu_alt_info env variable * specifying the mmc HW defined partition number */ diff --git a/drivers/dfu/dfu_mtd.c b/drivers/dfu/dfu_mtd.c index 75e2f6a4215..485586989c8 100644 --- a/drivers/dfu/dfu_mtd.c +++ b/drivers/dfu/dfu_mtd.c @@ -85,27 +85,41 @@ static int mtd_block_op(enum dfu_op op, struct dfu_entity *dfu, while (remaining) { if (erase_op.addr + remaining > lim) { - printf("Limit reached 0x%llx while erasing at offset 0x%llx\n", - lim, off); + printf("Limit reached 0x%llx while erasing at offset 0x%llx, remaining 0x%llx\n", + lim, erase_op.addr, remaining); return -EIO; } + /* Skip the block if it is bad, don't erase it again */ + ret = mtd_block_isbad(mtd, erase_op.addr); + if (ret) { + printf("Skipping %s at 0x%08llx\n", + ret == 1 ? "bad block" : "bbt reserved", + erase_op.addr); + erase_op.addr += mtd->erasesize; + continue; + } + ret = mtd_erase(mtd, &erase_op); if (ret) { - /* Abort if its not a bad block error */ - if (ret != -EIO) { - printf("Failure while erasing at offset 0x%llx\n", - erase_op.fail_addr); - return 0; + /* If this is not -EIO, we have no idea what to do. */ + if (ret == -EIO) { + printf("Marking bad block at 0x%08llx (%d)\n", + erase_op.fail_addr, ret); + ret = mtd_block_markbad(mtd, erase_op.addr); + } + /* Abort if it is not -EIO or can't mark bad */ + if (ret) { + printf("Failure while erasing at offset 0x%llx (%d)\n", + erase_op.fail_addr, ret); + return ret; } - printf("Skipping bad block at 0x%08llx\n", - erase_op.addr); } else { remaining -= mtd->erasesize; } - /* Continue erase behind bad block */ + /* Continue erase behind the current block */ erase_op.addr += mtd->erasesize; } } diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 0af54604211..3c64e894646 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -87,7 +87,6 @@ endif config DMA_LEGACY bool "Legacy DMA support" - default y if FSLDMAFEC help Enable legacy DMA support. This does not use driver model and should be migrated to the new API. diff --git a/drivers/dma/MCD_dmaApi.c b/drivers/dma/MCD_dmaApi.c deleted file mode 100644 index af0e1345220..00000000000 --- a/drivers/dma/MCD_dmaApi.c +++ /dev/null @@ -1,1010 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. - */ - -/*Main C file for multi-channel DMA API. */ - -#include <common.h> - -#include <MCD_dma.h> -#include <MCD_tasksInit.h> -#include <MCD_progCheck.h> - -/********************************************************************/ -/* This is an API-internal pointer to the DMA's registers */ -dmaRegs *MCD_dmaBar; - -/* - * These are the real and model task tables as generated by the - * build process - */ -extern TaskTableEntry MCD_realTaskTableSrc[NCHANNELS]; -extern TaskTableEntry MCD_modelTaskTableSrc[NUMOFVARIANTS]; - -/* - * However, this (usually) gets relocated to on-chip SRAM, at which - * point we access them as these tables - */ -volatile TaskTableEntry *MCD_taskTable; -TaskTableEntry *MCD_modelTaskTable; - -/* - * MCD_chStatus[] is an array of status indicators for remembering - * whether a DMA has ever been attempted on each channel, pausing - * status, etc. - */ -static int MCD_chStatus[NCHANNELS] = { - MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, - MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, - MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, - MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA -}; - -/* Prototypes for local functions */ -static void MCD_memcpy(int *dest, int *src, u32 size); -static void MCD_resmActions(int channel); - -/* - * Buffer descriptors used for storage of progress info for single Dmas - * Also used as storage for the DMA for CRCs for single DMAs - * Otherwise, the DMA does not parse these buffer descriptors - */ -#ifdef MCD_INCLUDE_EU -extern MCD_bufDesc MCD_singleBufDescs[NCHANNELS]; -#else -MCD_bufDesc MCD_singleBufDescs[NCHANNELS]; -#endif -MCD_bufDesc *MCD_relocBuffDesc; - -/* Defines for the debug control register's functions */ -#define DBG_CTL_COMP1_TASK (0x00002000) -#define DBG_CTL_ENABLE (DBG_CTL_AUTO_ARM | \ - DBG_CTL_BREAK | \ - DBG_CTL_INT_BREAK | \ - DBG_CTL_COMP1_TASK) -#define DBG_CTL_DISABLE (DBG_CTL_AUTO_ARM | \ - DBG_CTL_INT_BREAK | \ - DBG_CTL_COMP1_TASK) -#define DBG_KILL_ALL_STAT (0xFFFFFFFF) - -/* Offset to context save area where progress info is stored */ -#define CSAVE_OFFSET 10 - -/* Defines for Byte Swapping */ -#define MCD_BYTE_SWAP_KILLER 0xFFF8888F -#define MCD_NO_BYTE_SWAP_ATALL 0x00040000 - -/* Execution Unit Identifiers */ -#define MAC 0 /* legacy - not used */ -#define LUAC 1 /* legacy - not used */ -#define CRC 2 /* legacy - not used */ -#define LURC 3 /* Logic Unit with CRC */ - -/* Task Identifiers */ -#define TASK_CHAINNOEU 0 -#define TASK_SINGLENOEU 1 -#ifdef MCD_INCLUDE_EU -#define TASK_CHAINEU 2 -#define TASK_SINGLEEU 3 -#define TASK_FECRX 4 -#define TASK_FECTX 5 -#else -#define TASK_CHAINEU 0 -#define TASK_SINGLEEU 1 -#define TASK_FECRX 2 -#define TASK_FECTX 3 -#endif - -/* - * Structure to remember which variant is on which channel - * TBD- need this? - */ -typedef struct MCD_remVariants_struct MCD_remVariant; -struct MCD_remVariants_struct { - int remDestRsdIncr[NCHANNELS]; /* -1,0,1 */ - int remSrcRsdIncr[NCHANNELS]; /* -1,0,1 */ - s16 remDestIncr[NCHANNELS]; /* DestIncr */ - s16 remSrcIncr[NCHANNELS]; /* srcIncr */ - u32 remXferSize[NCHANNELS]; /* xferSize */ -}; - -/* Structure to remember the startDma parameters for each channel */ -MCD_remVariant MCD_remVariants; -/********************************************************************/ -/* Function: MCD_initDma - * Purpose: Initializes the DMA API by setting up a pointer to the DMA - * registers, relocating and creating the appropriate task - * structures, and setting up some global settings - * Arguments: - * dmaBarAddr - pointer to the multichannel DMA registers - * taskTableDest - location to move DMA task code and structs to - * flags - operational parameters - * Return Value: - * MCD_TABLE_UNALIGNED if taskTableDest is not 512-byte aligned - * MCD_OK otherwise - */ -extern u32 MCD_funcDescTab0[]; - -int MCD_initDma(dmaRegs * dmaBarAddr, void *taskTableDest, u32 flags) -{ - int i; - TaskTableEntry *entryPtr; - - /* setup the local pointer to register set */ - MCD_dmaBar = dmaBarAddr; - - /* do we need to move/create a task table */ - if ((flags & MCD_RELOC_TASKS) != 0) { - int fixedSize; - u32 *fixedPtr; - /*int *tablePtr = taskTableDest;TBD */ - int varTabsOffset, funcDescTabsOffset, contextSavesOffset; - int taskDescTabsOffset; - int taskTableSize, varTabsSize, funcDescTabsSize, - contextSavesSize; - int taskDescTabSize; - - int i; - - /* check if physical address is aligned on 512 byte boundary */ - if (((u32) taskTableDest & 0x000001ff) != 0) - return (MCD_TABLE_UNALIGNED); - - /* set up local pointer to task Table */ - MCD_taskTable = taskTableDest; - - /* - * Create a task table: - * - compute aligned base offsets for variable tables and - * function descriptor tables, then - * - loop through the task table and setup the pointers - * - copy over model task table with the the actual task - * descriptor tables - */ - - taskTableSize = NCHANNELS * sizeof(TaskTableEntry); - /* align variable tables to size */ - varTabsOffset = taskTableSize + (u32) taskTableDest; - if ((varTabsOffset & (VAR_TAB_SIZE - 1)) != 0) - varTabsOffset = - (varTabsOffset + VAR_TAB_SIZE) & (~VAR_TAB_SIZE); - /* align function descriptor tables */ - varTabsSize = NCHANNELS * VAR_TAB_SIZE; - funcDescTabsOffset = varTabsOffset + varTabsSize; - - if ((funcDescTabsOffset & (FUNCDESC_TAB_SIZE - 1)) != 0) - funcDescTabsOffset = - (funcDescTabsOffset + - FUNCDESC_TAB_SIZE) & (~FUNCDESC_TAB_SIZE); - - funcDescTabsSize = FUNCDESC_TAB_NUM * FUNCDESC_TAB_SIZE; - contextSavesOffset = funcDescTabsOffset + funcDescTabsSize; - contextSavesSize = (NCHANNELS * CONTEXT_SAVE_SIZE); - fixedSize = - taskTableSize + varTabsSize + funcDescTabsSize + - contextSavesSize; - - /* zero the thing out */ - fixedPtr = (u32 *) taskTableDest; - for (i = 0; i < (fixedSize / 4); i++) - fixedPtr[i] = 0; - - entryPtr = (TaskTableEntry *) MCD_taskTable; - /* set up fixed pointers */ - for (i = 0; i < NCHANNELS; i++) { - /* update ptr to local value */ - entryPtr[i].varTab = (u32) varTabsOffset; - entryPtr[i].FDTandFlags = - (u32) funcDescTabsOffset | MCD_TT_FLAGS_DEF; - entryPtr[i].contextSaveSpace = (u32) contextSavesOffset; - varTabsOffset += VAR_TAB_SIZE; -#ifdef MCD_INCLUDE_EU - /* if not there is only one, just point to the - same one */ - funcDescTabsOffset += FUNCDESC_TAB_SIZE; -#endif - contextSavesOffset += CONTEXT_SAVE_SIZE; - } - /* copy over the function descriptor table */ - for (i = 0; i < FUNCDESC_TAB_NUM; i++) { - MCD_memcpy((void *)(entryPtr[i]. - FDTandFlags & ~MCD_TT_FLAGS_MASK), - (void *)MCD_funcDescTab0, FUNCDESC_TAB_SIZE); - } - - /* copy model task table to where the context saves stuff - leaves off */ - MCD_modelTaskTable = (TaskTableEntry *) contextSavesOffset; - - MCD_memcpy((void *)MCD_modelTaskTable, - (void *)MCD_modelTaskTableSrc, - NUMOFVARIANTS * sizeof(TaskTableEntry)); - - /* point to local version of model task table */ - entryPtr = MCD_modelTaskTable; - taskDescTabsOffset = (u32) MCD_modelTaskTable + - (NUMOFVARIANTS * sizeof(TaskTableEntry)); - - /* copy actual task code and update TDT ptrs in local - model task table */ - for (i = 0; i < NUMOFVARIANTS; i++) { - taskDescTabSize = - entryPtr[i].TDTend - entryPtr[i].TDTstart + 4; - MCD_memcpy((void *)taskDescTabsOffset, - (void *)entryPtr[i].TDTstart, - taskDescTabSize); - entryPtr[i].TDTstart = (u32) taskDescTabsOffset; - taskDescTabsOffset += taskDescTabSize; - entryPtr[i].TDTend = (u32) taskDescTabsOffset - 4; - } -#ifdef MCD_INCLUDE_EU - /* Tack single DMA BDs onto end of code so API controls - where they are since DMA might write to them */ - MCD_relocBuffDesc = - (MCD_bufDesc *) (entryPtr[NUMOFVARIANTS - 1].TDTend + 4); -#else - /* DMA does not touch them so they can be wherever and we - don't need to waste SRAM on them */ - MCD_relocBuffDesc = MCD_singleBufDescs; -#endif - } else { - /* point the would-be relocated task tables and the - buffer descriptors to the ones the linker generated */ - - if (((u32) MCD_realTaskTableSrc & 0x000001ff) != 0) - return (MCD_TABLE_UNALIGNED); - - /* need to add code to make sure that every thing else is - aligned properly TBD. this is problematic if we init - more than once or after running tasks, need to add - variable to see if we have aleady init'd */ - entryPtr = MCD_realTaskTableSrc; - for (i = 0; i < NCHANNELS; i++) { - if (((entryPtr[i].varTab & (VAR_TAB_SIZE - 1)) != 0) || - ((entryPtr[i]. - FDTandFlags & (FUNCDESC_TAB_SIZE - 1)) != 0)) - return (MCD_TABLE_UNALIGNED); - } - - MCD_taskTable = MCD_realTaskTableSrc; - MCD_modelTaskTable = MCD_modelTaskTableSrc; - MCD_relocBuffDesc = MCD_singleBufDescs; - } - - /* Make all channels as totally inactive, and remember them as such: */ - - MCD_dmaBar->taskbar = (u32) MCD_taskTable; - for (i = 0; i < NCHANNELS; i++) { - MCD_dmaBar->taskControl[i] = 0x0; - MCD_chStatus[i] = MCD_NO_DMA; - } - - /* Set up pausing mechanism to inactive state: */ - /* no particular values yet for either comparator registers */ - MCD_dmaBar->debugComp1 = 0; - MCD_dmaBar->debugComp2 = 0; - MCD_dmaBar->debugControl = DBG_CTL_DISABLE; - MCD_dmaBar->debugStatus = DBG_KILL_ALL_STAT; - - /* enable or disable commbus prefetch, really need an ifdef or - something to keep from trying to set this in the 8220 */ - if ((flags & MCD_COMM_PREFETCH_EN) != 0) - MCD_dmaBar->ptdControl &= ~PTD_CTL_COMM_PREFETCH; - else - MCD_dmaBar->ptdControl |= PTD_CTL_COMM_PREFETCH; - - return (MCD_OK); -} - -/*********************** End of MCD_initDma() ***********************/ - -/********************************************************************/ -/* Function: MCD_dmaStatus - * Purpose: Returns the status of the DMA on the requested channel - * Arguments: channel - channel number - * Returns: Predefined status indicators - */ -int MCD_dmaStatus(int channel) -{ - u16 tcrValue; - - if ((channel < 0) || (channel >= NCHANNELS)) - return (MCD_CHANNEL_INVALID); - - tcrValue = MCD_dmaBar->taskControl[channel]; - if ((tcrValue & TASK_CTL_EN) == 0) { /* nothing running */ - /* if last reported with task enabled */ - if (MCD_chStatus[channel] == MCD_RUNNING - || MCD_chStatus[channel] == MCD_IDLE) - MCD_chStatus[channel] = MCD_DONE; - } else { /* something is running */ - - /* There are three possibilities: paused, running or idle. */ - if (MCD_chStatus[channel] == MCD_RUNNING - || MCD_chStatus[channel] == MCD_IDLE) { - MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT; - /* This register is selected to know which initiator is - actually asserted. */ - if ((MCD_dmaBar->ptdDebug >> channel) & 0x1) - MCD_chStatus[channel] = MCD_RUNNING; - else - MCD_chStatus[channel] = MCD_IDLE; - /* do not change the status if it is already paused. */ - } - } - return MCD_chStatus[channel]; -} - -/******************** End of MCD_dmaStatus() ************************/ - -/********************************************************************/ -/* Function: MCD_startDma - * Ppurpose: Starts a particular kind of DMA - * Arguments: - * srcAddr - the channel on which to run the DMA - * srcIncr - the address to move data from, or buffer-descriptor address - * destAddr - the amount to increment the source address per transfer - * destIncr - the address to move data to - * dmaSize - the amount to increment the destination address per transfer - * xferSize - the number bytes in of each data movement (1, 2, or 4) - * initiator - what device initiates the DMA - * priority - priority of the DMA - * flags - flags describing the DMA - * funcDesc - description of byte swapping, bit swapping, and CRC actions - * srcAddrVirt - virtual buffer descriptor address TBD - * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK - */ - -int MCD_startDma(int channel, s8 * srcAddr, s16 srcIncr, s8 * destAddr, - s16 destIncr, u32 dmaSize, u32 xferSize, u32 initiator, - int priority, u32 flags, u32 funcDesc -#ifdef MCD_NEED_ADDR_TRANS - s8 * srcAddrVirt -#endif - ) -{ - int srcRsdIncr, destRsdIncr; - int *cSave; - short xferSizeIncr; - int tcrCount = 0; -#ifdef MCD_INCLUDE_EU - u32 *realFuncArray; -#endif - - if ((channel < 0) || (channel >= NCHANNELS)) - return (MCD_CHANNEL_INVALID); - - /* tbd - need to determine the proper response to a bad funcDesc when - not including EU functions, for now, assign a benign funcDesc, but - maybe should return an error */ -#ifndef MCD_INCLUDE_EU - funcDesc = MCD_FUNC_NOEU1; -#endif - -#ifdef MCD_DEBUG - printf("startDma:Setting up params\n"); -#endif - /* Set us up for task-wise priority. We don't technically need to do - this on every start, but since the register involved is in the same - longword as other registers that users are in control of, setting - it more than once is probably preferable. That since the - documentation doesn't seem to be completely consistent about the - nature of the PTD control register. */ - MCD_dmaBar->ptdControl |= (u16) 0x8000; - - /* Not sure what we need to keep here rtm TBD */ -#if 1 - /* Calculate additional parameters to the regular DMA calls. */ - srcRsdIncr = srcIncr < 0 ? -1 : (srcIncr > 0 ? 1 : 0); - destRsdIncr = destIncr < 0 ? -1 : (destIncr > 0 ? 1 : 0); - - xferSizeIncr = (xferSize & 0xffff) | 0x20000000; - - /* Remember for each channel which variant is running. */ - MCD_remVariants.remSrcRsdIncr[channel] = srcRsdIncr; - MCD_remVariants.remDestRsdIncr[channel] = destRsdIncr; - MCD_remVariants.remDestIncr[channel] = destIncr; - MCD_remVariants.remSrcIncr[channel] = srcIncr; - MCD_remVariants.remXferSize[channel] = xferSize; -#endif - - cSave = - (int *)(MCD_taskTable[channel].contextSaveSpace) + CSAVE_OFFSET + - CURRBD; - -#ifdef MCD_INCLUDE_EU - /* may move this to EU specific calls */ - realFuncArray = - (u32 *) (MCD_taskTable[channel].FDTandFlags & 0xffffff00); - /* Modify the LURC's normal and byte-residue-loop functions according - to parameter. */ - realFuncArray[(LURC * 16)] = xferSize == 4 ? - funcDesc : xferSize == 2 ? - funcDesc & 0xfffff00f : funcDesc & 0xffff000f; - realFuncArray[(LURC * 16 + 1)] = - (funcDesc & MCD_BYTE_SWAP_KILLER) | MCD_NO_BYTE_SWAP_ATALL; -#endif - /* Write the initiator field in the TCR, and also set the - initiator-hold bit. Note that,due to a hardware quirk, this could - collide with an MDE access to the initiator-register file, so we - have to verify that the write reads back correctly. */ - - MCD_dmaBar->taskControl[channel] = - (initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM; - - while (((MCD_dmaBar->taskControl[channel] & 0x1fff) != - ((initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM)) - && (tcrCount < 1000)) { - tcrCount++; - /*MCD_dmaBar->ptd_tcr[channel] = (initiator << 8) | 0x0020; */ - MCD_dmaBar->taskControl[channel] = - (initiator << 8) | TASK_CTL_HIPRITSKEN | - TASK_CTL_HLDINITNUM; - } - - MCD_dmaBar->priority[channel] = (u8) priority & PRIORITY_PRI_MASK; - /* should be albe to handle this stuff with only one write to ts reg - - tbd */ - if (channel < 8 && channel >= 0) { - MCD_dmaBar->taskSize0 &= ~(0xf << (7 - channel) * 4); - MCD_dmaBar->taskSize0 |= - (xferSize & 3) << (((7 - channel) * 4) + 2); - MCD_dmaBar->taskSize0 |= (xferSize & 3) << ((7 - channel) * 4); - } else { - MCD_dmaBar->taskSize1 &= ~(0xf << (15 - channel) * 4); - MCD_dmaBar->taskSize1 |= - (xferSize & 3) << (((15 - channel) * 4) + 2); - MCD_dmaBar->taskSize1 |= (xferSize & 3) << ((15 - channel) * 4); - } - - /* setup task table flags/options which mostly control the line - buffers */ - MCD_taskTable[channel].FDTandFlags &= ~MCD_TT_FLAGS_MASK; - MCD_taskTable[channel].FDTandFlags |= (MCD_TT_FLAGS_MASK & flags); - - if (flags & MCD_FECTX_DMA) { - /* TDTStart and TDTEnd */ - MCD_taskTable[channel].TDTstart = - MCD_modelTaskTable[TASK_FECTX].TDTstart; - MCD_taskTable[channel].TDTend = - MCD_modelTaskTable[TASK_FECTX].TDTend; - MCD_startDmaENetXmit((char *)srcAddr, (char *)srcAddr, - (char *)destAddr, MCD_taskTable, - channel); - } else if (flags & MCD_FECRX_DMA) { - /* TDTStart and TDTEnd */ - MCD_taskTable[channel].TDTstart = - MCD_modelTaskTable[TASK_FECRX].TDTstart; - MCD_taskTable[channel].TDTend = - MCD_modelTaskTable[TASK_FECRX].TDTend; - MCD_startDmaENetRcv((char *)srcAddr, (char *)srcAddr, - (char *)destAddr, MCD_taskTable, - channel); - } else if (flags & MCD_SINGLE_DMA) { - /* this buffer descriptor is used for storing off initial - parameters for later progress query calculation and for the - DMA to write the resulting checksum. The DMA does not use - this to determine how to operate, that info is passed with - the init routine */ - MCD_relocBuffDesc[channel].srcAddr = srcAddr; - MCD_relocBuffDesc[channel].destAddr = destAddr; - - /* definitely not its final value */ - MCD_relocBuffDesc[channel].lastDestAddr = destAddr; - - MCD_relocBuffDesc[channel].dmaSize = dmaSize; - MCD_relocBuffDesc[channel].flags = 0; /* not used */ - MCD_relocBuffDesc[channel].csumResult = 0; /* not used */ - MCD_relocBuffDesc[channel].next = 0; /* not used */ - - /* Initialize the progress-querying stuff to show no - progress: */ - ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[SRCPTR + CSAVE_OFFSET] = (int)srcAddr; - ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[DESTPTR + CSAVE_OFFSET] = (int)destAddr; - ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0; - ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[CURRBD + CSAVE_OFFSET] = -(u32) & (MCD_relocBuffDesc[channel]); - /* tbd - need to keep the user from trying to call the EU - routine when MCD_INCLUDE_EU is not defined */ - if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) { - /* TDTStart and TDTEnd */ - MCD_taskTable[channel].TDTstart = - MCD_modelTaskTable[TASK_SINGLENOEU].TDTstart; - MCD_taskTable[channel].TDTend = - MCD_modelTaskTable[TASK_SINGLENOEU].TDTend; - MCD_startDmaSingleNoEu((char *)srcAddr, srcIncr, - (char *)destAddr, destIncr, - (int)dmaSize, xferSizeIncr, - flags, (int *) - &(MCD_relocBuffDesc[channel]), - cSave, MCD_taskTable, channel); - } else { - /* TDTStart and TDTEnd */ - MCD_taskTable[channel].TDTstart = - MCD_modelTaskTable[TASK_SINGLEEU].TDTstart; - MCD_taskTable[channel].TDTend = - MCD_modelTaskTable[TASK_SINGLEEU].TDTend; - MCD_startDmaSingleEu((char *)srcAddr, srcIncr, - (char *)destAddr, destIncr, - (int)dmaSize, xferSizeIncr, - flags, (int *) - &(MCD_relocBuffDesc[channel]), - cSave, MCD_taskTable, channel); - } - } else { /* chained DMAS */ - /* Initialize the progress-querying stuff to show no - progress: */ -#if 1 - /* (!defined(MCD_NEED_ADDR_TRANS)) */ - ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[SRCPTR + CSAVE_OFFSET] - = (int)((MCD_bufDesc *) srcAddr)->srcAddr; - ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[DESTPTR + CSAVE_OFFSET] - = (int)((MCD_bufDesc *) srcAddr)->destAddr; -#else - /* if using address translation, need the virtual addr of the - first buffdesc */ - ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[SRCPTR + CSAVE_OFFSET] - = (int)((MCD_bufDesc *) srcAddrVirt)->srcAddr; - ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[DESTPTR + CSAVE_OFFSET] - = (int)((MCD_bufDesc *) srcAddrVirt)->destAddr; -#endif - ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0; - ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[CURRBD + CSAVE_OFFSET] = (u32) srcAddr; - - if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) { - /*TDTStart and TDTEnd */ - MCD_taskTable[channel].TDTstart = - MCD_modelTaskTable[TASK_CHAINNOEU].TDTstart; - MCD_taskTable[channel].TDTend = - MCD_modelTaskTable[TASK_CHAINNOEU].TDTend; - MCD_startDmaChainNoEu((int *)srcAddr, srcIncr, - destIncr, xferSize, - xferSizeIncr, cSave, - MCD_taskTable, channel); - } else { - /*TDTStart and TDTEnd */ - MCD_taskTable[channel].TDTstart = - MCD_modelTaskTable[TASK_CHAINEU].TDTstart; - MCD_taskTable[channel].TDTend = - MCD_modelTaskTable[TASK_CHAINEU].TDTend; - MCD_startDmaChainEu((int *)srcAddr, srcIncr, destIncr, - xferSize, xferSizeIncr, cSave, - MCD_taskTable, channel); - } - } - MCD_chStatus[channel] = MCD_IDLE; - return (MCD_OK); -} - -/************************ End of MCD_startDma() *********************/ - -/********************************************************************/ -/* Function: MCD_XferProgrQuery - * Purpose: Returns progress of DMA on requested channel - * Arguments: channel - channel to retrieve progress for - * progRep - pointer to user supplied MCD_XferProg struct - * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK - * - * Notes: - * MCD_XferProgrQuery() upon completing or after aborting a DMA, or - * while the DMA is in progress, this function returns the first - * DMA-destination address not (or not yet) used in the DMA. When - * encountering a non-ready buffer descriptor, the information for - * the last completed descriptor is returned. - * - * MCD_XferProgQuery() has to avoid the possibility of getting - * partially-updated information in the event that we should happen - * to query DMA progress just as the DMA is updating it. It does that - * by taking advantage of the fact context is not saved frequently for - * the most part. We therefore read it at least twice until we get the - * same information twice in a row. - * - * Because a small, but not insignificant, amount of time is required - * to write out the progress-query information, especially upon - * completion of the DMA, it would be wise to guarantee some time lag - * between successive readings of the progress-query information. - */ - -/* How many iterations of the loop below to execute to stabilize values */ -#define STABTIME 0 - -int MCD_XferProgrQuery(int channel, MCD_XferProg * progRep) -{ - MCD_XferProg prevRep; - int again; /* true if we are to try again to ge - consistent results */ - int i; /* used as a time-waste counter */ - int destDiffBytes; /* Total no of bytes that we think actually - got xfered. */ - int numIterations; /* number of iterations */ - int bytesNotXfered; /* bytes that did not get xfered. */ - s8 *LWAlignedInitDestAddr, *LWAlignedCurrDestAddr; - int subModVal, addModVal; /* Mode values to added and subtracted - from the final destAddr */ - - if ((channel < 0) || (channel >= NCHANNELS)) - return (MCD_CHANNEL_INVALID); - - /* Read a trial value for the progress-reporting values */ - prevRep.lastSrcAddr = - (s8 *) ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[SRCPTR + CSAVE_OFFSET]; - prevRep.lastDestAddr = - (s8 *) ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[DESTPTR + CSAVE_OFFSET]; - prevRep.dmaSize = - ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[DCOUNT + - CSAVE_OFFSET]; - prevRep.currBufDesc = - (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[CURRBD + CSAVE_OFFSET]; - /* Repeatedly reread those values until they match previous values: */ - do { - /* Waste a little bit of time to ensure stability: */ - for (i = 0; i < STABTIME; i++) { - /* make sure this loop does something so that it - doesn't get optimized out */ - i += i >> 2; - } - /* Check them again: */ - progRep->lastSrcAddr = - (s8 *) ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[SRCPTR + CSAVE_OFFSET]; - progRep->lastDestAddr = - (s8 *) ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[DESTPTR + CSAVE_OFFSET]; - progRep->dmaSize = - ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[DCOUNT + CSAVE_OFFSET]; - progRep->currBufDesc = - (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel]. - contextSaveSpace)[CURRBD + CSAVE_OFFSET]; - /* See if they match: */ - if (prevRep.lastSrcAddr != progRep->lastSrcAddr - || prevRep.lastDestAddr != progRep->lastDestAddr - || prevRep.dmaSize != progRep->dmaSize - || prevRep.currBufDesc != progRep->currBufDesc) { - /* If they don't match, remember previous values and - try again: */ - prevRep.lastSrcAddr = progRep->lastSrcAddr; - prevRep.lastDestAddr = progRep->lastDestAddr; - prevRep.dmaSize = progRep->dmaSize; - prevRep.currBufDesc = progRep->currBufDesc; - again = MCD_TRUE; - } else - again = MCD_FALSE; - } while (again == MCD_TRUE); - - /* Update the dCount, srcAddr and destAddr */ - /* To calculate dmaCount, we consider destination address. C - overs M1,P1,Z for destination */ - switch (MCD_remVariants.remDestRsdIncr[channel]) { - case MINUS1: - subModVal = - ((int)progRep-> - lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) - - 1); - addModVal = - ((int)progRep->currBufDesc-> - destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1); - LWAlignedInitDestAddr = - (progRep->currBufDesc->destAddr) - addModVal; - LWAlignedCurrDestAddr = (progRep->lastDestAddr) - subModVal; - destDiffBytes = LWAlignedInitDestAddr - LWAlignedCurrDestAddr; - bytesNotXfered = - (destDiffBytes / MCD_remVariants.remDestIncr[channel]) * - (MCD_remVariants.remDestIncr[channel] - + MCD_remVariants.remXferSize[channel]); - progRep->dmaSize = - destDiffBytes - bytesNotXfered + addModVal - subModVal; - break; - case ZERO: - progRep->lastDestAddr = progRep->currBufDesc->destAddr; - break; - case PLUS1: - /* This value has to be subtracted from the final - calculated dCount. */ - subModVal = - ((int)progRep->currBufDesc-> - destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1); - /* These bytes are already in lastDestAddr. */ - addModVal = - ((int)progRep-> - lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) - - 1); - LWAlignedInitDestAddr = - (progRep->currBufDesc->destAddr) - subModVal; - LWAlignedCurrDestAddr = (progRep->lastDestAddr) - addModVal; - destDiffBytes = (progRep->lastDestAddr - LWAlignedInitDestAddr); - numIterations = - (LWAlignedCurrDestAddr - - LWAlignedInitDestAddr) / - MCD_remVariants.remDestIncr[channel]; - bytesNotXfered = - numIterations * (MCD_remVariants.remDestIncr[channel] - - MCD_remVariants.remXferSize[channel]); - progRep->dmaSize = destDiffBytes - bytesNotXfered - subModVal; - break; - default: - break; - } - - /* This covers M1,P1,Z for source */ - switch (MCD_remVariants.remSrcRsdIncr[channel]) { - case MINUS1: - progRep->lastSrcAddr = - progRep->currBufDesc->srcAddr + - (MCD_remVariants.remSrcIncr[channel] * - (progRep->dmaSize / MCD_remVariants.remXferSize[channel])); - break; - case ZERO: - progRep->lastSrcAddr = progRep->currBufDesc->srcAddr; - break; - case PLUS1: - progRep->lastSrcAddr = - progRep->currBufDesc->srcAddr + - (MCD_remVariants.remSrcIncr[channel] * - (progRep->dmaSize / MCD_remVariants.remXferSize[channel])); - break; - default: - break; - } - - return (MCD_OK); -} - -/******************* End of MCD_XferProgrQuery() ********************/ - -/********************************************************************/ -/* MCD_resmActions() does the majority of the actions of a DMA resume. - * It is called from MCD_killDma() and MCD_resumeDma(). It has to be - * a separate function because the kill function has to negate the task - * enable before resuming it, but the resume function has to do nothing - * if there is no DMA on that channel (i.e., if the enable bit is 0). - */ -static void MCD_resmActions(int channel) -{ - MCD_dmaBar->debugControl = DBG_CTL_DISABLE; - MCD_dmaBar->debugStatus = MCD_dmaBar->debugStatus; - /* This register is selected to know which initiator is - actually asserted. */ - MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT; - - if ((MCD_dmaBar->ptdDebug >> channel) & 0x1) - MCD_chStatus[channel] = MCD_RUNNING; - else - MCD_chStatus[channel] = MCD_IDLE; -} - -/********************* End of MCD_resmActions() *********************/ - -/********************************************************************/ -/* Function: MCD_killDma - * Purpose: Halt the DMA on the requested channel, without any - * intention of resuming the DMA. - * Arguments: channel - requested channel - * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK - * - * Notes: - * A DMA may be killed from any state, including paused state, and it - * always goes to the MCD_HALTED state even if it is killed while in - * the MCD_NO_DMA or MCD_IDLE states. - */ -int MCD_killDma(int channel) -{ - /* MCD_XferProg progRep; */ - - if ((channel < 0) || (channel >= NCHANNELS)) - return (MCD_CHANNEL_INVALID); - - MCD_dmaBar->taskControl[channel] = 0x0; - MCD_resumeDma(channel); - /* - * This must be after the write to the TCR so that the task doesn't - * start up again momentarily, and before the status assignment so - * as to override whatever MCD_resumeDma() may do to the channel - * status. - */ - MCD_chStatus[channel] = MCD_HALTED; - - /* - * Update the current buffer descriptor's lastDestAddr field - * - * MCD_XferProgrQuery (channel, &progRep); - * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr; - */ - return (MCD_OK); -} - -/************************ End of MCD_killDma() **********************/ - -/********************************************************************/ -/* Function: MCD_continDma - * Purpose: Continue a DMA which as stopped due to encountering an - * unready buffer descriptor. - * Arguments: channel - channel to continue the DMA on - * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK - * - * Notes: - * This routine does not check to see if there is a task which can - * be continued. Also this routine should not be used with single DMAs. - */ -int MCD_continDma(int channel) -{ - if ((channel < 0) || (channel >= NCHANNELS)) - return (MCD_CHANNEL_INVALID); - - MCD_dmaBar->taskControl[channel] |= TASK_CTL_EN; - MCD_chStatus[channel] = MCD_RUNNING; - - return (MCD_OK); -} - -/********************** End of MCD_continDma() **********************/ - -/********************************************************************* - * MCD_pauseDma() and MCD_resumeDma() below use the DMA's debug unit - * to freeze a task and resume it. We freeze a task by breakpointing - * on the stated task. That is, not any specific place in the task, - * but any time that task executes. In particular, when that task - * executes, we want to freeze that task and only that task. - * - * The bits of the debug control register influence interrupts vs. - * breakpoints as follows: - * - Bits 14 and 0 enable or disable debug functions. If enabled, you - * will get the interrupt but you may or may not get a breakpoint. - * - Bits 2 and 1 decide whether you also get a breakpoint in addition - * to an interrupt. - * - * The debug unit can do these actions in response to either internally - * detected breakpoint conditions from the comparators, or in response - * to the external breakpoint pin, or both. - * - Bits 14 and 1 perform the above-described functions for - * internally-generated conditions, i.e., the debug comparators. - * - Bits 0 and 2 perform the above-described functions for external - * conditions, i.e., the breakpoint external pin. - * - * Note that, although you "always" get the interrupt when you turn - * the debug functions, the interrupt can nevertheless, if desired, be - * masked by the corresponding bit in the PTD's IMR. Note also that - * this means that bits 14 and 0 must enable debug functions before - * bits 1 and 2, respectively, have any effect. - * - * NOTE: It's extremely important to not pause more than one DMA channel - * at a time. - ********************************************************************/ - -/********************************************************************/ -/* Function: MCD_pauseDma - * Purpose: Pauses the DMA on a given channel (if any DMA is running - * on that channel). - * Arguments: channel - * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK - */ -int MCD_pauseDma(int channel) -{ - /* MCD_XferProg progRep; */ - - if ((channel < 0) || (channel >= NCHANNELS)) - return (MCD_CHANNEL_INVALID); - - if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN) { - MCD_dmaBar->debugComp1 = channel; - MCD_dmaBar->debugControl = - DBG_CTL_ENABLE | (1 << (channel + 16)); - MCD_chStatus[channel] = MCD_PAUSED; - - /* - * Update the current buffer descriptor's lastDestAddr field - * - * MCD_XferProgrQuery (channel, &progRep); - * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr; - */ - } - return (MCD_OK); -} - -/************************* End of MCD_pauseDma() ********************/ - -/********************************************************************/ -/* Function: MCD_resumeDma - * Purpose: Resumes the DMA on a given channel (if any DMA is - * running on that channel). - * Arguments: channel - channel on which to resume DMA - * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK - */ -int MCD_resumeDma(int channel) -{ - if ((channel < 0) || (channel >= NCHANNELS)) - return (MCD_CHANNEL_INVALID); - - if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN) - MCD_resmActions(channel); - - return (MCD_OK); -} - -/************************ End of MCD_resumeDma() ********************/ - -/********************************************************************/ -/* Function: MCD_csumQuery - * Purpose: Provide the checksum after performing a non-chained DMA - * Arguments: channel - channel to report on - * csum - pointer to where to write the checksum/CRC - * Returns: MCD_ERROR if the channel is invalid, else MCD_OK - * - * Notes: - * - */ -int MCD_csumQuery(int channel, u32 * csum) -{ -#ifdef MCD_INCLUDE_EU - if ((channel < 0) || (channel >= NCHANNELS)) - return (MCD_CHANNEL_INVALID); - - *csum = MCD_relocBuffDesc[channel].csumResult; - return (MCD_OK); -#else - return (MCD_ERROR); -#endif -} - -/*********************** End of MCD_resumeDma() *********************/ - -/********************************************************************/ -/* Function: MCD_getCodeSize - * Purpose: Provide the size requirements of the microcoded tasks - * Returns: Size in bytes - */ -int MCD_getCodeSize(void) -{ -#ifdef MCD_INCLUDE_EU - return (0x2b5c); -#else - return (0x173c); -#endif -} - -/********************** End of MCD_getCodeSize() ********************/ - -/********************************************************************/ -/* Function: MCD_getVersion - * Purpose: Provide the version string and number - * Arguments: longVersion - user supplied pointer to a pointer to a char - * which points to the version string - * Returns: Version number and version string (by reference) - */ -char MCD_versionString[] = "Multi-channel DMA API Alpha v0.3 (2004-04-26)"; -#define MCD_REV_MAJOR 0x00 -#define MCD_REV_MINOR 0x03 - -int MCD_getVersion(char **longVersion) -{ - *longVersion = MCD_versionString; - return ((MCD_REV_MAJOR << 8) | MCD_REV_MINOR); -} - -/********************** End of MCD_getVersion() *********************/ - -/********************************************************************/ -/* Private version of memcpy() - * Note that everything this is used for is longword-aligned. - */ -static void MCD_memcpy(int *dest, int *src, u32 size) -{ - u32 i; - - for (i = 0; i < size; i += sizeof(int), dest++, src++) - *dest = *src; -} diff --git a/drivers/dma/MCD_tasks.c b/drivers/dma/MCD_tasks.c deleted file mode 100644 index 453d95413da..00000000000 --- a/drivers/dma/MCD_tasks.c +++ /dev/null @@ -1,2413 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. - */ - -/* Contains task code and structures for Multi-channel DMA */ - -#include <common.h> - -#include <MCD_dma.h> - -u32 MCD_varTab0[]; -u32 MCD_varTab1[]; -u32 MCD_varTab2[]; -u32 MCD_varTab3[]; -u32 MCD_varTab4[]; -u32 MCD_varTab5[]; -u32 MCD_varTab6[]; -u32 MCD_varTab7[]; -u32 MCD_varTab8[]; -u32 MCD_varTab9[]; -u32 MCD_varTab10[]; -u32 MCD_varTab11[]; -u32 MCD_varTab12[]; -u32 MCD_varTab13[]; -u32 MCD_varTab14[]; -u32 MCD_varTab15[]; - -u32 MCD_funcDescTab0[]; -#ifdef MCD_INCLUDE_EU -u32 MCD_funcDescTab1[]; -u32 MCD_funcDescTab2[]; -u32 MCD_funcDescTab3[]; -u32 MCD_funcDescTab4[]; -u32 MCD_funcDescTab5[]; -u32 MCD_funcDescTab6[]; -u32 MCD_funcDescTab7[]; -u32 MCD_funcDescTab8[]; -u32 MCD_funcDescTab9[]; -u32 MCD_funcDescTab10[]; -u32 MCD_funcDescTab11[]; -u32 MCD_funcDescTab12[]; -u32 MCD_funcDescTab13[]; -u32 MCD_funcDescTab14[]; -u32 MCD_funcDescTab15[]; -#endif - -u32 MCD_contextSave0[]; -u32 MCD_contextSave1[]; -u32 MCD_contextSave2[]; -u32 MCD_contextSave3[]; -u32 MCD_contextSave4[]; -u32 MCD_contextSave5[]; -u32 MCD_contextSave6[]; -u32 MCD_contextSave7[]; -u32 MCD_contextSave8[]; -u32 MCD_contextSave9[]; -u32 MCD_contextSave10[]; -u32 MCD_contextSave11[]; -u32 MCD_contextSave12[]; -u32 MCD_contextSave13[]; -u32 MCD_contextSave14[]; -u32 MCD_contextSave15[]; - -u32 MCD_realTaskTableSrc[] = { - 0x00000000, - 0x00000000, - (u32) MCD_varTab0, /* Task 0 Variable Table */ - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ - 0x00000000, - 0x00000000, - (u32) MCD_contextSave0, /* Task 0 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab1, /* Task 1 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab1, /* Task 1 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave1, /* Task 1 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab2, /* Task 2 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab2, /* Task 2 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave2, /* Task 2 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab3, /* Task 3 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab3, /* Task 3 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave3, /* Task 3 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab4, /* Task 4 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab4, /* Task 4 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave4, /* Task 4 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab5, /* Task 5 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab5, /* Task 5 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave5, /* Task 5 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab6, /* Task 6 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab6, /* Task 6 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave6, /* Task 6 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab7, /* Task 7 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab7, /* Task 7 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave7, /* Task 7 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab8, /* Task 8 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab8, /* Task 8 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave8, /* Task 8 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab9, /* Task 9 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab9, /* Task 9 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave9, /* Task 9 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab10, /* Task 10 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab10, /* Task 10 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave10, /* Task 10 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab11, /* Task 11 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab11, /* Task 11 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave11, /* Task 11 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab12, /* Task 12 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab12, /* Task 12 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave12, /* Task 12 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab13, /* Task 13 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab13, /* Task 13 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave13, /* Task 13 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab14, /* Task 14 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab14, /* Task 14 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave14, /* Task 14 context save space */ - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_varTab15, /* Task 15 Variable Table */ -#ifdef MCD_INCLUDE_EU - (u32) MCD_funcDescTab15, /* Task 15 Fn Desc. Table & Flags */ -#else - (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ -#endif - 0x00000000, - 0x00000000, - (u32) MCD_contextSave15, /* Task 15 context save space */ - 0x00000000, -}; - -u32 MCD_varTab0[] = { /* Task 0 Variable Table */ - 0x00000000, /* var[0] */ - 0x00000000, /* var[1] */ - 0x00000000, /* var[2] */ - 0x00000000, /* var[3] */ - 0x00000000, /* var[4] */ - 0x00000000, /* var[5] */ - 0x00000000, /* var[6] */ - 0x00000000, /* var[7] */ - 0x00000000, /* var[8] */ - 0x00000000, /* var[9] */ - 0x00000000, /* var[10] */ - 0x00000000, /* var[11] */ - 0x00000000, /* var[12] */ - 0x00000000, /* var[13] */ - 0x00000000, /* var[14] */ - 0x00000000, /* var[15] */ - 0x00000000, /* var[16] */ - 0x00000000, /* var[17] */ - 0x00000000, /* var[18] */ - 0x00000000, /* var[19] */ - 0x00000000, /* var[20] */ - 0x00000000, /* var[21] */ - 0x00000000, /* var[22] */ - 0x00000000, /* var[23] */ - 0xe0000000, /* inc[0] */ - 0x20000000, /* inc[1] */ - 0x2000ffff, /* inc[2] */ - 0x00000000, /* inc[3] */ - 0x00000000, /* inc[4] */ - 0x00000000, /* inc[5] */ - 0x00000000, /* inc[6] */ - 0x00000000, /* inc[7] */ -}; - -u32 MCD_varTab1[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab2[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab3[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab4[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab5[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab6[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab7[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab8[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab9[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab10[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab11[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab12[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab13[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab14[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_varTab15[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xe0000000, - 0x20000000, - 0x2000ffff, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_funcDescTab0[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -#ifdef MCD_INCLUDE_EU -u32 MCD_funcDescTab1[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab2[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab3[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab4[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab5[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab6[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab7[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab8[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab9[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab10[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab11[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab12[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab13[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab14[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; - -u32 MCD_funcDescTab15[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0xa0045670, - 0xa0000000, - 0xa0000000, - 0x20000000, - 0x21800000, - 0x21e00000, - 0x20400000, - 0x20500000, - 0x205a0000, - 0x20a00000, - 0x202fa000, - 0x202f9000, - 0x202ea000, - 0x202da000, - 0x202e2000, - 0x202f2000, -}; -#endif /*MCD_INCLUDE_EU */ - -u32 MCD_contextSave0[128]; /* Task 0 context save space */ -u32 MCD_contextSave1[128]; /* Task 1 context save space */ -u32 MCD_contextSave2[128]; /* Task 2 context save space */ -u32 MCD_contextSave3[128]; /* Task 3 context save space */ -u32 MCD_contextSave4[128]; /* Task 4 context save space */ -u32 MCD_contextSave5[128]; /* Task 5 context save space */ -u32 MCD_contextSave6[128]; /* Task 6 context save space */ -u32 MCD_contextSave7[128]; /* Task 7 context save space */ -u32 MCD_contextSave8[128]; /* Task 8 context save space */ -u32 MCD_contextSave9[128]; /* Task 9 context save space */ -u32 MCD_contextSave10[128]; /* Task 10 context save space */ -u32 MCD_contextSave11[128]; /* Task 11 context save space */ -u32 MCD_contextSave12[128]; /* Task 12 context save space */ -u32 MCD_contextSave13[128]; /* Task 13 context save space */ -u32 MCD_contextSave14[128]; /* Task 14 context save space */ -u32 MCD_contextSave15[128]; /* Task 15 context save space */ - -u32 MCD_ChainNoEu_TDT[]; -u32 MCD_SingleNoEu_TDT[]; -#ifdef MCD_INCLUDE_EU -u32 MCD_ChainEu_TDT[]; -u32 MCD_SingleEu_TDT[]; -#endif -u32 MCD_ENetRcv_TDT[]; -u32 MCD_ENetXmit_TDT[]; - -u32 MCD_modelTaskTableSrc[] = { - (u32) MCD_ChainNoEu_TDT, - (u32) & ((u8 *) MCD_ChainNoEu_TDT)[0x0000016c], - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_SingleNoEu_TDT, - (u32) & ((u8 *) MCD_SingleNoEu_TDT)[0x000000d4], - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -#ifdef MCD_INCLUDE_EU - (u32) MCD_ChainEu_TDT, - (u32) & ((u8 *) MCD_ChainEu_TDT)[0x000001b4], - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_SingleEu_TDT, - (u32) & ((u8 *) MCD_SingleEu_TDT)[0x00000124], - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -#endif - (u32) MCD_ENetRcv_TDT, - (u32) & ((u8 *) MCD_ENetRcv_TDT)[0x0000009c], - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - (u32) MCD_ENetXmit_TDT, - (u32) & ((u8 *) MCD_ENetXmit_TDT)[0x000000d0], - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -u32 MCD_ChainNoEu_TDT[] = { - 0x80004000, - 0x8118801b, - 0xb8c60018, - 0x10002b10, - 0x7000000d, - 0x018cf89f, - 0x6000000a, - 0x080cf89f, - 0x000001f8, - 0x98180364, - 0x8118801b, - 0xf8c6001a, - 0xb8c6601b, - 0x10002710, - 0x00000f18, - 0xb8c6001d, - 0x10001310, - 0x60000007, - 0x014cf88b, - 0x98c6001c, - 0x00000710, - 0x98c70018, - 0x10001f10, - 0x0000c818, - 0x000001f8, - 0xc1476018, - 0xc003231d, - 0x811a601b, - 0xc1862102, - 0x849be009, - 0x03fed7b8, - 0xda9b001b, - 0x9b9be01b, - 0x1000cb20, - 0x70000006, - 0x088cf88f, - 0x1000cb28, - 0x70000006, - 0x088cf88f, - 0x1000cb30, - 0x70000006, - 0x088cf88f, - 0x1000cb38, - 0x0000c728, - 0x000001f8, - 0xc1476018, - 0xc003241d, - 0x811a601b, - 0xda9b001b, - 0x9b9be01b, - 0x0000d3a0, - 0xc1862102, - 0x849be009, - 0x0bfed7b8, - 0xda9b001b, - 0x9b9be01b, - 0x1000cb20, - 0x70000006, - 0x088cf88f, - 0x1000cb28, - 0x70000006, - 0x088cf88f, - 0x1000cb30, - 0x70000006, - 0x088cf88f, - 0x1000cb38, - 0x0000c728, - 0x000001f8, - 0x8118801b, - 0xd8c60018, - 0x98c6601c, - 0x6000000b, - 0x0c8cfc9f, - 0x000001f8, - 0xa146001e, - 0x10000b08, - 0x10002050, - 0xb8c60018, - 0x10002b10, - 0x7000000a, - 0x080cf89f, - 0x6000000d, - 0x018cf89f, - 0x000001f8, - 0x8618801b, - 0x7000000e, - 0x084cf21f, - 0xd8990336, - 0x8019801b, - 0x040001f8, - 0x000001f8, - 0x000001f8, -}; - -u32 MCD_SingleNoEu_TDT[] = { - 0x8198001b, - 0x7000000d, - 0x080cf81f, - 0x8198801b, - 0x6000000e, - 0x084cf85f, - 0x000001f8, - 0x8298001b, - 0x7000000d, - 0x010cf81f, - 0x6000000e, - 0x018cf81f, - 0xc202601b, - 0xc002221c, - 0x809a601b, - 0xc10420c2, - 0x839be009, - 0x03fed7b8, - 0xda9b001b, - 0x9b9be01b, - 0x70000006, - 0x088cf889, - 0x1000cb28, - 0x70000006, - 0x088cf889, - 0x1000cb30, - 0x70000006, - 0x088cf889, - 0x0000cb38, - 0x000001f8, - 0xc202601b, - 0xc002229c, - 0x809a601b, - 0xda9b001b, - 0x9b9be01b, - 0x0000d3a0, - 0xc10420c2, - 0x839be009, - 0x0bfed7b8, - 0xda9b001b, - 0x9b9be01b, - 0x70000006, - 0x088cf889, - 0x1000cb28, - 0x70000006, - 0x088cf889, - 0x1000cb30, - 0x70000006, - 0x088cf889, - 0x0000cb38, - 0x000001f8, - 0xc318022d, - 0x8018801b, - 0x040001f8, -}; - -#ifdef MCD_INCLUDE_EU -u32 MCD_ChainEu_TDT[] = { - 0x80004000, - 0x8198801b, - 0xb8c68018, - 0x10002f10, - 0x7000000d, - 0x01ccf89f, - 0x6000000a, - 0x080cf89f, - 0x000001f8, - 0x981803a4, - 0x8198801b, - 0xf8c6801a, - 0xb8c6e01b, - 0x10002b10, - 0x00001318, - 0xb8c6801d, - 0x10001710, - 0x60000007, - 0x018cf88c, - 0x98c6801c, - 0x00000b10, - 0x98c78018, - 0x10002310, - 0x0000c820, - 0x000001f8, - 0x8698801b, - 0x7000000f, - 0x084cf2df, - 0xd899042d, - 0x8019801b, - 0x60000003, - 0x2cd7c7df, - 0xd8990364, - 0x8019801b, - 0x60000003, - 0x2c17c7df, - 0x000001f8, - 0xc1c7e018, - 0xc003a35e, - 0x819a601b, - 0xc206a142, - 0x851be009, - 0x63fe0000, - 0x0d4cfddf, - 0xda9b001b, - 0x9b9be01b, - 0x70000002, - 0x004cf81f, - 0x1000cb20, - 0x70000006, - 0x088cf891, - 0x1000cb28, - 0x70000006, - 0x088cf891, - 0x1000cb30, - 0x70000006, - 0x088cf891, - 0x1000cb38, - 0x0000c728, - 0x000001f8, - 0xc1c7e018, - 0xc003a49e, - 0x819a601b, - 0xda9b001b, - 0x9b9be01b, - 0x0000d3a0, - 0xc206a142, - 0x851be009, - 0x6bfe0000, - 0x0d4cfddf, - 0xda9b001b, - 0x9b9be01b, - 0x70000002, - 0x004cf81f, - 0x1000cb20, - 0x70000006, - 0x088cf891, - 0x1000cb28, - 0x70000006, - 0x088cf891, - 0x1000cb30, - 0x70000006, - 0x088cf891, - 0x1000cb38, - 0x0000c728, - 0x000001f8, - 0x8198801b, - 0xd8c68018, - 0x98c6e01c, - 0x6000000b, - 0x0c8cfc9f, - 0x0000cc08, - 0xa1c6801e, - 0x10000f08, - 0x10002458, - 0xb8c68018, - 0x10002f10, - 0x7000000a, - 0x080cf89f, - 0x6000000d, - 0x01ccf89f, - 0x000001f8, - 0x8698801b, - 0x7000000e, - 0x084cf25f, - 0xd899037f, - 0x8019801b, - 0x040001f8, - 0x000001f8, - 0x000001f8, -}; - -u32 MCD_SingleEu_TDT[] = { - 0x8218001b, - 0x7000000d, - 0x080cf81f, - 0x8218801b, - 0x6000000e, - 0x084cf85f, - 0x000001f8, - 0x8318001b, - 0x7000000d, - 0x014cf81f, - 0x6000000e, - 0x01ccf81f, - 0x8498001b, - 0x7000000f, - 0x080cf19f, - 0xd81882a4, - 0x8019001b, - 0x60000003, - 0x2c97c7df, - 0xd818826d, - 0x8019001b, - 0x60000003, - 0x2c17c7df, - 0x000001f8, - 0xc282e01b, - 0xc002a25e, - 0x811a601b, - 0xc184a102, - 0x841be009, - 0x63fe0000, - 0x0d4cfddf, - 0xda9b001b, - 0x9b9be01b, - 0x70000002, - 0x004cf99f, - 0x70000006, - 0x088cf88b, - 0x1000cb28, - 0x70000006, - 0x088cf88b, - 0x1000cb30, - 0x70000006, - 0x088cf88b, - 0x0000cb38, - 0x000001f8, - 0xc282e01b, - 0xc002a31e, - 0x811a601b, - 0xda9b001b, - 0x9b9be01b, - 0x0000d3a0, - 0xc184a102, - 0x841be009, - 0x6bfe0000, - 0x0d4cfddf, - 0xda9b001b, - 0x9b9be01b, - 0x70000002, - 0x004cf99f, - 0x70000006, - 0x088cf88b, - 0x1000cb28, - 0x70000006, - 0x088cf88b, - 0x1000cb30, - 0x70000006, - 0x088cf88b, - 0x0000cb38, - 0x000001f8, - 0x8144801c, - 0x0000c008, - 0xc398027f, - 0x8018801b, - 0x040001f8, -}; -#endif -u32 MCD_ENetRcv_TDT[] = { - 0x80004000, - 0x81988000, - 0x10000788, - 0x6000000a, - 0x080cf05f, - 0x98180209, - 0x81c40004, - 0x7000000e, - 0x010cf05f, - 0x7000000c, - 0x01ccf05f, - 0x70000004, - 0x014cf049, - 0x70000004, - 0x004cf04a, - 0x00000b88, - 0xc4030150, - 0x8119e012, - 0x03e0cf90, - 0x81188000, - 0x000ac788, - 0xc4030000, - 0x8199e000, - 0x70000004, - 0x084cfc8b, - 0x60000005, - 0x0cccf841, - 0x81c60000, - 0xc399021b, - 0x80198000, - 0x00008400, - 0x00000f08, - 0x81988000, - 0x10000788, - 0x6000000a, - 0x080cf05f, - 0xc2188209, - 0x80190000, - 0x040001f8, - 0x000001f8, -}; - -u32 MCD_ENetXmit_TDT[] = { - 0x80004000, - 0x81988000, - 0x10000788, - 0x6000000a, - 0x080cf05f, - 0x98180309, - 0x80004003, - 0x81c60004, - 0x7000000e, - 0x014cf05f, - 0x7000000c, - 0x028cf05f, - 0x7000000d, - 0x018cf05f, - 0x70000004, - 0x01ccf04d, - 0x10000b90, - 0x60000004, - 0x020cf0a1, - 0xc3188312, - 0x83c70000, - 0x00001f10, - 0xc583a3c3, - 0x81042325, - 0x03e0c798, - 0xd8990000, - 0x9999e000, - 0x000acf98, - 0xd8992306, - 0x9999e03f, - 0x03eac798, - 0xd8990000, - 0x9999e000, - 0x000acf98, - 0xd8990000, - 0x99832302, - 0x0beac798, - 0x81988000, - 0x6000000b, - 0x0c4cfc5f, - 0x81c80000, - 0xc5190312, - 0x80198000, - 0x00008400, - 0x00000f08, - 0x81988000, - 0x10000788, - 0x6000000a, - 0x080cf05f, - 0xc2988309, - 0x80190000, - 0x040001f8, - 0x000001f8, -}; - -#ifdef MCD_INCLUDE_EU -MCD_bufDesc MCD_singleBufDescs[NCHANNELS]; -#endif diff --git a/drivers/dma/MCD_tasksInit.c b/drivers/dma/MCD_tasksInit.c deleted file mode 100644 index 079cd0af3c0..00000000000 --- a/drivers/dma/MCD_tasksInit.c +++ /dev/null @@ -1,225 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. - */ - -#include <common.h> - -/* Functions for initializing variable tables of different types of tasks. */ - -/* - * Do not edit! - */ - -#include <MCD_dma.h> - -extern dmaRegs *MCD_dmaBar; - -/* Task 0 */ - -void MCD_startDmaChainNoEu(int *currBD, short srcIncr, short destIncr, - int xferSize, short xferSizeIncr, int *cSave, - volatile TaskTableEntry * taskTable, int channel) -{ - volatile TaskTableEntry *taskChan = taskTable + channel; - - MCD_SET_VAR(taskChan, 2, (u32) currBD); /* var[2] */ - MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */ - MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */ - MCD_SET_VAR(taskChan, 11, (u32) xferSize); /* var[11] */ - MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */ - MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */ - MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */ - MCD_SET_VAR(taskChan, 3, (u32) 0x00000000); /* var[3] */ - MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */ - MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */ - MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */ - MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */ - MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */ - MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */ - MCD_SET_VAR(taskChan, 10, (u32) 0x00000000); /* var[10] */ - MCD_SET_VAR(taskChan, 12, (u32) 0x00000000); /* var[12] */ - MCD_SET_VAR(taskChan, 13, (u32) 0x80000000); /* var[13] */ - MCD_SET_VAR(taskChan, 14, (u32) 0x00000010); /* var[14] */ - MCD_SET_VAR(taskChan, 15, (u32) 0x00000004); /* var[15] */ - MCD_SET_VAR(taskChan, 16, (u32) 0x08000000); /* var[16] */ - MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */ - MCD_SET_VAR(taskChan, 28, (u32) 0x80000000); /* inc[4] */ - MCD_SET_VAR(taskChan, 29, (u32) 0x80000001); /* inc[5] */ - MCD_SET_VAR(taskChan, 30, (u32) 0x40000000); /* inc[6] */ - - /* Set the task's Enable bit in its Task Control Register */ - MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; -} - -/* Task 1 */ - -void MCD_startDmaSingleNoEu(char *srcAddr, short srcIncr, char *destAddr, - short destIncr, int dmaSize, short xferSizeIncr, - int flags, int *currBD, int *cSave, - volatile TaskTableEntry * taskTable, int channel) -{ - volatile TaskTableEntry *taskChan = taskTable + channel; - - MCD_SET_VAR(taskChan, 7, (u32) srcAddr); /* var[7] */ - MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */ - MCD_SET_VAR(taskChan, 2, (u32) destAddr); /* var[2] */ - MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */ - MCD_SET_VAR(taskChan, 3, (u32) dmaSize); /* var[3] */ - MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */ - MCD_SET_VAR(taskChan, 5, (u32) flags); /* var[5] */ - MCD_SET_VAR(taskChan, 1, (u32) currBD); /* var[1] */ - MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */ - MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */ - MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */ - MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */ - MCD_SET_VAR(taskChan, 9, (u32) 0x00000004); /* var[9] */ - MCD_SET_VAR(taskChan, 10, (u32) 0x08000000); /* var[10] */ - MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */ - MCD_SET_VAR(taskChan, 28, (u32) 0x80000001); /* inc[4] */ - MCD_SET_VAR(taskChan, 29, (u32) 0x40000000); /* inc[5] */ - - /* Set the task's Enable bit in its Task Control Register */ - MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; -} - -/* Task 2 */ - -void MCD_startDmaChainEu(int *currBD, short srcIncr, short destIncr, - int xferSize, short xferSizeIncr, int *cSave, - volatile TaskTableEntry * taskTable, int channel) -{ - volatile TaskTableEntry *taskChan = taskTable + channel; - - MCD_SET_VAR(taskChan, 3, (u32) currBD); /* var[3] */ - MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */ - MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */ - MCD_SET_VAR(taskChan, 12, (u32) xferSize); /* var[12] */ - MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */ - MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */ - MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */ - MCD_SET_VAR(taskChan, 2, (u32) 0x00000000); /* var[2] */ - MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */ - MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */ - MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */ - MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */ - MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */ - MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */ - MCD_SET_VAR(taskChan, 10, (u32) 0x00000000); /* var[10] */ - MCD_SET_VAR(taskChan, 11, (u32) 0x00000000); /* var[11] */ - MCD_SET_VAR(taskChan, 13, (u32) 0x00000000); /* var[13] */ - MCD_SET_VAR(taskChan, 14, (u32) 0x80000000); /* var[14] */ - MCD_SET_VAR(taskChan, 15, (u32) 0x00000010); /* var[15] */ - MCD_SET_VAR(taskChan, 16, (u32) 0x00000001); /* var[16] */ - MCD_SET_VAR(taskChan, 17, (u32) 0x00000004); /* var[17] */ - MCD_SET_VAR(taskChan, 18, (u32) 0x08000000); /* var[18] */ - MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */ - MCD_SET_VAR(taskChan, 28, (u32) 0x80000000); /* inc[4] */ - MCD_SET_VAR(taskChan, 29, (u32) 0xc0000000); /* inc[5] */ - MCD_SET_VAR(taskChan, 30, (u32) 0x80000001); /* inc[6] */ - MCD_SET_VAR(taskChan, 31, (u32) 0x40000000); /* inc[7] */ - - /* Set the task's Enable bit in its Task Control Register */ - MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; -} - -/* Task 3 */ - -void MCD_startDmaSingleEu(char *srcAddr, short srcIncr, char *destAddr, - short destIncr, int dmaSize, short xferSizeIncr, - int flags, int *currBD, int *cSave, - volatile TaskTableEntry * taskTable, int channel) -{ - volatile TaskTableEntry *taskChan = taskTable + channel; - - MCD_SET_VAR(taskChan, 8, (u32) srcAddr); /* var[8] */ - MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */ - MCD_SET_VAR(taskChan, 3, (u32) destAddr); /* var[3] */ - MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */ - MCD_SET_VAR(taskChan, 4, (u32) dmaSize); /* var[4] */ - MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */ - MCD_SET_VAR(taskChan, 6, (u32) flags); /* var[6] */ - MCD_SET_VAR(taskChan, 2, (u32) currBD); /* var[2] */ - MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */ - MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */ - MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */ - MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */ - MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */ - MCD_SET_VAR(taskChan, 10, (u32) 0x00000001); /* var[10] */ - MCD_SET_VAR(taskChan, 11, (u32) 0x00000004); /* var[11] */ - MCD_SET_VAR(taskChan, 12, (u32) 0x08000000); /* var[12] */ - MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */ - MCD_SET_VAR(taskChan, 28, (u32) 0xc0000000); /* inc[4] */ - MCD_SET_VAR(taskChan, 29, (u32) 0x80000000); /* inc[5] */ - MCD_SET_VAR(taskChan, 30, (u32) 0x80000001); /* inc[6] */ - MCD_SET_VAR(taskChan, 31, (u32) 0x40000000); /* inc[7] */ - - /* Set the task's Enable bit in its Task Control Register */ - MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; -} - -/* Task 4 */ - -void MCD_startDmaENetRcv(char *bDBase, char *currBD, char *rcvFifoPtr, - volatile TaskTableEntry * taskTable, int channel) -{ - volatile TaskTableEntry *taskChan = taskTable + channel; - - MCD_SET_VAR(taskChan, 0, (u32) bDBase); /* var[0] */ - MCD_SET_VAR(taskChan, 3, (u32) currBD); /* var[3] */ - MCD_SET_VAR(taskChan, 6, (u32) rcvFifoPtr); /* var[6] */ - MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */ - MCD_SET_VAR(taskChan, 2, (u32) 0x00000000); /* var[2] */ - MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */ - MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */ - MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */ - MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */ - MCD_SET_VAR(taskChan, 9, (u32) 0x0000ffff); /* var[9] */ - MCD_SET_VAR(taskChan, 10, (u32) 0x30000000); /* var[10] */ - MCD_SET_VAR(taskChan, 11, (u32) 0x0fffffff); /* var[11] */ - MCD_SET_VAR(taskChan, 12, (u32) 0x00000008); /* var[12] */ - MCD_SET_VAR(taskChan, 24, (u32) 0x00000000); /* inc[0] */ - MCD_SET_VAR(taskChan, 25, (u32) 0x60000000); /* inc[1] */ - MCD_SET_VAR(taskChan, 26, (u32) 0x20000004); /* inc[2] */ - MCD_SET_VAR(taskChan, 27, (u32) 0x40000000); /* inc[3] */ - - /* Set the task's Enable bit in its Task Control Register */ - MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; -} - -/* Task 5 */ - -void MCD_startDmaENetXmit(char *bDBase, char *currBD, char *xmitFifoPtr, - volatile TaskTableEntry * taskTable, int channel) -{ - volatile TaskTableEntry *taskChan = taskTable + channel; - - MCD_SET_VAR(taskChan, 0, (u32) bDBase); /* var[0] */ - MCD_SET_VAR(taskChan, 3, (u32) currBD); /* var[3] */ - MCD_SET_VAR(taskChan, 11, (u32) xmitFifoPtr); /* var[11] */ - MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */ - MCD_SET_VAR(taskChan, 2, (u32) 0x00000000); /* var[2] */ - MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */ - MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */ - MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */ - MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */ - MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */ - MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */ - MCD_SET_VAR(taskChan, 10, (u32) 0x00000000); /* var[10] */ - MCD_SET_VAR(taskChan, 12, (u32) 0x00000000); /* var[12] */ - MCD_SET_VAR(taskChan, 13, (u32) 0x0000ffff); /* var[13] */ - MCD_SET_VAR(taskChan, 14, (u32) 0xffffffff); /* var[14] */ - MCD_SET_VAR(taskChan, 15, (u32) 0x00000004); /* var[15] */ - MCD_SET_VAR(taskChan, 16, (u32) 0x00000008); /* var[16] */ - MCD_SET_VAR(taskChan, 24, (u32) 0x00000000); /* inc[0] */ - MCD_SET_VAR(taskChan, 25, (u32) 0x60000000); /* inc[1] */ - MCD_SET_VAR(taskChan, 26, (u32) 0x40000000); /* inc[2] */ - MCD_SET_VAR(taskChan, 27, (u32) 0xc000fffc); /* inc[3] */ - MCD_SET_VAR(taskChan, 28, (u32) 0xe0000004); /* inc[4] */ - MCD_SET_VAR(taskChan, 29, (u32) 0x80000000); /* inc[5] */ - MCD_SET_VAR(taskChan, 30, (u32) 0x4000ffff); /* inc[6] */ - MCD_SET_VAR(taskChan, 31, (u32) 0xe0000001); /* inc[7] */ - - /* Set the task's Enable bit in its Task Control Register */ - MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; -} diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index a75572fe5de..48811eaaeb3 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -5,7 +5,6 @@ obj-$(CONFIG_DMA) += dma-uclass.o -obj-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o obj-$(CONFIG_APBH_DMA) += apbh_dma.o obj-$(CONFIG_BCM6348_IUDMA) += bcm6348-iudma.o obj-$(CONFIG_FSL_DMA) += fsl_dma.o diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile index 6807eb8e8b2..f4e0271efbf 100644 --- a/drivers/dma/ti/Makefile +++ b/drivers/dma/ti/Makefile @@ -8,3 +8,4 @@ k3-psil-data-$(CONFIG_SOC_K3_J721E) += k3-psil-j721e.o k3-psil-data-$(CONFIG_SOC_K3_J721S2) += k3-psil-j721s2.o k3-psil-data-$(CONFIG_SOC_K3_AM642) += k3-psil-am64.o k3-psil-data-$(CONFIG_SOC_K3_AM625) += k3-psil-am62.o +k3-psil-data-$(CONFIG_SOC_K3_AM62A7) += k3-psil-am62a.o diff --git a/drivers/dma/ti/k3-psil-am62a.c b/drivers/dma/ti/k3-psil-am62a.c new file mode 100644 index 00000000000..ca9d71f9142 --- /dev/null +++ b/drivers/dma/ti/k3-psil-am62a.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com + */ + +#include <linux/kernel.h> + +#include "k3-psil-priv.h" + +#define PSIL_PDMA_XY_TR(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_PDMA_XY, \ + .mapped_channel_id = -1, \ + .default_flow_id = -1, \ + }, \ + } + +#define PSIL_PDMA_XY_PKT(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_PDMA_XY, \ + .mapped_channel_id = -1, \ + .default_flow_id = -1, \ + .pkt_mode = 1, \ + }, \ + } + +#define PSIL_ETHERNET(x, ch, flow_base, flow_cnt) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_NATIVE, \ + .pkt_mode = 1, \ + .needs_epib = 1, \ + .psd_size = 16, \ + .mapped_channel_id = ch, \ + .flow_start = flow_base, \ + .flow_num = flow_cnt, \ + .default_flow_id = flow_base, \ + }, \ + } + +#define PSIL_SAUL(x, ch, flow_base, flow_cnt, default_flow, tx) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_NATIVE, \ + .pkt_mode = 1, \ + .needs_epib = 1, \ + .psd_size = 64, \ + .mapped_channel_id = ch, \ + .flow_start = flow_base, \ + .flow_num = flow_cnt, \ + .default_flow_id = default_flow, \ + .notdpkt = tx, \ + }, \ + } + +#define PSIL_PDMA_MCASP(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_PDMA_XY, \ + .pdma_acc32 = 1, \ + .pdma_burst = 1, \ + }, \ + } + +#define PSIL_CSI2RX(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_NATIVE, \ + }, \ + } + +/* PSI-L source thread IDs, used for RX (DMA_DEV_TO_MEM) */ +static struct psil_ep am62a_src_ep_map[] = { + /* SAUL */ + PSIL_SAUL(0x7504, 20, 35, 8, 35, 0), + PSIL_SAUL(0x7505, 21, 35, 8, 36, 0), + PSIL_SAUL(0x7506, 22, 43, 8, 43, 0), + PSIL_SAUL(0x7507, 23, 43, 8, 44, 0), + /* PDMA_MAIN0 - SPI0-3 */ + PSIL_PDMA_XY_PKT(0x4302), + PSIL_PDMA_XY_PKT(0x4303), + PSIL_PDMA_XY_PKT(0x4304), + PSIL_PDMA_XY_PKT(0x4305), + PSIL_PDMA_XY_PKT(0x4306), + PSIL_PDMA_XY_PKT(0x4307), + PSIL_PDMA_XY_PKT(0x4308), + PSIL_PDMA_XY_PKT(0x4309), + PSIL_PDMA_XY_PKT(0x430a), + PSIL_PDMA_XY_PKT(0x430b), + PSIL_PDMA_XY_PKT(0x430c), + PSIL_PDMA_XY_PKT(0x430d), + /* PDMA_MAIN1 - UART0-6 */ + PSIL_PDMA_XY_PKT(0x4400), + PSIL_PDMA_XY_PKT(0x4401), + PSIL_PDMA_XY_PKT(0x4402), + PSIL_PDMA_XY_PKT(0x4403), + PSIL_PDMA_XY_PKT(0x4404), + PSIL_PDMA_XY_PKT(0x4405), + PSIL_PDMA_XY_PKT(0x4406), + /* PDMA_MAIN2 - MCASP0-2 */ + PSIL_PDMA_MCASP(0x4500), + PSIL_PDMA_MCASP(0x4501), + PSIL_PDMA_MCASP(0x4502), + /* CPSW3G */ + PSIL_ETHERNET(0x4600, 19, 19, 16), + /* CSI2RX */ + PSIL_CSI2RX(0x5000), + PSIL_CSI2RX(0x5001), + PSIL_CSI2RX(0x5002), + PSIL_CSI2RX(0x5003), + PSIL_CSI2RX(0x5004), + PSIL_CSI2RX(0x5005), + PSIL_CSI2RX(0x5006), + PSIL_CSI2RX(0x5007), + PSIL_CSI2RX(0x5008), + PSIL_CSI2RX(0x5009), + PSIL_CSI2RX(0x500a), + PSIL_CSI2RX(0x500b), + PSIL_CSI2RX(0x500c), + PSIL_CSI2RX(0x500d), + PSIL_CSI2RX(0x500e), + PSIL_CSI2RX(0x500f), + PSIL_CSI2RX(0x5010), + PSIL_CSI2RX(0x5011), + PSIL_CSI2RX(0x5012), + PSIL_CSI2RX(0x5013), + PSIL_CSI2RX(0x5014), + PSIL_CSI2RX(0x5015), + PSIL_CSI2RX(0x5016), + PSIL_CSI2RX(0x5017), + PSIL_CSI2RX(0x5018), + PSIL_CSI2RX(0x5019), + PSIL_CSI2RX(0x501a), + PSIL_CSI2RX(0x501b), + PSIL_CSI2RX(0x501c), + PSIL_CSI2RX(0x501d), + PSIL_CSI2RX(0x501e), + PSIL_CSI2RX(0x501f), +}; + +/* PSI-L destination thread IDs, used for TX (DMA_MEM_TO_DEV) */ +static struct psil_ep am62a_dst_ep_map[] = { + /* SAUL */ + PSIL_SAUL(0xf500, 27, 83, 8, 83, 1), + PSIL_SAUL(0xf501, 28, 91, 8, 91, 1), + /* PDMA_MAIN0 - SPI0-3 */ + PSIL_PDMA_XY_PKT(0xc302), + PSIL_PDMA_XY_PKT(0xc303), + PSIL_PDMA_XY_PKT(0xc304), + PSIL_PDMA_XY_PKT(0xc305), + PSIL_PDMA_XY_PKT(0xc306), + PSIL_PDMA_XY_PKT(0xc307), + PSIL_PDMA_XY_PKT(0xc308), + PSIL_PDMA_XY_PKT(0xc309), + PSIL_PDMA_XY_PKT(0xc30a), + PSIL_PDMA_XY_PKT(0xc30b), + PSIL_PDMA_XY_PKT(0xc30c), + PSIL_PDMA_XY_PKT(0xc30d), + /* PDMA_MAIN1 - UART0-6 */ + PSIL_PDMA_XY_PKT(0xc400), + PSIL_PDMA_XY_PKT(0xc401), + PSIL_PDMA_XY_PKT(0xc402), + PSIL_PDMA_XY_PKT(0xc403), + PSIL_PDMA_XY_PKT(0xc404), + PSIL_PDMA_XY_PKT(0xc405), + PSIL_PDMA_XY_PKT(0xc406), + /* PDMA_MAIN2 - MCASP0-2 */ + PSIL_PDMA_MCASP(0xc500), + PSIL_PDMA_MCASP(0xc501), + PSIL_PDMA_MCASP(0xc502), + /* CPSW3G */ + PSIL_ETHERNET(0xc600, 19, 19, 8), + PSIL_ETHERNET(0xc601, 20, 27, 8), + PSIL_ETHERNET(0xc602, 21, 35, 8), + PSIL_ETHERNET(0xc603, 22, 43, 8), + PSIL_ETHERNET(0xc604, 23, 51, 8), + PSIL_ETHERNET(0xc605, 24, 59, 8), + PSIL_ETHERNET(0xc606, 25, 67, 8), + PSIL_ETHERNET(0xc607, 26, 75, 8), +}; + +struct psil_ep_map am62a_ep_map = { + .name = "am62a", + .src = am62a_src_ep_map, + .src_count = ARRAY_SIZE(am62a_src_ep_map), + .dst = am62a_dst_ep_map, + .dst_count = ARRAY_SIZE(am62a_dst_ep_map), +}; diff --git a/drivers/dma/ti/k3-psil-am64.c b/drivers/dma/ti/k3-psil-am64.c index 15742c3723d..6180e2a1996 100644 --- a/drivers/dma/ti/k3-psil-am64.c +++ b/drivers/dma/ti/k3-psil-am64.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> */ diff --git a/drivers/dma/ti/k3-psil-am654.c b/drivers/dma/ti/k3-psil-am654.c index d16c07566b5..ce86600e556 100644 --- a/drivers/dma/ti/k3-psil-am654.c +++ b/drivers/dma/ti/k3-psil-am654.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> */ diff --git a/drivers/dma/ti/k3-psil-priv.h b/drivers/dma/ti/k3-psil-priv.h index 28078c6bd8d..83f873b84ce 100644 --- a/drivers/dma/ti/k3-psil-priv.h +++ b/drivers/dma/ti/k3-psil-priv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com */ #ifndef K3_PSIL_PRIV_H_ @@ -42,5 +42,6 @@ extern struct psil_ep_map j721e_ep_map; extern struct psil_ep_map j721s2_ep_map; extern struct psil_ep_map am64_ep_map; extern struct psil_ep_map am62_ep_map; +extern struct psil_ep_map am62a_ep_map; #endif /* K3_PSIL_PRIV_H_ */ diff --git a/drivers/dma/ti/k3-psil.c b/drivers/dma/ti/k3-psil.c index f23c8ca2b74..d4d4fede8fa 100644 --- a/drivers/dma/ti/k3-psil.c +++ b/drivers/dma/ti/k3-psil.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> */ @@ -26,6 +26,8 @@ struct psil_endpoint_config *psil_get_ep_config(u32 thread_id) soc_ep_map = &am64_ep_map; else if (IS_ENABLED(CONFIG_SOC_K3_AM625)) soc_ep_map = &am62_ep_map; + else if (IS_ENABLED(CONFIG_SOC_K3_AM62A7)) + soc_ep_map = &am62a_ep_map; } if (thread_id & K3_PSIL_DST_THREAD_ID_OFFSET && soc_ep_map->dst) { diff --git a/drivers/dma/ti/k3-psil.h b/drivers/dma/ti/k3-psil.h index 1e0fe06c0a4..af60a9924e2 100644 --- a/drivers/dma/ti/k3-psil.h +++ b/drivers/dma/ti/k3-psil.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com */ #ifndef K3_PSIL_H_ diff --git a/drivers/dma/ti/k3-udma-hwdef.h b/drivers/dma/ti/k3-udma-hwdef.h index 5d50bbcb031..3d6b4d10fff 100644 --- a/drivers/dma/ti/k3-udma-hwdef.h +++ b/drivers/dma/ti/k3-udma-hwdef.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com * * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 4f2effd39a8..8a62d63dfef 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> */ #define pr_fmt(fmt) "udma: " fmt @@ -1287,7 +1287,7 @@ static int udma_get_mmrs(struct udevice *dev) u32 cap2, cap3, cap4; int i; - ud->mmrs[MMR_GCFG] = (uint32_t *)devfdt_get_addr_name(dev, mmr_names[MMR_GCFG]); + ud->mmrs[MMR_GCFG] = dev_read_addr_name_ptr(dev, mmr_names[MMR_GCFG]); if (!ud->mmrs[MMR_GCFG]) return -EINVAL; @@ -1325,8 +1325,7 @@ static int udma_get_mmrs(struct udevice *dev) if (i == MMR_RCHANRT && ud->rchan_cnt == 0) continue; - ud->mmrs[i] = (uint32_t *)devfdt_get_addr_name(dev, - mmr_names[i]); + ud->mmrs[i] = dev_read_addr_name_ptr(dev, mmr_names[i]); if (!ud->mmrs[i]) return -EINVAL; } diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index 837c6f1180d..11fc0fe1c80 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -1,4 +1,5 @@ menu "Fastboot support" + depends on CMDLINE config FASTBOOT bool diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c index 4e9d9b719c6..3576b067729 100644 --- a/drivers/fastboot/fb_common.c +++ b/drivers/fastboot/fb_common.c @@ -91,6 +91,7 @@ void fastboot_okay(const char *reason, char *response) */ int __weak fastboot_set_reboot_flag(enum fastboot_reboot_reason reason) { + int ret; static const char * const boot_cmds[] = { [FASTBOOT_REBOOT_REASON_BOOTLOADER] = "bootonce-bootloader", [FASTBOOT_REBOOT_REASON_FASTBOOTD] = "boot-fastboot", @@ -105,7 +106,18 @@ int __weak fastboot_set_reboot_flag(enum fastboot_reboot_reason reason) if (reason >= FASTBOOT_REBOOT_REASONS_COUNT) return -EINVAL; - return bcb_write_reboot_reason(mmc_dev, "misc", boot_cmds[reason]); + ret = bcb_find_partition_and_load("mmc", mmc_dev, "misc"); + if (ret) + goto out; + + ret = bcb_set(BCB_FIELD_COMMAND, boot_cmds[reason]); + if (ret) + goto out; + + ret = bcb_store(); +out: + bcb_reset(); + return ret; } /** diff --git a/drivers/firmware/arm-ffa/arm-ffa-uclass.c b/drivers/firmware/arm-ffa/arm-ffa-uclass.c index 8c17b19eaf5..f1e91d151ea 100644 --- a/drivers/firmware/arm-ffa/arm-ffa-uclass.c +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -144,8 +144,6 @@ static int ffa_print_error_log(u32 ffa_id, int ffa_errno) return -EINVAL; abi_idx = FFA_ID_TO_ERRMAP_ID(ffa_id); - if (abi_idx < 0 || abi_idx >= FFA_ERRMAP_COUNT) - return -EINVAL; if (!err_msg_map[abi_idx].err_str[err_idx]) return -EINVAL; diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c index 43fb7fa7787..8ea15c7ed33 100644 --- a/drivers/firmware/firmware-zynqmp.c +++ b/drivers/firmware/firmware-zynqmp.c @@ -203,6 +203,8 @@ int zynqmp_pm_feature(const u32 api_id) /* Check feature check API version */ ret = xilinx_pm_request(PM_FEATURE_CHECK, api_id, 0, 0, 0, ret_payload); + if (ret) + return ret; /* Return feature check version */ return ret_payload[1] & FIRMWARE_VERSION_MASK; diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile index b2ff483c75a..dae42863589 100644 --- a/drivers/firmware/scmi/Makefile +++ b/drivers/firmware/scmi/Makefile @@ -1,6 +1,8 @@ obj-y += scmi_agent-uclass.o +obj-y += base.o obj-y += smt.o obj-$(CONFIG_SCMI_AGENT_SMCCC) += smccc_agent.o obj-$(CONFIG_SCMI_AGENT_MAILBOX) += mailbox_agent.o obj-$(CONFIG_SCMI_AGENT_OPTEE) += optee_agent.o +obj-$(CONFIG_SCMI_POWER_DOMAIN) += pwdom.o obj-$(CONFIG_SANDBOX) += sandbox-scmi_agent.o sandbox-scmi_devices.o diff --git a/drivers/firmware/scmi/base.c b/drivers/firmware/scmi/base.c new file mode 100644 index 00000000000..1d41a8a98fc --- /dev/null +++ b/drivers/firmware/scmi/base.c @@ -0,0 +1,664 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SCMI Base protocol as U-Boot device + * + * Copyright (C) 2023 Linaro Limited + * author: AKASHI Takahiro <takahiro.akashi@linaro.org> + */ + +#include <common.h> +#include <dm.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include <stdlib.h> +#include <string.h> +#include <asm/types.h> +#include <dm/device_compat.h> +#include <linux/kernel.h> + +/** + * scmi_generic_protocol_version - get protocol version + * @dev: SCMI device + * @id: SCMI protocol ID + * @version: Pointer to SCMI protocol version + * + * Obtain the protocol version number in @version. + * + * Return: 0 on success, error code on failure + */ +int scmi_generic_protocol_version(struct udevice *dev, + enum scmi_std_protocol id, u32 *version) +{ + struct scmi_protocol_version_out out; + struct scmi_msg msg = { + .protocol_id = id, + .message_id = SCMI_PROTOCOL_VERSION, + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + *version = out.version; + + return 0; +} + +/** + * scmi_base_protocol_version_int - get Base protocol version + * @dev: SCMI device + * @version: Pointer to SCMI protocol version + * + * Obtain the protocol version number in @version for Base protocol. + * + * Return: 0 on success, error code on failure + */ +static int scmi_base_protocol_version_int(struct udevice *dev, u32 *version) +{ + return scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_BASE, + version); +} + +/** + * scmi_protocol_attrs_int - get protocol attributes + * @dev: SCMI device + * @num_agents: Number of SCMI agents + * @num_protocols: Number of SCMI protocols + * + * Obtain the protocol attributes, the number of agents and the number + * of protocols, in @num_agents and @num_protocols respectively, that + * the device provides. + * + * Return: 0 on success, error code on failure + */ +static int scmi_protocol_attrs_int(struct udevice *dev, u32 *num_agents, + u32 *num_protocols) +{ + struct scmi_protocol_attrs_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_BASE, + .message_id = SCMI_PROTOCOL_ATTRIBUTES, + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + *num_agents = SCMI_PROTOCOL_ATTRS_NUM_AGENTS(out.attributes); + *num_protocols = SCMI_PROTOCOL_ATTRS_NUM_PROTOCOLS(out.attributes); + + return 0; +} + +/** + * scmi_protocol_message_attrs_int - get message-specific attributes + * @dev: SCMI device + * @message_id: SCMI message ID + * @attributes: Message-specific attributes + * + * Obtain the message-specific attributes in @attributes. + * This command succeeds if the message is implemented and available. + * + * Return: 0 on success, error code on failure + */ +static int scmi_protocol_message_attrs_int(struct udevice *dev, u32 message_id, + u32 *attributes) +{ + struct scmi_protocol_msg_attrs_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_BASE, + .message_id = SCMI_PROTOCOL_MESSAGE_ATTRIBUTES, + .in_msg = (u8 *)&message_id, + .in_msg_sz = sizeof(message_id), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + *attributes = out.attributes; + + return 0; +} + +/** + * scmi_base_discover_vendor_int - get vendor name + * @dev: SCMI device + * @vendor: Pointer to vendor name + * + * Obtain the vendor's name in @vendor. + * It is a caller's responsibility to free @vendor. + * + * Return: 0 on success, error code on failure + */ +static int scmi_base_discover_vendor_int(struct udevice *dev, u8 **vendor) +{ + struct scmi_base_discover_vendor_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_BASE, + .message_id = SCMI_BASE_DISCOVER_VENDOR, + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + if (!vendor) + return -EINVAL; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + *vendor = strdup(out.vendor_identifier); + if (!*vendor) + return -ENOMEM; + + return 0; +} + +/** + * scmi_base_discover_sub_vendor_int - get sub-vendor name + * @dev: SCMI device + * @sub_vendor: Pointer to sub-vendor name + * + * Obtain the sub-vendor's name in @sub_vendor. + * It is a caller's responsibility to free @sub_vendor. + * + * Return: 0 on success, error code on failure + */ +static int scmi_base_discover_sub_vendor_int(struct udevice *dev, + u8 **sub_vendor) +{ + struct scmi_base_discover_vendor_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_BASE, + .message_id = SCMI_BASE_DISCOVER_SUB_VENDOR, + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + if (!sub_vendor) + return -EINVAL; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + *sub_vendor = strdup(out.vendor_identifier); + if (!*sub_vendor) + return -ENOMEM; + + return 0; +} + +/** + * scmi_base_discover_impl_version_int - get implementation version + * @dev: SCMI device + * @impl_version: Pointer to implementation version + * + * Obtain the implementation version number in @impl_version. + * + * Return: 0 on success, error code on failure + */ +static int scmi_base_discover_impl_version_int(struct udevice *dev, + u32 *impl_version) +{ + struct scmi_base_discover_impl_version_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_BASE, + .message_id = SCMI_BASE_DISCOVER_IMPL_VERSION, + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + *impl_version = out.impl_version; + + return 0; +} + +/** + * scmi_base_discover_list_protocols_int - get list of protocols + * @dev: SCMI device + * @protocols: Pointer to array of SCMI protocols + * + * Obtain the list of protocols provided in @protocols. + * The number of elements in @protocols always match to the number of + * protocols returned by smci_protocol_attrs() when this function succeeds. + * It is a caller's responsibility to free @protocols. + * + * Return: the number of protocols in @protocols on success, error code on + * failure + */ +static int scmi_base_discover_list_protocols_int(struct udevice *dev, + u8 **protocols) +{ + struct scmi_base_discover_list_protocols_out out; + int cur; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_BASE, + .message_id = SCMI_BASE_DISCOVER_LIST_PROTOCOLS, + .in_msg = (u8 *)&cur, + .in_msg_sz = sizeof(cur), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + u32 num_agents, num_protocols; + u8 *buf; + int i, ret; + + ret = scmi_base_protocol_attrs(dev, &num_agents, &num_protocols); + if (ret) + return ret; + + buf = calloc(sizeof(u8), num_protocols); + if (!buf) + return -ENOMEM; + + cur = 0; + do { + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + goto err; + if (out.status) { + ret = scmi_to_linux_errno(out.status); + goto err; + } + + for (i = 0; i < out.num_protocols; i++, cur++) + buf[cur] = out.protocols[i / 4] >> ((i % 4) * 8); + } while (cur < num_protocols); + + *protocols = buf; + + return num_protocols; +err: + free(buf); + + return ret; +} + +/** + * scmi_base_discover_agent_int - identify agent + * @dev: SCMI device + * @agent_id: SCMI agent ID + * @ret_agent_id: Pointer to SCMI agent ID + * @name: Pointer to SCMI agent name + * + * Obtain the agent's name in @name. If @agent_id is equal to 0xffffffff, + * this function returns the caller's agent id in @ret_agent_id. + * It is a caller's responsibility to free @name. + * + * Return: 0 on success, error code on failure + */ +static int scmi_base_discover_agent_int(struct udevice *dev, u32 agent_id, + u32 *ret_agent_id, u8 **name) +{ + struct scmi_base_discover_agent_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_BASE, + .message_id = SCMI_BASE_DISCOVER_AGENT, + .in_msg = (u8 *)&agent_id, + .in_msg_sz = sizeof(agent_id), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + if (ret_agent_id) + *ret_agent_id = out.agent_id; + if (name) { + *name = strdup(out.name); + if (!*name) + return -ENOMEM; + } + + return 0; +} + +/** + * scmi_base_set_device_permissions_int - configure access permission to device + * @dev: SCMI device + * @agent_id: SCMI agent ID + * @device_id: ID of device to access + * @flags: A set of flags + * + * Ask for allowing or denying access permission to the device, @device_id. + * The meaning of @flags is defined in SCMI specification. + * + * Return: 0 on success, error code on failure + */ +static int scmi_base_set_device_permissions_int(struct udevice *dev, u32 agent_id, + u32 device_id, u32 flags) +{ + struct scmi_base_set_device_permissions_in in = { + .agent_id = agent_id, + .device_id = device_id, + .flags = flags, + }; + s32 status; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_BASE, + .message_id = SCMI_BASE_SET_DEVICE_PERMISSIONS, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + .out_msg = (u8 *)&status, + .out_msg_sz = sizeof(status), + }; + int ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (status) + return scmi_to_linux_errno(status); + + return 0; +} + +/** + * scmi_base_set_protocol_permissions_int - configure access permission to + * protocol on device + * @dev: SCMI device + * @agent_id: SCMI agent ID + * @device_id: ID of device to access + * @command_id: SCMI command ID + * @flags: A set of flags + * + * Ask for allowing or denying access permission to the protocol, @command_id, + * on the device, @device_id. + * The meaning of @flags is defined in SCMI specification. + * + * Return: 0 on success, error code on failure + */ +static int scmi_base_set_protocol_permissions_int(struct udevice *dev, + u32 agent_id, u32 device_id, + u32 command_id, u32 flags) +{ + struct scmi_base_set_protocol_permissions_in in = { + .agent_id = agent_id, + .device_id = device_id, + .command_id = command_id, + .flags = flags, + }; + s32 status; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_BASE, + .message_id = SCMI_BASE_SET_PROTOCOL_PERMISSIONS, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + .out_msg = (u8 *)&status, + .out_msg_sz = sizeof(status), + }; + int ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (status) + return scmi_to_linux_errno(status); + + return 0; +} + +/** + * scmi_base_reset_agent_configuration_int - reset resource settings + * @dev: SCMI device + * @agent_id: SCMI agent ID + * @flags: A set of flags + * + * Reset all the resource settings against @agent_id. + * The meaning of @flags is defined in SCMI specification. + * + * Return: 0 on success, error code on failure + */ +static int scmi_base_reset_agent_configuration_int(struct udevice *dev, + u32 agent_id, u32 flags) +{ + struct scmi_base_reset_agent_configuration_in in = { + .agent_id = agent_id, + .flags = flags, + }; + s32 status; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_BASE, + .message_id = SCMI_BASE_RESET_AGENT_CONFIGURATION, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + .out_msg = (u8 *)&status, + .out_msg_sz = sizeof(status), + }; + int ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (status) + return scmi_to_linux_errno(status); + + return 0; +} + +/** + * scmi_base_probe - probe base protocol device + * @dev: SCMI device + * + * Probe the device for SCMI base protocol and initialize the private data. + * + * Return: 0 on success, error code on failure + */ +static int scmi_base_probe(struct udevice *dev) +{ + u32 version; + int ret; + + ret = devm_scmi_of_get_channel(dev); + if (ret) { + dev_err(dev, "get_channel failed\n"); + return ret; + } + ret = scmi_base_protocol_version_int(dev, &version); + if (ret) { + dev_err(dev, "getting protocol version failed\n"); + return ret; + } + if (version < SCMI_BASE_PROTOCOL_VERSION) + return -EINVAL; + + return ret; +} + +static struct scmi_base_ops scmi_base_ops = { + /* Commands */ + .protocol_version = scmi_base_protocol_version_int, + .protocol_attrs = scmi_protocol_attrs_int, + .protocol_message_attrs = scmi_protocol_message_attrs_int, + .base_discover_vendor = scmi_base_discover_vendor_int, + .base_discover_sub_vendor = scmi_base_discover_sub_vendor_int, + .base_discover_impl_version = scmi_base_discover_impl_version_int, + .base_discover_list_protocols = scmi_base_discover_list_protocols_int, + .base_discover_agent = scmi_base_discover_agent_int, + .base_notify_errors = NULL, + .base_set_device_permissions = scmi_base_set_device_permissions_int, + .base_set_protocol_permissions = scmi_base_set_protocol_permissions_int, + .base_reset_agent_configuration = + scmi_base_reset_agent_configuration_int, +}; + +int scmi_base_protocol_version(struct udevice *dev, u32 *version) +{ + const struct scmi_base_ops *ops = device_get_ops(dev); + + if (ops->protocol_version) + return (*ops->protocol_version)(dev, version); + + return -EOPNOTSUPP; +} + +int scmi_base_protocol_attrs(struct udevice *dev, u32 *num_agents, + u32 *num_protocols) +{ + const struct scmi_base_ops *ops = device_get_ops(dev); + + if (ops->protocol_attrs) + return (*ops->protocol_attrs)(dev, num_agents, num_protocols); + + return -EOPNOTSUPP; +} + +int scmi_base_protocol_message_attrs(struct udevice *dev, u32 message_id, + u32 *attributes) +{ + const struct scmi_base_ops *ops = device_get_ops(dev); + + if (ops->protocol_message_attrs) + return (*ops->protocol_message_attrs)(dev, message_id, + attributes); + + return -EOPNOTSUPP; +} + +int scmi_base_discover_vendor(struct udevice *dev, u8 **vendor) +{ + const struct scmi_base_ops *ops = device_get_ops(dev); + + if (ops->base_discover_vendor) + return (*ops->base_discover_vendor)(dev, vendor); + + return -EOPNOTSUPP; +} + +int scmi_base_discover_sub_vendor(struct udevice *dev, u8 **sub_vendor) +{ + const struct scmi_base_ops *ops = device_get_ops(dev); + + if (ops->base_discover_sub_vendor) + return (*ops->base_discover_sub_vendor)(dev, sub_vendor); + + return -EOPNOTSUPP; +} + +int scmi_base_discover_impl_version(struct udevice *dev, u32 *impl_version) +{ + const struct scmi_base_ops *ops = device_get_ops(dev); + + if (ops->base_discover_impl_version) + return (*ops->base_discover_impl_version)(dev, impl_version); + + return -EOPNOTSUPP; +} + +int scmi_base_discover_list_protocols(struct udevice *dev, u8 **protocols) +{ + const struct scmi_base_ops *ops = device_get_ops(dev); + + if (ops->base_discover_list_protocols) + return (*ops->base_discover_list_protocols)(dev, protocols); + + return -EOPNOTSUPP; +} + +int scmi_base_discover_agent(struct udevice *dev, u32 agent_id, + u32 *ret_agent_id, u8 **name) +{ + const struct scmi_base_ops *ops = device_get_ops(dev); + + if (ops->base_discover_agent) + return (*ops->base_discover_agent)(dev, agent_id, ret_agent_id, + name); + + return -EOPNOTSUPP; +} + +int scmi_base_notify_errors(struct udevice *dev, u32 enable) +{ + const struct scmi_base_ops *ops = device_get_ops(dev); + + if (ops->base_notify_errors) + return (*ops->base_notify_errors)(dev, enable); + + return -EOPNOTSUPP; +} + +int scmi_base_set_device_permissions(struct udevice *dev, u32 agent_id, + u32 device_id, u32 flags) +{ + const struct scmi_base_ops *ops = device_get_ops(dev); + + if (ops->base_set_device_permissions) + return (*ops->base_set_device_permissions)(dev, agent_id, + device_id, flags); + + return -EOPNOTSUPP; +} + +int scmi_base_set_protocol_permissions(struct udevice *dev, + u32 agent_id, u32 device_id, + u32 command_id, u32 flags) +{ + const struct scmi_base_ops *ops = device_get_ops(dev); + + if (ops->base_set_protocol_permissions) + return (*ops->base_set_protocol_permissions)(dev, agent_id, + device_id, + command_id, + flags); + + return -EOPNOTSUPP; +} + +int scmi_base_reset_agent_configuration(struct udevice *dev, u32 agent_id, + u32 flags) +{ + const struct scmi_base_ops *ops = device_get_ops(dev); + + if (ops->base_reset_agent_configuration) + return (*ops->base_reset_agent_configuration)(dev, agent_id, + flags); + + return -EOPNOTSUPP; +} + +U_BOOT_DRIVER(scmi_base_drv) = { + .id = UCLASS_SCMI_BASE, + .name = "scmi_base_drv", + .ops = &scmi_base_ops, + .probe = scmi_base_probe, +}; + +UCLASS_DRIVER(scmi_base) = { + .id = UCLASS_SCMI_BASE, + .name = "scmi_base", +}; diff --git a/drivers/firmware/scmi/mailbox_agent.c b/drivers/firmware/scmi/mailbox_agent.c index 8277c186060..7ad3e8da9f0 100644 --- a/drivers/firmware/scmi/mailbox_agent.c +++ b/drivers/firmware/scmi/mailbox_agent.c @@ -94,13 +94,14 @@ static int setup_channel(struct udevice *dev, struct scmi_mbox_channel *chan) } static int scmi_mbox_get_channel(struct udevice *dev, + struct udevice *protocol, struct scmi_channel **channel) { struct scmi_mbox_channel *base_chan = dev_get_plat(dev); struct scmi_mbox_channel *chan; int ret; - if (!dev_read_prop(dev, "shmem", NULL)) { + if (!dev_read_prop(protocol, "shmem", NULL)) { /* Uses agent base channel */ *channel = container_of(base_chan, struct scmi_channel, ref); @@ -112,7 +113,7 @@ static int scmi_mbox_get_channel(struct udevice *dev, return -ENOMEM; /* Setup a dedicated channel for the protocol */ - ret = setup_channel(dev, chan); + ret = setup_channel(protocol, chan); if (ret) { free(chan); return ret; diff --git a/drivers/firmware/scmi/optee_agent.c b/drivers/firmware/scmi/optee_agent.c index db927fb2140..48dbb88a3fb 100644 --- a/drivers/firmware/scmi/optee_agent.c +++ b/drivers/firmware/scmi/optee_agent.c @@ -149,7 +149,7 @@ static int open_channel(struct udevice *dev, struct scmi_optee_channel *chan, struct tee_param param[1] = { }; int ret; - memset(sess, 0, sizeof(sess)); + memset(sess, 0, sizeof(*sess)); sess->tee = tee_find_device(NULL, NULL, NULL, NULL); if (!sess->tee) @@ -324,6 +324,7 @@ static int setup_channel(struct udevice *dev, struct scmi_optee_channel *chan) } static int scmi_optee_get_channel(struct udevice *dev, + struct udevice *protocol, struct scmi_channel **channel) { struct scmi_optee_channel *base_chan = dev_get_plat(dev); @@ -331,7 +332,7 @@ static int scmi_optee_get_channel(struct udevice *dev, u32 channel_id; int ret; - if (dev_read_u32(dev, "linaro,optee-channel-id", &channel_id)) { + if (dev_read_u32(protocol, "linaro,optee-channel-id", &channel_id)) { /* Uses agent base channel */ *channel = container_of(base_chan, struct scmi_channel, ref); @@ -343,7 +344,7 @@ static int scmi_optee_get_channel(struct udevice *dev, if (!chan) return -ENOMEM; - ret = setup_channel(dev, chan); + ret = setup_channel(protocol, chan); if (ret) { free(chan); return ret; diff --git a/drivers/firmware/scmi/pwdom.c b/drivers/firmware/scmi/pwdom.c new file mode 100644 index 00000000000..de2ba4755ac --- /dev/null +++ b/drivers/firmware/scmi/pwdom.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SCMI Power domain management protocol + * + * Copyright (C) 2023 Linaro Limited + * author: AKASHI Takahiro <takahiro.akashi@linaro.org> + */ + +#include <dm.h> +#include <malloc.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include <string.h> +#include <asm/types.h> + +int scmi_pwd_protocol_attrs(struct udevice *dev, int *num_pwdoms, + u64 *stats_addr, size_t *stats_len) +{ + struct scmi_pwd_protocol_attrs_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN, + .message_id = SCMI_PROTOCOL_ATTRIBUTES, + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + if (!dev || !num_pwdoms || !stats_addr || !stats_len) + return -EINVAL; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + *num_pwdoms = SCMI_PWD_PROTO_ATTRS_NUM_PWD(out.attributes); + *stats_addr = ((u64)out.stats_addr_high << 32) + out.stats_addr_low; + *stats_len = out.stats_len; + + return 0; +} + +int scmi_pwd_protocol_message_attrs(struct udevice *dev, s32 message_id, + u32 *attributes) +{ + struct scmi_pwd_protocol_msg_attrs_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN, + .message_id = SCMI_PROTOCOL_MESSAGE_ATTRIBUTES, + .in_msg = (u8 *)&message_id, + .in_msg_sz = sizeof(message_id), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + if (!dev || !attributes) + return -EINVAL; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + *attributes = out.attributes; + + return 0; +} + +int scmi_pwd_attrs(struct udevice *dev, u32 domain_id, u32 *attributes, + u8 **name) +{ + struct scmi_pwd_attrs_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN, + .message_id = SCMI_PWD_ATTRIBUTES, + .in_msg = (u8 *)&domain_id, + .in_msg_sz = sizeof(domain_id), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + if (!dev || !attributes || !name) + return -EINVAL; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + *name = strdup(out.name); + if (!*name) + return -ENOMEM; + + *attributes = out.attributes; + + return 0; +} + +int scmi_pwd_state_set(struct udevice *dev, u32 flags, u32 domain_id, + u32 pstate) +{ + struct scmi_pwd_state_set_in in; + s32 status; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN, + .message_id = SCMI_PWD_STATE_SET, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + .out_msg = (u8 *)&status, + .out_msg_sz = sizeof(status), + }; + int ret; + + if (!dev) + return -EINVAL; + + in.flags = flags; + in.domain_id = domain_id; + in.pstate = pstate; + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (status) + return scmi_to_linux_errno(status); + + return 0; +} + +int scmi_pwd_state_get(struct udevice *dev, u32 domain_id, u32 *pstate) +{ + struct scmi_pwd_state_get_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN, + .message_id = SCMI_PWD_STATE_GET, + .in_msg = (u8 *)&domain_id, + .in_msg_sz = sizeof(domain_id), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + if (!dev || !pstate) + return -EINVAL; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + *pstate = out.pstate; + + return 0; +} + +int scmi_pwd_name_get(struct udevice *dev, u32 domain_id, u8 **name) +{ + struct scmi_pwd_name_get_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN, + .message_id = SCMI_PWD_NAME_GET, + .in_msg = (u8 *)&domain_id, + .in_msg_sz = sizeof(domain_id), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + if (!dev || !name) + return -EINVAL; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + *name = strdup(out.extended_name); + if (!*name) + return -ENOMEM; + + return 0; +} diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c index 031882998df..cc9011c7312 100644 --- a/drivers/firmware/scmi/sandbox-scmi_agent.c +++ b/drivers/firmware/scmi/sandbox-scmi_agent.c @@ -14,11 +14,14 @@ #include <asm/io.h> #include <asm/scmi_test.h> #include <dm/device_compat.h> +#include <linux/bitfield.h> +#include <linux/kernel.h> /* * The sandbox SCMI agent driver simulates to some extend a SCMI message * processing. It simulates few of the SCMI services for some of the * SCMI protocols embedded in U-Boot. Currently: + * - SCMI base protocol * - SCMI clock protocol emulates an agent exposing 2 clocks * - SCMI reset protocol emulates an agent exposing a reset controller * - SCMI voltage domain protocol emulates an agent exposing 2 regulators @@ -33,6 +36,50 @@ * various uclass devices, as clocks and reset controllers. */ +#define SANDBOX_SCMI_BASE_PROTOCOL_VERSION SCMI_BASE_PROTOCOL_VERSION +#define SANDBOX_SCMI_VENDOR "U-Boot" +#define SANDBOX_SCMI_SUB_VENDOR "Sandbox" +#define SANDBOX_SCMI_IMPL_VERSION 0x1 +#define SANDBOX_SCMI_AGENT_NAME "OSPM" +#define SANDBOX_SCMI_PLATFORM_NAME "platform" + +#define SANDBOX_SCMI_PWD_PROTOCOL_VERSION SCMI_PWD_PROTOCOL_VERSION + +/** + * struct sandbox_channel - Description of sandbox transport + * @channel_id: Channel identifier + * + * Dummy channel. This will be used to test if a protocol-specific + * channel is properly used. + * Id 0 means a channel for the sandbox agent. + */ +struct sandbox_channel { + unsigned int channel_id; +}; + +/** + * struct scmi_channel - Channel instance referenced in SCMI drivers + * @ref: Reference to local channel instance + **/ +struct scmi_channel { + struct sandbox_channel ref; +}; + +static u8 protocols[] = { + CONFIG_IS_ENABLED(SCMI_POWER_DOMAIN, (SCMI_PROTOCOL_ID_POWER_DOMAIN,)) + CONFIG_IS_ENABLED(CLK_SCMI, (SCMI_PROTOCOL_ID_CLOCK,)) + CONFIG_IS_ENABLED(RESET_SCMI, (SCMI_PROTOCOL_ID_RESET_DOMAIN,)) + CONFIG_IS_ENABLED(DM_REGULATOR_SCMI, (SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,)) +}; + +#define NUM_PROTOCOLS ARRAY_SIZE(protocols) + +static struct sandbox_scmi_pwd scmi_pwdom[] = { + { .id = 0 }, + { .id = 1 }, + { .id = 2 }, +}; + static struct sandbox_scmi_clk scmi_clk[] = { { .rate = 333 }, { .rate = 200 }, @@ -48,11 +95,9 @@ static struct sandbox_scmi_voltd scmi_voltd[] = { { .id = 1, .voltage_uv = 1800000 }, }; -static struct sandbox_scmi_service sandbox_scmi_service_state; - -struct sandbox_scmi_service *sandbox_scmi_service_ctx(void) +struct sandbox_scmi_agent *sandbox_scmi_agent_ctx(struct udevice *dev) { - return &sandbox_scmi_service_state; + return dev_get_priv(dev); } static void debug_print_agent_state(struct udevice *dev, char *str) @@ -114,6 +159,548 @@ static struct sandbox_scmi_voltd *get_scmi_voltd_state(uint domain_id) * Sandbox SCMI agent ops */ +/* Base Protocol */ + +/** + * sandbox_scmi_base_protocol_version - implement SCMI_BASE_PROTOCOL_VERSION + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_BASE_PROTOCOL_VERSION command. + */ +static int sandbox_scmi_base_protocol_version(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_protocol_version_out *out = NULL; + + if (!msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + out = (struct scmi_protocol_version_out *)msg->out_msg; + out->version = SANDBOX_SCMI_BASE_PROTOCOL_VERSION; + out->status = SCMI_SUCCESS; + + return 0; +} + +/** + * sandbox_scmi_base_protocol_attrs - implement SCMI_BASE_PROTOCOL_ATTRIBUTES + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_BASE_PROTOCOL_ATTRIBUTES command. + */ +static int sandbox_scmi_base_protocol_attrs(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_protocol_attrs_out *out = NULL; + + if (!msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + out = (struct scmi_protocol_attrs_out *)msg->out_msg; + out->attributes = FIELD_PREP(0xff00, 2) | NUM_PROTOCOLS; + out->status = SCMI_SUCCESS; + + return 0; +} + +/** + * sandbox_scmi_base_message_attrs - implement + * SCMI_BASE_PROTOCOL_MESSAGE_ATTRIBUTES + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_BASE_PROTOCOL_MESSAGE_ATTRIBUTES command. + */ +static int sandbox_scmi_base_message_attrs(struct udevice *dev, + struct scmi_msg *msg) +{ + u32 message_id; + struct scmi_protocol_msg_attrs_out *out = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(message_id) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + message_id = *(u32 *)msg->in_msg; + out = (struct scmi_protocol_msg_attrs_out *)msg->out_msg; + + if (message_id >= SCMI_PROTOCOL_VERSION && + message_id <= SCMI_BASE_RESET_AGENT_CONFIGURATION && + message_id != SCMI_BASE_NOTIFY_ERRORS) { + out->attributes = 0; + out->status = SCMI_SUCCESS; + } else { + out->status = SCMI_NOT_FOUND; + } + + return 0; +} + +/** + * sandbox_scmi_base_discover_vendor - implement SCMI_BASE_DISCOVER_VENDOR + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_BASE_DISCOVER_VENDOR command + */ +static int sandbox_scmi_base_discover_vendor(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_base_discover_vendor_out *out = NULL; + + if (!msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + out = (struct scmi_base_discover_vendor_out *)msg->out_msg; + strcpy(out->vendor_identifier, SANDBOX_SCMI_VENDOR); + out->status = SCMI_SUCCESS; + + return 0; +} + +/** + * sandbox_scmi_base_discover_sub_vendor - implement + * SCMI_BASE_DISCOVER_SUB_VENDOR + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_BASE_DISCOVER_SUB_VENDOR command + */ +static int sandbox_scmi_base_discover_sub_vendor(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_base_discover_vendor_out *out = NULL; + + if (!msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + out = (struct scmi_base_discover_vendor_out *)msg->out_msg; + strcpy(out->vendor_identifier, SANDBOX_SCMI_SUB_VENDOR); + out->status = SCMI_SUCCESS; + + return 0; +} + +/** + * sandbox_scmi_base_discover_impl_version - implement + * SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION command + */ +static int sandbox_scmi_base_discover_impl_version(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_base_discover_impl_version_out *out = NULL; + + if (!msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + out = (struct scmi_base_discover_impl_version_out *)msg->out_msg; + out->impl_version = SANDBOX_SCMI_IMPL_VERSION; + out->status = SCMI_SUCCESS; + + return 0; +} + +/** + * sandbox_scmi_base_discover_list_protocols - implement + * SCMI_BASE_DISCOVER_LIST_PROTOCOLS + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_BASE_DISCOVER_LIST_PROTOCOLS command + */ +static int sandbox_scmi_base_discover_list_protocols(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_base_discover_list_protocols_out *out = NULL; + + if (!msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + out = (struct scmi_base_discover_list_protocols_out *)msg->out_msg; + memcpy(out->protocols, protocols, sizeof(protocols)); + out->num_protocols = NUM_PROTOCOLS; + out->status = SCMI_SUCCESS; + + return 0; +} + +/** + * sandbox_scmi_base_discover_agent - implement SCMI_BASE_DISCOVER_AGENT + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_BASE_DISCOVER_AGENT command + */ +static int sandbox_scmi_base_discover_agent(struct udevice *dev, + struct scmi_msg *msg) +{ + u32 agent_id; + struct scmi_base_discover_agent_out *out = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(agent_id) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + agent_id = *(u32 *)msg->in_msg; + out = (struct scmi_base_discover_agent_out *)msg->out_msg; + out->status = SCMI_SUCCESS; + if (agent_id == 0xffffffff || agent_id == 1) { + out->agent_id = 1; + strcpy(out->name, SANDBOX_SCMI_AGENT_NAME); + } else if (!agent_id) { + out->agent_id = agent_id; + strcpy(out->name, SANDBOX_SCMI_PLATFORM_NAME); + } else { + out->status = SCMI_NOT_FOUND; + } + + return 0; +} + +/** + * sandbox_scmi_base_set_device_permissions - implement + * SCMI_BASE_SET_DEVICE_PERMISSIONS + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_BASE_SET_DEVICE_PERMISSIONS command + */ +static int sandbox_scmi_base_set_device_permissions(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_base_set_device_permissions_in *in = NULL; + u32 *status; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*status)) + return -EINVAL; + + in = (struct scmi_base_set_device_permissions_in *)msg->in_msg; + status = (u32 *)msg->out_msg; + + if (in->agent_id != 1 || in->device_id != 0) + *status = SCMI_NOT_FOUND; + else if (in->flags & ~SCMI_BASE_SET_DEVICE_PERMISSIONS_ACCESS) + *status = SCMI_INVALID_PARAMETERS; + else if (in->flags & SCMI_BASE_SET_DEVICE_PERMISSIONS_ACCESS) + *status = SCMI_SUCCESS; + else + /* unset not allowed */ + *status = SCMI_DENIED; + + return 0; +} + +/** + * sandbox_scmi_base_set_protocol_permissions - implement + * SCMI_BASE_SET_PROTOCOL_PERMISSIONS + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_BASE_SET_PROTOCOL_PERMISSIONS command + */ +static int sandbox_scmi_base_set_protocol_permissions(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_base_set_protocol_permissions_in *in = NULL; + u32 *status; + int i; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*status)) + return -EINVAL; + + in = (struct scmi_base_set_protocol_permissions_in *)msg->in_msg; + status = (u32 *)msg->out_msg; + + for (i = 0; i < ARRAY_SIZE(protocols); i++) + if (protocols[i] == in->command_id) + break; + if (in->agent_id != 1 || in->device_id != 0 || + i == ARRAY_SIZE(protocols)) + *status = SCMI_NOT_FOUND; + else if (in->flags & ~SCMI_BASE_SET_PROTOCOL_PERMISSIONS_ACCESS) + *status = SCMI_INVALID_PARAMETERS; + else if (in->flags & SCMI_BASE_SET_PROTOCOL_PERMISSIONS_ACCESS) + *status = SCMI_SUCCESS; + else + /* unset not allowed */ + *status = SCMI_DENIED; + + return 0; +} + +/** + * sandbox_scmi_base_reset_agent_configuration - implement + * SCMI_BASE_RESET_AGENT_CONFIGURATION + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_BASE_RESET_AGENT_CONFIGURATION command + */ +static int sandbox_scmi_base_reset_agent_configuration(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_base_reset_agent_configuration_in *in = NULL; + u32 *status; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*status)) + return -EINVAL; + + in = (struct scmi_base_reset_agent_configuration_in *)msg->in_msg; + status = (u32 *)msg->out_msg; + + if (in->agent_id != 1) + *status = SCMI_NOT_FOUND; + else if (in->flags & ~SCMI_BASE_RESET_ALL_ACCESS_PERMISSIONS) + *status = SCMI_INVALID_PARAMETERS; + else + *status = SCMI_DENIED; + + return 0; +} + +/* Power Domain Management Protocol */ + +/** + * sandbox_scmi_pwd_protocol_version - implement SCMI_PROTOCOL_VERSION + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_PROTOCOL_VERSION command. + * + * Return: 0 on success, error code on failure + */ +static int sandbox_scmi_pwd_protocol_version(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_protocol_version_out *out = NULL; + + if (!msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + out = (struct scmi_protocol_version_out *)msg->out_msg; + out->version = SANDBOX_SCMI_PWD_PROTOCOL_VERSION; + out->status = SCMI_SUCCESS; + + return 0; +} + +/** + * sandbox_scmi_pwd_protocol_attribs - implement SCMI_PWD_PROTOCOL_ATTRS + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_PWD_PROTOCOL_ATTRS command. + * + * Return: 0 on success, error code on failure + */ +static int sandbox_scmi_pwd_protocol_attribs(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_pwd_protocol_attrs_out *out; + + if (!msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + out = (struct scmi_pwd_protocol_attrs_out *)msg->out_msg; + + out->attributes = ARRAY_SIZE(scmi_pwdom); + out->stats_addr_low = 0; + out->stats_addr_high = 0; + out->stats_len = 0; + out->status = SCMI_SUCCESS; + + return 0; +} + +/** + * sandbox_scmi_pwd_protocol_msg_attribs - implement + SCMI_PWD_PROTOCOL_MESSAGE_ATTRS + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_PWD_PROTOCOL_MESSAGE_ATTRS command. + * + * Return: 0 on success, error code on failure + */ +static int sandbox_scmi_pwd_protocol_msg_attribs(struct udevice *dev, + struct scmi_msg *msg) +{ + u32 message_id; + struct scmi_pwd_protocol_msg_attrs_out *out; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(message_id) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + message_id = *(u32 *)msg->in_msg; + + out = (struct scmi_pwd_protocol_msg_attrs_out *)msg->out_msg; + if (message_id <= SCMI_PWD_STATE_GET || + message_id == SCMI_PWD_NAME_GET) { + out->attributes = 0; + out->status = SCMI_SUCCESS; + } else { + out->status = SCMI_NOT_FOUND; + } + + return 0; +} + +/** + * sandbox_scmi_pwd_attribs - implement SCMI_PWD_ATTRS + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_PWD_ATTRS command. + * + * Return: 0 on success, error code on failure + */ +static int sandbox_scmi_pwd_attribs(struct udevice *dev, struct scmi_msg *msg) +{ + u32 domain_id; + struct scmi_pwd_attrs_out *out; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(domain_id) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + domain_id = *(u32 *)msg->in_msg; + out = (struct scmi_pwd_attrs_out *)msg->out_msg; + + if (domain_id >= ARRAY_SIZE(scmi_pwdom)) { + out->status = SCMI_NOT_FOUND; + + return 0; + } + + out->attributes = + SCMI_PWD_ATTR_PSTATE_SYNC | SCMI_PWD_ATTR_EXTENDED_NAME; + /* just 15-char + NULL */ + snprintf(out->name, SCMI_PWD_NAME_LENGTH_MAX, "power-domain--%d", + domain_id); + out->status = SCMI_SUCCESS; + + return 0; +} + +/** + * sandbox_scmi_pwd_state_set - implement SCMI_PWD_STATE_SET + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_PWD_STATE_SET command. + * + * Return: 0 on success, error code on failure + */ +static int sandbox_scmi_pwd_state_set(struct udevice *dev, struct scmi_msg *msg) +{ + struct scmi_pwd_state_set_in *in; + s32 *status; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*status)) + return -EINVAL; + + in = (struct scmi_pwd_state_set_in *)msg->in_msg; + status = (s32 *)msg->out_msg; + + if (in->domain_id >= ARRAY_SIZE(scmi_pwdom)) { + *status = SCMI_NOT_FOUND; + + return 0; + } + + if ((in->flags & SCMI_PWD_SET_FLAGS_ASYNC) || + (in->pstate != SCMI_PWD_PSTATE_TYPE_LOST && in->pstate)) { + *status = SCMI_INVALID_PARAMETERS; + + return 0; + } + + scmi_pwdom[in->domain_id].pstate = in->pstate; + *status = SCMI_SUCCESS; + + return 0; +} + +/** + * sandbox_scmi_pwd_state_get - implement SCMI_PWD_STATE_GET + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_PWD_STATE_GET command. + * + * Return: 0 on success, error code on failure + */ +static int sandbox_scmi_pwd_state_get(struct udevice *dev, struct scmi_msg *msg) +{ + u32 domain_id; + struct scmi_pwd_state_get_out *out; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(domain_id) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + domain_id = *(u32 *)msg->in_msg; + out = (struct scmi_pwd_state_get_out *)msg->out_msg; + + if (domain_id >= ARRAY_SIZE(scmi_pwdom)) { + out->status = SCMI_NOT_FOUND; + + return 0; + } + + out->pstate = scmi_pwdom[domain_id].pstate; + out->status = SCMI_SUCCESS; + + return 0; +} + +/** + * sandbox_scmi_pwd_name_get - implement SCMI_PWD_NAME_GET + * @dev: SCMI device + * @msg: SCMI message + * + * Implement SCMI_PWD_NAME_GET command. + * + * Return: 0 on success, error code on failure + */ +static int sandbox_scmi_pwd_name_get(struct udevice *dev, struct scmi_msg *msg) +{ + u32 domain_id; + struct scmi_pwd_name_get_out *out; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(domain_id) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + domain_id = *(u32 *)msg->in_msg; + out = (struct scmi_pwd_name_get_out *)msg->out_msg; + + if (domain_id >= ARRAY_SIZE(scmi_pwdom)) { + out->status = SCMI_NOT_FOUND; + + return 0; + } + + snprintf(out->extended_name, SCMI_PWD_EXTENDED_NAME_MAX, + "power-domain--%d-extended", domain_id); + out->status = SCMI_SUCCESS; + + return 0; +} + +/* Clock Protocol */ + static int sandbox_scmi_clock_protocol_attribs(struct udevice *dev, struct scmi_msg *msg) { @@ -470,12 +1057,142 @@ static int sandbox_scmi_voltd_level_get(struct udevice *dev, return 0; } +/** + * sandbox_scmi_of_get_channel - assigne a channel + * @dev: SCMI agent device + * @protocol: SCMI protocol device + * @channel: Pointer to channel info + * + * Assign a channel for the protocol, @protocol, in @channel, + * based on a device tree's property. + * + * Return: 0 on success, error code on failure + */ +static int sandbox_scmi_of_get_channel(struct udevice *dev, + struct udevice *protocol, + struct scmi_channel **channel) +{ + struct sandbox_channel *agent_chan = dev_get_plat(dev); + struct sandbox_channel *chan; + u32 channel_id; + + if (dev_read_u32(protocol, "linaro,sandbox-channel-id", &channel_id)) { + /* Uses agent channel */ + *channel = container_of(agent_chan, struct scmi_channel, ref); + + return 0; + } + + /* Setup a dedicated channel */ + chan = calloc(1, sizeof(*chan)); + if (!chan) + return -ENOMEM; + + chan->channel_id = channel_id; + + *channel = container_of(chan, struct scmi_channel, ref); + + return 0; +} + +/** + * sandbox_scmi_of_to_plat - assigne a channel to agent + * @dev: SCMI agent device + * + * Assign a channel for the agent, @protocol. + * + * Return: always 0 + */ +static int sandbox_scmi_of_to_plat(struct udevice *dev) +{ + struct sandbox_channel *chan = dev_get_plat(dev); + + /* The channel for agent is always 0 */ + chan->channel_id = 0; + + return 0; +} + +unsigned int sandbox_scmi_channel_id(struct udevice *dev) +{ + struct scmi_agent_proto_priv *priv; + struct sandbox_channel *chan; + + priv = dev_get_parent_priv(dev); + chan = (struct sandbox_channel *)&priv->channel->ref; + + return chan->channel_id; +} + +static int sandbox_proto_not_supported(struct scmi_msg *msg) +{ + *(u32 *)msg->out_msg = SCMI_NOT_SUPPORTED; + + return 0; +} + static int sandbox_scmi_test_process_msg(struct udevice *dev, struct scmi_channel *channel, struct scmi_msg *msg) { switch (msg->protocol_id) { + case SCMI_PROTOCOL_ID_BASE: + switch (msg->message_id) { + case SCMI_PROTOCOL_VERSION: + return sandbox_scmi_base_protocol_version(dev, msg); + case SCMI_PROTOCOL_ATTRIBUTES: + return sandbox_scmi_base_protocol_attrs(dev, msg); + case SCMI_PROTOCOL_MESSAGE_ATTRIBUTES: + return sandbox_scmi_base_message_attrs(dev, msg); + case SCMI_BASE_DISCOVER_VENDOR: + return sandbox_scmi_base_discover_vendor(dev, msg); + case SCMI_BASE_DISCOVER_SUB_VENDOR: + return sandbox_scmi_base_discover_sub_vendor(dev, msg); + case SCMI_BASE_DISCOVER_IMPL_VERSION: + return sandbox_scmi_base_discover_impl_version(dev, msg); + case SCMI_BASE_DISCOVER_LIST_PROTOCOLS: + return sandbox_scmi_base_discover_list_protocols(dev, msg); + case SCMI_BASE_DISCOVER_AGENT: + return sandbox_scmi_base_discover_agent(dev, msg); + case SCMI_BASE_NOTIFY_ERRORS: + break; + case SCMI_BASE_SET_DEVICE_PERMISSIONS: + return sandbox_scmi_base_set_device_permissions(dev, msg); + case SCMI_BASE_SET_PROTOCOL_PERMISSIONS: + return sandbox_scmi_base_set_protocol_permissions(dev, msg); + case SCMI_BASE_RESET_AGENT_CONFIGURATION: + return sandbox_scmi_base_reset_agent_configuration(dev, msg); + default: + break; + } + break; + case SCMI_PROTOCOL_ID_POWER_DOMAIN: + if (!CONFIG_IS_ENABLED(SCMI_POWER_DOMAIN)) + return sandbox_proto_not_supported(msg); + + switch (msg->message_id) { + case SCMI_PROTOCOL_VERSION: + return sandbox_scmi_pwd_protocol_version(dev, msg); + case SCMI_PROTOCOL_ATTRIBUTES: + return sandbox_scmi_pwd_protocol_attribs(dev, msg); + case SCMI_PROTOCOL_MESSAGE_ATTRIBUTES: + return sandbox_scmi_pwd_protocol_msg_attribs(dev, msg); + case SCMI_PWD_ATTRIBUTES: + return sandbox_scmi_pwd_attribs(dev, msg); + case SCMI_PWD_STATE_SET: + return sandbox_scmi_pwd_state_set(dev, msg); + case SCMI_PWD_STATE_GET: + return sandbox_scmi_pwd_state_get(dev, msg); + case SCMI_PWD_NAME_GET: + return sandbox_scmi_pwd_name_get(dev, msg); + default: + break; + } + break; case SCMI_PROTOCOL_ID_CLOCK: + if (!CONFIG_IS_ENABLED(CLK_SCMI)) + return sandbox_proto_not_supported(msg); + switch (msg->message_id) { case SCMI_PROTOCOL_ATTRIBUTES: return sandbox_scmi_clock_protocol_attribs(dev, msg); @@ -492,6 +1209,9 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev, } break; case SCMI_PROTOCOL_ID_RESET_DOMAIN: + if (!CONFIG_IS_ENABLED(RESET_SCMI)) + return sandbox_proto_not_supported(msg); + switch (msg->message_id) { case SCMI_RESET_DOMAIN_ATTRIBUTES: return sandbox_scmi_rd_attribs(dev, msg); @@ -502,6 +1222,9 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev, } break; case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: + if (!CONFIG_IS_ENABLED(DM_REGULATOR_SCMI)) + return sandbox_proto_not_supported(msg); + switch (msg->message_id) { case SCMI_VOLTAGE_DOMAIN_ATTRIBUTES: return sandbox_scmi_voltd_attribs(dev, msg); @@ -517,13 +1240,10 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev, break; } break; - case SCMI_PROTOCOL_ID_BASE: - case SCMI_PROTOCOL_ID_POWER_DOMAIN: case SCMI_PROTOCOL_ID_SYSTEM: case SCMI_PROTOCOL_ID_PERF: case SCMI_PROTOCOL_ID_SENSOR: - *(u32 *)msg->out_msg = SCMI_NOT_SUPPORTED; - return 0; + return sandbox_proto_not_supported(msg); default: break; } @@ -541,16 +1261,8 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev, static int sandbox_scmi_test_remove(struct udevice *dev) { - struct sandbox_scmi_agent *agent = dev_get_priv(dev); - - if (agent != sandbox_scmi_service_ctx()->agent) - return -EINVAL; - debug_print_agent_state(dev, "removed"); - /* We only need to dereference the agent in the context */ - sandbox_scmi_service_ctx()->agent = NULL; - return 0; } @@ -558,10 +1270,10 @@ static int sandbox_scmi_test_probe(struct udevice *dev) { struct sandbox_scmi_agent *agent = dev_get_priv(dev); - if (sandbox_scmi_service_ctx()->agent) - return -EINVAL; - *agent = (struct sandbox_scmi_agent){ + .pwdom_version = SANDBOX_SCMI_PWD_PROTOCOL_VERSION, + .pwdom = scmi_pwdom, + .pwdom_count = ARRAY_SIZE(scmi_pwdom), .clk = scmi_clk, .clk_count = ARRAY_SIZE(scmi_clk), .reset = scmi_reset, @@ -572,9 +1284,6 @@ static int sandbox_scmi_test_probe(struct udevice *dev) debug_print_agent_state(dev, "probed"); - /* Save reference for tests purpose */ - sandbox_scmi_service_ctx()->agent = agent; - return 0; }; @@ -584,6 +1293,7 @@ static const struct udevice_id sandbox_scmi_test_ids[] = { }; struct scmi_agent_ops sandbox_scmi_test_ops = { + .of_get_channel = sandbox_scmi_of_get_channel, .process_msg = sandbox_scmi_test_process_msg, }; @@ -592,6 +1302,8 @@ U_BOOT_DRIVER(sandbox_scmi_agent) = { .id = UCLASS_SCMI_AGENT, .of_match = sandbox_scmi_test_ids, .priv_auto = sizeof(struct sandbox_scmi_agent), + .plat_auto = sizeof(struct sandbox_channel), + .of_to_plat = sandbox_scmi_of_to_plat, .probe = sandbox_scmi_test_probe, .remove = sandbox_scmi_test_remove, .ops = &sandbox_scmi_test_ops, diff --git a/drivers/firmware/scmi/sandbox-scmi_devices.c b/drivers/firmware/scmi/sandbox-scmi_devices.c index 9baeb469ec0..603e2bb40af 100644 --- a/drivers/firmware/scmi/sandbox-scmi_devices.c +++ b/drivers/firmware/scmi/sandbox-scmi_devices.c @@ -29,12 +29,14 @@ /* * struct sandbox_scmi_device_priv - Storage for device handles used by test + * @pwdom: Power domain device * @clk: Array of clock instances used by tests * @reset_clt: Array of the reset controller instances used by tests * @regulators: Array of regulator device references used by the tests * @devices: Resources exposed by sandbox_scmi_devices_ctx() */ struct sandbox_scmi_device_priv { + struct power_domain pwdom; struct clk clk[SCMI_TEST_DEVICES_CLK_COUNT]; struct reset_ctl reset_ctl[SCMI_TEST_DEVICES_RD_COUNT]; struct udevice *regulators[SCMI_TEST_DEVICES_VOLTD_COUNT]; @@ -60,12 +62,13 @@ static int sandbox_scmi_devices_remove(struct udevice *dev) if (!devices) return 0; - for (n = 0; n < SCMI_TEST_DEVICES_RD_COUNT; n++) { - int ret2 = reset_free(devices->reset + n); + if (CONFIG_IS_ENABLED(RESET_SCMI)) + for (n = 0; n < SCMI_TEST_DEVICES_RD_COUNT; n++) { + int ret2 = reset_free(devices->reset + n); - if (ret2 && !ret) - ret = ret2; - } + if (ret2 && !ret) + ret = ret2; + } return ret; } @@ -77,6 +80,8 @@ static int sandbox_scmi_devices_probe(struct udevice *dev) size_t n; priv->devices = (struct sandbox_scmi_devices){ + .pwdom = &priv->pwdom, + .pwdom_count = 1, .clk = priv->clk, .clk_count = SCMI_TEST_DEVICES_CLK_COUNT, .reset = priv->reset_ctl, @@ -85,33 +90,53 @@ static int sandbox_scmi_devices_probe(struct udevice *dev) .regul_count = SCMI_TEST_DEVICES_VOLTD_COUNT, }; - for (n = 0; n < SCMI_TEST_DEVICES_CLK_COUNT; n++) { - ret = clk_get_by_index(dev, n, priv->devices.clk + n); + if (CONFIG_IS_ENABLED(SCMI_POWER_DOMAIN)) { + ret = power_domain_get_by_index(dev, priv->devices.pwdom, 0); if (ret) { - dev_err(dev, "%s: Failed on clk %zu\n", __func__, n); + dev_err(dev, "%s: Failed on power domain\n", __func__); return ret; } } - for (n = 0; n < SCMI_TEST_DEVICES_RD_COUNT; n++) { - ret = reset_get_by_index(dev, n, priv->devices.reset + n); - if (ret) { - dev_err(dev, "%s: Failed on reset %zu\n", __func__, n); - goto err_reset; + if (CONFIG_IS_ENABLED(CLK_SCMI)) { + for (n = 0; n < SCMI_TEST_DEVICES_CLK_COUNT; n++) { + ret = clk_get_by_index(dev, n, priv->devices.clk + n); + if (ret) { + dev_err(dev, "%s: Failed on clk %zu\n", + __func__, n); + return ret; + } } } - for (n = 0; n < SCMI_TEST_DEVICES_VOLTD_COUNT; n++) { - char name[32]; - - ret = snprintf(name, sizeof(name), "regul%zu-supply", n); - assert(ret >= 0 && ret < sizeof(name)); + if (CONFIG_IS_ENABLED(RESET_SCMI)) { + for (n = 0; n < SCMI_TEST_DEVICES_RD_COUNT; n++) { + ret = reset_get_by_index(dev, n, + priv->devices.reset + n); + if (ret) { + dev_err(dev, "%s: Failed on reset %zu\n", + __func__, n); + goto err_reset; + } + } + } - ret = device_get_supply_regulator(dev, name, - priv->devices.regul + n); - if (ret) { - dev_err(dev, "%s: Failed on voltd %zu\n", __func__, n); - goto err_regul; + if (CONFIG_IS_ENABLED(DM_REGULATOR_SCMI)) { + for (n = 0; n < SCMI_TEST_DEVICES_VOLTD_COUNT; n++) { + char name[32]; + + ret = snprintf(name, sizeof(name), "regul%zu-supply", + n); + assert(ret >= 0 && ret < sizeof(name)); + + ret = device_get_supply_regulator(dev, name, + priv->devices.regul + + n); + if (ret) { + dev_err(dev, "%s: Failed on voltd %zu\n", + __func__, n); + goto err_regul; + } } } @@ -120,8 +145,9 @@ static int sandbox_scmi_devices_probe(struct udevice *dev) err_regul: n = SCMI_TEST_DEVICES_RD_COUNT; err_reset: - for (; n > 0; n--) - reset_free(priv->devices.reset + n - 1); + if (CONFIG_IS_ENABLED(RESET_SCMI)) + for (; n > 0; n--) + reset_free(priv->devices.reset + n - 1); return ret; } diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 02de692d66f..0f1003e167e 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -8,6 +8,7 @@ #include <common.h> #include <dm.h> #include <errno.h> +#include <scmi_agent.h> #include <scmi_agent-uclass.h> #include <scmi_protocols.h> #include <dm/device_compat.h> @@ -37,6 +38,124 @@ static const struct error_code scmi_linux_errmap[] = { { .scmi = SCMI_PROTOCOL_ERROR, .errno = -EPROTO, }, }; +/** + * scmi_protocol_is_supported - check availability of protocol + * @dev: SCMI agent device + * @proto_id: Identifier of protocol + * + * check if the protocol, @proto_id, is provided by the SCMI agent, + * @dev. + * + * Return: 0 on success, error code otherwise + */ +static bool scmi_protocol_is_supported(struct udevice *dev, + enum scmi_std_protocol proto_id) +{ + struct scmi_agent_priv *priv; + int i; + + if (proto_id == SCMI_PROTOCOL_ID_BASE) + return true; + + priv = dev_get_uclass_plat(dev); + if (!priv) { + dev_err(dev, "No priv data found\n"); + return false; + } + + for (i = 0; i < priv->num_protocols; i++) + if (priv->protocols[i] == proto_id) + return true; + + return false; +} + +struct udevice *scmi_get_protocol(struct udevice *dev, + enum scmi_std_protocol id) +{ + struct scmi_agent_priv *priv; + struct udevice *proto; + + priv = dev_get_uclass_plat(dev); + if (!priv) { + dev_err(dev, "No priv data found\n"); + return NULL; + } + + switch (id) { + case SCMI_PROTOCOL_ID_BASE: + proto = priv->base_dev; + break; + case SCMI_PROTOCOL_ID_POWER_DOMAIN: + proto = priv->pwdom_dev; + break; + case SCMI_PROTOCOL_ID_CLOCK: + proto = priv->clock_dev; + break; + case SCMI_PROTOCOL_ID_RESET_DOMAIN: + proto = priv->resetdom_dev; + break; + case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: + proto = priv->voltagedom_dev; + break; + default: + dev_err(dev, "Protocol not supported\n"); + proto = NULL; + break; + } + if (proto && device_probe(proto)) + dev_err(dev, "Probe failed\n"); + + return proto; +} + +/** + * scmi_add_protocol - add protocol to agent + * @dev: SCMI agent device + * @proto_id: SCMI protocol ID + * @proto: SCMI protocol device + * + * Associate the protocol instance, @proto, to the agent, @dev, + * for later use. + * + * Return: 0 on success, error code on failure + */ +static int scmi_add_protocol(struct udevice *dev, + enum scmi_std_protocol proto_id, + struct udevice *proto) +{ + struct scmi_agent_priv *priv; + + priv = dev_get_uclass_plat(dev); + if (!priv) { + dev_err(dev, "No priv data found\n"); + return -ENODEV; + } + + switch (proto_id) { + case SCMI_PROTOCOL_ID_BASE: + priv->base_dev = proto; + break; + case SCMI_PROTOCOL_ID_POWER_DOMAIN: + priv->pwdom_dev = proto; + break; + case SCMI_PROTOCOL_ID_CLOCK: + priv->clock_dev = proto; + break; + case SCMI_PROTOCOL_ID_RESET_DOMAIN: + priv->resetdom_dev = proto; + break; + case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: + priv->voltagedom_dev = proto; + break; + default: + dev_err(dev, "Protocol not supported\n"); + return -EPROTO; + } + + return 0; +} + int scmi_to_linux_errno(s32 scmi_code) { int n; @@ -51,8 +170,191 @@ int scmi_to_linux_errno(s32 scmi_code) return -EPROTO; } +static struct udevice *find_scmi_protocol_device(struct udevice *dev) +{ + struct udevice *parent = NULL, *protocol; + + for (protocol = dev; protocol; protocol = parent) { + parent = dev_get_parent(protocol); + if (!parent || + device_get_uclass_id(parent) == UCLASS_SCMI_AGENT) + break; + } + + if (!parent) { + dev_err(dev, "Invalid SCMI device, agent not found\n"); + return NULL; + } + + return protocol; +} + +static const struct scmi_agent_ops *transport_dev_ops(struct udevice *dev) +{ + return (const struct scmi_agent_ops *)dev->driver->ops; +} + +/** + * scmi_of_get_channel() - Get SCMI channel handle + * + * @dev: SCMI agent device + * @channel: Output reference to the SCMI channel upon success + * + * On return, @channel will be set. + * Return 0 on success and a negative errno on failure + */ +static int scmi_of_get_channel(struct udevice *dev, struct udevice *protocol, + struct scmi_channel **channel) +{ + const struct scmi_agent_ops *ops; + + ops = transport_dev_ops(dev); + if (ops->of_get_channel) + return ops->of_get_channel(dev, protocol, channel); + else + return -EPROTONOSUPPORT; +} + +int devm_scmi_of_get_channel(struct udevice *dev) +{ + struct udevice *protocol; + struct scmi_agent_proto_priv *priv; + int ret; + + protocol = find_scmi_protocol_device(dev); + if (!protocol) + return -ENODEV; + + priv = dev_get_parent_priv(protocol); + ret = scmi_of_get_channel(protocol->parent, protocol, &priv->channel); + if (ret == -EPROTONOSUPPORT) { + /* Drivers without a get_channel operator don't need a channel ref */ + priv->channel = NULL; + + return 0; + } + + return ret; +} + +/** + * scmi_process_msg() - Send and process an SCMI message + * + * Send a message to an SCMI server. + * Caller sets scmi_msg::out_msg_sz to the output message buffer size. + * + * @dev: SCMI agent device + * @channel: Communication channel for the device + * @msg: Message structure reference + * + * On return, scmi_msg::out_msg_sz stores the response payload size. + * Return: 0 on success and a negative errno on failure + */ +static int scmi_process_msg(struct udevice *dev, struct scmi_channel *channel, + struct scmi_msg *msg) +{ + const struct scmi_agent_ops *ops; + + ops = transport_dev_ops(dev); + if (ops->process_msg) + return ops->process_msg(dev, channel, msg); + else + return -EPROTONOSUPPORT; +} + +int devm_scmi_process_msg(struct udevice *dev, struct scmi_msg *msg) +{ + struct udevice *protocol; + struct scmi_agent_proto_priv *priv; + + protocol = find_scmi_protocol_device(dev); + if (!protocol) + return -ENODEV; + + priv = dev_get_parent_priv(protocol); + + return scmi_process_msg(protocol->parent, priv->channel, msg); +} + +/** + * scmi_fill_base_info - get base information about SCMI server + * @agent: SCMI agent device + * @dev: SCMI protocol device + * + * By using Base protocol commands, collect the base information + * about SCMI server. + * + * Return: 0 on success, error code on failure + */ +static int scmi_fill_base_info(struct udevice *agent, struct udevice *dev) +{ + struct scmi_agent_priv *priv = dev_get_uclass_plat(agent); + int ret; + + ret = scmi_base_protocol_version(dev, &priv->version); + if (ret) { + dev_err(dev, "protocol_version() failed (%d)\n", ret); + return ret; + } + /* check for required version */ + if (priv->version < SCMI_BASE_PROTOCOL_VERSION) { + dev_err(dev, "base protocol version (%d) lower than expected\n", + priv->version); + return -EPROTO; + } + + ret = scmi_base_protocol_attrs(dev, &priv->num_agents, + &priv->num_protocols); + if (ret) { + dev_err(dev, "protocol_attrs() failed (%d)\n", ret); + return ret; + } + ret = scmi_base_discover_vendor(dev, &priv->vendor); + if (ret) { + dev_err(dev, "base_discover_vendor() failed (%d)\n", ret); + return ret; + } + ret = scmi_base_discover_sub_vendor(dev, &priv->sub_vendor); + if (ret) { + if (ret != -EOPNOTSUPP) { + dev_err(dev, "base_discover_sub_vendor() failed (%d)\n", + ret); + return ret; + } + priv->sub_vendor = "NA"; + } + ret = scmi_base_discover_impl_version(dev, &priv->impl_version); + if (ret) { + dev_err(dev, "base_discover_impl_version() failed (%d)\n", + ret); + return ret; + } + + ret = scmi_base_discover_agent(dev, 0xffffffff, + &priv->agent_id, &priv->agent_name); + if (ret) { + if (ret != -EOPNOTSUPP) { + dev_err(dev, + "base_discover_agent() failed for myself (%d)\n", + ret); + return ret; + } + priv->agent_id = 0xffffffff; + priv->agent_name = "NA"; + } + + ret = scmi_base_discover_list_protocols(dev, &priv->protocols); + if (ret != priv->num_protocols) { + dev_err(dev, "base_discover_list_protocols() failed (%d)\n", + ret); + return -EPROTO; + } + + return 0; +} + /* - * SCMI agent devices binds devices of various uclasses depeding on + * SCMI agent devices binds devices of various uclasses depending on * the FDT description. scmi_bind_protocol() is a generic bind sequence * called by the uclass at bind stage, that is uclass post_bind. */ @@ -61,9 +363,43 @@ static int scmi_bind_protocols(struct udevice *dev) int ret = 0; ofnode node; const char *name; + struct driver *drv; + struct udevice *agent, *proto; + + if (!uclass_get_device(UCLASS_SCMI_AGENT, 1, &agent)) { + /* This is a second SCMI agent */ + dev_err(dev, "Cannot have more than one SCMI agent\n"); + return -EEXIST; + } + + /* initialize the device from device tree */ + drv = DM_DRIVER_GET(scmi_base_drv); + name = "scmi-base.0"; + ret = device_bind(dev, drv, name, NULL, ofnode_null(), &proto); + if (ret) { + dev_err(dev, "failed to bind base protocol\n"); + return ret; + } + ret = scmi_add_protocol(dev, SCMI_PROTOCOL_ID_BASE, proto); + if (ret) { + dev_err(dev, "failed to add protocol: %s, ret: %d\n", + proto->name, ret); + return ret; + } + + ret = device_probe(proto); + if (ret) { + dev_err(dev, "failed to probe base protocol\n"); + return ret; + } + + ret = scmi_fill_base_info(dev, proto); + if (ret) { + dev_err(dev, "failed to get base information\n"); + return ret; + } dev_for_each_subnode(node, dev) { - struct driver *drv = NULL; u32 protocol_id; if (!ofnode_is_enabled(node)) @@ -72,18 +408,27 @@ static int scmi_bind_protocols(struct udevice *dev) if (ofnode_read_u32(node, "reg", &protocol_id)) continue; + drv = NULL; name = ofnode_get_name(node); switch (protocol_id) { + case SCMI_PROTOCOL_ID_POWER_DOMAIN: + if (CONFIG_IS_ENABLED(SCMI_POWER_DOMAIN) && + scmi_protocol_is_supported(dev, protocol_id)) + drv = DM_DRIVER_GET(scmi_power_domain); + break; case SCMI_PROTOCOL_ID_CLOCK: - if (CONFIG_IS_ENABLED(CLK_SCMI)) + if (CONFIG_IS_ENABLED(CLK_SCMI) && + scmi_protocol_is_supported(dev, protocol_id)) drv = DM_DRIVER_GET(scmi_clock); break; case SCMI_PROTOCOL_ID_RESET_DOMAIN: - if (IS_ENABLED(CONFIG_RESET_SCMI)) + if (IS_ENABLED(CONFIG_RESET_SCMI) && + scmi_protocol_is_supported(dev, protocol_id)) drv = DM_DRIVER_GET(scmi_reset_domain); break; case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: - if (IS_ENABLED(CONFIG_DM_REGULATOR_SCMI)) { + if (IS_ENABLED(CONFIG_DM_REGULATOR_SCMI) && + scmi_protocol_is_supported(dev, protocol_id)) { node = ofnode_find_subnode(node, "regulators"); if (!ofnode_valid(node)) { dev_err(dev, "no regulators node\n"); @@ -102,70 +447,26 @@ static int scmi_bind_protocols(struct udevice *dev) continue; } - ret = device_bind(dev, drv, name, NULL, node, NULL); - if (ret) + ret = device_bind(dev, drv, name, NULL, node, &proto); + if (ret) { + dev_err(dev, "failed to bind %s protocol\n", drv->name); + break; + } + ret = scmi_add_protocol(dev, protocol_id, proto); + if (ret) { + dev_err(dev, "failed to add protocol: %s, ret: %d\n", + proto->name, ret); break; + } } return ret; } -static struct udevice *find_scmi_transport_device(struct udevice *dev) -{ - struct udevice *parent = dev; - - do { - parent = dev_get_parent(parent); - } while (parent && device_get_uclass_id(parent) != UCLASS_SCMI_AGENT); - - if (!parent) - dev_err(dev, "Invalid SCMI device, agent not found\n"); - - return parent; -} - -static const struct scmi_agent_ops *transport_dev_ops(struct udevice *dev) -{ - return (const struct scmi_agent_ops *)dev->driver->ops; -} - -int devm_scmi_of_get_channel(struct udevice *dev, struct scmi_channel **channel) -{ - struct udevice *parent; - - parent = find_scmi_transport_device(dev); - if (!parent) - return -ENODEV; - - if (transport_dev_ops(parent)->of_get_channel) - return transport_dev_ops(parent)->of_get_channel(parent, channel); - - /* Drivers without a get_channel operator don't need a channel ref */ - *channel = NULL; - - return 0; -} - -int devm_scmi_process_msg(struct udevice *dev, struct scmi_channel *channel, - struct scmi_msg *msg) -{ - const struct scmi_agent_ops *ops; - struct udevice *parent; - - parent = find_scmi_transport_device(dev); - if (!parent) - return -ENODEV; - - ops = transport_dev_ops(parent); - - if (ops->process_msg) - return ops->process_msg(parent, channel, msg); - - return -EPROTONOSUPPORT; -} - UCLASS_DRIVER(scmi_agent) = { .id = UCLASS_SCMI_AGENT, .name = "scmi_agent", .post_bind = scmi_bind_protocols, + .per_device_plat_auto = sizeof(struct scmi_agent_priv), + .per_child_auto = sizeof(struct scmi_agent_proto_priv), }; diff --git a/drivers/firmware/scmi/smccc_agent.c b/drivers/firmware/scmi/smccc_agent.c index 6a52cd75d67..972c6addde2 100644 --- a/drivers/firmware/scmi/smccc_agent.c +++ b/drivers/firmware/scmi/smccc_agent.c @@ -81,6 +81,7 @@ static int setup_channel(struct udevice *dev, struct scmi_smccc_channel *chan) } static int scmi_smccc_get_channel(struct udevice *dev, + struct udevice *protocol, struct scmi_channel **channel) { struct scmi_smccc_channel *base_chan = dev_get_plat(dev); @@ -88,7 +89,7 @@ static int scmi_smccc_get_channel(struct udevice *dev, u32 func_id; int ret; - if (dev_read_u32(dev, "arm,smc-id", &func_id)) { + if (dev_read_u32(protocol, "arm,smc-id", &func_id)) { /* Uses agent base channel */ *channel = container_of(base_chan, struct scmi_channel, ref); @@ -100,7 +101,7 @@ static int scmi_smccc_get_channel(struct udevice *dev, if (!chan) return -ENOMEM; - ret = setup_channel(dev, chan); + ret = setup_channel(protocol, chan); if (ret) { free(chan); return ret; diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 166bd78ca50..6e9f93e9a30 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -3,7 +3,7 @@ * Texas Instruments System Control Interface Protocol Driver * Based on drivers/firmware/ti_sci.c from Linux. * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Lokesh Vutla <lokeshvutla@ti.com> */ diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h index 101210eb215..bb8bc7beead 100644 --- a/drivers/firmware/ti_sci.h +++ b/drivers/firmware/ti_sci.h @@ -6,7 +6,7 @@ * The system works in a message response protocol * See: http://processors.wiki.ti.com/index.php/TISCI for details * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Based on drivers/firmware/ti_sci.h from Linux. * */ diff --git a/drivers/firmware/ti_sci_static_data.h b/drivers/firmware/ti_sci_static_data.h index 1a461fab619..567ce8911a7 100644 --- a/drivers/firmware/ti_sci_static_data.h +++ b/drivers/firmware/ti_sci_static_data.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause */ /* - * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ * */ diff --git a/drivers/fpga/zynqmppl.c b/drivers/fpga/zynqmppl.c index b1f201fb18b..2656f5fc5ec 100644 --- a/drivers/fpga/zynqmppl.c +++ b/drivers/fpga/zynqmppl.c @@ -2,7 +2,7 @@ /* * (C) Copyright 2015 - 2016, Xilinx, Inc, * Michal Simek <michal.simek@amd.com> - * Siva Durga Prasad <siva.durga.prasad.paladugu@amd.com>> + * Siva Durga Prasad Paladugu <siva.durga.prasad.paladugu@amd.com> */ #include <console.h> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 9bf6e428ded..ba42b0768e1 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -372,6 +372,13 @@ config SUNXI_GPIO help Support the GPIO device in Allwinner SoCs. +config SUNXI_NEW_PINCTRL + bool + depends on SUNXI_GPIO + ---help--- + The Allwinner D1 and other new SoCs use a different register map + for the GPIO block, which we need to know about in the SPL. + config XILINX_GPIO bool "Xilinx GPIO driver" depends on DM_GPIO @@ -659,4 +666,11 @@ config ADP5585_GPIO help Support ADP5585 GPIO expander. +config RZG2L_GPIO + bool "Renesas RZ/G2L family GPIO driver" + depends on DM_GPIO && PINCTRL_RZG2L + help + Support the gpio functionality of the pin function controller (PFC) + on the Renesas RZ/G2L SoC family. + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 64a36c472eb..c8b3fd78141 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -74,3 +74,4 @@ obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o obj-$(CONFIG_$(SPL_TPL_)TURRIS_OMNIA_MCU) += turris_omnia_mcu.o obj-$(CONFIG_FTGPIO010) += ftgpio010.o obj-$(CONFIG_ADP5585_GPIO) += adp5585_gpio.o +obj-$(CONFIG_RZG2L_GPIO) += rzg2l-gpio.o diff --git a/drivers/gpio/axp_gpio.c b/drivers/gpio/axp_gpio.c index 49672193ffc..af6631697f5 100644 --- a/drivers/gpio/axp_gpio.c +++ b/drivers/gpio/axp_gpio.c @@ -14,6 +14,7 @@ #include <dm/lists.h> #include <dm/root.h> #include <errno.h> +#include <sunxi_gpio.h> static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val); diff --git a/drivers/gpio/pcf8575_gpio.c b/drivers/gpio/pcf8575_gpio.c index d5930d941fc..f38e215c4d6 100644 --- a/drivers/gpio/pcf8575_gpio.c +++ b/drivers/gpio/pcf8575_gpio.c @@ -2,7 +2,7 @@ /* * PCF8575 I2C GPIO EXPANDER DRIVER * - * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/ * * Vignesh R <vigneshr@ti.com> * diff --git a/drivers/gpio/rzg2l-gpio.c b/drivers/gpio/rzg2l-gpio.c new file mode 100644 index 00000000000..2477af7874b --- /dev/null +++ b/drivers/gpio/rzg2l-gpio.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G2L Pin Function Controller + * + * Copyright (C) 2021-2023 Renesas Electronics Corp. + */ + +#include <asm-generic/gpio.h> +#include <asm/io.h> +#include <dm/device.h> +#include <dm/device_compat.h> +#include <renesas/rzg2l-pfc.h> + +static void rzg2l_gpio_set(const struct rzg2l_pfc_data *data, u32 port, u8 pin, + bool value) +{ + if (value) + setbits_8(data->base + P(port), BIT(pin)); + else + clrbits_8(data->base + P(port), BIT(pin)); +} + +static int rzg2l_gpio_get_value(struct udevice *dev, unsigned int offset) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + const u32 port = RZG2L_PINMUX_TO_PORT(offset); + const u8 pin = RZG2L_PINMUX_TO_PIN(offset); + u16 pm_state; + + pm_state = (readw(data->base + PM(port)) >> (pin * 2)) & PM_MASK; + switch (pm_state) { + case PM_INPUT: + return !!(readb(data->base + PIN(port)) & BIT(pin)); + case PM_OUTPUT: + case PM_OUTPUT_IEN: + return !!(readb(data->base + P(port)) & BIT(pin)); + default: /* PM_HIGH_Z */ + return 0; + } +} + +static int rzg2l_gpio_set_value(struct udevice *dev, unsigned int offset, + int value) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + const u32 port = RZG2L_PINMUX_TO_PORT(offset); + const u8 pin = RZG2L_PINMUX_TO_PIN(offset); + + rzg2l_gpio_set(data, port, pin, (bool)value); + return 0; +} + +static void rzg2l_gpio_set_direction(const struct rzg2l_pfc_data *data, + u32 port, u8 pin, bool output) +{ + clrsetbits_le16(data->base + PM(port), PM_MASK << (pin * 2), + (output ? PM_OUTPUT : PM_INPUT) << (pin * 2)); +} + +static int rzg2l_gpio_direction_input(struct udevice *dev, unsigned int offset) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + const u32 port = RZG2L_PINMUX_TO_PORT(offset); + const u8 pin = RZG2L_PINMUX_TO_PIN(offset); + + rzg2l_gpio_set_direction(data, port, pin, false); + return 0; +} + +static int rzg2l_gpio_direction_output(struct udevice *dev, unsigned int offset, + int value) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + const u32 port = RZG2L_PINMUX_TO_PORT(offset); + const u8 pin = RZG2L_PINMUX_TO_PIN(offset); + + rzg2l_gpio_set(data, port, pin, (bool)value); + rzg2l_gpio_set_direction(data, port, pin, true); + return 0; +} + +static int rzg2l_gpio_request(struct udevice *dev, unsigned int offset, + const char *label) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + const u32 port = RZG2L_PINMUX_TO_PORT(offset); + const u8 pin = RZG2L_PINMUX_TO_PIN(offset); + + if (!rzg2l_port_validate(data, port, pin)) { + dev_err(dev, "Invalid GPIO %u:%u\n", port, pin); + return -EINVAL; + } + + /* Select GPIO mode in PMC Register */ + clrbits_8(data->base + PMC(port), BIT(pin)); + + return 0; +} + +static int rzg2l_gpio_get_function(struct udevice *dev, unsigned int offset) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + const u32 port = RZG2L_PINMUX_TO_PORT(offset); + const u8 pin = RZG2L_PINMUX_TO_PIN(offset); + u16 pm_state; + u8 pmc_state; + + if (!rzg2l_port_validate(data, port, pin)) { + /* This offset does not correspond to a valid GPIO pin. */ + return -ENOENT; + } + + /* Check if the pin is in GPIO or function mode. */ + pmc_state = readb(data->base + PMC(port)) & BIT(pin); + if (pmc_state) + return GPIOF_FUNC; + + /* Check the pin direction. */ + pm_state = (readw(data->base + PM(port)) >> (pin * 2)) & PM_MASK; + switch (pm_state) { + case PM_INPUT: + return GPIOF_INPUT; + case PM_OUTPUT: + case PM_OUTPUT_IEN: + return GPIOF_OUTPUT; + default: /* PM_HIGH_Z */ + return GPIOF_UNUSED; + } +} + +static const struct dm_gpio_ops rzg2l_gpio_ops = { + .direction_input = rzg2l_gpio_direction_input, + .direction_output = rzg2l_gpio_direction_output, + .get_value = rzg2l_gpio_get_value, + .set_value = rzg2l_gpio_set_value, + .request = rzg2l_gpio_request, + .get_function = rzg2l_gpio_get_function, +}; + +static int rzg2l_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct ofnode_phandle_args args; + int ret; + + uc_priv->bank_name = "rzg2l-pfc-gpio"; + ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "gpio-ranges", + NULL, 3, 0, &args); + if (ret < 0) { + dev_err(dev, "Failed to parse gpio-ranges: %d\n", ret); + return -EINVAL; + } + + uc_priv->gpio_count = args.args[2]; + return rzg2l_pfc_enable(dev); +} + +U_BOOT_DRIVER(rzg2l_pfc_gpio) = { + .name = "rzg2l-pfc-gpio", + .id = UCLASS_GPIO, + .ops = &rzg2l_gpio_ops, + .probe = rzg2l_gpio_probe, +}; diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c index f0b42e4fdb7..e4463a223f7 100644 --- a/drivers/gpio/sunxi_gpio.c +++ b/drivers/gpio/sunxi_gpio.c @@ -17,37 +17,165 @@ #include <asm/io.h> #include <asm/gpio.h> #include <dt-bindings/gpio/gpio.h> +#include <sunxi_gpio.h> -#if !CONFIG_IS_ENABLED(DM_GPIO) -static int sunxi_gpio_output(u32 pin, u32 val) +/* + * ======================================================================= + * Low level GPIO/pin controller access functions, to be shared by non-DM + * SPL code and the DM pinctrl/GPIO drivers. + * The functions ending in "bank" take a base pointer to a GPIO bank, and + * the pin offset is relative to that bank. + * The functions without "bank" in their name take a linear GPIO number, + * covering all ports, and starting at 0 for PortA. + * ======================================================================= + */ + +#define GPIO_BANK(pin) ((pin) >> 5) +#define GPIO_NUM(pin) ((pin) & 0x1f) + +#define GPIO_CFG_REG_OFFSET 0x00 +#define GPIO_CFG_INDEX(pin) (((pin) & 0x1f) >> 3) +#define GPIO_CFG_OFFSET(pin) ((((pin) & 0x1f) & 0x7) << 2) + +#define GPIO_DAT_REG_OFFSET 0x10 + +#define GPIO_DRV_REG_OFFSET 0x14 + +/* Newer SoCs use a slightly different register layout */ +#ifdef CONFIG_SUNXI_NEW_PINCTRL +/* pin drive strength: 4 bits per pin */ +#define GPIO_DRV_INDEX(pin) ((pin) / 8) +#define GPIO_DRV_OFFSET(pin) (((pin) % 8) * 4) + +#define GPIO_PULL_REG_OFFSET 0x24 + +#else /* older generation pin controllers */ +/* pin drive strength: 2 bits per pin */ +#define GPIO_DRV_INDEX(pin) ((pin) / 16) +#define GPIO_DRV_OFFSET(pin) (((pin) % 16) * 2) + +#define GPIO_PULL_REG_OFFSET 0x1c +#endif + +#define GPIO_PULL_INDEX(pin) (((pin) & 0x1f) >> 4) +#define GPIO_PULL_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1) + +static void* BANK_TO_GPIO(int bank) +{ + void *pio_base; + + if (bank < SUNXI_GPIO_L) { + pio_base = (void *)(uintptr_t)SUNXI_PIO_BASE; + } else { + pio_base = (void *)(uintptr_t)SUNXI_R_PIO_BASE; + bank -= SUNXI_GPIO_L; + } + + return pio_base + bank * SUNXI_PINCTRL_BANK_SIZE; +} + +void sunxi_gpio_set_cfgbank(void *bank_base, int pin_offset, u32 val) +{ + u32 index = GPIO_CFG_INDEX(pin_offset); + u32 offset = GPIO_CFG_OFFSET(pin_offset); + + clrsetbits_le32(bank_base + GPIO_CFG_REG_OFFSET + index * 4, + 0xfU << offset, val << offset); +} + +void sunxi_gpio_set_cfgpin(u32 pin, u32 val) { - u32 dat; u32 bank = GPIO_BANK(pin); - u32 num = GPIO_NUM(pin); - struct sunxi_gpio *pio = BANK_TO_GPIO(bank); + void *pio = BANK_TO_GPIO(bank); - dat = readl(&pio->dat); - if (val) - dat |= 0x1 << num; - else - dat &= ~(0x1 << num); + sunxi_gpio_set_cfgbank(pio, GPIO_NUM(pin), val); +} + +int sunxi_gpio_get_cfgbank(void *bank_base, int pin_offset) +{ + u32 index = GPIO_CFG_INDEX(pin_offset); + u32 offset = GPIO_CFG_OFFSET(pin_offset); + u32 cfg; - writel(dat, &pio->dat); + cfg = readl(bank_base + GPIO_CFG_REG_OFFSET + index * 4); + cfg >>= offset; - return 0; + return cfg & 0xf; +} + +int sunxi_gpio_get_cfgpin(u32 pin) +{ + u32 bank = GPIO_BANK(pin); + void *bank_base = BANK_TO_GPIO(bank); + + return sunxi_gpio_get_cfgbank(bank_base, GPIO_NUM(pin)); +} + +static void sunxi_gpio_set_value_bank(void *bank_base, int pin, bool set) +{ + u32 mask = 1U << pin; + + clrsetbits_le32(bank_base + GPIO_DAT_REG_OFFSET, + set ? 0 : mask, set ? mask : 0); +} + +static int sunxi_gpio_get_value_bank(void *bank_base, int pin) +{ + return !!(readl(bank_base + GPIO_DAT_REG_OFFSET) & (1U << pin)); +} + +void sunxi_gpio_set_drv(u32 pin, u32 val) +{ + u32 bank = GPIO_BANK(pin); + void *bank_base = BANK_TO_GPIO(bank); + + sunxi_gpio_set_drv_bank(bank_base, GPIO_NUM(pin), val); +} + +void sunxi_gpio_set_drv_bank(void *bank_base, u32 pin_offset, u32 val) +{ + u32 index = GPIO_DRV_INDEX(pin_offset); + u32 offset = GPIO_DRV_OFFSET(pin_offset); + + clrsetbits_le32(bank_base + GPIO_DRV_REG_OFFSET + index * 4, + 0x3U << offset, val << offset); +} + +void sunxi_gpio_set_pull(u32 pin, u32 val) +{ + u32 bank = GPIO_BANK(pin); + void *bank_base = BANK_TO_GPIO(bank); + + sunxi_gpio_set_pull_bank(bank_base, GPIO_NUM(pin), val); } -static int sunxi_gpio_input(u32 pin) +void sunxi_gpio_set_pull_bank(void *bank_base, int pin_offset, u32 val) +{ + u32 index = GPIO_PULL_INDEX(pin_offset); + u32 offset = GPIO_PULL_OFFSET(pin_offset); + + clrsetbits_le32(bank_base + GPIO_PULL_REG_OFFSET + index * 4, + 0x3U << offset, val << offset); +} + + +/* =========== Non-DM code, used by the SPL. ============ */ + +#if !CONFIG_IS_ENABLED(DM_GPIO) +static void sunxi_gpio_set_value(u32 pin, bool set) { - u32 dat; u32 bank = GPIO_BANK(pin); - u32 num = GPIO_NUM(pin); - struct sunxi_gpio *pio = BANK_TO_GPIO(bank); + void *pio = BANK_TO_GPIO(bank); + + sunxi_gpio_set_value_bank(pio, GPIO_NUM(pin), set); +} - dat = readl(&pio->dat); - dat >>= num; +static int sunxi_gpio_get_value(u32 pin) +{ + u32 bank = GPIO_BANK(pin); + void *pio = BANK_TO_GPIO(bank); - return dat & 0x1; + return sunxi_gpio_get_value_bank(pio, GPIO_NUM(pin)); } int gpio_request(unsigned gpio, const char *label) @@ -70,18 +198,21 @@ int gpio_direction_input(unsigned gpio) int gpio_direction_output(unsigned gpio, int value) { sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT); + sunxi_gpio_set_value(gpio, value); - return sunxi_gpio_output(gpio, value); + return 0; } int gpio_get_value(unsigned gpio) { - return sunxi_gpio_input(gpio); + return sunxi_gpio_get_value(gpio); } int gpio_set_value(unsigned gpio, int value) { - return sunxi_gpio_output(gpio, value); + sunxi_gpio_set_value(gpio, value); + + return 0; } int sunxi_name_to_gpio(const char *name) @@ -106,7 +237,9 @@ int sunxi_name_to_gpio(const char *name) return -1; return group * 32 + pin; } -#endif /* DM_GPIO */ +#endif /* !DM_GPIO */ + +/* =========== DM code, used by U-Boot proper. ============ */ #if CONFIG_IS_ENABLED(DM_GPIO) /* TODO(sjg@chromium.org): Remove this function and use device tree */ @@ -131,13 +264,8 @@ int sunxi_name_to_gpio(const char *name) static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset) { struct sunxi_gpio_plat *plat = dev_get_plat(dev); - u32 num = GPIO_NUM(offset); - unsigned dat; - - dat = readl(&plat->regs->dat); - dat >>= num; - return dat & 0x1; + return sunxi_gpio_get_value_bank(plat->regs, offset); } static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset) @@ -175,9 +303,8 @@ static int sunxi_gpio_set_flags(struct udevice *dev, unsigned int offset, if (flags & GPIOD_IS_OUT) { u32 value = !!(flags & GPIOD_IS_OUT_ACTIVE); - u32 num = GPIO_NUM(offset); - clrsetbits_le32(&plat->regs->dat, 1 << num, value << num); + sunxi_gpio_set_value_bank(plat->regs, offset, value); sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT); } else if (flags & GPIOD_IS_IN) { u32 pull = 0; diff --git a/drivers/gpio/tegra186_gpio.c b/drivers/gpio/tegra186_gpio.c index 82dcaf96312..94a20d143e1 100644 --- a/drivers/gpio/tegra186_gpio.c +++ b/drivers/gpio/tegra186_gpio.c @@ -176,8 +176,8 @@ static int tegra186_gpio_bind(struct udevice *parent) if (parent_plat) return 0; - regs = (uint32_t *)devfdt_get_addr_name(parent, "gpio"); - if (regs == (uint32_t *)FDT_ADDR_T_NONE) + regs = dev_read_addr_name_ptr(parent, "gpio"); + if (!regs) return -EINVAL; for (port = 0; port < ctlr_data->port_count; port++) { diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index d5b85f398db..a96a8c7e955 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -3,7 +3,7 @@ # (C) Copyright 2000-2007 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. obj-$(CONFIG_$(SPL_)DM_I2C) += i2c-uclass.o -ifdef CONFIG_ACPIGEN +ifdef CONFIG_$(SPL_)ACPIGEN obj-$(CONFIG_$(SPL_)DM_I2C) += acpi_i2c.o endif obj-$(CONFIG_$(SPL_)DM_I2C_GPIO) += i2c-gpio.o diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index e54de42abc3..215ce010cb7 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -24,6 +24,17 @@ */ #define DW_I2C_COMP_TYPE 0x44570140 +/* + * This constant is used to calculate when during the clock high phase the data + * bit shall be read. The value was copied from the Linux v6.5 function + * i2c_dw_scl_hcnt() which provides the following explanation: + * + * "This is just an experimental rule: the tHD;STA period turned out to be + * proportinal to (_HCNT + 3). With this setting, we could meet both tHIGH and + * tHD;STA timing specs." + */ +#define T_HD_STA_OFFSET 3 + static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable) { u32 ena = enable ? IC_ENABLE_0B : 0; @@ -155,10 +166,10 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode, /* * Back-solve for hcnt and lcnt according to the following equations: - * SCL_High_time = [(HCNT + IC_*_SPKLEN + 7) * ic_clk] + SCL_Fall_time + * SCL_High_time = [(HCNT + IC_*_SPKLEN + T_HD_STA_OFFSET) * ic_clk] + SCL_Fall_time * SCL_Low_time = [(LCNT + 1) * ic_clk] - SCL_Fall_time + SCL_Rise_time */ - hcnt = min_thigh_cnt - fall_cnt - 7 - spk_cnt; + hcnt = min_thigh_cnt - fall_cnt - T_HD_STA_OFFSET - spk_cnt; lcnt = min_tlow_cnt - rise_cnt + fall_cnt - 1; if (hcnt < 0 || lcnt < 0) { @@ -170,13 +181,13 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode, * Now add things back up to ensure the period is hit. If it is off, * split the difference and bias to lcnt for remainder */ - tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1; + tot = hcnt + lcnt + T_HD_STA_OFFSET + spk_cnt + rise_cnt + 1; if (tot < period_cnt) { diff = (period_cnt - tot) / 2; hcnt += diff; lcnt += diff; - tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1; + tot = hcnt + lcnt + T_HD_STA_OFFSET + spk_cnt + rise_cnt + 1; lcnt += period_cnt - tot; } diff --git a/drivers/i2c/i2c-emul-uclass.c b/drivers/i2c/i2c-emul-uclass.c index 1107cf309fc..d421ddfcbe2 100644 --- a/drivers/i2c/i2c-emul-uclass.c +++ b/drivers/i2c/i2c-emul-uclass.c @@ -46,7 +46,7 @@ int i2c_emul_find(struct udevice *dev, struct udevice **emulp) struct udevice *emul; int ret; - if (!CONFIG_IS_ENABLED(OF_PLATDATA)) { + if (CONFIG_IS_ENABLED(OF_REAL)) { ret = uclass_find_device_by_phandle(UCLASS_I2C_EMUL, dev, "sandbox,emul", &emul); } else { diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 8867a560bd8..98f95859f3b 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -388,6 +388,81 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len, return 0; } +/* Find and probe I2C bus based on a chip attached to it */ +static int i2c_get_parent_bus(ofnode chip, struct udevice **devp) +{ + ofnode node; + struct udevice *dev; + int ret; + + node = ofnode_get_parent(chip); + if (!ofnode_valid(node)) + return -ENODEV; + + ret = uclass_get_device_by_ofnode(UCLASS_I2C, node, &dev); + if (ret) { + *devp = NULL; + return ret; + } + + *devp = dev; + return 0; +} + +int i2c_get_chip_by_phandle(const struct udevice *parent, const char *prop_name, + struct udevice **devp) +{ + ofnode node; + uint phandle; + struct udevice *bus, *chip; + char *dev_name; + int ret; + + debug("%s: Searching I2C chip for phandle \"%s\"\n", + __func__, prop_name); + + dev_name = strdup(prop_name); + if (!dev_name) { + ret = -ENOMEM; + goto err_exit; + } + + ret = dev_read_u32(parent, prop_name, &phandle); + if (ret) + goto err_exit; + + node = ofnode_get_by_phandle(phandle); + if (!ofnode_valid(node)) { + ret = -ENODEV; + goto err_exit; + } + + ret = i2c_get_parent_bus(node, &bus); + if (ret) + goto err_exit; + + ret = device_bind_driver_to_node(bus, "i2c_generic_chip_drv", + dev_name, node, &chip); + if (ret) + goto err_exit; + + ret = device_probe(chip); + if (ret) { + device_unbind(chip); + goto err_exit; + } + + debug("%s succeeded\n", __func__); + *devp = chip; + return 0; + +err_exit: + free(dev_name); + debug("%s failed, ret = %d\n", __func__, ret); + *devp = NULL; + return ret; +} + int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, struct udevice **devp) { diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c index 14cdb0f6635..c38330f758a 100644 --- a/drivers/i2c/mvtwsi.c +++ b/drivers/i2c/mvtwsi.c @@ -124,7 +124,8 @@ enum mvtwsi_ctrl_register_fields { * on other platforms, it is a normal r/w bit, which is cleared by writing 0. */ -#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) +#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || \ + defined(CONFIG_SUNXI_GEN_NCAT2) #define MVTWSI_CONTROL_CLEAR_IFLG 0x00000008 #else #define MVTWSI_CONTROL_CLEAR_IFLG 0x00000000 diff --git a/drivers/i2c/npcm_i2c.c b/drivers/i2c/npcm_i2c.c index ea4ef532565..b867b6c8e91 100644 --- a/drivers/i2c/npcm_i2c.c +++ b/drivers/i2c/npcm_i2c.c @@ -517,11 +517,6 @@ static int npcm_i2c_init_clk(struct npcm_i2c_bus *bus, u32 bus_freq) u32 sclfrq; u8 hldt, val; - if (bus_freq > I2C_FREQ_100K) { - printf("Support standard mode only\n"); - return -EINVAL; - } - /* SCLFRQ = T(SCL)/4/T(CLK) = FREQ(CLK)/4/FREQ(SCL) */ sclfrq = freq / (bus_freq * 4); if (sclfrq < SCLFRQ_MIN || sclfrq > SCLFRQ_MAX) diff --git a/drivers/i2c/stm32f7_i2c.c b/drivers/i2c/stm32f7_i2c.c index b6c71789eec..eaa1d692898 100644 --- a/drivers/i2c/stm32f7_i2c.c +++ b/drivers/i2c/stm32f7_i2c.c @@ -20,6 +20,7 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/printk.h> +#include <linux/time.h> /* STM32 I2C registers */ struct stm32_i2c_regs { @@ -121,8 +122,6 @@ struct stm32_i2c_regs { #define STM32_SCLH_MAX BIT(8) #define STM32_SCLL_MAX BIT(8) -#define STM32_NSEC_PER_SEC 1000000000L - /** * struct stm32_i2c_spec - private i2c specification timing * @rate: I2C bus speed (Hz) @@ -591,7 +590,7 @@ static int stm32_i2c_choose_solution(u32 i2cclk, struct stm32_i2c_timings *s) { struct stm32_i2c_timings *v; - u32 i2cbus = DIV_ROUND_CLOSEST(STM32_NSEC_PER_SEC, + u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->speed_freq); u32 clk_error_prev = i2cbus; u32 clk_min, clk_max; @@ -607,8 +606,8 @@ static int stm32_i2c_choose_solution(u32 i2cclk, dnf_delay = setup->dnf * i2cclk; tsync = af_delay_min + dnf_delay + (2 * i2cclk); - clk_max = STM32_NSEC_PER_SEC / specs->rate_min; - clk_min = STM32_NSEC_PER_SEC / specs->rate_max; + clk_max = NSEC_PER_SEC / specs->rate_min; + clk_min = NSEC_PER_SEC / specs->rate_max; /* * Among Prescaler possibilities discovered above figures out SCL Low @@ -686,7 +685,7 @@ static int stm32_i2c_compute_timing(struct stm32_i2c_priv *i2c_priv, const struct stm32_i2c_spec *specs; struct stm32_i2c_timings *v, *_v; struct list_head solutions; - u32 i2cclk = DIV_ROUND_CLOSEST(STM32_NSEC_PER_SEC, setup->clock_src); + u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->clock_src); int ret; specs = get_specs(setup->speed_freq); diff --git a/drivers/i2c/sun6i_p2wi.c b/drivers/i2c/sun6i_p2wi.c index d221323295d..b8e07a533ca 100644 --- a/drivers/i2c/sun6i_p2wi.c +++ b/drivers/i2c/sun6i_p2wi.c @@ -20,10 +20,10 @@ #include <errno.h> #include <i2c.h> #include <reset.h> +#include <sunxi_gpio.h> #include <time.h> #include <asm/io.h> #include <asm/arch/cpu.h> -#include <asm/arch/gpio.h> #include <asm/arch/p2wi.h> #include <asm/arch/prcm.h> #include <asm/arch/sys_proto.h> diff --git a/drivers/i2c/sun8i_rsb.c b/drivers/i2c/sun8i_rsb.c index 47fa05b6d1c..f36f2c7afac 100644 --- a/drivers/i2c/sun8i_rsb.c +++ b/drivers/i2c/sun8i_rsb.c @@ -14,10 +14,10 @@ #include <dm.h> #include <errno.h> #include <i2c.h> +#include <sunxi_gpio.h> #include <reset.h> #include <time.h> #include <asm/arch/cpu.h> -#include <asm/arch/gpio.h> #include <asm/arch/prcm.h> #include <asm/arch/rsb.h> diff --git a/drivers/input/input.c b/drivers/input/input.c index a4341e8c7ce..8a6506e7c6f 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -669,17 +669,22 @@ int input_stdio_register(struct stdio_dev *dev) int error; error = stdio_register(dev); -#if !defined(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(ENV_SUPPORT) - /* check if this is the standard input device */ - if (!error && strcmp(env_get("stdin"), dev->name) == 0) { - /* reassign the console */ - if (OVERWRITE_CONSOLE || - console_assign(stdin, dev->name)) - return -1; + + if (!CONFIG_IS_ENABLED(ENV_SUPPORT)) + return 0; + + if (!error) { + const char *cstdin; + + /* check if this is the standard input device */ + cstdin = env_get("stdin"); + if (cstdin && !strcmp(cstdin, dev->name)) { + /* reassign the console */ + if (OVERWRITE_CONSOLE || + console_assign(stdin, dev->name)) + return -1; + } } -#else - error = error; -#endif return 0; } diff --git a/drivers/mailbox/k3-sec-proxy.c b/drivers/mailbox/k3-sec-proxy.c index 815808498f2..05f6b1795d6 100644 --- a/drivers/mailbox/k3-sec-proxy.c +++ b/drivers/mailbox/k3-sec-proxy.c @@ -2,7 +2,7 @@ /* * Texas Instruments' K3 Secure proxy Driver * - * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2017-2018 Texas Instruments Incorporated - https://www.ti.com/ * Lokesh Vutla <lokeshvutla@ti.com> */ @@ -84,9 +84,9 @@ struct k3_sec_proxy_mbox { struct mbox_chan chan; struct k3_sec_proxy_desc *desc; struct k3_sec_proxy_thread *chans; - phys_addr_t target_data; - phys_addr_t scfg; - phys_addr_t rt; + void *target_data; + void *scfg; + void *rt; }; static inline u32 sp_readl(void __iomem *addr, unsigned int offset) @@ -319,20 +319,20 @@ static int k3_sec_proxy_of_to_priv(struct udevice *dev, return -ENODEV; } - spm->target_data = devfdt_get_addr_name(dev, "target_data"); - if (spm->target_data == FDT_ADDR_T_NONE) { + spm->target_data = dev_read_addr_name_ptr(dev, "target_data"); + if (!spm->target_data) { dev_err(dev, "No reg property for target data base\n"); return -EINVAL; } - spm->scfg = devfdt_get_addr_name(dev, "scfg"); - if (spm->rt == FDT_ADDR_T_NONE) { + spm->scfg = dev_read_addr_name_ptr(dev, "scfg"); + if (!spm->scfg) { dev_err(dev, "No reg property for Secure Cfg base\n"); return -EINVAL; } - spm->rt = devfdt_get_addr_name(dev, "rt"); - if (spm->rt == FDT_ADDR_T_NONE) { + spm->rt = dev_read_addr_name_ptr(dev, "rt"); + if (!spm->rt) { dev_err(dev, "No reg property for Real Time Cfg base\n"); return -EINVAL; } diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 22cb9d637c5..d10edd27746 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -61,7 +61,6 @@ config TI_GPMC if TI_GPMC config TI_GPMC_DEBUG bool "Debug Texas Instruments GPMC timings" - default n help Enable this to print GPMC timings before and after the GPMC registers are programmed. This should not be left enabled on production systems. diff --git a/drivers/memory/stm32-fmc2-ebi.c b/drivers/memory/stm32-fmc2-ebi.c index 212bb4f5dc0..a722a3836f7 100644 --- a/drivers/memory/stm32-fmc2-ebi.c +++ b/drivers/memory/stm32-fmc2-ebi.c @@ -14,6 +14,7 @@ #include <linux/err.h> #include <linux/iopoll.h> #include <linux/ioport.h> +#include <linux/time.h> /* FMC2 Controller Registers */ #define FMC2_BCR1 0x0 @@ -90,8 +91,6 @@ #define FMC2_BTR_DATLAT_MAX 0xf #define FMC2_PCSCNTR_CSCOUNT_MAX 0xff -#define FMC2_NSEC_PER_SEC 1000000000L - enum stm32_fmc2_ebi_bank { FMC2_EBI1 = 0, FMC2_EBI2, @@ -279,7 +278,7 @@ static u32 stm32_fmc2_ebi_ns_to_clock_cycles(struct stm32_fmc2_ebi *ebi, int cs, u32 setup) { unsigned long hclk = clk_get_rate(&ebi->clk); - unsigned long hclkp = FMC2_NSEC_PER_SEC / (hclk / 1000); + unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000); return DIV_ROUND_UP(setup * 1000, hclkp); } diff --git a/drivers/memory/ti-aemif.c b/drivers/memory/ti-aemif.c index c4bc88c1510..41325eb0f94 100644 --- a/drivers/memory/ti-aemif.c +++ b/drivers/memory/ti-aemif.c @@ -7,6 +7,7 @@ */ #include <common.h> +#include <asm/arch/hardware.h> #include <asm/ti-common/ti-aemif.h> #define AEMIF_WAITCYCLE_CONFIG (KS2_AEMIF_CNTRL_BASE + 0x4) diff --git a/drivers/memory/ti-gpmc.c b/drivers/memory/ti-gpmc.c index f511a529b1e..775e78c9a5b 100644 --- a/drivers/memory/ti-gpmc.c +++ b/drivers/memory/ti-gpmc.c @@ -2,7 +2,7 @@ /* * Texas Instruments GPMC Driver * - * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ */ #include <asm/io.h> diff --git a/drivers/memory/ti-gpmc.h b/drivers/memory/ti-gpmc.h index 90f8e656c8f..6fe098af0ec 100644 --- a/drivers/memory/ti-gpmc.h +++ b/drivers/memory/ti-gpmc.h @@ -2,7 +2,7 @@ /* * Texas Instruments GPMC Driver * - * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ */ /* GPMC register offsets */ diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index fccd9b89b81..97057de8bf9 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -375,7 +375,6 @@ config SPL_MXC_OCOTP config NPCM_OTP bool "Nnvoton NPCM BMC On-Chip OTP Memory Support" depends on (ARM && ARCH_NPCM) - default n help Support NPCM BMC OTP memory (fuse). To compile this driver as a module, choose M here: the module diff --git a/drivers/misc/cros_ec_sandbox.c b/drivers/misc/cros_ec_sandbox.c index 8dbe0b188a4..1201535f4af 100644 --- a/drivers/misc/cros_ec_sandbox.c +++ b/drivers/misc/cros_ec_sandbox.c @@ -81,6 +81,7 @@ struct ec_pwm_channel { /** * struct ec_state - Information about the EC state * + * @valid: true if this struct contains valid state data * @vbnv_context: Vboot context data stored by EC * @ec_config: FDT config information about the EC (e.g. flashmap) * @flash_data: Contents of flash memory @@ -95,6 +96,7 @@ struct ec_pwm_channel { * @pwm: Information per PWM channel */ struct ec_state { + bool valid; u8 vbnv_context[EC_VBNV_BLOCK_SIZE_V2]; struct fdt_cros_ec ec_config; uint8_t *flash_data; @@ -145,6 +147,7 @@ static int cros_ec_read_state(const void *blob, int node) memcpy(ec->flash_data, prop, len); debug("%s: Loaded EC flash data size %#x\n", __func__, len); } + ec->valid = true; return 0; } @@ -589,6 +592,7 @@ static int process_cmd(struct ec_state *ec, printf(" ** Unknown EC command %#02x\n", req_hdr->command); return -1; } + debug(" - EC command %#0x, result %d\n", req_hdr->command, len); return len; } @@ -675,7 +679,10 @@ int cros_ec_probe(struct udevice *dev) ofnode node; int err; - memcpy(ec, &s_state, sizeof(*ec)); + if (s_state.valid) + memcpy(ec, &s_state, sizeof(*ec)); + else + ec->current_image = EC_IMAGE_RO; err = cros_ec_decode_ec_flash(dev, &ec->ec_config); if (err) { debug("%s: Cannot device EC flash\n", __func__); diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c index 001f0a85ca9..591ff30df89 100644 --- a/drivers/misc/cros_ec_spi.c +++ b/drivers/misc/cros_ec_spi.c @@ -151,7 +151,7 @@ int cros_ec_spi_command(struct udevice *udev, uint8_t cmd, int cmd_version, /* Response code is first byte of message */ if (p[0] != EC_RES_SUCCESS) { - printf("%s: Returned status %d\n", __func__, p[0]); + log_debug("Returned status %d\n", p[0]); return -(int)(p[0]); } diff --git a/drivers/misc/esm_pmic.c b/drivers/misc/esm_pmic.c index b971f32f6a1..a518f750611 100644 --- a/drivers/misc/esm_pmic.c +++ b/drivers/misc/esm_pmic.c @@ -2,7 +2,7 @@ /* * PMIC Error Signal Monitor driver * - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ * Tero Kristo <t-kristo@ti.com> * */ diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c index ccf5c7a8037..1ffc199ba1e 100644 --- a/drivers/misc/fs_loader.c +++ b/drivers/misc/fs_loader.c @@ -316,7 +316,7 @@ int get_fs_loader(struct udevice **dev) return ret; /* Just create a new device */ - ret = device_bind(dm_root(), DM_DRIVER_GET(fs_loader), "default-loader", + ret = device_bind(dm_root(), DM_DRIVER_REF(fs_loader), "default-loader", &default_plat, ofnode_null(), dev); if (ret) return ret; diff --git a/drivers/misc/i2c_eeprom.c b/drivers/misc/i2c_eeprom.c index bdd7e018cc6..9111bd724cb 100644 --- a/drivers/misc/i2c_eeprom.c +++ b/drivers/misc/i2c_eeprom.c @@ -60,6 +60,17 @@ static int i2c_eeprom_std_read(struct udevice *dev, int offset, uint8_t *buf, return dm_i2c_read(dev, offset, buf, size); } +static int i2c_eeprom_len(int offset, int len, int pagesize) +{ + int page_offset = offset & (pagesize - 1); + int maxlen = pagesize - page_offset; + + if (len > maxlen) + len = maxlen; + + return len; +} + static int i2c_eeprom_std_write(struct udevice *dev, int offset, const uint8_t *buf, int size) { @@ -67,7 +78,7 @@ static int i2c_eeprom_std_write(struct udevice *dev, int offset, int ret; while (size > 0) { - int write_size = min_t(int, size, priv->pagesize); + int write_size = i2c_eeprom_len(offset, size, priv->pagesize); ret = dm_i2c_write(dev, offset, buf, write_size); if (ret) @@ -227,6 +238,13 @@ static const struct i2c_eeprom_drv_data atmel24c32_data = { .offset_len = 2, }; +static const struct i2c_eeprom_drv_data atmel24c32d_wlp_data = { + .size = 32, + .pagesize = 32, + .addr_offset_mask = 0, + .offset_len = 2, +}; + static const struct i2c_eeprom_drv_data atmel24c64_data = { .size = 8192, .pagesize = 32, @@ -266,6 +284,7 @@ static const struct udevice_id i2c_eeprom_std_ids[] = { { .compatible = "atmel,24c16a", (ulong)&atmel24c16a_data }, { .compatible = "atmel,24mac402", (ulong)&atmel24mac402_data }, { .compatible = "atmel,24c32", (ulong)&atmel24c32_data }, + { .compatible = "atmel,24c32d-wl", (ulong)&atmel24c32d_wlp_data }, { .compatible = "atmel,24c64", (ulong)&atmel24c64_data }, { .compatible = "atmel,24c128", (ulong)&atmel24c128_data }, { .compatible = "atmel,24c256", (ulong)&atmel24c256_data }, diff --git a/drivers/misc/k3_avs.c b/drivers/misc/k3_avs.c index acfc7318452..0d29eff1ac0 100644 --- a/drivers/misc/k3_avs.c +++ b/drivers/misc/k3_avs.c @@ -2,7 +2,7 @@ /* * Texas Instruments' K3 Clas 0 Adaptive Voltage Scaling driver * - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ * Tero Kristo <t-kristo@ti.com> * */ @@ -15,6 +15,7 @@ #include <k3-avs.h> #include <dm/device_compat.h> #include <linux/bitops.h> +#include <linux/delay.h> #include <power/regulator.h> #define AM6_VTM_DEVINFO(i) (priv->base + 0x100 + 0x20 * (i)) @@ -25,11 +26,28 @@ #define AM6_VTM_OPP_SHIFT(opp) (8 * (opp)) #define AM6_VTM_OPP_MASK 0xff +#define K3_VTM_DEVINFO_PWR0_OFFSET 0x4 +#define K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK 0xf0 +#define K3_VTM_TMPSENS0_CTRL_OFFSET 0x300 +#define K3_VTM_TMPSENS_STAT_OFFSET 0x8 +#define K3_VTM_ANYMAXT_OUTRG_ALERT_EN 0x1 +#define K3_VTM_LOW_TEMP_OFFSET 0x10 +#define K3_VTM_MISC_CTRL2_OFFSET 0x10 +#define K3_VTM_MISC_CTRL1_OFFSET 0xc +#define K3_VTM_TMPSENS_CTRL1_SOC BIT(5) +#define K3_VTM_TMPSENS_CTRL_CLRZ BIT(6) +#define K3_VTM_TMPSENS_CTRL_MAXT_OUTRG_EN BIT(11) +#define K3_VTM_ADC_COUNT_FOR_123C 0x2f8 +#define K3_VTM_ADC_COUNT_FOR_105C 0x288 +#define K3_VTM_ADC_WA_VALUE 0x2c +#define K3_VTM_FUSE_MASK 0xc0000000 + #define VD_FLAG_INIT_DONE BIT(0) struct k3_avs_privdata { void *base; struct vd_config *vd_config; + struct udevice *dev; }; struct opp { @@ -237,6 +255,88 @@ static int k3_avs_configure(struct udevice *dev, struct k3_avs_privdata *priv) return 0; } +/* k3_avs_program_tshut : Program thermal shutdown value for SOC + * set the values corresponding to thresholds to ~123C and 105C + * This is optional feature, Few times OS driver takes care of + * tshut programing. + */ + +static void k3_avs_program_tshut(struct k3_avs_privdata *priv) +{ + int cnt, id, val; + int workaround_needed = 0; + u32 ctrl_offset; + void __iomem *cfg2_base; + void __iomem *fuse_base; + + cfg2_base = (void __iomem *)devfdt_get_addr_index(priv->dev, 1); + if (IS_ERR(cfg2_base)) { + dev_err(priv->dev, "cfg base is not defined\n"); + return; + } + + /* + * Some of TI's J721E SoCs require a software trimming procedure + * for the temperature monitors to function properly. To determine + * if this particular SoC is NOT affected, both bits in the + * WKUP_SPARE_FUSE0[31:30] will be set (0xC0000000) indicating + * when software trimming should NOT be applied. + * + * https://www.ti.com/lit/er/sprz455c/sprz455c.pdf + * This routine checks if workaround_needed to be applied or not + * based upon workaround_needed, adjust fixed value of tshut high and low + */ + + if (device_is_compatible(priv->dev, "ti,j721e-vtm")) { + fuse_base = (void __iomem *)devfdt_get_addr_index(priv->dev, 2); + if (IS_ERR(fuse_base)) { + dev_err(priv->dev, "fuse-base is not defined for J721E Soc\n"); + return; + } + + if (!((readl(fuse_base) & K3_VTM_FUSE_MASK) == K3_VTM_FUSE_MASK)) + workaround_needed = 1; + } + + dev_dbg(priv->dev, "Work around %sneeded\n", workaround_needed ? "" : "not "); + + /* Get the sensor count in the VTM */ + val = readl(priv->base + K3_VTM_DEVINFO_PWR0_OFFSET); + cnt = val & K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK; + cnt >>= __ffs(K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK); + + /* Program the thermal sensors */ + for (id = 0; id < cnt; id++) { + ctrl_offset = K3_VTM_TMPSENS0_CTRL_OFFSET + id * 0x20; + + val = readl(cfg2_base + ctrl_offset); + val |= (K3_VTM_TMPSENS_CTRL_MAXT_OUTRG_EN | + K3_VTM_TMPSENS_CTRL1_SOC | + K3_VTM_TMPSENS_CTRL_CLRZ | BIT(4)); + writel(val, cfg2_base + ctrl_offset); + } + + /* + * Program TSHUT thresholds + * Step 1: set the thresholds to ~123C and 105C WKUP_VTM_MISC_CTRL2 + * Step 2: WKUP_VTM_TMPSENS_CTRL_j set the MAXT_OUTRG_EN bit + * This is already taken care as per of init + * Step 3: WKUP_VTM_MISC_CTRL set the ANYMAXT_OUTRG_ALERT_EN bit + */ + + /* Low thresholds for tshut*/ + val = (K3_VTM_ADC_COUNT_FOR_105C - workaround_needed * K3_VTM_ADC_WA_VALUE) + << K3_VTM_LOW_TEMP_OFFSET; + /* high thresholds */ + val |= K3_VTM_ADC_COUNT_FOR_123C - workaround_needed * K3_VTM_ADC_WA_VALUE; + + writel(val, cfg2_base + K3_VTM_MISC_CTRL2_OFFSET); + /* ramp-up delay from Linux code */ + mdelay(100); + val = readl(cfg2_base + K3_VTM_MISC_CTRL1_OFFSET) | K3_VTM_ANYMAXT_OUTRG_ALERT_EN; + writel(val, cfg2_base + K3_VTM_MISC_CTRL1_OFFSET); +} + /** * k3_avs_probe: parses VD info from VTM, and re-configures the OPP data * @@ -255,6 +355,7 @@ static int k3_avs_probe(struct udevice *dev) int ret; priv = dev_get_priv(dev); + priv->dev = dev; k3_avs_priv = priv; @@ -294,6 +395,9 @@ static int k3_avs_probe(struct udevice *dev) k3_avs_program_voltage(priv, vd, vd->opp); } + if (!device_is_compatible(priv->dev, "ti,am654-avs")) + k3_avs_program_tshut(priv); + return 0; } diff --git a/drivers/misc/k3_esm.c b/drivers/misc/k3_esm.c index 41faeb3d858..f6ac18bdc75 100644 --- a/drivers/misc/k3_esm.c +++ b/drivers/misc/k3_esm.c @@ -2,7 +2,7 @@ /* * Texas Instruments' K3 Error Signalling Module driver * - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ * Tero Kristo <t-kristo@ti.com> * */ diff --git a/drivers/misc/rockchip-otp.c b/drivers/misc/rockchip-otp.c index 4814e0e501c..4f757083a1b 100644 --- a/drivers/misc/rockchip-otp.c +++ b/drivers/misc/rockchip-otp.c @@ -61,11 +61,20 @@ #define RK3588_OTPC_INT_ST 0x0084 #define RK3588_RD_DONE BIT(1) +#define RV1126_OTP_NVM_CEB 0x00 +#define RV1126_OTP_NVM_RSTB 0x04 +#define RV1126_OTP_NVM_ST 0x18 +#define RV1126_OTP_NVM_RADDR 0x1C +#define RV1126_OTP_NVM_RSTART 0x20 +#define RV1126_OTP_NVM_RDATA 0x24 +#define RV1126_OTP_READ_ST 0x30 + struct rockchip_otp_plat { void __iomem *base; }; struct rockchip_otp_data { + int (*init)(struct udevice *dev); int (*read)(struct udevice *dev, int offset, void *buf, int size); int offset; int size; @@ -232,6 +241,48 @@ static int rockchip_rk3588_otp_read(struct udevice *dev, int offset, return 0; } +static int rockchip_rv1126_otp_init(struct udevice *dev) +{ + struct rockchip_otp_plat *otp = dev_get_plat(dev); + int ret; + + writel(0x0, otp->base + RV1126_OTP_NVM_CEB); + ret = rockchip_otp_poll_timeout(otp, 0x1, RV1126_OTP_NVM_ST); + + if (ret) + return ret; + + writel(0x1, otp->base + RV1126_OTP_NVM_RSTB); + ret = rockchip_otp_poll_timeout(otp, 0x4, RV1126_OTP_NVM_ST); + + if (ret) + return ret; + + return 0; +} + +static int rockchip_rv1126_otp_read(struct udevice *dev, int offset, void *buf, + int size) +{ + struct rockchip_otp_plat *otp = dev_get_plat(dev); + u32 status = 0; + u8 *buffer = buf; + int ret = 0; + + while (size--) { + writel(offset++, otp->base + RV1126_OTP_NVM_RADDR); + writel(0x1, otp->base + RV1126_OTP_NVM_RSTART); + ret = readl_poll_timeout(otp->base + RV1126_OTP_READ_ST, + status, !status, OTPC_TIMEOUT); + if (ret) + return ret; + + *buffer++ = (u8)(readl(otp->base + RV1126_OTP_NVM_RDATA) & 0xFF); + } + + return 0; +} + static int rockchip_otp_read(struct udevice *dev, int offset, void *buf, int size) { @@ -286,6 +337,20 @@ static int rockchip_otp_of_to_plat(struct udevice *dev) return 0; } +static int rockchip_otp_probe(struct udevice *dev) +{ + struct rockchip_otp_data *data; + + data = (struct rockchip_otp_data *)dev_get_driver_data(dev); + if (!data) + return -EINVAL; + + if (data->init) + return data->init(dev); + + return 0; +} + static const struct rockchip_otp_data px30_data = { .read = rockchip_px30_otp_read, .size = 0x40, @@ -304,6 +369,12 @@ static const struct rockchip_otp_data rk3588_data = { .block_size = 4, }; +static const struct rockchip_otp_data rv1126_data = { + .init = rockchip_rv1126_otp_init, + .read = rockchip_rv1126_otp_read, + .size = 0x40, +}; + static const struct udevice_id rockchip_otp_ids[] = { { .compatible = "rockchip,px30-otp", @@ -321,6 +392,10 @@ static const struct udevice_id rockchip_otp_ids[] = { .compatible = "rockchip,rk3588-otp", .data = (ulong)&rk3588_data, }, + { + .compatible = "rockchip,rv1126-otp", + .data = (ulong)&rv1126_data, + }, {} }; @@ -331,4 +406,5 @@ U_BOOT_DRIVER(rockchip_otp) = { .of_to_plat = rockchip_otp_of_to_plat, .plat_auto = sizeof(struct rockchip_otp_plat), .ops = &rockchip_otp_ops, + .probe = rockchip_otp_probe, }; diff --git a/drivers/misc/vexpress_config.c b/drivers/misc/vexpress_config.c index 2baca48109f..99aad1412ae 100644 --- a/drivers/misc/vexpress_config.c +++ b/drivers/misc/vexpress_config.c @@ -92,7 +92,7 @@ static struct misc_ops vexpress_config_ops = { static int vexpress_config_probe(struct udevice *dev) { struct ofnode_phandle_args args; - struct vexpress_config_sysreg *priv; + struct vexpress_config_sysreg *priv = dev_get_priv(dev); const char *prop; int err, prop_size; @@ -105,11 +105,9 @@ static int vexpress_config_probe(struct udevice *dev) if (!prop || (strncmp(prop, "arm,vexpress-sysreg", 19) != 0)) return -ENOENT; - priv = calloc(1, sizeof(*priv)); if (!priv) return -ENOMEM; - dev_get_uclass_priv(dev) = priv; priv->addr = ofnode_get_addr(args.node); return dev_read_u32(dev, "arm,vexpress,site", &priv->site); @@ -127,4 +125,5 @@ U_BOOT_DRIVER(vexpress_config_drv) = { .bind = dm_scan_fdt_dev, .probe = vexpress_config_probe, .ops = &vexpress_config_ops, + .priv_auto = sizeof(struct vexpress_config_sysreg), }; diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index de01b9687ba..17618c3bdcc 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -46,6 +46,7 @@ config SPL_DM_MMC depends on SPL_DM && DM_MMC default n if ARCH_MVEBU && !MVEBU_SPL_BOOT_DEVICE_MMC default y + select SPL_BLK help This enables the MultiMediaCard (MMC) uclass which supports MMC and Secure Digital I/O (SDIO) cards. Both removable (SD, micro-SD, etc.) @@ -390,12 +391,6 @@ config HSMMC2_8BIT depends on MMC_OMAP_HS && (OMAP44XX || OMAP54XX || DRA7XX || AM33XX || \ AM43XX || ARCH_KEYSTONE) -config SH_SDHI - bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support" - depends on ARCH_RMOBILE - help - Support for the on-chip SDHI host controller on SuperH/Renesas ARM SoCs platform - config SH_MMCIF bool "SuperH/Renesas ARM SoCs on-chip MMCIF host controller support" depends on ARCH_RMOBILE || SH diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 2c65c4765ab..e9cf1fcc640 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -49,7 +49,6 @@ obj-$(CONFIG_MMC_PCI) += pci_mmc.o obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o obj-$(CONFIG_MMC_SANDBOX) += sandbox_mmc.o obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o -obj-$(CONFIG_SH_SDHI) += sh_sdhi.o obj-$(CONFIG_STM32_SDMMC2) += stm32_sdmmc2.o obj-$(CONFIG_JZ47XX_MMC) += jz_mmc.o obj-$(CONFIG_NEXELL_DWMMC) += nexell_dw_mmc.o diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c index fd667aeafda..05595bdac39 100644 --- a/drivers/mmc/am654_sdhci.c +++ b/drivers/mmc/am654_sdhci.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * * Texas Instruments' K3 SD Host Controller Interface */ diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 0e157672eae..328456831dd 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -412,7 +412,7 @@ int mmc_bind(struct udevice *dev, struct mmc *mmc, const struct mmc_config *cfg) debug("%s: alias devnum=%d\n", __func__, dev_seq(dev)); ret = blk_create_devicef(dev, "mmc_blk", "blk", UCLASS_MMC, - dev_seq(dev), 512, 0, &bdev); + dev_seq(dev), DEFAULT_BLKSZ, 0, &bdev); if (ret) { debug("Cannot create block device\n"); return ret; diff --git a/drivers/mmc/octeontx_hsmmc.h b/drivers/mmc/octeontx_hsmmc.h index 70844b1cbab..9849121f174 100644 --- a/drivers/mmc/octeontx_hsmmc.h +++ b/drivers/mmc/octeontx_hsmmc.h @@ -32,8 +32,6 @@ */ #define MMC_TIMEOUT_SHORT 20 -#define NSEC_PER_SEC 1000000000L - #define MAX_NO_OF_TAPS 64 #define EXT_CSD_POWER_CLASS 187 /* R/W */ diff --git a/drivers/mmc/pci_mmc.c b/drivers/mmc/pci_mmc.c index 9fb70440293..4d163ccba04 100644 --- a/drivers/mmc/pci_mmc.c +++ b/drivers/mmc/pci_mmc.c @@ -50,8 +50,8 @@ static int pci_mmc_probe(struct udevice *dev) desc = mmc_get_blk_desc(&plat->mmc); desc->removable = !(plat->cfg.host_caps & MMC_CAP_NONREMOVABLE); - host->ioaddr = (void *)dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0, 0, PCI_REGION_TYPE, - PCI_REGION_MEM); + host->ioaddr = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0, 0, + PCI_REGION_TYPE, PCI_REGION_MEM); host->name = dev->name; host->cd_gpio = priv->cd_gpio; host->mmc = &plat->mmc; diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index 8e716f74491..97aaf1e4ec3 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -3,7 +3,6 @@ * Copyright (C) 2018 Marek Vasut <marek.vasut@gmail.com> */ -#include <common.h> #include <bouncebuf.h> #include <clk.h> #include <fdtdec.h> @@ -20,6 +19,7 @@ #include <linux/io.h> #include <linux/sizes.h> #include <power/regulator.h> +#include <reset.h> #include <asm/unaligned.h> #include "tmio-common.h" @@ -318,7 +318,7 @@ static unsigned int renesas_sdhi_init_tuning(struct tmio_sd_priv *priv) RENESAS_SDHI_SCC_DTCNTL_TAPNUM_MASK; } -static void renesas_sdhi_reset_tuning(struct tmio_sd_priv *priv) +static void renesas_sdhi_reset_tuning(struct tmio_sd_priv *priv, bool clk_disable) { u32 reg; @@ -350,6 +350,12 @@ static void renesas_sdhi_reset_tuning(struct tmio_sd_priv *priv) reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); + + if (clk_disable) { + reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); + reg &= ~TMIO_SD_CLKCTL_SCLKEN; + tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); + } } static int renesas_sdhi_hs400(struct udevice *dev) @@ -629,7 +635,7 @@ int renesas_sdhi_execute_tuning(struct udevice *dev, uint opcode) out: if (ret < 0) { dev_warn(dev, "Tuning procedure failed\n"); - renesas_sdhi_reset_tuning(priv); + renesas_sdhi_reset_tuning(priv, true); } return ret; @@ -668,7 +674,7 @@ static int renesas_sdhi_set_ios(struct udevice *dev) (mmc->selected_mode != UHS_SDR104) && (mmc->selected_mode != MMC_HS_200) && (mmc->selected_mode != MMC_HS_400)) { - renesas_sdhi_reset_tuning(priv); + renesas_sdhi_reset_tuning(priv, mmc->clk_disable); } #endif @@ -958,17 +964,87 @@ static void renesas_sdhi_filter_caps(struct udevice *dev) priv->needs_clkh_fallback = false; } +static int rzg2l_sdhi_setup(struct udevice *dev) +{ + struct tmio_sd_priv *priv = dev_get_priv(dev); + struct clk imclk2, aclk; + struct reset_ctl rst; + int ret; + + /* + * On members of the RZ/G2L SoC family, we need to enable + * additional chip detect and bus clocks, then release the SDHI + * module from reset. + */ + ret = clk_get_by_name(dev, "cd", &imclk2); + if (ret < 0) { + dev_err(dev, "failed to get imclk2 (chip detect clk)\n"); + goto err_get_imclk2; + } + + ret = clk_get_by_name(dev, "aclk", &aclk); + if (ret < 0) { + dev_err(dev, "failed to get aclk\n"); + goto err_get_aclk; + } + + ret = clk_enable(&imclk2); + if (ret < 0) { + dev_err(dev, "failed to enable imclk2 (chip detect clk)\n"); + goto err_imclk2; + } + + ret = clk_enable(&aclk); + if (ret < 0) { + dev_err(dev, "failed to enable aclk\n"); + goto err_aclk; + } + + ret = reset_get_by_index(dev, 0, &rst); + if (ret < 0) { + dev_err(dev, "failed to get reset line\n"); + goto err_get_reset; + } + + ret = reset_deassert(&rst); + if (ret < 0) { + dev_err(dev, "failed to de-assert reset line\n"); + goto err_reset; + } + + ret = tmio_sd_probe(dev, priv->quirks); + if (ret) + goto err_tmio_probe; + + return 0; + +err_tmio_probe: + reset_assert(&rst); +err_reset: + reset_free(&rst); +err_get_reset: + clk_disable(&aclk); +err_aclk: + clk_disable(&imclk2); +err_imclk2: + clk_free(&aclk); +err_get_aclk: + clk_free(&imclk2); +err_get_imclk2: + return ret; +} + static int renesas_sdhi_probe(struct udevice *dev) { struct tmio_sd_priv *priv = dev_get_priv(dev); - u32 quirks = dev_get_driver_data(dev); struct fdt_resource reg_res; DECLARE_GLOBAL_DATA_PTR; int ret; priv->clk_get_rate = renesas_sdhi_clk_get_rate; - if (quirks == RENESAS_GEN2_QUIRKS) { + priv->quirks = dev_get_driver_data(dev); + if (priv->quirks == RENESAS_GEN2_QUIRKS) { ret = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg", 0, ®_res); if (ret < 0) { @@ -978,7 +1054,7 @@ static int renesas_sdhi_probe(struct udevice *dev) } if (fdt_resource_size(®_res) == 0x100) - quirks |= TMIO_SD_CAP_16BIT; + priv->quirks |= TMIO_SD_CAP_16BIT; } ret = clk_get_by_index(dev, 0, &priv->clk); @@ -1012,8 +1088,10 @@ static int renesas_sdhi_probe(struct udevice *dev) goto err_clkh; } - priv->quirks = quirks; - ret = tmio_sd_probe(dev, quirks); + if (device_is_compatible(dev, "renesas,sdhi-r9a07g044")) + ret = rzg2l_sdhi_setup(dev); + else + ret = tmio_sd_probe(dev, priv->quirks); if (ret) goto err_tmio_probe; @@ -1023,7 +1101,7 @@ static int renesas_sdhi_probe(struct udevice *dev) CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) if (priv->caps & TMIO_SD_CAP_RCAR_UHS) - renesas_sdhi_reset_tuning(priv); + renesas_sdhi_reset_tuning(priv, true); #endif return 0; diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index fc9c6c37996..0178ed8a11e 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -306,14 +306,19 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, if (stat & SDHCI_INT_ERROR) break; - if (get_timer(start) >= SDHCI_READ_STATUS_TIMEOUT) { - if (host->quirks & SDHCI_QUIRK_BROKEN_R1B) { + if (host->quirks & SDHCI_QUIRK_BROKEN_R1B && + cmd->resp_type & MMC_RSP_BUSY && !data) { + unsigned int state = + sdhci_readl(host, SDHCI_PRESENT_STATE); + + if (!(state & SDHCI_DAT_ACTIVE)) return 0; - } else { - printf("%s: Timeout for status update!\n", - __func__); - return -ETIMEDOUT; - } + } + + if (get_timer(start) >= SDHCI_READ_STATUS_TIMEOUT) { + printf("%s: Timeout for status update: %08x %08x\n", + __func__, stat, mask); + return -ETIMEDOUT; } } while ((stat & mask) != mask); diff --git a/drivers/mmc/sh_sdhi.c b/drivers/mmc/sh_sdhi.c deleted file mode 100644 index 3ce7cbf71f8..00000000000 --- a/drivers/mmc/sh_sdhi.c +++ /dev/null @@ -1,910 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * drivers/mmc/sh_sdhi.c - * - * SD/MMC driver for Renesas rmobile ARM SoCs. - * - * Copyright (C) 2011,2013-2017 Renesas Electronics Corporation - * Copyright (C) 2014 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> - * Copyright (C) 2008-2009 Renesas Solutions Corp. - */ - -#include <common.h> -#include <log.h> -#include <malloc.h> -#include <mmc.h> -#include <dm.h> -#include <part.h> -#include <dm/device_compat.h> -#include <linux/bitops.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/compat.h> -#include <linux/io.h> -#include <linux/sizes.h> -#include <asm/arch/rmobile.h> -#include <asm/arch/sh_sdhi.h> -#include <asm/global_data.h> -#include <clk.h> - -#define DRIVER_NAME "sh-sdhi" - -struct sh_sdhi_host { - void __iomem *addr; - int ch; - int bus_shift; - unsigned long quirks; - unsigned char wait_int; - unsigned char sd_error; - unsigned char detect_waiting; - unsigned char app_cmd; -}; - -static inline void sh_sdhi_writeq(struct sh_sdhi_host *host, int reg, u64 val) -{ - writeq(val, host->addr + (reg << host->bus_shift)); -} - -static inline u64 sh_sdhi_readq(struct sh_sdhi_host *host, int reg) -{ - return readq(host->addr + (reg << host->bus_shift)); -} - -static inline void sh_sdhi_writew(struct sh_sdhi_host *host, int reg, u16 val) -{ - writew(val, host->addr + (reg << host->bus_shift)); -} - -static inline u16 sh_sdhi_readw(struct sh_sdhi_host *host, int reg) -{ - return readw(host->addr + (reg << host->bus_shift)); -} - -static void sh_sdhi_detect(struct sh_sdhi_host *host) -{ - sh_sdhi_writew(host, SDHI_OPTION, - OPT_BUS_WIDTH_1 | sh_sdhi_readw(host, SDHI_OPTION)); - - host->detect_waiting = 0; -} - -static int sh_sdhi_intr(void *dev_id) -{ - struct sh_sdhi_host *host = dev_id; - int state1 = 0, state2 = 0; - - state1 = sh_sdhi_readw(host, SDHI_INFO1); - state2 = sh_sdhi_readw(host, SDHI_INFO2); - - debug("%s: state1 = %x, state2 = %x\n", __func__, state1, state2); - - /* CARD Insert */ - if (state1 & INFO1_CARD_IN) { - sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_IN); - if (!host->detect_waiting) { - host->detect_waiting = 1; - sh_sdhi_detect(host); - } - sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END | - INFO1M_ACCESS_END | INFO1M_CARD_IN | - INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); - return -EAGAIN; - } - /* CARD Removal */ - if (state1 & INFO1_CARD_RE) { - sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_RE); - if (!host->detect_waiting) { - host->detect_waiting = 1; - sh_sdhi_detect(host); - } - sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END | - INFO1M_ACCESS_END | INFO1M_CARD_RE | - INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); - sh_sdhi_writew(host, SDHI_SDIO_INFO1_MASK, SDIO_INFO1M_ON); - sh_sdhi_writew(host, SDHI_SDIO_MODE, SDIO_MODE_OFF); - return -EAGAIN; - } - - if (state2 & INFO2_ALL_ERR) { - sh_sdhi_writew(host, SDHI_INFO2, - (unsigned short)~(INFO2_ALL_ERR)); - sh_sdhi_writew(host, SDHI_INFO2_MASK, - INFO2M_ALL_ERR | - sh_sdhi_readw(host, SDHI_INFO2_MASK)); - host->sd_error = 1; - host->wait_int = 1; - return 0; - } - /* Respons End */ - if (state1 & INFO1_RESP_END) { - sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END); - sh_sdhi_writew(host, SDHI_INFO1_MASK, - INFO1M_RESP_END | - sh_sdhi_readw(host, SDHI_INFO1_MASK)); - host->wait_int = 1; - return 0; - } - /* SD_BUF Read Enable */ - if (state2 & INFO2_BRE_ENABLE) { - sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BRE_ENABLE); - sh_sdhi_writew(host, SDHI_INFO2_MASK, - INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ | - sh_sdhi_readw(host, SDHI_INFO2_MASK)); - host->wait_int = 1; - return 0; - } - /* SD_BUF Write Enable */ - if (state2 & INFO2_BWE_ENABLE) { - sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BWE_ENABLE); - sh_sdhi_writew(host, SDHI_INFO2_MASK, - INFO2_BWE_ENABLE | INFO2M_BUF_ILL_WRITE | - sh_sdhi_readw(host, SDHI_INFO2_MASK)); - host->wait_int = 1; - return 0; - } - /* Access End */ - if (state1 & INFO1_ACCESS_END) { - sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_ACCESS_END); - sh_sdhi_writew(host, SDHI_INFO1_MASK, - INFO1_ACCESS_END | - sh_sdhi_readw(host, SDHI_INFO1_MASK)); - host->wait_int = 1; - return 0; - } - return -EAGAIN; -} - -static int sh_sdhi_wait_interrupt_flag(struct sh_sdhi_host *host) -{ - int timeout = 10000000; - - while (1) { - timeout--; - if (timeout < 0) { - debug(DRIVER_NAME": %s timeout\n", __func__); - return 0; - } - - if (!sh_sdhi_intr(host)) - break; - - udelay(1); /* 1 usec */ - } - - return 1; /* Return value: NOT 0 = complete waiting */ -} - -static int sh_sdhi_clock_control(struct sh_sdhi_host *host, unsigned long clk) -{ - u32 clkdiv, i, timeout; - - if (sh_sdhi_readw(host, SDHI_INFO2) & (1 << 14)) { - printf(DRIVER_NAME": Busy state ! Cannot change the clock\n"); - return -EBUSY; - } - - sh_sdhi_writew(host, SDHI_CLK_CTRL, - ~CLK_ENABLE & sh_sdhi_readw(host, SDHI_CLK_CTRL)); - - if (clk == 0) - return -EIO; - - clkdiv = 0x80; - i = CONFIG_SH_SDHI_FREQ >> (0x8 + 1); - for (; clkdiv && clk >= (i << 1); (clkdiv >>= 1)) - i <<= 1; - - sh_sdhi_writew(host, SDHI_CLK_CTRL, clkdiv); - - timeout = 100000; - /* Waiting for SD Bus busy to be cleared */ - while (timeout--) { - if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000)) - break; - } - - if (timeout) - sh_sdhi_writew(host, SDHI_CLK_CTRL, - CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL)); - else - return -EBUSY; - - return 0; -} - -static int sh_sdhi_sync_reset(struct sh_sdhi_host *host) -{ - u32 timeout; - sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_ON); - sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_OFF); - sh_sdhi_writew(host, SDHI_CLK_CTRL, - CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL)); - - timeout = 100000; - while (timeout--) { - if (!(sh_sdhi_readw(host, SDHI_INFO2) & INFO2_CBUSY)) - break; - udelay(100); - } - - if (!timeout) - return -EBUSY; - - if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF) - sh_sdhi_writew(host, SDHI_HOST_MODE, 1); - - return 0; -} - -static int sh_sdhi_error_manage(struct sh_sdhi_host *host) -{ - unsigned short e_state1, e_state2; - int ret; - - host->sd_error = 0; - host->wait_int = 0; - - e_state1 = sh_sdhi_readw(host, SDHI_ERR_STS1); - e_state2 = sh_sdhi_readw(host, SDHI_ERR_STS2); - if (e_state2 & ERR_STS2_SYS_ERROR) { - if (e_state2 & ERR_STS2_RES_STOP_TIMEOUT) - ret = -ETIMEDOUT; - else - ret = -EILSEQ; - debug("%s: ERR_STS2 = %04x\n", - DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS2)); - sh_sdhi_sync_reset(host); - - sh_sdhi_writew(host, SDHI_INFO1_MASK, - INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); - return ret; - } - if (e_state1 & ERR_STS1_CRC_ERROR || e_state1 & ERR_STS1_CMD_ERROR) - ret = -EILSEQ; - else - ret = -ETIMEDOUT; - - debug("%s: ERR_STS1 = %04x\n", - DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS1)); - sh_sdhi_sync_reset(host); - sh_sdhi_writew(host, SDHI_INFO1_MASK, - INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); - return ret; -} - -static int sh_sdhi_single_read(struct sh_sdhi_host *host, struct mmc_data *data) -{ - long time; - unsigned short blocksize, i; - unsigned short *p = (unsigned short *)data->dest; - u64 *q = (u64 *)data->dest; - - if ((unsigned long)p & 0x00000001) { - debug(DRIVER_NAME": %s: The data pointer is unaligned.", - __func__); - return -EIO; - } - - host->wait_int = 0; - sh_sdhi_writew(host, SDHI_INFO2_MASK, - ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) & - sh_sdhi_readw(host, SDHI_INFO2_MASK)); - sh_sdhi_writew(host, SDHI_INFO1_MASK, - ~INFO1M_ACCESS_END & - sh_sdhi_readw(host, SDHI_INFO1_MASK)); - time = sh_sdhi_wait_interrupt_flag(host); - if (time == 0 || host->sd_error != 0) - return sh_sdhi_error_manage(host); - - host->wait_int = 0; - blocksize = sh_sdhi_readw(host, SDHI_SIZE); - if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF) - for (i = 0; i < blocksize / 8; i++) - *q++ = sh_sdhi_readq(host, SDHI_BUF0); - else - for (i = 0; i < blocksize / 2; i++) - *p++ = sh_sdhi_readw(host, SDHI_BUF0); - - time = sh_sdhi_wait_interrupt_flag(host); - if (time == 0 || host->sd_error != 0) - return sh_sdhi_error_manage(host); - - host->wait_int = 0; - return 0; -} - -static int sh_sdhi_multi_read(struct sh_sdhi_host *host, struct mmc_data *data) -{ - long time; - unsigned short blocksize, i, sec; - unsigned short *p = (unsigned short *)data->dest; - u64 *q = (u64 *)data->dest; - - if ((unsigned long)p & 0x00000001) { - debug(DRIVER_NAME": %s: The data pointer is unaligned.", - __func__); - return -EIO; - } - - debug("%s: blocks = %d, blocksize = %d\n", - __func__, data->blocks, data->blocksize); - - host->wait_int = 0; - for (sec = 0; sec < data->blocks; sec++) { - sh_sdhi_writew(host, SDHI_INFO2_MASK, - ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) & - sh_sdhi_readw(host, SDHI_INFO2_MASK)); - - time = sh_sdhi_wait_interrupt_flag(host); - if (time == 0 || host->sd_error != 0) - return sh_sdhi_error_manage(host); - - host->wait_int = 0; - blocksize = sh_sdhi_readw(host, SDHI_SIZE); - if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF) - for (i = 0; i < blocksize / 8; i++) - *q++ = sh_sdhi_readq(host, SDHI_BUF0); - else - for (i = 0; i < blocksize / 2; i++) - *p++ = sh_sdhi_readw(host, SDHI_BUF0); - } - - return 0; -} - -static int sh_sdhi_single_write(struct sh_sdhi_host *host, - struct mmc_data *data) -{ - long time; - unsigned short blocksize, i; - const unsigned short *p = (const unsigned short *)data->src; - const u64 *q = (const u64 *)data->src; - - if ((unsigned long)p & 0x00000001) { - debug(DRIVER_NAME": %s: The data pointer is unaligned.", - __func__); - return -EIO; - } - - debug("%s: blocks = %d, blocksize = %d\n", - __func__, data->blocks, data->blocksize); - - host->wait_int = 0; - sh_sdhi_writew(host, SDHI_INFO2_MASK, - ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) & - sh_sdhi_readw(host, SDHI_INFO2_MASK)); - sh_sdhi_writew(host, SDHI_INFO1_MASK, - ~INFO1M_ACCESS_END & - sh_sdhi_readw(host, SDHI_INFO1_MASK)); - - time = sh_sdhi_wait_interrupt_flag(host); - if (time == 0 || host->sd_error != 0) - return sh_sdhi_error_manage(host); - - host->wait_int = 0; - blocksize = sh_sdhi_readw(host, SDHI_SIZE); - if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF) - for (i = 0; i < blocksize / 8; i++) - sh_sdhi_writeq(host, SDHI_BUF0, *q++); - else - for (i = 0; i < blocksize / 2; i++) - sh_sdhi_writew(host, SDHI_BUF0, *p++); - - time = sh_sdhi_wait_interrupt_flag(host); - if (time == 0 || host->sd_error != 0) - return sh_sdhi_error_manage(host); - - host->wait_int = 0; - return 0; -} - -static int sh_sdhi_multi_write(struct sh_sdhi_host *host, struct mmc_data *data) -{ - long time; - unsigned short i, sec, blocksize; - const unsigned short *p = (const unsigned short *)data->src; - const u64 *q = (const u64 *)data->src; - - debug("%s: blocks = %d, blocksize = %d\n", - __func__, data->blocks, data->blocksize); - - host->wait_int = 0; - for (sec = 0; sec < data->blocks; sec++) { - sh_sdhi_writew(host, SDHI_INFO2_MASK, - ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) & - sh_sdhi_readw(host, SDHI_INFO2_MASK)); - - time = sh_sdhi_wait_interrupt_flag(host); - if (time == 0 || host->sd_error != 0) - return sh_sdhi_error_manage(host); - - host->wait_int = 0; - blocksize = sh_sdhi_readw(host, SDHI_SIZE); - if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF) - for (i = 0; i < blocksize / 8; i++) - sh_sdhi_writeq(host, SDHI_BUF0, *q++); - else - for (i = 0; i < blocksize / 2; i++) - sh_sdhi_writew(host, SDHI_BUF0, *p++); - } - - return 0; -} - -static void sh_sdhi_get_response(struct sh_sdhi_host *host, struct mmc_cmd *cmd) -{ - unsigned short i, j, cnt = 1; - unsigned short resp[8]; - - if (cmd->resp_type & MMC_RSP_136) { - cnt = 4; - resp[0] = sh_sdhi_readw(host, SDHI_RSP00); - resp[1] = sh_sdhi_readw(host, SDHI_RSP01); - resp[2] = sh_sdhi_readw(host, SDHI_RSP02); - resp[3] = sh_sdhi_readw(host, SDHI_RSP03); - resp[4] = sh_sdhi_readw(host, SDHI_RSP04); - resp[5] = sh_sdhi_readw(host, SDHI_RSP05); - resp[6] = sh_sdhi_readw(host, SDHI_RSP06); - resp[7] = sh_sdhi_readw(host, SDHI_RSP07); - - /* SDHI REGISTER SPECIFICATION */ - for (i = 7, j = 6; i > 0; i--) { - resp[i] = (resp[i] << 8) & 0xff00; - resp[i] |= (resp[j--] >> 8) & 0x00ff; - } - resp[0] = (resp[0] << 8) & 0xff00; - } else { - resp[0] = sh_sdhi_readw(host, SDHI_RSP00); - resp[1] = sh_sdhi_readw(host, SDHI_RSP01); - } - -#if defined(__BIG_ENDIAN_BITFIELD) - if (cnt == 4) { - cmd->response[0] = (resp[6] << 16) | resp[7]; - cmd->response[1] = (resp[4] << 16) | resp[5]; - cmd->response[2] = (resp[2] << 16) | resp[3]; - cmd->response[3] = (resp[0] << 16) | resp[1]; - } else { - cmd->response[0] = (resp[0] << 16) | resp[1]; - } -#else - if (cnt == 4) { - cmd->response[0] = (resp[7] << 16) | resp[6]; - cmd->response[1] = (resp[5] << 16) | resp[4]; - cmd->response[2] = (resp[3] << 16) | resp[2]; - cmd->response[3] = (resp[1] << 16) | resp[0]; - } else { - cmd->response[0] = (resp[1] << 16) | resp[0]; - } -#endif /* __BIG_ENDIAN_BITFIELD */ -} - -static unsigned short sh_sdhi_set_cmd(struct sh_sdhi_host *host, - struct mmc_data *data, unsigned short opc) -{ - if (host->app_cmd) { - if (!data) - host->app_cmd = 0; - return opc | BIT(6); - } - - switch (opc) { - case MMC_CMD_SWITCH: - return opc | (data ? 0x1c00 : 0x40); - case MMC_CMD_SEND_EXT_CSD: - return opc | (data ? 0x1c00 : 0); - case MMC_CMD_SEND_OP_COND: - return opc | 0x0700; - case MMC_CMD_APP_CMD: - host->app_cmd = 1; - default: - return opc; - } -} - -static unsigned short sh_sdhi_data_trans(struct sh_sdhi_host *host, - struct mmc_data *data, unsigned short opc) -{ - if (host->app_cmd) { - host->app_cmd = 0; - switch (opc) { - case SD_CMD_APP_SEND_SCR: - case SD_CMD_APP_SD_STATUS: - return sh_sdhi_single_read(host, data); - default: - printf(DRIVER_NAME": SD: NOT SUPPORT APP CMD = d'%04d\n", - opc); - return -EINVAL; - } - } else { - switch (opc) { - case MMC_CMD_WRITE_MULTIPLE_BLOCK: - return sh_sdhi_multi_write(host, data); - case MMC_CMD_READ_MULTIPLE_BLOCK: - return sh_sdhi_multi_read(host, data); - case MMC_CMD_WRITE_SINGLE_BLOCK: - return sh_sdhi_single_write(host, data); - case MMC_CMD_READ_SINGLE_BLOCK: - case MMC_CMD_SWITCH: - case MMC_CMD_SEND_EXT_CSD:; - return sh_sdhi_single_read(host, data); - default: - printf(DRIVER_NAME": SD: NOT SUPPORT CMD = d'%04d\n", opc); - return -EINVAL; - } - } -} - -static int sh_sdhi_start_cmd(struct sh_sdhi_host *host, - struct mmc_data *data, struct mmc_cmd *cmd) -{ - long time; - unsigned short shcmd, opc = cmd->cmdidx; - int ret = 0; - unsigned long timeout; - - debug("opc = %d, arg = %x, resp_type = %x\n", - opc, cmd->cmdarg, cmd->resp_type); - - if (opc == MMC_CMD_STOP_TRANSMISSION) { - /* SDHI sends the STOP command automatically by STOP reg */ - sh_sdhi_writew(host, SDHI_INFO1_MASK, ~INFO1M_ACCESS_END & - sh_sdhi_readw(host, SDHI_INFO1_MASK)); - - time = sh_sdhi_wait_interrupt_flag(host); - if (time == 0 || host->sd_error != 0) - return sh_sdhi_error_manage(host); - - sh_sdhi_get_response(host, cmd); - return 0; - } - - if (data) { - if ((opc == MMC_CMD_READ_MULTIPLE_BLOCK) || - opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) { - sh_sdhi_writew(host, SDHI_STOP, STOP_SEC_ENABLE); - sh_sdhi_writew(host, SDHI_SECCNT, data->blocks); - } - sh_sdhi_writew(host, SDHI_SIZE, data->blocksize); - } - - shcmd = sh_sdhi_set_cmd(host, data, opc); - - /* - * U-Boot cannot use interrupt. - * So this flag may not be clear by timing - */ - sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END); - - sh_sdhi_writew(host, SDHI_INFO1_MASK, - INFO1M_RESP_END | sh_sdhi_readw(host, SDHI_INFO1_MASK)); - sh_sdhi_writew(host, SDHI_ARG0, - (unsigned short)(cmd->cmdarg & ARG0_MASK)); - sh_sdhi_writew(host, SDHI_ARG1, - (unsigned short)((cmd->cmdarg >> 16) & ARG1_MASK)); - - timeout = 100000; - /* Waiting for SD Bus busy to be cleared */ - while (timeout--) { - if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000)) - break; - } - - host->wait_int = 0; - sh_sdhi_writew(host, SDHI_INFO1_MASK, - ~INFO1M_RESP_END & sh_sdhi_readw(host, SDHI_INFO1_MASK)); - sh_sdhi_writew(host, SDHI_INFO2_MASK, - ~(INFO2M_CMD_ERROR | INFO2M_CRC_ERROR | - INFO2M_END_ERROR | INFO2M_TIMEOUT | - INFO2M_RESP_TIMEOUT | INFO2M_ILA) & - sh_sdhi_readw(host, SDHI_INFO2_MASK)); - - sh_sdhi_writew(host, SDHI_CMD, (unsigned short)(shcmd & CMD_MASK)); - time = sh_sdhi_wait_interrupt_flag(host); - if (!time) { - host->app_cmd = 0; - return sh_sdhi_error_manage(host); - } - - if (host->sd_error) { - switch (cmd->cmdidx) { - case MMC_CMD_ALL_SEND_CID: - case MMC_CMD_SELECT_CARD: - case SD_CMD_SEND_IF_COND: - case MMC_CMD_APP_CMD: - ret = -ETIMEDOUT; - break; - default: - debug(DRIVER_NAME": Cmd(d'%d) err\n", opc); - debug(DRIVER_NAME": cmdidx = %d\n", cmd->cmdidx); - ret = sh_sdhi_error_manage(host); - break; - } - host->sd_error = 0; - host->wait_int = 0; - host->app_cmd = 0; - return ret; - } - - if (sh_sdhi_readw(host, SDHI_INFO1) & INFO1_RESP_END) { - host->app_cmd = 0; - return -EINVAL; - } - - if (host->wait_int) { - sh_sdhi_get_response(host, cmd); - host->wait_int = 0; - } - - if (data) - ret = sh_sdhi_data_trans(host, data, opc); - - debug("ret = %d, resp = %08x, %08x, %08x, %08x\n", - ret, cmd->response[0], cmd->response[1], - cmd->response[2], cmd->response[3]); - return ret; -} - -static int sh_sdhi_send_cmd_common(struct sh_sdhi_host *host, - struct mmc_cmd *cmd, struct mmc_data *data) -{ - host->sd_error = 0; - - return sh_sdhi_start_cmd(host, data, cmd); -} - -static int sh_sdhi_set_ios_common(struct sh_sdhi_host *host, struct mmc *mmc) -{ - int ret; - - ret = sh_sdhi_clock_control(host, mmc->clock); - if (ret) - return -EINVAL; - - if (mmc->bus_width == 8) - sh_sdhi_writew(host, SDHI_OPTION, - OPT_BUS_WIDTH_8 | (~OPT_BUS_WIDTH_M & - sh_sdhi_readw(host, SDHI_OPTION))); - else if (mmc->bus_width == 4) - sh_sdhi_writew(host, SDHI_OPTION, - OPT_BUS_WIDTH_4 | (~OPT_BUS_WIDTH_M & - sh_sdhi_readw(host, SDHI_OPTION))); - else - sh_sdhi_writew(host, SDHI_OPTION, - OPT_BUS_WIDTH_1 | (~OPT_BUS_WIDTH_M & - sh_sdhi_readw(host, SDHI_OPTION))); - - debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width); - - return 0; -} - -static int sh_sdhi_initialize_common(struct sh_sdhi_host *host) -{ - int ret = sh_sdhi_sync_reset(host); - - sh_sdhi_writew(host, SDHI_PORTSEL, USE_1PORT); - -#if defined(__BIG_ENDIAN_BITFIELD) - sh_sdhi_writew(host, SDHI_EXT_SWAP, SET_SWAP); -#endif - - sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END | - INFO1M_ACCESS_END | INFO1M_CARD_RE | - INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); - - return ret; -} - -#ifndef CONFIG_DM_MMC -static void *mmc_priv(struct mmc *mmc) -{ - return (void *)mmc->priv; -} - -static int sh_sdhi_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) -{ - struct sh_sdhi_host *host = mmc_priv(mmc); - - return sh_sdhi_send_cmd_common(host, cmd, data); -} - -static int sh_sdhi_set_ios(struct mmc *mmc) -{ - struct sh_sdhi_host *host = mmc_priv(mmc); - - return sh_sdhi_set_ios_common(host, mmc); -} - -static int sh_sdhi_initialize(struct mmc *mmc) -{ - struct sh_sdhi_host *host = mmc_priv(mmc); - - return sh_sdhi_initialize_common(host); -} - -static const struct mmc_ops sh_sdhi_ops = { - .send_cmd = sh_sdhi_send_cmd, - .set_ios = sh_sdhi_set_ios, - .init = sh_sdhi_initialize, -}; - -#ifdef CONFIG_RCAR_GEN3 -static struct mmc_config sh_sdhi_cfg = { - .name = DRIVER_NAME, - .ops = &sh_sdhi_ops, - .f_min = CLKDEV_INIT, - .f_max = CLKDEV_HS_DATA, - .voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34, - .host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | MMC_MODE_HS | - MMC_MODE_HS_52MHz, - .part_type = PART_TYPE_DOS, - .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT, -}; -#else -static struct mmc_config sh_sdhi_cfg = { - .name = DRIVER_NAME, - .ops = &sh_sdhi_ops, - .f_min = CLKDEV_INIT, - .f_max = CLKDEV_HS_DATA, - .voltages = MMC_VDD_32_33 | MMC_VDD_33_34, - .host_caps = MMC_MODE_4BIT | MMC_MODE_HS, - .part_type = PART_TYPE_DOS, - .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT, -}; -#endif - -int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks) -{ - int ret = 0; - struct mmc *mmc; - struct sh_sdhi_host *host = NULL; - - if (ch >= CFG_SYS_SH_SDHI_NR_CHANNEL) - return -ENODEV; - - host = malloc(sizeof(struct sh_sdhi_host)); - if (!host) - return -ENOMEM; - - mmc = mmc_create(&sh_sdhi_cfg, host); - if (!mmc) { - ret = -1; - goto error; - } - - host->ch = ch; - host->addr = (void __iomem *)addr; - host->quirks = quirks; - - if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF) - host->bus_shift = 2; - else if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF) - host->bus_shift = 1; - - return ret; -error: - free(host); - return ret; -} - -#else - -struct sh_sdhi_plat { - struct mmc_config cfg; - struct mmc mmc; -}; - -int sh_sdhi_dm_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, - struct mmc_data *data) -{ - struct sh_sdhi_host *host = dev_get_priv(dev); - - return sh_sdhi_send_cmd_common(host, cmd, data); -} - -int sh_sdhi_dm_set_ios(struct udevice *dev) -{ - struct sh_sdhi_host *host = dev_get_priv(dev); - struct mmc *mmc = mmc_get_mmc_dev(dev); - - return sh_sdhi_set_ios_common(host, mmc); -} - -static const struct dm_mmc_ops sh_sdhi_dm_ops = { - .send_cmd = sh_sdhi_dm_send_cmd, - .set_ios = sh_sdhi_dm_set_ios, -}; - -static int sh_sdhi_dm_bind(struct udevice *dev) -{ - struct sh_sdhi_plat *plat = dev_get_plat(dev); - - return mmc_bind(dev, &plat->mmc, &plat->cfg); -} - -static int sh_sdhi_dm_probe(struct udevice *dev) -{ - struct sh_sdhi_plat *plat = dev_get_plat(dev); - struct sh_sdhi_host *host = dev_get_priv(dev); - struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); - struct clk sh_sdhi_clk; - const u32 quirks = dev_get_driver_data(dev); - fdt_addr_t base; - int ret; - - base = dev_read_addr(dev); - if (base == FDT_ADDR_T_NONE) - return -EINVAL; - - host->addr = devm_ioremap(dev, base, SZ_2K); - if (!host->addr) - return -ENOMEM; - - ret = clk_get_by_index(dev, 0, &sh_sdhi_clk); - if (ret) { - debug("failed to get clock, ret=%d\n", ret); - return ret; - } - - ret = clk_enable(&sh_sdhi_clk); - if (ret) { - debug("failed to enable clock, ret=%d\n", ret); - return ret; - } - - host->quirks = quirks; - - if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF) - host->bus_shift = 2; - else if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF) - host->bus_shift = 1; - - plat->cfg.name = dev->name; - plat->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS; - - switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "bus-width", - 1)) { - case 8: - plat->cfg.host_caps |= MMC_MODE_8BIT; - break; - case 4: - plat->cfg.host_caps |= MMC_MODE_4BIT; - break; - case 1: - break; - default: - dev_err(dev, "Invalid \"bus-width\" value\n"); - return -EINVAL; - } - - sh_sdhi_initialize_common(host); - - plat->cfg.voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34; - plat->cfg.f_min = CLKDEV_INIT; - plat->cfg.f_max = CLKDEV_HS_DATA; - plat->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; - - upriv->mmc = &plat->mmc; - - return 0; -} - -static const struct udevice_id sh_sdhi_sd_match[] = { - { .compatible = "renesas,sdhi-r8a7795", .data = SH_SDHI_QUIRK_64BIT_BUF }, - { .compatible = "renesas,sdhi-r8a7796", .data = SH_SDHI_QUIRK_64BIT_BUF }, - { /* sentinel */ } -}; - -U_BOOT_DRIVER(sh_sdhi_mmc) = { - .name = "sh-sdhi-mmc", - .id = UCLASS_MMC, - .of_match = sh_sdhi_sd_match, - .bind = sh_sdhi_dm_bind, - .probe = sh_sdhi_dm_probe, - .priv_auto = sizeof(struct sh_sdhi_host), - .plat_auto = sizeof(struct sh_sdhi_plat), - .ops = &sh_sdhi_dm_ops, -}; -#endif diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index 23bc7da917a..714706d2411 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -25,8 +25,13 @@ #include <asm/io.h> #include <asm/arch/clock.h> #include <asm/arch/cpu.h> +#if !CONFIG_IS_ENABLED(DM_MMC) #include <asm/arch/mmc.h> +#endif #include <linux/delay.h> +#include <sunxi_gpio.h> + +#include "sunxi_mmc.h" #ifndef CCM_MMC_CTRL_MODE_SEL_NEW #define CCM_MMC_CTRL_MODE_SEL_NEW 0 @@ -56,6 +61,7 @@ static bool sunxi_mmc_can_calibrate(void) return IS_ENABLED(CONFIG_MACH_SUN50I) || IS_ENABLED(CONFIG_MACH_SUN50I_H5) || IS_ENABLED(CONFIG_SUN50I_GEN_H6) || + IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2) || IS_ENABLED(CONFIG_MACH_SUN8I_R40); } @@ -190,7 +196,7 @@ static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc) rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK; writel(rval, &priv->reg->clkcr); -#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) +#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2) /* A64 supports calibration of delays on MMC controller and we * have to set delay of zero before starting calibration. * Allwinner BSP driver sets a delay only in the case of @@ -543,7 +549,7 @@ struct mmc *sunxi_mmc_init(int sdc_no) /* config ahb clock */ debug("init mmc %d clock and io\n", sdc_no); -#if !defined(CONFIG_SUN50I_GEN_H6) +#if !defined(CONFIG_SUN50I_GEN_H6) && !defined(CONFIG_SUNXI_GEN_NCAT2) setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no)); #ifdef CONFIG_SUNXI_GEN_SUN6I @@ -618,7 +624,7 @@ static unsigned get_mclk_offset(void) if (IS_ENABLED(CONFIG_MACH_SUN9I_A80)) return 0x410; - if (IS_ENABLED(CONFIG_SUN50I_GEN_H6)) + if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) return 0x830; return 0x88; @@ -699,6 +705,7 @@ static const struct udevice_id sunxi_mmc_ids[] = { { .compatible = "allwinner,sun7i-a20-mmc" }, { .compatible = "allwinner,sun8i-a83t-emmc" }, { .compatible = "allwinner,sun9i-a80-mmc" }, + { .compatible = "allwinner,sun20i-d1-mmc" }, { .compatible = "allwinner,sun50i-a64-mmc" }, { .compatible = "allwinner,sun50i-a64-emmc" }, { .compatible = "allwinner,sun50i-h6-mmc" }, diff --git a/drivers/mmc/sunxi_mmc.h b/drivers/mmc/sunxi_mmc.h new file mode 100644 index 00000000000..f4ae5a790c8 --- /dev/null +++ b/drivers/mmc/sunxi_mmc.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2007-2011 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Aaron <leafy.myeh@allwinnertech.com> + * + * MMC register definition for allwinner sunxi platform. + */ + +#ifndef _SUNXI_MMC_H +#define _SUNXI_MMC_H + +#include <linux/types.h> + +struct sunxi_mmc { + u32 gctrl; /* 0x00 global control */ + u32 clkcr; /* 0x04 clock control */ + u32 timeout; /* 0x08 time out */ + u32 width; /* 0x0c bus width */ + u32 blksz; /* 0x10 block size */ + u32 bytecnt; /* 0x14 byte count */ + u32 cmd; /* 0x18 command */ + u32 arg; /* 0x1c argument */ + u32 resp0; /* 0x20 response 0 */ + u32 resp1; /* 0x24 response 1 */ + u32 resp2; /* 0x28 response 2 */ + u32 resp3; /* 0x2c response 3 */ + u32 imask; /* 0x30 interrupt mask */ + u32 mint; /* 0x34 masked interrupt status */ + u32 rint; /* 0x38 raw interrupt status */ + u32 status; /* 0x3c status */ + u32 ftrglevel; /* 0x40 FIFO threshold watermark*/ + u32 funcsel; /* 0x44 function select */ + u32 cbcr; /* 0x48 CIU byte count */ + u32 bbcr; /* 0x4c BIU byte count */ + u32 dbgc; /* 0x50 debug enable */ + u32 res0; /* 0x54 reserved */ + u32 a12a; /* 0x58 Auto command 12 argument */ + u32 ntsr; /* 0x5c New timing set register */ + u32 res1[8]; + u32 dmac; /* 0x80 internal DMA control */ + u32 dlba; /* 0x84 internal DMA descr list base address */ + u32 idst; /* 0x88 internal DMA status */ + u32 idie; /* 0x8c internal DMA interrupt enable */ + u32 chda; /* 0x90 */ + u32 cbda; /* 0x94 */ + u32 res2[26]; +#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2) + u32 res3[17]; + u32 samp_dl; + u32 res4[46]; +#endif + u32 fifo; /* 0x100 / 0x200 FIFO access address */ +}; + +#define SUNXI_MMC_CLK_POWERSAVE (0x1 << 17) +#define SUNXI_MMC_CLK_ENABLE (0x1 << 16) +#define SUNXI_MMC_CLK_DIVIDER_MASK (0xff) + +#define SUNXI_MMC_GCTRL_SOFT_RESET (0x1 << 0) +#define SUNXI_MMC_GCTRL_FIFO_RESET (0x1 << 1) +#define SUNXI_MMC_GCTRL_DMA_RESET (0x1 << 2) +#define SUNXI_MMC_GCTRL_RESET (SUNXI_MMC_GCTRL_SOFT_RESET|\ + SUNXI_MMC_GCTRL_FIFO_RESET|\ + SUNXI_MMC_GCTRL_DMA_RESET) +#define SUNXI_MMC_GCTRL_DMA_ENABLE (0x1 << 5) +#define SUNXI_MMC_GCTRL_ACCESS_BY_AHB (0x1 << 31) + +#define SUNXI_MMC_CMD_RESP_EXPIRE (0x1 << 6) +#define SUNXI_MMC_CMD_LONG_RESPONSE (0x1 << 7) +#define SUNXI_MMC_CMD_CHK_RESPONSE_CRC (0x1 << 8) +#define SUNXI_MMC_CMD_DATA_EXPIRE (0x1 << 9) +#define SUNXI_MMC_CMD_WRITE (0x1 << 10) +#define SUNXI_MMC_CMD_AUTO_STOP (0x1 << 12) +#define SUNXI_MMC_CMD_WAIT_PRE_OVER (0x1 << 13) +#define SUNXI_MMC_CMD_SEND_INIT_SEQ (0x1 << 15) +#define SUNXI_MMC_CMD_UPCLK_ONLY (0x1 << 21) +#define SUNXI_MMC_CMD_START (0x1 << 31) + +#define SUNXI_MMC_RINT_RESP_ERROR (0x1 << 1) +#define SUNXI_MMC_RINT_COMMAND_DONE (0x1 << 2) +#define SUNXI_MMC_RINT_DATA_OVER (0x1 << 3) +#define SUNXI_MMC_RINT_TX_DATA_REQUEST (0x1 << 4) +#define SUNXI_MMC_RINT_RX_DATA_REQUEST (0x1 << 5) +#define SUNXI_MMC_RINT_RESP_CRC_ERROR (0x1 << 6) +#define SUNXI_MMC_RINT_DATA_CRC_ERROR (0x1 << 7) +#define SUNXI_MMC_RINT_RESP_TIMEOUT (0x1 << 8) +#define SUNXI_MMC_RINT_DATA_TIMEOUT (0x1 << 9) +#define SUNXI_MMC_RINT_VOLTAGE_CHANGE_DONE (0x1 << 10) +#define SUNXI_MMC_RINT_FIFO_RUN_ERROR (0x1 << 11) +#define SUNXI_MMC_RINT_HARD_WARE_LOCKED (0x1 << 12) +#define SUNXI_MMC_RINT_START_BIT_ERROR (0x1 << 13) +#define SUNXI_MMC_RINT_AUTO_COMMAND_DONE (0x1 << 14) +#define SUNXI_MMC_RINT_END_BIT_ERROR (0x1 << 15) +#define SUNXI_MMC_RINT_SDIO_INTERRUPT (0x1 << 16) +#define SUNXI_MMC_RINT_CARD_INSERT (0x1 << 30) +#define SUNXI_MMC_RINT_CARD_REMOVE (0x1 << 31) +#define SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT \ + (SUNXI_MMC_RINT_RESP_ERROR | \ + SUNXI_MMC_RINT_RESP_CRC_ERROR | \ + SUNXI_MMC_RINT_DATA_CRC_ERROR | \ + SUNXI_MMC_RINT_RESP_TIMEOUT | \ + SUNXI_MMC_RINT_DATA_TIMEOUT | \ + SUNXI_MMC_RINT_VOLTAGE_CHANGE_DONE | \ + SUNXI_MMC_RINT_FIFO_RUN_ERROR | \ + SUNXI_MMC_RINT_HARD_WARE_LOCKED | \ + SUNXI_MMC_RINT_START_BIT_ERROR | \ + SUNXI_MMC_RINT_END_BIT_ERROR) /* 0xbfc2 */ +#define SUNXI_MMC_RINT_INTERRUPT_DONE_BIT \ + (SUNXI_MMC_RINT_AUTO_COMMAND_DONE | \ + SUNXI_MMC_RINT_DATA_OVER | \ + SUNXI_MMC_RINT_COMMAND_DONE | \ + SUNXI_MMC_RINT_VOLTAGE_CHANGE_DONE) + +#define SUNXI_MMC_STATUS_RXWL_FLAG (0x1 << 0) +#define SUNXI_MMC_STATUS_TXWL_FLAG (0x1 << 1) +#define SUNXI_MMC_STATUS_FIFO_EMPTY (0x1 << 2) +#define SUNXI_MMC_STATUS_FIFO_FULL (0x1 << 3) +#define SUNXI_MMC_STATUS_CARD_PRESENT (0x1 << 8) +#define SUNXI_MMC_STATUS_CARD_DATA_BUSY (0x1 << 9) +#define SUNXI_MMC_STATUS_DATA_FSM_BUSY (0x1 << 10) +#define SUNXI_MMC_STATUS_FIFO_LEVEL(reg) (((reg) >> 17) & 0x3fff) + +#define SUNXI_MMC_NTSR_MODE_SEL_NEW (0x1 << 31) + +#define SUNXI_MMC_IDMAC_RESET (0x1 << 0) +#define SUNXI_MMC_IDMAC_FIXBURST (0x1 << 1) +#define SUNXI_MMC_IDMAC_ENABLE (0x1 << 7) + +#define SUNXI_MMC_IDIE_TXIRQ (0x1 << 0) +#define SUNXI_MMC_IDIE_RXIRQ (0x1 << 1) + +#define SUNXI_MMC_COMMON_CLK_GATE (1 << 16) +#define SUNXI_MMC_COMMON_RESET (1 << 18) + +#define SUNXI_MMC_CAL_DL_SW_EN (0x1 << 7) + +#endif /* _SUNXI_MMC_H */ diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index f76fee3ea0f..d507adbb363 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -37,6 +37,9 @@ struct tegra_mmc_priv { unsigned int version; /* SDHCI spec. version */ unsigned int clock; /* Current clock (MHz) */ int mmc_id; /* peripheral id */ + + int tap_value; + int trim_value; }; static void tegra_mmc_set_power(struct tegra_mmc_priv *priv, @@ -526,31 +529,6 @@ static void tegra_mmc_pad_init(struct tegra_mmc_priv *priv) printf("%s: Warning: Autocal timed out!\n", __func__); /* TBD: Set CFG2TMC_SDMMC1_PAD_CAL_DRV* regs here */ } - -#if defined(CONFIG_TEGRA210) - u32 tap_value, trim_value; - - /* Set tap/trim values for SDMMC1/3 @ <48MHz here */ - val = readl(&priv->reg->venspictl); /* aka VENDOR_SYS_SW_CNTL */ - val &= IO_TRIM_BYPASS_MASK; - if (id == PERIPH_ID_SDMMC1) { - tap_value = 4; /* default */ - if (val) - tap_value = 3; - trim_value = 2; - } else { /* SDMMC3 */ - tap_value = 3; - trim_value = 3; - } - - val = readl(&priv->reg->venclkctl); - val &= ~TRIM_VAL_MASK; - val |= (trim_value << TRIM_VAL_SHIFT); - val &= ~TAP_VAL_MASK; - val |= (tap_value << TAP_VAL_SHIFT); - writel(val, &priv->reg->venclkctl); - debug("%s: VENDOR_CLOCK_CNTRL = 0x%08X\n", __func__, val); -#endif /* T210 */ #endif /* T30/T210 */ } @@ -588,6 +566,22 @@ static void tegra_mmc_reset(struct tegra_mmc_priv *priv, struct mmc *mmc) /* Make sure SDIO pads are set up */ tegra_mmc_pad_init(priv); + + if (!IS_ERR_VALUE(priv->tap_value) || + !IS_ERR_VALUE(priv->trim_value)) { + u32 val; + + val = readl(&priv->reg->venclkctl); + + val &= ~TRIM_VAL_MASK; + val |= (priv->trim_value << TRIM_VAL_SHIFT); + + val &= ~TAP_VAL_MASK; + val |= (priv->tap_value << TAP_VAL_SHIFT); + + writel(val, &priv->reg->venclkctl); + debug("%s: VENDOR_CLOCK_CNTRL = 0x%08X\n", __func__, val); + } } static int tegra_mmc_init(struct udevice *dev) @@ -742,6 +736,14 @@ static int tegra_mmc_probe(struct udevice *dev) if (dm_gpio_is_valid(&priv->pwr_gpio)) dm_gpio_set_value(&priv->pwr_gpio, 1); + ret = dev_read_u32(dev, "nvidia,default-tap", &priv->tap_value); + if (ret) + priv->tap_value = ret; + + ret = dev_read_u32(dev, "nvidia,default-trim", &priv->trim_value); + if (ret) + priv->trim_value = ret; + upriv->mmc = &plat->mmc; return tegra_mmc_init(dev); diff --git a/drivers/mmc/tmio-common.c b/drivers/mmc/tmio-common.c index d1e26815996..890c496b535 100644 --- a/drivers/mmc/tmio-common.c +++ b/drivers/mmc/tmio-common.c @@ -122,7 +122,10 @@ static int tmio_sd_wait_for_irq(struct udevice *dev, struct mmc_cmd *cmd, long wait = 1000000; int ret; - while (!(tmio_sd_readl(priv, reg) & flag)) { + while (true) { + if (tmio_sd_readl(priv, reg) & flag) + return tmio_sd_check_error(dev, cmd); + if (wait-- < 0) { dev_err(dev, "timeout\n"); return -ETIMEDOUT; @@ -756,7 +759,8 @@ int tmio_sd_probe(struct udevice *dev, u32 quirks) dev_dbg(dev, "version %x\n", priv->version); if (priv->version >= 0x10) { priv->caps |= TMIO_SD_CAP_DMA_INTERNAL; - priv->caps |= TMIO_SD_CAP_DIV1024; + if (!(priv->caps & TMIO_SD_CAP_RCAR)) + priv->caps |= TMIO_SD_CAP_DIV1024; } if (fdt_get_property(gd->fdt_blob, dev_of_offset(dev), "non-removable", diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index c638980ea2b..c2fc80b10f0 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -31,7 +31,7 @@ obj-$(CONFIG_NVMXIP) += nvmxip/ else ifneq ($(mtd-y),) -obj-$(CONFIG_SPL_MTD_SUPPORT) += mtd.o +obj-$(CONFIG_SPL_MTD) += mtd.o endif obj-$(CONFIG_$(SPL_TPL_)NAND_SUPPORT) += nand/ obj-$(CONFIG_SPL_ONENAND_SUPPORT) += onenand/ diff --git a/drivers/mtd/hbmc-am654.c b/drivers/mtd/hbmc-am654.c index c86e504da30..8161087b50c 100644 --- a/drivers/mtd/hbmc-am654.c +++ b/drivers/mtd/hbmc-am654.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // -// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ +// Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ // Author: Vignesh Raghavendra <vigneshr@ti.com> #include <common.h> diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index d624589a892..bb9994b8626 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -447,6 +447,22 @@ config NAND_PXA3XX This enables the driver for the NAND flash device found on PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2). +config NAND_SANDBOX + bool "Support for NAND in sandbox" + depends on SANDBOX + select SYS_NAND_SELF_INIT + select SPL_SYS_NAND_SELF_INIT + select SPL_NAND_INIT + select SYS_NAND_SOFT_ECC + select BCH + select NAND_ECC_BCH + imply CMD_NAND + help + Enable a dummy NAND driver for sandbox. It simulates any number of + arbitrary NAND chips with a RAM buffer. It will also inject errors to + test ECC. At the moment, only 8-bit busses and single-chip devices are + supported. + config NAND_SUNXI bool "Support for NAND on Allwinner SoCs" default ARCH_SUNXI @@ -611,6 +627,14 @@ config ROCKCHIP_NAND NFC v800: RK3308, RV1108 NFC v900: PX30, RK3326 +config ROCKCHIP_NAND_SKIP_BBTSCAN + bool "Skip the automatic BBT scan with Rockchip NAND controllers" + depends on ROCKCHIP_NAND + help + Skip the automatic BBT scan with the NAND_SKIP_BBTSCAN + option when data content is not in MTD format or + must remain unchanged. + config TEGRA_NAND bool "Support for NAND controller on Tegra SoCs" depends on ARCH_TEGRA @@ -651,20 +675,13 @@ config SYS_NAND_ONFI_DETECTION And fetching device parameters flashed on device, by parsing ONFI parameter page. -config SYS_NAND_PAGE_COUNT - hex "NAND chip page count" - depends on SPL_NAND_SUPPORT && (NAND_ATMEL || NAND_MXC || \ - SPL_NAND_AM33XX_BCH || SPL_NAND_LOAD || SPL_NAND_SIMPLE || \ - NAND_OMAP_GPMC) - help - Number of pages in the NAND chip. - config SYS_NAND_PAGE_SIZE hex "NAND chip page size" depends on ARCH_SUNXI || NAND_OMAP_GPMC || NAND_LPC32XX_SLC || \ SPL_NAND_SIMPLE || (NAND_MXC && SPL_NAND_SUPPORT) || \ MVEBU_SPL_BOOT_DEVICE_NAND || \ - (NAND_ATMEL && SPL_NAND_SUPPORT) || SPL_GENERATE_ATMEL_PMECC_HEADER + (NAND_ATMEL && SPL_NAND_SUPPORT) || \ + SPL_GENERATE_ATMEL_PMECC_HEADER || NAND_SANDBOX depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && !NAND_MT7621 help Number of data bytes in one page for the NAND chip on the diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index add2b4cf655..ddbba899e58 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o +obj-$(CONFIG_NAND_SANDBOX) += sand_nand.o obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o obj-$(CONFIG_NAND_MXIC) += mxic_nand.o obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o diff --git a/drivers/mtd/nand/raw/am335x_spl_bch.c b/drivers/mtd/nand/raw/am335x_spl_bch.c index 6ab3f1f42c5..6831af98b73 100644 --- a/drivers/mtd/nand/raw/am335x_spl_bch.c +++ b/drivers/mtd/nand/raw/am335x_spl_bch.c @@ -11,6 +11,7 @@ #include <common.h> #include <nand.h> +#include <system-constants.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/mtd/nand_ecc.h> @@ -32,7 +33,7 @@ static int nand_command(int block, int page, uint32_t offs, u8 cmd) { struct nand_chip *this = mtd_to_nand(mtd); - int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + int page_addr = page + block * SYS_NAND_BLOCK_PAGES; void (*hwctrl)(struct mtd_info *mtd, int cmd, unsigned int ctrl) = this->cmd_ctrl; @@ -217,6 +218,11 @@ void nand_init(void) nand_command(0, 0, 0, NAND_CMD_RESET); } +unsigned int nand_page_size(void) +{ + return nand_to_mtd(&nand_chip)->writesize; +} + /* Unselect after operation */ void nand_deselect(void) { diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index fa962ba591c..a2151f98491 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -64,6 +64,7 @@ #include <linux/mfd/syscon/atmel-smc.h> #include <linux/mtd/rawnand.h> #include <linux/mtd/mtd.h> +#include <linux/time.h> #include <mach/at91_sfr.h> #include <nand.h> #include <regmap.h> @@ -71,8 +72,6 @@ #include "pmecc.h" -#define NSEC_PER_SEC 1000000000L - #define ATMEL_HSMC_NFC_CFG 0x0 #define ATMEL_HSMC_NFC_CFG_SPARESIZE(x) (((x) / 4) << 24) #define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK GENMASK(30, 24) @@ -352,40 +351,6 @@ static int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc, bool poll, return ret; } -static void iowrite8_rep(void *addr, const uint8_t *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - writeb(buf[i], addr); -} - -static void ioread8_rep(void *addr, uint8_t *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - buf[i] = readb(addr); -} - -static void ioread16_rep(void *addr, void *buf, int len) -{ - int i; - u16 *p = (u16 *)buf; - - for (i = 0; i < len; i++) - p[i] = readw(addr); -} - -static void iowrite16_rep(void *addr, const void *buf, int len) -{ - int i; - u16 *p = (u16 *)buf; - - for (i = 0; i < len; i++) - writew(p[i], addr); -} - static u8 atmel_nand_read_byte(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); diff --git a/drivers/mtd/nand/raw/atmel_nand.c b/drivers/mtd/nand/raw/atmel_nand.c index 6b17e744a69..6d94e7af38e 100644 --- a/drivers/mtd/nand/raw/atmel_nand.c +++ b/drivers/mtd/nand/raw/atmel_nand.c @@ -12,6 +12,7 @@ #include <common.h> #include <log.h> +#include <system-constants.h> #include <asm/gpio.h> #include <asm/arch/gpio.h> #include <dm/device_compat.h> @@ -1258,7 +1259,7 @@ static struct nand_chip nand_chip; static int nand_command(int block, int page, uint32_t offs, u8 cmd) { struct nand_chip *this = mtd_to_nand(mtd); - int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + int page_addr = page + block * SYS_NAND_BLOCK_PAGES; void (*hwctrl)(struct mtd_info *mtd, int cmd, unsigned int ctrl) = this->cmd_ctrl; @@ -1359,7 +1360,7 @@ int spl_nand_erase_one(int block, int page) if (nand_chip.select_chip) nand_chip.select_chip(mtd, 0); - page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + page_addr = page + block * SYS_NAND_BLOCK_PAGES; hwctrl(mtd, NAND_CMD_ERASE1, NAND_CTRL_CLE | NAND_CTRL_CHANGE); /* Row address */ hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE | NAND_CTRL_CHANGE); @@ -1451,6 +1452,11 @@ void nand_init(void) nand_chip.select_chip(mtd, 0); } +unsigned int nand_page_size(void) +{ + return nand_to_mtd(&nand_chip)->writesize; +} + void nand_deselect(void) { if (nand_chip.select_chip) diff --git a/drivers/mtd/nand/raw/denali_spl.c b/drivers/mtd/nand/raw/denali_spl.c index 690279c9976..165a23312cb 100644 --- a/drivers/mtd/nand/raw/denali_spl.c +++ b/drivers/mtd/nand/raw/denali_spl.c @@ -234,4 +234,9 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) return 0; } +unsigned int nand_page_size(void) +{ + return page_size; +} + void nand_deselect(void) {} diff --git a/drivers/mtd/nand/raw/fsl_ifc_spl.c b/drivers/mtd/nand/raw/fsl_ifc_spl.c index c67065eaf8c..69d26f1f79a 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_spl.c +++ b/drivers/mtd/nand/raw/fsl_ifc_spl.c @@ -106,6 +106,8 @@ static inline int bad_block(uchar *marker, int port_size) return __raw_readw((u16 *)marker) != 0xffff; } +static int saved_page_size; + int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) { struct fsl_ifc_fcm *gregs = (void *)CFG_SYS_IFC_ADDR; @@ -150,6 +152,7 @@ int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) if (port_size == 8) bad_marker = 5; } + saved_page_size = page_size; ver = ifc_in32(&gregs->ifc_rev); if (ver >= FSL_IFC_V2_0_0) @@ -302,6 +305,11 @@ void nand_init(void) { } +unsigned int nand_page_size(void) +{ + return saved_page_size; +} + void nand_deselect(void) { } diff --git a/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c b/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c index ac2e669d46b..f8ae216d56c 100644 --- a/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c @@ -765,4 +765,9 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) return 0; } +unsigned int nand_page_size(void) +{ + return BYTES_PER_PAGE; +} + #endif /* CONFIG_SPL_BUILD */ diff --git a/drivers/mtd/nand/raw/mt7621_nand_spl.c b/drivers/mtd/nand/raw/mt7621_nand_spl.c index 114fc8b7cea..a2be9ba80e0 100644 --- a/drivers/mtd/nand/raw/mt7621_nand_spl.c +++ b/drivers/mtd/nand/raw/mt7621_nand_spl.c @@ -203,6 +203,11 @@ unsigned long nand_size(void) return SZ_2G; } +unsigned int nand_page_size(void) +{ + return nfc_dev.nand.mtd.writesize; +} + void nand_deselect(void) { } diff --git a/drivers/mtd/nand/raw/mxc_nand_spl.c b/drivers/mtd/nand/raw/mxc_nand_spl.c index 309e75d01e5..a855c9987f8 100644 --- a/drivers/mtd/nand/raw/mxc_nand_spl.c +++ b/drivers/mtd/nand/raw/mxc_nand_spl.c @@ -13,6 +13,7 @@ #include <common.h> #include <hang.h> #include <nand.h> +#include <system-constants.h> #include <linux/mtd/rawnand.h> #include <asm/arch/imx-regs.h> #include <asm/io.h> @@ -304,13 +305,13 @@ int nand_spl_load_image(uint32_t from, unsigned int size, void *buf) * Check if we have crossed a block boundary, and if so * check for bad block. */ - if (!(page % CONFIG_SYS_NAND_PAGE_COUNT)) { + if (!(page % SYS_NAND_BLOCK_PAGES)) { /* * Yes, new block. See if this block is good. If not, * loop until we find a good block. */ while (is_badblock(page)) { - page = page + CONFIG_SYS_NAND_PAGE_COUNT; + page = page + SYS_NAND_BLOCK_PAGES; /* Check i we've reached the end of flash. */ if (page >= maxpages) return -1; @@ -350,3 +351,8 @@ __used void nand_boot(void) void nand_init(void) {} void nand_deselect(void) {} + +unsigned int nand_page_size(void) +{ + return CONFIG_SYS_NAND_PAGE_SIZE; +} diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c index 65eab4c8088..fd65772af80 100644 --- a/drivers/mtd/nand/raw/mxs_nand.c +++ b/drivers/mtd/nand/raw/mxs_nand.c @@ -31,6 +31,7 @@ #include <linux/errno.h> #include <linux/mtd/rawnand.h> #include <linux/sizes.h> +#include <linux/time.h> #include <linux/types.h> #include <linux/math64.h> @@ -52,8 +53,6 @@ #endif #define MXS_NAND_BCH_TIMEOUT 10000 -#define USEC_PER_SEC 1000000 -#define NSEC_PER_SEC 1000000000L #define TO_CYCLES(duration, period) DIV_ROUND_UP_ULL(duration, period) diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c index 300662994cf..f7d3f02f85a 100644 --- a/drivers/mtd/nand/raw/mxs_nand_spl.c +++ b/drivers/mtd/nand/raw/mxs_nand_spl.c @@ -295,6 +295,11 @@ int nand_default_bbt(struct mtd_info *mtd) return 0; } +unsigned int nand_page_size(void) +{ + return nand_to_mtd(&nand_chip)->writesize; +} + void nand_deselect(void) { } diff --git a/drivers/mtd/nand/raw/nand.c b/drivers/mtd/nand/raw/nand.c index eacd99c4e27..4c18861aa25 100644 --- a/drivers/mtd/nand/raw/nand.c +++ b/drivers/mtd/nand/raw/nand.c @@ -60,13 +60,11 @@ int nand_register(int devnum, struct mtd_info *mtd) sprintf(dev_name[devnum], "nand%d", devnum); mtd->name = dev_name[devnum]; -#ifdef CONFIG_MTD /* * Add MTD device so that we can reference it later * via the mtdcore infrastructure (e.g. ubi). */ add_mtd_device(mtd); -#endif total_nand_size += mtd->size / 1024; @@ -76,6 +74,23 @@ int nand_register(int devnum, struct mtd_info *mtd) return 0; } +void nand_unregister(struct mtd_info *mtd) +{ + int devnum = nand_mtd_to_devnum(mtd); + + if (devnum < 0) + return; + + if (nand_curr_device == devnum) + nand_curr_device = -1; + + total_nand_size -= mtd->size / 1024; + + del_mtd_device(nand_info[devnum]); + + nand_info[devnum] = NULL; +} + #if !CONFIG_IS_ENABLED(SYS_NAND_SELF_INIT) static void nand_init_chip(int i) { @@ -100,6 +115,8 @@ static void nand_init_chip(int i) #endif #ifdef CONFIG_MTD_CONCAT +struct mtd_info *concat_mtd; + static void create_mtd_concat(void) { struct mtd_info *nand_info_list[CONFIG_SYS_MAX_NAND_DEVICE]; @@ -114,28 +131,40 @@ static void create_mtd_concat(void) } } if (nand_devices_found > 1) { - struct mtd_info *mtd; char c_mtd_name[16]; /* * We detected multiple devices. Concatenate them together. */ sprintf(c_mtd_name, "nand%d", nand_devices_found); - mtd = mtd_concat_create(nand_info_list, nand_devices_found, - c_mtd_name); + concat_mtd = mtd_concat_create(nand_info_list, + nand_devices_found, c_mtd_name); - if (mtd == NULL) + if (!concat_mtd) return; - nand_register(nand_devices_found, mtd); + nand_register(nand_devices_found, concat_mtd); } return; } + +static void destroy_mtd_concat(void) +{ + if (!concat_mtd) + return; + + mtd_concat_destroy(concat_mtd); + concat_mtd = NULL; +} #else static void create_mtd_concat(void) { } + +static void destroy_mtd_concat(void) +{ +} #endif unsigned long nand_size(void) @@ -143,10 +172,10 @@ unsigned long nand_size(void) return total_nand_size; } +static int initialized; + void nand_init(void) { - static int initialized; - /* * Avoid initializing NAND Flash multiple times, * otherwise it will calculate a wrong total size. @@ -174,3 +203,22 @@ void nand_init(void) create_mtd_concat(); } + +void nand_reinit(void) +{ + int i; + + destroy_mtd_concat(); + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + assert(!nand_info[i]); + + initialized = 0; + nand_init(); +} + +unsigned int nand_page_size(void) +{ + struct mtd_info *mtd = get_nand_dev_by_index(nand_curr_device); + + return mtd ? mtd->writesize : 1; +} diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 6b4adcf6bdc..c40a0f23d7b 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -245,39 +245,6 @@ static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte) chip->write_buf(mtd, (uint8_t *)&word, 2); } -static void iowrite8_rep(void *addr, const uint8_t *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - writeb(buf[i], addr); -} -static void ioread8_rep(void *addr, uint8_t *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - buf[i] = readb(addr); -} - -static void ioread16_rep(void *addr, void *buf, int len) -{ - int i; - u16 *p = (u16 *) buf; - - for (i = 0; i < len; i++) - p[i] = readw(addr); -} - -static void iowrite16_rep(void *addr, void *buf, int len) -{ - int i; - u16 *p = (u16 *) buf; - - for (i = 0; i < len; i++) - writew(p[i], addr); -} - /** * nand_write_buf - [DEFAULT] write buffer to chip * @mtd: MTD device structure @@ -4462,17 +4429,14 @@ ident_done: else if (chip->jedec_version) pr_info("%s %s\n", manufacturer_desc->name, chip->jedec_params.model); - else + else if (manufacturer_desc) pr_info("%s %s\n", manufacturer_desc->name, type->name); #else if (chip->jedec_version) pr_info("%s %s\n", manufacturer_desc->name, chip->jedec_params.model); - else + else if (manufacturer_desc) pr_info("%s %s\n", manufacturer_desc->name, type->name); - - pr_info("%s %s\n", manufacturer_desc->name, - type->name); #endif pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n", diff --git a/drivers/mtd/nand/raw/nand_spl_loaders.c b/drivers/mtd/nand/raw/nand_spl_loaders.c index 156b44d8358..db4213ea3dc 100644 --- a/drivers/mtd/nand/raw/nand_spl_loaders.c +++ b/drivers/mtd/nand/raw/nand_spl_loaders.c @@ -12,8 +12,11 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) while (block <= lastblock) { if (!nand_is_bad_block(block)) { /* Skip bad blocks */ - while (page < CONFIG_SYS_NAND_PAGE_COUNT) { + while (size && page < SYS_NAND_BLOCK_PAGES) { nand_read_page(block, page, dst); + + size -= min(size, CONFIG_SYS_NAND_PAGE_SIZE - + page_offset); /* * When offs is not aligned to page address the * extra offset is copied to dst as well. Copy diff --git a/drivers/mtd/nand/raw/nand_spl_simple.c b/drivers/mtd/nand/raw/nand_spl_simple.c index 2f3af9edd4c..80d6e0e1e4e 100644 --- a/drivers/mtd/nand/raw/nand_spl_simple.c +++ b/drivers/mtd/nand/raw/nand_spl_simple.c @@ -6,6 +6,7 @@ #include <common.h> #include <nand.h> +#include <system-constants.h> #include <asm/io.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/rawnand.h> @@ -27,7 +28,7 @@ static int nand_command(int block, int page, uint32_t offs, u8 cmd) { struct nand_chip *this = mtd_to_nand(mtd); - int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + int page_addr = page + block * SYS_NAND_BLOCK_PAGES; while (!this->dev_ready(mtd)) ; @@ -59,7 +60,7 @@ static int nand_command(int block, int page, uint32_t offs, u8 cmd) { struct nand_chip *this = mtd_to_nand(mtd); - int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + int page_addr = page + block * SYS_NAND_BLOCK_PAGES; void (*hwctrl)(struct mtd_info *mtd, int cmd, unsigned int ctrl) = this->cmd_ctrl; @@ -226,6 +227,11 @@ void nand_init(void) nand_chip.select_chip(mtd, 0); } +unsigned int nand_page_size(void) +{ + return nand_to_mtd(&nand_chip)->writesize; +} + /* Unselect after operation */ void nand_deselect(void) { diff --git a/drivers/mtd/nand/raw/octeontx_nand.c b/drivers/mtd/nand/raw/octeontx_nand.c index 65a03d22c1d..3b20685fac0 100644 --- a/drivers/mtd/nand/raw/octeontx_nand.c +++ b/drivers/mtd/nand/raw/octeontx_nand.c @@ -24,6 +24,7 @@ #include <linux/mtd/nand_bch.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/rawnand.h> +#include <linux/time.h> #include <asm/global_data.h> #include <asm/io.h> #include <asm/types.h> @@ -291,7 +292,6 @@ union ndf_cmd { #define OCTEONTX_NAND_DRIVER_NAME "octeontx_nand" #define NDF_TIMEOUT 1000 /** Timeout in ms */ -#define USEC_PER_SEC 1000000 /** Linux compatibility */ #ifndef NAND_MAX_CHIPS # define NAND_MAX_CHIPS 8 /** Linux compatibility */ #endif diff --git a/drivers/mtd/nand/raw/omap_gpmc.c b/drivers/mtd/nand/raw/omap_gpmc.c index 1a5ed0de31a..0e25bd5dc28 100644 --- a/drivers/mtd/nand/raw/omap_gpmc.c +++ b/drivers/mtd/nand/raw/omap_gpmc.c @@ -6,6 +6,7 @@ #include <common.h> #include <log.h> +#include <system-constants.h> #include <asm/io.h> #include <dm/uclass.h> #include <linux/errno.h> @@ -1298,7 +1299,7 @@ static int nand_is_bad_block(int block) static int nand_read_page(int block, int page, uchar *dst) { - int page_addr = block * CONFIG_SYS_NAND_PAGE_COUNT + page; + int page_addr = block * SYS_NAND_BLOCK_PAGES + page; loff_t ofs = page_addr * CONFIG_SYS_NAND_PAGE_SIZE; int ret; size_t len = CONFIG_SYS_NAND_PAGE_SIZE; diff --git a/drivers/mtd/nand/raw/rockchip_nfc.c b/drivers/mtd/nand/raw/rockchip_nfc.c index 274489ecbc6..088cc7fead2 100644 --- a/drivers/mtd/nand/raw/rockchip_nfc.c +++ b/drivers/mtd/nand/raw/rockchip_nfc.c @@ -955,6 +955,9 @@ static int rk_nfc_nand_chip_init(ofnode node, struct rk_nfc *nfc, int devnum) chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; + if (IS_ENABLED(CONFIG_ROCKCHIP_NAND_SKIP_BBTSCAN)) + chip->options |= NAND_SKIP_BBTSCAN; + rk_nfc_hw_init(nfc); ret = nand_scan_ident(mtd, nsels, NULL); if (ret) diff --git a/drivers/mtd/nand/raw/sand_nand.c b/drivers/mtd/nand/raw/sand_nand.c new file mode 100644 index 00000000000..229d7b5b65a --- /dev/null +++ b/drivers/mtd/nand/raw/sand_nand.c @@ -0,0 +1,707 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) Sean Anderson <seanga2@gmail.com> + */ + +#define LOG_CATEGORY UCLASS_MTD +#include <errno.h> +#include <hexdump.h> +#include <log.h> +#include <nand.h> +#include <os.h> +#include <rand.h> +#include <spl.h> +#include <system-constants.h> +#include <dm/device_compat.h> +#include <dm/read.h> +#include <dm/uclass.h> +#include <asm/bitops.h> +#include <linux/bitmap.h> +#include <linux/mtd/rawnand.h> +#include <linux/sizes.h> + +enum sand_nand_state { + STATE_READY, + STATE_IDLE, + STATE_READ, + STATE_READ_ID, + STATE_READ_ONFI, + STATE_PARAM_ONFI, + STATE_STATUS, + STATE_PROG, + STATE_ERASE, +}; + +static const char *const state_name[] = { + [STATE_READY] = "READY", + [STATE_IDLE] = "IDLE", + [STATE_READ] = "READ", + [STATE_READ_ID] = "READ_ID", + [STATE_READ_ONFI] = "READ_ONFI", + [STATE_PARAM_ONFI] = "PARAM_ONFI", + [STATE_STATUS] = "STATUS", + [STATE_PROG] = "PROG", + [STATE_ERASE] = "ERASE", +}; + +/** + * struct sand_nand_chip - Per-device private data + * @nand: The nand chip + * @node: The next device in this controller + * @programmed: Bitmap of whether sectors are programmed + * @id: ID to report for NAND_CMD_READID + * @id_len: Length of @id + * @onfi: Three copies of ONFI parameter page + * @status: Status to report for NAND_CMD_STATUS + * @chunksize: Size of one "chunk" (page + oob) in bytes + * @pageize: Size of one page in bytes + * @pages: Total number of pages + * @pages_per_erase: Number of pages per eraseblock + * @err_count: Number of errors to inject per @err_step_bits of data + * @err_step_bits: Number of data bits per error "step" + * @err_steps: Number of err steps in a page + * @cs: Chip select for this device + * @state: Current state of the device + * @column: Column of the most-recent command + * @page_addr: Page address of the most-recent command + * @fd: File descriptor for the backing data + * @fd_page_addr: Page address that @fd is seek'd to + * @selected: Whether this device is selected + * @tmp: "Cache" buffer used to store transferred data before committing it + * @tmp_dirty: Whether @tmp is dirty (modified) or clean (all ones) + * + * Data is stored with the OOB area in-line. For example, with 512-byte pages + * and and 16-byte OOB areas, the first page would start at offset 0, the second + * at offset 528, the third at offset 1056, and so on + */ +struct sand_nand_chip { + struct nand_chip nand; + struct list_head node; + long *programmed; + const u8 *id; + u32 chunksize, pagesize, pages, pages_per_erase; + u32 err_count, err_step_bits, err_steps, ecc_bits; + unsigned int cs; + enum sand_nand_state state; + int column, page_addr, fd, fd_page_addr; + bool selected, tmp_dirty; + u8 status; + u8 id_len; + u8 tmp[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE]; + u8 onfi[sizeof(struct nand_onfi_params) * 3]; +}; + +#define SAND_DEBUG(chip, fmt, ...) \ + dev_dbg((chip)->nand.mtd.dev, "%u (%s): " fmt, (chip)->cs, \ + state_name[(chip)->state], ##__VA_ARGS__) + +static inline void to_state(struct sand_nand_chip *chip, + enum sand_nand_state new_state) +{ + if (new_state != chip->state) + SAND_DEBUG(chip, "to state %s\n", state_name[new_state]); + chip->state = new_state; +} + +static inline struct sand_nand_chip *to_sand_nand(struct nand_chip *nand) +{ + return container_of(nand, struct sand_nand_chip, nand); +} + +struct sand_nand_priv { + struct list_head chips; +}; + +static int sand_nand_dev_ready(struct mtd_info *mtd) +{ + return 1; +} + +static int sand_nand_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + u8 status; + + return nand_status_op(chip, &status) ?: status; +} + +static int sand_nand_seek(struct sand_nand_chip *chip) +{ + if (chip->fd_page_addr == chip->page_addr) + return 0; + + if (os_lseek(chip->fd, (off_t)chip->page_addr * chip->chunksize, + OS_SEEK_SET) < 0) { + SAND_DEBUG(chip, "could not seek: %d\n", errno); + return -EIO; + } + + chip->fd_page_addr = chip->page_addr; + return 0; +} + +static void sand_nand_inject_error(struct sand_nand_chip *chip, + unsigned int step, unsigned int pos) +{ + int byte, index; + + if (pos < chip->err_step_bits) { + __change_bit(step * chip->err_step_bits + pos, chip->tmp); + return; + } + + /* + * Only ECC bytes are covered in the OOB area, so + * pretend that those are the only bytes which can have + * errors. + */ + byte = (pos - chip->err_step_bits + step * chip->ecc_bits) / 8; + index = chip->nand.ecc.layout->eccpos[byte]; + /* Avoid endianness issues by working with bytes */ + chip->tmp[chip->pagesize + index] ^= BIT(pos & 0x7); +} + +static int sand_nand_read(struct sand_nand_chip *chip) +{ + unsigned int i, stop = 0; + + if (chip->column == chip->pagesize) + stop = chip->err_step_bits; + + if (test_bit(chip->page_addr, chip->programmed)) { + if (sand_nand_seek(chip)) + return -EIO; + + if (os_read(chip->fd, chip->tmp, chip->chunksize) != + chip->chunksize) { + SAND_DEBUG(chip, "could not read: %d\n", errno); + return -EIO; + } + chip->fd_page_addr++; + } else if (chip->tmp_dirty) { + memset(chip->tmp + chip->column, 0xff, + chip->chunksize - chip->column); + } + + /* + * Inject some errors; this is Method A from "An Efficient Algorithm for + * Sequential Random Sampling" (Vitter 87). This is still slow when + * generating a lot (dozens) of ECC errors. + * + * To avoid generating too many errors in any one ECC step, we separate + * our error generation by ECC step. + */ + chip->tmp_dirty = true; + for (i = 0; i < chip->err_steps; i++) { + u32 bit_errors = chip->err_count; + unsigned int j = chip->err_step_bits + chip->ecc_bits; + + while (bit_errors) { + unsigned int u = rand(); + float quot = 1ULL << 32; + + do { + quot *= j - bit_errors; + quot /= j; + j--; + + if (j < stop) + goto next; + } while (u < quot); + + sand_nand_inject_error(chip, i, j); + bit_errors--; + } +next: + ; + } + + return 0; +} + +static void sand_nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sand_nand_chip *chip = to_sand_nand(nand); + enum sand_nand_state new_state = chip->state; + + SAND_DEBUG(chip, "command=%02x column=%d page_addr=%d\n", command, + column, page_addr); + + if (!chip->selected) + return; + + switch (chip->state) { + case STATE_READY: + if (command == NAND_CMD_RESET) + goto reset; + break; + case STATE_PROG: + new_state = STATE_IDLE; + if (command != NAND_CMD_PAGEPROG || + test_and_set_bit(chip->page_addr, chip->programmed)) { + chip->status |= NAND_STATUS_FAIL; + break; + } + + if (sand_nand_seek(chip)) { + chip->status |= NAND_STATUS_FAIL; + break; + } + + if (os_write(chip->fd, chip->tmp, chip->chunksize) != + chip->chunksize) { + SAND_DEBUG(chip, "could not write: %d\n", errno); + chip->status |= NAND_STATUS_FAIL; + break; + } + + chip->fd_page_addr++; + break; + case STATE_ERASE: + new_state = STATE_IDLE; + if (command != NAND_CMD_ERASE2) { + chip->status |= NAND_STATUS_FAIL; + break; + } + + if (chip->page_addr < 0 || + chip->page_addr >= chip->pages || + chip->page_addr % chip->pages_per_erase) + chip->status |= NAND_STATUS_FAIL; + else + bitmap_clear(chip->programmed, chip->page_addr, + chip->pages_per_erase); + break; + default: + chip->column = column; + chip->page_addr = page_addr; + switch (command) { + case NAND_CMD_READOOB: + if (column >= 0) + chip->column += chip->pagesize; + fallthrough; + case NAND_CMD_READ0: + new_state = STATE_IDLE; + if (page_addr < 0 || page_addr >= chip->pages) + break; + + if (chip->column < 0 || chip->column >= chip->chunksize) + break; + + if (sand_nand_read(chip)) + break; + + chip->page_addr = page_addr; + new_state = STATE_READ; + break; + case NAND_CMD_ERASE1: + new_state = STATE_ERASE; + chip->status = ~NAND_STATUS_FAIL; + break; + case NAND_CMD_STATUS: + new_state = STATE_STATUS; + chip->column = 0; + break; + case NAND_CMD_SEQIN: + new_state = STATE_PROG; + chip->status = ~NAND_STATUS_FAIL; + if (page_addr < 0 || page_addr >= chip->pages || + chip->column < 0 || + chip->column >= chip->chunksize) { + chip->status |= NAND_STATUS_FAIL; + } else if (chip->tmp_dirty) { + memset(chip->tmp, 0xff, chip->chunksize); + chip->tmp_dirty = false; + } + break; + case NAND_CMD_READID: + if (chip->onfi[0] && column == 0x20) + new_state = STATE_READ_ONFI; + else + new_state = STATE_READ_ID; + chip->column = 0; + break; + case NAND_CMD_PARAM: + if (chip->onfi[0] && !column) + new_state = STATE_PARAM_ONFI; + else + new_state = STATE_IDLE; + break; + case NAND_CMD_RESET: +reset: + new_state = STATE_IDLE; + chip->column = -1; + chip->page_addr = -1; + chip->status = ~NAND_STATUS_FAIL; + break; + default: + new_state = STATE_IDLE; + SAND_DEBUG(chip, "Unsupported command %02x\n", command); + } + } + + to_state(chip, new_state); +} + +static void sand_nand_select_chip(struct mtd_info *mtd, int n) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sand_nand_chip *chip = to_sand_nand(nand); + + chip->selected = !n; +} + +static void sand_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sand_nand_chip *chip = to_sand_nand(nand); + unsigned int to_copy; + int src_len = 0; + const u8 *src = NULL; + + if (!chip->selected) + goto copy; + + switch (chip->state) { + case STATE_READ: + src = chip->tmp; + src_len = chip->chunksize; + break; + case STATE_READ_ID: + src = chip->id; + src_len = chip->id_len; + break; + case STATE_READ_ONFI: + src = "ONFI"; + src_len = 4; + break; + case STATE_PARAM_ONFI: + src = chip->onfi; + src_len = sizeof(chip->onfi); + break; + case STATE_STATUS: + src = &chip->status; + src_len = 1; + break; + default: + break; + } + +copy: + if (chip->column >= 0) + to_copy = max(min(len, src_len - chip->column), 0); + else + to_copy = 0; + memcpy(buf, src + chip->column, to_copy); + memset(buf + to_copy, 0xff, len - to_copy); + chip->column += to_copy; + + if (len == 1) { + SAND_DEBUG(chip, "read [ %02x ]\n", buf[0]); + } else if (src_len) { + SAND_DEBUG(chip, "read %d bytes\n", len); +#ifdef VERBOSE_DEBUG + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); +#endif + } + + if (src_len && chip->column == src_len) + to_state(chip, STATE_IDLE); +} + +static u8 sand_nand_read_byte(struct mtd_info *mtd) +{ + u8 ret; + + sand_nand_read_buf(mtd, &ret, 1); + return ret; +} + +static u16 sand_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sand_nand_chip *chip = to_sand_nand(nand); + + SAND_DEBUG(chip, "16-bit access unsupported\n"); + return sand_nand_read_byte(mtd) | 0xff00; +} + +static void sand_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sand_nand_chip *chip = to_sand_nand(nand); + + SAND_DEBUG(chip, "write %d bytes\n", len); +#ifdef VERBOSE_DEBUG + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); +#endif + + if (chip->state != STATE_PROG || chip->status & NAND_STATUS_FAIL) + return; + + chip->tmp_dirty = true; + len = min((unsigned int)len, chip->chunksize - chip->column); + memcpy(chip->tmp + chip->column, buf, len); + chip->column += len; +} + +static struct nand_chip *nand_chip; + +int sand_nand_remove(struct udevice *dev) +{ + struct sand_nand_priv *priv = dev_get_priv(dev); + struct sand_nand_chip *chip; + + list_for_each_entry(chip, &priv->chips, node) { + struct nand_chip *nand = &chip->nand; + + if (nand_chip == nand) + nand_chip = NULL; + + nand_unregister(nand_to_mtd(nand)); + free(chip->programmed); + os_close(chip->fd); + free(chip); + } + + return 0; +} + +static int sand_nand_probe(struct udevice *dev) +{ + struct sand_nand_priv *priv = dev_get_priv(dev); + struct sand_nand_chip *chip; + int ret, devnum = 0; + ofnode np; + + INIT_LIST_HEAD(&priv->chips); + + dev_for_each_subnode(np, dev) { + struct nand_chip *nand; + struct mtd_info *mtd; + u32 erasesize, oobsize, pagesize, pages; + u32 err_count, err_step_size; + off_t expected_size; + char filename[30]; + fdt_addr_t cs; + const u8 *id, *onfi; + int id_len, onfi_len; + + cs = ofnode_get_addr_size_index_notrans(np, 0, NULL); + if (cs == FDT_ADDR_T_NONE) { + dev_dbg(dev, "Invalid cs for chip %s\n", + ofnode_get_name(np)); + ret = -ENOENT; + goto err; + } + + id = ofnode_read_prop(np, "sandbox,id", &id_len); + if (!id) { + dev_dbg(dev, "No sandbox,id property for chip %s\n", + ofnode_get_name(np)); + ret = -EINVAL; + goto err; + } + + onfi = ofnode_read_prop(np, "sandbox,onfi", &onfi_len); + if (onfi && onfi_len != sizeof(struct nand_onfi_params)) { + dev_dbg(dev, "Invalid length %d for onfi params\n", + onfi_len); + ret = -EINVAL; + goto err; + } + + ret = ofnode_read_u32(np, "sandbox,erasesize", &erasesize); + if (ret) { + dev_dbg(dev, "No sandbox,erasesize property for chip %s", + ofnode_get_name(np)); + goto err; + } + + ret = ofnode_read_u32(np, "sandbox,oobsize", &oobsize); + if (ret) { + dev_dbg(dev, "No sandbox,oobsize property for chip %s", + ofnode_get_name(np)); + goto err; + } + + ret = ofnode_read_u32(np, "sandbox,pagesize", &pagesize); + if (ret) { + dev_dbg(dev, "No sandbox,pagesize property for chip %s", + ofnode_get_name(np)); + goto err; + } + + ret = ofnode_read_u32(np, "sandbox,pages", &pages); + if (ret) { + dev_dbg(dev, "No sandbox,pages property for chip %s", + ofnode_get_name(np)); + goto err; + } + + ret = ofnode_read_u32(np, "sandbox,err-count", &err_count); + if (ret) { + dev_dbg(dev, + "No sandbox,err-count property for chip %s", + ofnode_get_name(np)); + goto err; + } + + ret = ofnode_read_u32(np, "sandbox,err-step-size", + &err_step_size); + if (ret) { + dev_dbg(dev, + "No sandbox,err-step-size property for chip %s", + ofnode_get_name(np)); + goto err; + } + + chip = calloc(sizeof(*chip), 1); + if (!chip) { + ret = -ENOMEM; + goto err; + } + + chip->cs = cs; + chip->id = id; + chip->id_len = id_len; + chip->chunksize = pagesize + oobsize; + chip->pagesize = pagesize; + chip->pages = pages; + chip->pages_per_erase = erasesize / pagesize; + memset(chip->tmp, 0xff, chip->chunksize); + + chip->err_count = err_count; + chip->err_step_bits = err_step_size * 8; + chip->err_steps = pagesize / err_step_size; + + expected_size = (off_t)pages * chip->chunksize; + snprintf(filename, sizeof(filename), + "/tmp/u-boot.nand%d.XXXXXX", devnum); + chip->fd = os_mktemp(filename, expected_size); + if (chip->fd < 0) { + dev_dbg(dev, "Could not create temp file %s\n", + filename); + ret = chip->fd; + goto err_chip; + } + + chip->programmed = calloc(sizeof(long), + BITS_TO_LONGS(pages)); + if (!chip->programmed) { + ret = -ENOMEM; + goto err_fd; + } + + if (onfi) { + memcpy(chip->onfi, onfi, onfi_len); + memcpy(chip->onfi + onfi_len, onfi, onfi_len); + memcpy(chip->onfi + 2 * onfi_len, onfi, onfi_len); + } + + nand = &chip->nand; + nand->options = spl_in_proper() ? 0 : NAND_SKIP_BBTSCAN; + nand->flash_node = np; + nand->dev_ready = sand_nand_dev_ready; + nand->cmdfunc = sand_nand_command; + nand->waitfunc = sand_nand_wait; + nand->select_chip = sand_nand_select_chip; + nand->read_byte = sand_nand_read_byte; + nand->read_word = sand_nand_read_word; + nand->read_buf = sand_nand_read_buf; + nand->write_buf = sand_nand_write_buf; + nand->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; + + mtd = nand_to_mtd(nand); + mtd->dev = dev; + + ret = nand_scan(mtd, CONFIG_SYS_NAND_MAX_CHIPS); + if (ret) { + dev_dbg(dev, "Could not scan chip %s: %d\n", + ofnode_get_name(np), ret); + goto err_prog; + } + chip->ecc_bits = nand->ecc.layout->eccbytes * 8 / + chip->err_steps; + + ret = nand_register(devnum, mtd); + if (ret) { + dev_dbg(dev, "Could not register nand %d: %d\n", devnum, + ret); + goto err_prog; + } + + if (!nand_chip) + nand_chip = nand; + + list_add_tail(&chip->node, &priv->chips); + devnum++; + continue; + +err_prog: + free(chip->programmed); +err_fd: + os_close(chip->fd); +err_chip: + free(chip); + goto err; + } + + return 0; + +err: + sand_nand_remove(dev); + return ret; +} + +static const struct udevice_id sand_nand_ids[] = { + { .compatible = "sandbox,nand" }, + { } +}; + +U_BOOT_DRIVER(sand_nand) = { + .name = "sand-nand", + .id = UCLASS_MTD, + .of_match = sand_nand_ids, + .probe = sand_nand_probe, + .remove = sand_nand_remove, + .priv_auto = sizeof(struct sand_nand_priv), +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int err; + + err = uclass_get_device_by_driver(UCLASS_MTD, DM_DRIVER_REF(sand_nand), + &dev); + if (err && err != -ENODEV) + log_info("Failed to get sandbox NAND: %d\n", err); +} + +#if IS_ENABLED(CONFIG_SPL_BUILD) && IS_ENABLED(CONFIG_SPL_NAND_INIT) +void nand_deselect(void) +{ + nand_chip->select_chip(nand_to_mtd(nand_chip), -1); +} + +static int nand_is_bad_block(int block) +{ + struct mtd_info *mtd = nand_to_mtd(nand_chip); + + return mtd_block_isbad(mtd, block << mtd->erasesize_shift); +} + +static int nand_read_page(int block, int page, uchar *dst) +{ + struct mtd_info *mtd = nand_to_mtd(nand_chip); + loff_t ofs = ((loff_t)block << mtd->erasesize_shift) + + ((loff_t)page << mtd->writesize_shift); + size_t len = mtd->writesize; + + return nand_read(mtd, ofs, &len, dst); +} + +#include "nand_spl_loaders.c" +#endif /* CONFIG_SPL_NAND_INIT */ diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index 64be6486b4e..3528824575b 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -22,6 +22,7 @@ #include <linux/ioport.h> #include <linux/mtd/rawnand.h> #include <linux/printk.h> +#include <linux/time.h> /* Bad block marker length */ #define FMC2_BBM_LEN 2 @@ -127,8 +128,6 @@ #define FMC2_BCHDSR4_EBP7 GENMASK(12, 0) #define FMC2_BCHDSR4_EBP8 GENMASK(28, 16) -#define FMC2_NSEC_PER_SEC 1000000000L - #define FMC2_TIMEOUT_5S 5000000 enum stm32_fmc2_ecc { @@ -603,7 +602,7 @@ static void stm32_fmc2_nfc_calc_timings(struct nand_chip *chip, struct stm32_fmc2_nand *nand = to_fmc2_nand(chip); struct stm32_fmc2_timings *tims = &nand->timings; unsigned long hclk = clk_get_rate(&nfc->clk); - unsigned long hclkp = FMC2_NSEC_PER_SEC / (hclk / 1000); + unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000); unsigned long timing, tar, tclr, thiz, twait; unsigned long tset_mem, tset_att, thold_mem, thold_att; diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 6de0b0a3554..c9b8c78ed75 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -524,9 +524,10 @@ static int nand_read_buffer(struct nfc_config *conf, uint32_t offs, return 0; } +static struct nfc_config conf; + int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) { - static struct nfc_config conf = { }; int ret; ret = nand_detect_config(&conf, offs, dest); @@ -536,6 +537,11 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) return nand_read_buffer(&conf, offs, size, dest); } +unsigned int nand_page_size(void) +{ + return conf.page_size; +} + void nand_deselect(void) { struct sunxi_ccm_reg *const ccm = diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 6c65b187e86..3051de4f7ef 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o gigadevice.o macronix.o micron.o toshiba.o winbond.o +spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 4ee11e812d8..597b088ca7e 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -17,6 +17,7 @@ #include <linux/mtd/spinand.h> #include <linux/of.h> #include <linux/slab.h> +#include <linux/string.h> #include <linux/spi/spi.h> #include <linux/spi/spi-mem.h> #else @@ -326,6 +327,13 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand, u16 column = 0; int ret; + /* + * Looks like PROGRAM LOAD (AKA write cache) does not necessarily reset + * the cache content to 0xFF (depends on vendor implementation), so we + * must fill the page cache entirely even if we only want to program + * the data portion of the page, otherwise we might corrupt the BBM or + * user data previously programmed in OOB area. + */ memset(spinand->databuf, 0xff, nanddev_page_size(nand) + nanddev_per_page_oobsize(nand)); @@ -452,9 +460,11 @@ out: return status & STATUS_BUSY ? -ETIMEDOUT : 0; } -static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf) +static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr, + u8 ndummy, u8 *buf) { - struct spi_mem_op op = SPINAND_READID_OP(0, spinand->scratchbuf, + struct spi_mem_op op = SPINAND_READID_OP(naddr, ndummy, + spinand->scratchbuf, SPINAND_MAX_ID_LEN); int ret; @@ -596,12 +606,12 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, if (ret == -EBADMSG) { ecc_failed = true; mtd->ecc_stats.failed++; - ret = 0; } else { mtd->ecc_stats.corrected += ret; max_bitflips = max_t(unsigned int, max_bitflips, ret); } + ret = 0; ops->retlen += iter.req.datalen; ops->oobretlen += iter.req.ooblen; } @@ -667,16 +677,9 @@ static bool spinand_isbad(struct nand_device *nand, const struct nand_pos *pos) .oobbuf.in = marker, .mode = MTD_OPS_RAW, }; - int ret; - - ret = spinand_select_target(spinand, pos->target); - if (ret) - return ret; - - ret = spinand_read_page(spinand, &req, false); - if (ret) - return ret; + spinand_select_target(spinand, pos->target); + spinand_read_page(spinand, &req, false); if (marker[0] != 0xff || marker[1] != 0xff) return true; @@ -720,6 +723,10 @@ static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) if (ret) return ret; + ret = spinand_write_enable_op(spinand); + if (ret) + return ret; + return spinand_write_page(spinand, &req); } @@ -808,21 +815,6 @@ static int spinand_mtd_block_isreserved(struct mtd_info *mtd, loff_t offs) return ret; } -const struct spi_mem_op * -spinand_find_supported_op(struct spinand_device *spinand, - const struct spi_mem_op *ops, - unsigned int nops) -{ - unsigned int i; - - for (i = 0; i < nops; i++) { - if (spi_mem_supports_op(spinand->slave, &ops[i])) - return &ops[i]; - } - - return NULL; -} - static const struct nand_ops spinand_ops = { .erase = spinand_erase, .markbad = spinand_markbad, @@ -833,28 +825,67 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { &gigadevice_spinand_manufacturer, ¯onix_spinand_manufacturer, µn_spinand_manufacturer, + ¶gon_spinand_manufacturer, &toshiba_spinand_manufacturer, &winbond_spinand_manufacturer, }; -static int spinand_manufacturer_detect(struct spinand_device *spinand) +static int spinand_manufacturer_match(struct spinand_device *spinand, + enum spinand_readid_method rdid_method) { + u8 *id = spinand->id.data; unsigned int i; int ret; for (i = 0; i < ARRAY_SIZE(spinand_manufacturers); i++) { - ret = spinand_manufacturers[i]->ops->detect(spinand); - if (ret > 0) { - spinand->manufacturer = spinand_manufacturers[i]; - return 0; - } else if (ret < 0) { - return ret; - } - } + const struct spinand_manufacturer *manufacturer = + spinand_manufacturers[i]; + + if (id[0] != manufacturer->id) + continue; + + ret = spinand_match_and_init(spinand, + manufacturer->chips, + manufacturer->nchips, + rdid_method); + if (ret < 0) + continue; + spinand->manufacturer = manufacturer; + return 0; + } return -ENOTSUPP; } +static int spinand_id_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + ret = spinand_read_id_op(spinand, 0, 0, id); + if (ret) + return ret; + ret = spinand_manufacturer_match(spinand, SPINAND_READID_METHOD_OPCODE); + if (!ret) + return 0; + + ret = spinand_read_id_op(spinand, 1, 0, id); + if (ret) + return ret; + ret = spinand_manufacturer_match(spinand, + SPINAND_READID_METHOD_OPCODE_ADDR); + if (!ret) + return 0; + + ret = spinand_read_id_op(spinand, 0, 1, id); + if (ret) + return ret; + ret = spinand_manufacturer_match(spinand, + SPINAND_READID_METHOD_OPCODE_DUMMY); + + return ret; +} + static int spinand_manufacturer_init(struct spinand_device *spinand) { if (spinand->manufacturer->ops->init) @@ -910,9 +941,9 @@ spinand_select_op_variant(struct spinand_device *spinand, * @spinand: SPI NAND object * @table: SPI NAND device description table * @table_size: size of the device description table + * @rdid_method: read id method to match * - * Should be used by SPI NAND manufacturer drivers when they want to find a - * match between a device ID retrieved through the READ_ID command and an + * Match between a device ID retrieved through the READ_ID command and an * entry in the SPI NAND description table. If a match is found, the spinand * object will be initialized with information provided by the matching * spinand_info entry. @@ -921,8 +952,10 @@ spinand_select_op_variant(struct spinand_device *spinand, */ int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *table, - unsigned int table_size, u8 devid) + unsigned int table_size, + enum spinand_readid_method rdid_method) { + u8 *id = spinand->id.data; struct nand_device *nand = spinand_to_nand(spinand); unsigned int i; @@ -930,13 +963,17 @@ int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *info = &table[i]; const struct spi_mem_op *op; - if (devid != info->devid) + if (rdid_method != info->devid.method) + continue; + + if (memcmp(id + 1, info->devid.id, info->devid.len)) continue; nand->memorg = table[i].memorg; nand->eccreq = table[i].eccreq; spinand->eccinfo = table[i].eccinfo; spinand->flags = table[i].flags; + spinand->id.len = 1 + table[i].devid.len; spinand->select_target = table[i].select_target; op = spinand_select_op_variant(spinand, @@ -972,13 +1009,7 @@ static int spinand_detect(struct spinand_device *spinand) if (ret) return ret; - ret = spinand_read_id_op(spinand, spinand->id.data); - if (ret) - return ret; - - spinand->id.len = SPINAND_MAX_ID_LEN; - - ret = spinand_manufacturer_detect(spinand); + ret = spinand_id_detect(spinand); if (ret) { dev_err(spinand->slave->dev, "unknown raw ID %02x %02x %02x %02x\n", spinand->id.data[0], spinand->id.data[1], @@ -1083,11 +1114,11 @@ static int spinand_init(struct spinand_device *spinand) for (i = 0; i < nand->memorg.ntargets; i++) { ret = spinand_select_target(spinand, i); if (ret) - goto err_free_bufs; + goto err_manuf_cleanup; ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED); if (ret) - goto err_free_bufs; + goto err_manuf_cleanup; } ret = nanddev_init(nand, &spinand_ops, THIS_MODULE); diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index a2c93486f49..f2ecf47f8d4 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -7,13 +7,13 @@ */ #ifndef __UBOOT__ -#include <malloc.h> #include <linux/device.h> #include <linux/kernel.h> #endif #include <linux/mtd/spinand.h> #define SPINAND_MFR_GIGADEVICE 0xC8 + #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4) #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4) @@ -22,8 +22,12 @@ #define GD5FXGQXXEXXG_REG_STATUS2 0xf0 -/* Q4 devices, QUADIO: Dummy bytes valid for 1 and 2 GBit variants */ -static SPINAND_OP_VARIANTS(gd5fxgq4_read_cache_variants, +#define GD5FXGQ4UXFXXG_STATUS_ECC_MASK (7 << 4) +#define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS (0 << 4) +#define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS (1 << 4) +#define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR (7 << 4) + +static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), @@ -31,14 +35,13 @@ static SPINAND_OP_VARIANTS(gd5fxgq4_read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); -/* Q5 devices, QUADIO: Dummy bytes only valid for 1 GBit variants */ -static SPINAND_OP_VARIANTS(gd5f1gq5_read_cache_variants, - SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), +static SPINAND_OP_VARIANTS(read_cache_variants_f, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0)); static SPINAND_OP_VARIANTS(write_cache_variants, SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), @@ -48,7 +51,65 @@ static SPINAND_OP_VARIANTS(update_cache_variants, SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0)); -static int gd5fxgqxxexxg_ooblayout_ecc(struct mtd_info *mtd, int section, +static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 8; + region->length = 8; + + return 0; +} + +static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + if (section) { + region->offset = 16 * section; + region->length = 8; + } else { + /* section 0 has one byte reserved for bad block mark */ + region->offset = 1; + region->length = 7; + } + return 0; +} + +static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { + .ecc = gd5fxgq4xa_ooblayout_ecc, + .rfree = gd5fxgq4xa_ooblayout_free, +}; + +static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: + /* 1-7 bits are flipped. return the maximum. */ + return 7; + + case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: + return 8; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + +static int gd5fxgqx_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { if (section) @@ -60,7 +121,7 @@ static int gd5fxgqxxexxg_ooblayout_ecc(struct mtd_info *mtd, int section, return 0; } -static int gd5fxgqxxexxg_ooblayout_free(struct mtd_info *mtd, int section, +static int gd5fxgqx_variant2_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { if (section) @@ -73,7 +134,42 @@ static int gd5fxgqxxexxg_ooblayout_free(struct mtd_info *mtd, int section, return 0; } -static int gd5fxgq4xexxg_ecc_get_status(struct spinand_device *spinand, +/* Valid for Q4/Q5 and Q6 (untested) devices */ +static const struct mtd_ooblayout_ops gd5fxgqx_variant2_ooblayout = { + .ecc = gd5fxgqx_variant2_ooblayout_ecc, + .rfree = gd5fxgqx_variant2_ooblayout_free, +}; + +static int gd5fxgq4xc_ooblayout_256_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 128; + oobregion->length = 128; + + return 0; +} + +static int gd5fxgq4xc_ooblayout_256_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 1; + oobregion->length = 127; + + return 0; +} + +static const struct mtd_ooblayout_ops gd5fxgq4xc_oob_256_ops = { + .ecc = gd5fxgq4xc_ooblayout_256_ecc, + .rfree = gd5fxgq4xc_ooblayout_256_free, +}; + +static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, u8 status) { u8 status2; @@ -152,59 +248,116 @@ static int gd5fxgq5xexxg_ecc_get_status(struct spinand_device *spinand, return -EINVAL; } -static const struct mtd_ooblayout_ops gd5fxgqxxexxg_ooblayout = { - .ecc = gd5fxgqxxexxg_ooblayout_ecc, - .rfree = gd5fxgqxxexxg_ooblayout_free, -}; +static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) { + case GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS: + return 0; + + case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS: + return 3; + + case GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */ + return ((status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) >> 4) + 2; + } + + return -EINVAL; +} static const struct spinand_info gigadevice_spinand_table[] = { - SPINAND_INFO("GD5F1GQ4UExxG", 0xd1, - NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + SPINAND_INFO("GD5F1GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), + SPINAND_INFO("GD5F2GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf2), + NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&gd5fxgq4_read_cache_variants, + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - 0, - SPINAND_ECCINFO(&gd5fxgqxxexxg_ooblayout, - gd5fxgq4xexxg_ecc_get_status)), - SPINAND_INFO("GD5F1GQ5UExxG", 0x51, - NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), + SPINAND_INFO("GD5F4GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4), + NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), + SPINAND_INFO("GD5F4GQ4RC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xa4, 0x68), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgq4xc_oob_256_ops, + gd5fxgq4ufxxg_ecc_get_status)), + SPINAND_INFO("GD5F4GQ4UC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb4, 0x68), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgq4xc_oob_256_ops, + gd5fxgq4ufxxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ4UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ4UFxxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4ufxxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ5UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(4, 512), - SPINAND_INFO_OP_VARIANTS(&gd5f1gq5_read_cache_variants, + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - 0, - SPINAND_ECCINFO(&gd5fxgqxxexxg_ooblayout, + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, gd5fxgq5xexxg_ecc_get_status)), }; -static int gigadevice_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * For GD NANDs, There is an address byte needed to shift in before IDs - * are read out, so the first byte in raw_id is dummy. - */ - if (id[1] != SPINAND_MFR_GIGADEVICE) - return 0; - - ret = spinand_match_and_init(spinand, gigadevice_spinand_table, - ARRAY_SIZE(gigadevice_spinand_table), - id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { - .detect = gigadevice_spinand_detect, }; const struct spinand_manufacturer gigadevice_spinand_manufacturer = { .id = SPINAND_MFR_GIGADEVICE, .name = "GigaDevice", + .chips = gigadevice_spinand_table, + .nchips = ARRAY_SIZE(gigadevice_spinand_table), .ops = &gigadevice_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 6d643a8000d..86bffc2800b 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -6,7 +6,6 @@ */ #ifndef __UBOOT__ -#include <malloc.h> #include <linux/device.h> #include <linux/kernel.h> #endif @@ -16,7 +15,6 @@ #define SPINAND_MFR_MACRONIX 0xC2 #define MACRONIX_ECCSR_MASK 0x0F - static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), @@ -62,7 +60,6 @@ static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr) SPI_MEM_OP_DATA_IN(1, eccsr, 1)); int ret = spi_mem_exec_op(spinand->slave, &op); - if (ret) return ret; @@ -105,8 +102,9 @@ static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info macronix_spinand_table[] = { - SPINAND_INFO("MX35LF1GE4AB", 0x12, - NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + SPINAND_INFO("MX35LF1GE4AB", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(4, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -114,16 +112,65 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), - SPINAND_INFO("MX35LF2GE4AB", 0x22, - NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1), + SPINAND_INFO("MX35LF2GE4AB", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x22), + NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(4, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), - SPINAND_INFO("MX35UF4GE4AD", 0xb7, - NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + SPINAND_INFO("MX35LF2GE4AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x26), + NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35LF4GE4AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37), + NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35LF1G24AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_INFO("MX35LF2G24AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_INFO("MX35LF4G24AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_INFO("MX31LF1GE4BC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -131,8 +178,9 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), - SPINAND_INFO("MX35UF2GE4AD", 0xa6, - NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), + SPINAND_INFO("MX31UF1GE4BC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9e), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -140,8 +188,10 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), - SPINAND_INFO("MX35UF2GE4AC", 0xa2, - NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), + + SPINAND_INFO("MX35LF2G14AC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x20), + NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(4, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -149,8 +199,9 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), - SPINAND_INFO("MX35UF1GE4AD", 0x96, - NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + SPINAND_INFO("MX35UF4G24AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb5), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -158,8 +209,89 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), - SPINAND_INFO("MX35UF1GE4AC", 0x92, - NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + SPINAND_INFO("MX35UF4GE4AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35UF2G14AC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa0), + NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35UF2G24AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa4), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35UF2GE4AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35UF2GE4AC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa2), + NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35UF1G14AC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x90), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35UF1G24AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x94), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35UF1GE4AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35UF1GE4AC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(4, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -170,33 +302,13 @@ static const struct spinand_info macronix_spinand_table[] = { }; -static int macronix_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Macronix SPI NAND read ID needs a dummy byte, so the first byte in - * raw_id is garbage. - */ - if (id[1] != SPINAND_MFR_MACRONIX) - return 0; - - ret = spinand_match_and_init(spinand, macronix_spinand_table, - ARRAY_SIZE(macronix_spinand_table), - id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { - .detect = macronix_spinand_detect, }; const struct spinand_manufacturer macronix_spinand_manufacturer = { .id = SPINAND_MFR_MACRONIX, .name = "Macronix", + .chips = macronix_spinand_table, + .nchips = ARRAY_SIZE(macronix_spinand_table), .ops = ¯onix_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index 6bacf14aafe..b538213ed8e 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -7,11 +7,9 @@ */ #ifndef __UBOOT__ -#include <malloc.h> #include <linux/device.h> #include <linux/kernel.h> #endif -#include <linux/bitops.h> #include <linux/mtd/spinand.h> #define SPINAND_MFR_MICRON 0x2c @@ -32,7 +30,7 @@ #define MICRON_SELECT_DIE(x) ((x) << 6) -static SPINAND_OP_VARIANTS(read_cache_variants, +static SPINAND_OP_VARIANTS(quadio_read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), @@ -40,14 +38,27 @@ static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); -static SPINAND_OP_VARIANTS(write_cache_variants, +static SPINAND_OP_VARIANTS(x4_write_cache_variants, SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), SPINAND_PROG_LOAD(true, 0, NULL, 0)); -static SPINAND_OP_VARIANTS(update_cache_variants, +static SPINAND_OP_VARIANTS(x4_update_cache_variants, SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0)); +/* Micron MT29F2G01AAAED Device */ +static SPINAND_OP_VARIANTS(x4_read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(x1_write_cache_variants, + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(x1_update_cache_variants, + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + static int micron_8_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { @@ -78,6 +89,47 @@ static const struct mtd_ooblayout_ops micron_8_ooblayout = { .rfree = micron_8_ooblayout_free, }; +static int micron_4_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + + if (section >= spinand->base.memorg.pagesize / + mtd->ecc_step_size) + return -ERANGE; + + region->offset = (section * 16) + 8; + region->length = 8; + + return 0; +} + +static int micron_4_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + + if (section >= spinand->base.memorg.pagesize / + mtd->ecc_step_size) + return -ERANGE; + + if (section) { + region->offset = 16 * section; + region->length = 8; + } else { + /* section 0 has two bytes reserved for the BBM */ + region->offset = 2; + region->length = 6; + } + + return 0; +} + +static const struct mtd_ooblayout_ops micron_4_ooblayout = { + .ecc = micron_4_ooblayout_ecc, + .rfree = micron_4_ooblayout_free, +}; + static int micron_select_target(struct spinand_device *spinand, unsigned int target) { @@ -120,120 +172,119 @@ static int micron_8_ecc_get_status(struct spinand_device *spinand, static const struct spinand_info micron_spinand_table[] = { /* M79A 2Gb 3.3V */ - SPINAND_INFO("MT29F2G01ABAGD", 0x24, - NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1), + SPINAND_INFO("MT29F2G01ABAGD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), /* M79A 2Gb 1.8V */ - SPINAND_INFO("MT29F2G01ABBGD", 0x25, - NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1), + SPINAND_INFO("MT29F2G01ABBGD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), /* M78A 1Gb 3.3V */ - SPINAND_INFO("MT29F1G01ABAFD", 0x14, - NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + SPINAND_INFO("MT29F1G01ABAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), /* M78A 1Gb 1.8V */ - SPINAND_INFO("MT29F1G01ABAFD", 0x15, - NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + SPINAND_INFO("MT29F1G01ABAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), /* M79A 4Gb 3.3V */ - SPINAND_INFO("MT29F4G01ADAGD", 0x36, - NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 2), + SPINAND_INFO("MT29F4G01ADAGD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36), + NAND_MEMORG(1, 2048, 128, 64, 2048, 80, 2, 1, 2), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status), SPINAND_SELECT_TARGET(micron_select_target)), /* M70A 4Gb 3.3V */ - SPINAND_INFO("MT29F4G01ABAFD", 0x34, - NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + SPINAND_INFO("MT29F4G01ABAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), SPINAND_HAS_CR_FEAT_BIT, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), /* M70A 4Gb 1.8V */ - SPINAND_INFO("MT29F4G01ABBFD", 0x35, - NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + SPINAND_INFO("MT29F4G01ABBFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), SPINAND_HAS_CR_FEAT_BIT, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), /* M70A 8Gb 3.3V */ - SPINAND_INFO("MT29F8G01ADAFD", 0x46, - NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 2), + SPINAND_INFO("MT29F8G01ADAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), SPINAND_HAS_CR_FEAT_BIT, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status), SPINAND_SELECT_TARGET(micron_select_target)), /* M70A 8Gb 1.8V */ - SPINAND_INFO("MT29F8G01ADBFD", 0x47, - NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 2), + SPINAND_INFO("MT29F8G01ADBFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), SPINAND_HAS_CR_FEAT_BIT, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status), SPINAND_SELECT_TARGET(micron_select_target)), + /* M69A 2Gb 3.3V */ + SPINAND_INFO("MT29F2G01AAAED", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9F), + NAND_MEMORG(1, 2048, 64, 64, 2048, 80, 2, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&x4_read_cache_variants, + &x1_write_cache_variants, + &x1_update_cache_variants), + 0, + SPINAND_ECCINFO(µn_4_ooblayout, NULL)), }; -static int micron_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Micron SPI NAND read ID need a dummy byte, - * so the first byte in raw_id is dummy. - */ - if (id[1] != SPINAND_MFR_MICRON) - return 0; - - ret = spinand_match_and_init(spinand, micron_spinand_table, - ARRAY_SIZE(micron_spinand_table), id[2]); - if (ret) - return ret; - - return 1; -} - static int micron_spinand_init(struct spinand_device *spinand) { /* @@ -248,12 +299,13 @@ static int micron_spinand_init(struct spinand_device *spinand) } static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { - .detect = micron_spinand_detect, .init = micron_spinand_init, }; const struct spinand_manufacturer micron_spinand_manufacturer = { .id = SPINAND_MFR_MICRON, .name = "Micron", + .chips = micron_spinand_table, + .nchips = ARRAY_SIZE(micron_spinand_table), .ops = µn_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/paragon.c b/drivers/mtd/nand/spi/paragon.c new file mode 100644 index 00000000000..0c123930f14 --- /dev/null +++ b/drivers/mtd/nand/spi/paragon.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Jeff Kletsky + * + * Author: Jeff Kletsky <git-commits@allycomm.com> + */ + +#ifndef __UBOOT__ +#include <linux/device.h> +#include <linux/kernel.h> +#endif +#include <linux/mtd/spinand.h> + + +#define SPINAND_MFR_PARAGON 0xa1 + + +#define PN26G0XA_STATUS_ECC_BITMASK (3 << 4) + +#define PN26G0XA_STATUS_ECC_NONE_DETECTED (0 << 4) +#define PN26G0XA_STATUS_ECC_1_7_CORRECTED (1 << 4) +#define PN26G0XA_STATUS_ECC_ERRORED (2 << 4) +#define PN26G0XA_STATUS_ECC_8_CORRECTED (3 << 4) + + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + + +static int pn26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = 6 + (15 * section); /* 4 BBM + 2 user bytes */ + region->length = 13; + + return 0; +} + +static int pn26g0xa_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 4) + return -ERANGE; + + if (section == 4) { + region->offset = 64; + region->length = 64; + } else { + region->offset = 4 + (15 * section); + region->length = 2; + } + + return 0; +} + +static int pn26g0xa_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & PN26G0XA_STATUS_ECC_BITMASK) { + case PN26G0XA_STATUS_ECC_NONE_DETECTED: + return 0; + + case PN26G0XA_STATUS_ECC_1_7_CORRECTED: + return 7; /* Return upper limit by convention */ + + case PN26G0XA_STATUS_ECC_8_CORRECTED: + return 8; + + case PN26G0XA_STATUS_ECC_ERRORED: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + +static const struct mtd_ooblayout_ops pn26g0xa_ooblayout = { + .ecc = pn26g0xa_ooblayout_ecc, + .rfree = pn26g0xa_ooblayout_free, +}; + + +static const struct spinand_info paragon_spinand_table[] = { + SPINAND_INFO("PN26G01A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe1), + NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&pn26g0xa_ooblayout, + pn26g0xa_ecc_get_status)), + SPINAND_INFO("PN26G02A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe2), + NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&pn26g0xa_ooblayout, + pn26g0xa_ecc_get_status)), +}; + +static const struct spinand_manufacturer_ops paragon_spinand_manuf_ops = { +}; + +const struct spinand_manufacturer paragon_spinand_manufacturer = { + .id = SPINAND_MFR_PARAGON, + .name = "Paragon", + .chips = paragon_spinand_table, + .nchips = ARRAY_SIZE(paragon_spinand_table), + .ops = ¶gon_spinand_manuf_ops, +}; diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index c2cd3b426b3..b9908e79271 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -7,13 +7,13 @@ */ #ifndef __UBOOT__ -#include <malloc.h> #include <linux/device.h> #include <linux/kernel.h> #endif #include <linux/bug.h> #include <linux/mtd/spinand.h> +/* Kioxia is new name of Toshiba memory. */ #define SPINAND_MFR_TOSHIBA 0x98 #define TOSH_STATUS_ECC_HAS_BITFLIPS_T (3 << 4) @@ -31,7 +31,7 @@ static SPINAND_OP_VARIANTS(update_cache_x4_variants, SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0)); -/** +/* * Backward compatibility for 1st generation Serial NAND devices * which don't support Quad Program Load operation. */ @@ -42,7 +42,7 @@ static SPINAND_OP_VARIANTS(update_cache_variants, SPINAND_PROG_LOAD(false, 0, NULL, 0)); static int tx58cxgxsxraix_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *region) + struct mtd_oob_region *region) { if (section > 0) return -ERANGE; @@ -54,7 +54,7 @@ static int tx58cxgxsxraix_ooblayout_ecc(struct mtd_info *mtd, int section, } static int tx58cxgxsxraix_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *region) + struct mtd_oob_region *region) { if (section > 0) return -ERANGE; @@ -72,7 +72,7 @@ static const struct mtd_ooblayout_ops tx58cxgxsxraix_ooblayout = { }; static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, - u8 status) + u8 status) { struct nand_device *nand = spinand_to_nand(spinand); u8 mbf = 0; @@ -111,8 +111,9 @@ static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, static const struct spinand_info toshiba_spinand_table[] = { /* 3.3V 1Gb (1st generation) */ - SPINAND_INFO("TC58CVG0S3HRAIG", 0xC2, - NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + SPINAND_INFO("TC58CVG0S3HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -121,8 +122,9 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), /* 3.3V 2Gb (1st generation) */ - SPINAND_INFO("TC58CVG1S3HRAIG", 0xCB, - NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), + SPINAND_INFO("TC58CVG1S3HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -131,8 +133,9 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), /* 3.3V 4Gb (1st generation) */ - SPINAND_INFO("TC58CVG2S0HRAIG", 0xCD, - NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + SPINAND_INFO("TC58CVG2S0HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -141,8 +144,9 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), /* 1.8V 1Gb (1st generation) */ - SPINAND_INFO("TC58CYG0S3HRAIG", 0xB2, - NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + SPINAND_INFO("TC58CYG0S3HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -151,8 +155,9 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), /* 1.8V 2Gb (1st generation) */ - SPINAND_INFO("TC58CYG1S3HRAIG", 0xBB, - NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), + SPINAND_INFO("TC58CYG1S3HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -161,8 +166,9 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), /* 1.8V 4Gb (1st generation) */ - SPINAND_INFO("TC58CYG2S0HRAIG", 0xBD, - NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + SPINAND_INFO("TC58CYG2S0HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -176,8 +182,9 @@ static const struct spinand_info toshiba_spinand_table[] = { * QE_BIT. */ /* 3.3V 1Gb (2nd generation) */ - SPINAND_INFO("TC58CVG0S3HRAIJ", 0xE2, - NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + SPINAND_INFO("TC58CVG0S3HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_x4_variants, @@ -186,8 +193,9 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), /* 3.3V 2Gb (2nd generation) */ - SPINAND_INFO("TC58CVG1S3HRAIJ", 0xEB, - NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), + SPINAND_INFO("TC58CVG1S3HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xEB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_x4_variants, @@ -196,8 +204,9 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), /* 3.3V 4Gb (2nd generation) */ - SPINAND_INFO("TC58CVG2S0HRAIJ", 0xED, - NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + SPINAND_INFO("TC58CVG2S0HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_x4_variants, @@ -206,8 +215,9 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), /* 3.3V 8Gb (2nd generation) */ - SPINAND_INFO("TH58CVG3S0HRAIJ", 0xE4, - NAND_MEMORG(1, 4096, 256, 64, 4096, 1, 1, 1), + SPINAND_INFO("TH58CVG3S0HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4), + NAND_MEMORG(1, 4096, 256, 64, 4096, 80, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_x4_variants, @@ -216,8 +226,9 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), /* 1.8V 1Gb (2nd generation) */ - SPINAND_INFO("TC58CYG0S3HRAIJ", 0xD2, - NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + SPINAND_INFO("TC58CYG0S3HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_x4_variants, @@ -226,8 +237,9 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), /* 1.8V 2Gb (2nd generation) */ - SPINAND_INFO("TC58CYG1S3HRAIJ", 0xDB, - NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), + SPINAND_INFO("TC58CYG1S3HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_x4_variants, @@ -236,8 +248,9 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), /* 1.8V 4Gb (2nd generation) */ - SPINAND_INFO("TC58CYG2S0HRAIJ", 0xDD, - NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + SPINAND_INFO("TC58CYG2S0HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_x4_variants, @@ -246,8 +259,9 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), /* 1.8V 8Gb (2nd generation) */ - SPINAND_INFO("TH58CYG3S0HRAIJ", 0xD4, - NAND_MEMORG(1, 4096, 256, 64, 4096, 1, 1, 1), + SPINAND_INFO("TH58CYG3S0HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD4), + NAND_MEMORG(1, 4096, 256, 64, 4096, 80, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_x4_variants, @@ -257,33 +271,13 @@ static const struct spinand_info toshiba_spinand_table[] = { tx58cxgxsxraix_ecc_get_status)), }; -static int toshiba_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Toshiba SPI NAND read ID needs a dummy byte, - * so the first byte in id is garbage. - */ - if (id[1] != SPINAND_MFR_TOSHIBA) - return 0; - - ret = spinand_match_and_init(spinand, toshiba_spinand_table, - ARRAY_SIZE(toshiba_spinand_table), - id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { - .detect = toshiba_spinand_detect, }; const struct spinand_manufacturer toshiba_spinand_manufacturer = { .id = SPINAND_MFR_TOSHIBA, .name = "Toshiba", + .chips = toshiba_spinand_table, + .nchips = ARRAY_SIZE(toshiba_spinand_table), .ops = &toshiba_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index c119486efb7..dd4ed257a83 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -8,11 +8,10 @@ */ #ifndef __UBOOT__ -#include <malloc.h> #include <linux/device.h> #include <linux/kernel.h> #endif -#include <linux/bitops.h> +#include <linux/bug.h> #include <linux/mtd/spinand.h> #define SPINAND_MFR_WINBOND 0xEF @@ -78,9 +77,76 @@ static int w25m02gv_select_target(struct spinand_device *spinand, return spi_mem_exec_op(spinand->slave, &op); } +static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = 64 + (16 * section); + region->length = 13; + + return 0; +} + +static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 2; + region->length = 14; + + return 0; +} + +static const struct mtd_ooblayout_ops w25n02kv_ooblayout = { + .ecc = w25n02kv_ooblayout_ecc, + .rfree = w25n02kv_ooblayout_free, +}; + +static int w25n02kv_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + struct nand_device *nand = spinand_to_nand(spinand); + u8 mbf = 0; + struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf); + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + case STATUS_ECC_HAS_BITFLIPS: + /* + * Let's try to retrieve the real maximum number of bitflips + * in order to avoid forcing the wear-leveling layer to move + * data around if it's not necessary. + */ + if (spi_mem_exec_op(spinand->slave, &op)) + return nand->eccreq.strength; + + mbf >>= 4; + + if (WARN_ON(mbf > nand->eccreq.strength || !mbf)) + return nand->eccreq.strength; + + return mbf; + + default: + break; + } + + return -EINVAL; +} + static const struct spinand_info winbond_spinand_table[] = { - SPINAND_INFO("W25M02GV", 0xAB, - NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2), + SPINAND_INFO("W25M02GV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2), NAND_ECCREQ(1, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, @@ -88,41 +154,26 @@ static const struct spinand_info winbond_spinand_table[] = { 0, SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), SPINAND_SELECT_TARGET(w25m02gv_select_target)), - SPINAND_INFO("W25N01GV", 0xAA, - NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + SPINAND_INFO("W25N01GV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(1, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), 0, SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), + SPINAND_INFO("W25N02KV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)), }; -/** - * winbond_spinand_detect - initialize device related part in spinand_device - * struct if it is a Winbond device. - * @spinand: SPI NAND device structure - */ -static int winbond_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Winbond SPI NAND read ID need a dummy byte, - * so the first byte in raw_id is dummy. - */ - if (id[1] != SPINAND_MFR_WINBOND) - return 0; - - ret = spinand_match_and_init(spinand, winbond_spinand_table, - ARRAY_SIZE(winbond_spinand_table), id[2]); - if (ret) - return ret; - - return 1; -} - static int winbond_spinand_init(struct spinand_device *spinand) { struct nand_device *nand = spinand_to_nand(spinand); @@ -142,12 +193,13 @@ static int winbond_spinand_init(struct spinand_device *spinand) } static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { - .detect = winbond_spinand_detect, .init = winbond_spinand_init, }; const struct spinand_manufacturer winbond_spinand_manufacturer = { .id = SPINAND_MFR_WINBOND, .name = "Winbond", + .chips = winbond_spinand_table, + .nchips = ARRAY_SIZE(winbond_spinand_table), .ops = &winbond_spinand_manuf_ops, }; diff --git a/drivers/mtd/onenand/onenand_uboot.c b/drivers/mtd/onenand/onenand_uboot.c index 04791df69bb..ecacabefadc 100644 --- a/drivers/mtd/onenand/onenand_uboot.c +++ b/drivers/mtd/onenand/onenand_uboot.c @@ -44,14 +44,12 @@ void onenand_init(void) puts("Flex-"); puts("OneNAND: "); -#ifdef CONFIG_MTD /* * Add MTD device so that we can reference it later * via the mtdcore infrastructure (e.g. ubi). */ onenand_mtd.name = dev_name; add_mtd_device(&onenand_mtd); -#endif } print_size(onenand_chip.chipsize, "\n"); } diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index a9617c6c58c..732b0760452 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -82,6 +82,7 @@ if SPI_FLASH config BOOTDEV_SPI_FLASH bool "SPI Flash bootdev support" + depends on BOOTSTD help Enable a boot device for SPI flash. This allows reading a script from SPI flash so that it can be used to boot an Operating System. @@ -107,7 +108,6 @@ config SPI_FLASH_SMART_HWCAPS config SPI_NOR_BOOT_SOFT_RESET_EXT_INVERT bool "Command extension type is INVERT for Software Reset on boot" - default n help Because of SFDP information can not be get before boot. So define command extension type is INVERT when Software Reset on boot only. @@ -134,6 +134,13 @@ config SPI_FLASH_BAR Bank/Extended address registers are used to access the flash which has size > 16MiB in 3-byte addressing. +config SPI_FLASH_LOCK + bool "Enable the Locking feature" + default y + help + Enable the SPI flash lock support. By default this is set to y. + If you intend not to use the lock support you should say n here. + config SPI_FLASH_UNLOCK_ALL bool "Unlock the entire SPI flash on u-boot startup" default y diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index db20feb4dae..9a1801ba93d 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -1100,6 +1100,7 @@ static int spansion_erase_non_uniform(struct spi_nor *nor, u32 addr, } #endif +#if defined(CONFIG_SPI_FLASH_LOCK) #if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST) /* Write status register and ensure bits in mask match written values */ static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask) @@ -1387,6 +1388,7 @@ static int stm_is_unlocked(struct spi_nor *nor, loff_t ofs, uint64_t len) return stm_is_unlocked_sr(nor, ofs, len, status); } #endif /* CONFIG_SPI_FLASH_STMICRO */ +#endif static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) { @@ -1462,6 +1464,7 @@ read_err: return ret; } +#if defined(CONFIG_SPI_FLASH_LOCK) #ifdef CONFIG_SPI_FLASH_SST /* * sst26 flash series has its own block protection implementation: @@ -1730,6 +1733,8 @@ sst_write_err: return ret; } #endif +#endif + /* * Write an address range to the nor chip. Data must be written in * FLASH_PAGESIZE chunks. The address range may be any size provided @@ -4104,6 +4109,7 @@ int spi_nor_scan(struct spi_nor *nor) mtd->_read = spi_nor_read; mtd->_write = spi_nor_write; +#if defined(CONFIG_SPI_FLASH_LOCK) #if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST) /* NOR protection support for STmicro/Micron chips and similar */ if (JEDEC_MFR(info) == SNOR_MFR_ST || @@ -4127,7 +4133,7 @@ int spi_nor_scan(struct spi_nor *nor) nor->flash_is_unlocked = sst26_is_unlocked; } #endif - +#endif if (info->flags & USE_FSR) nor->flags |= SNOR_F_USE_FSR; if (info->flags & SPI_NOR_HAS_TB) diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c index b03dd1cd08e..3cb132dcffc 100644 --- a/drivers/mtd/spi/spi-nor-ids.c +++ b/drivers/mtd/spi/spi-nor-ids.c @@ -3,7 +3,7 @@ * * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. * Copyright (C) 2016 Jagan Teki <jagan@openedev.com> - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ */ #include <common.h> @@ -532,6 +532,7 @@ const struct flash_info spi_nor_ids[] = { { INFO("XM25QH64A", 0x207017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { INFO("XM25QH64C", 0x204017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { INFO("XM25QH128A", 0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("XM25QU128C", 0x204118, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, #endif #ifdef CONFIG_SPI_FLASH_XTX /* XTX Technology Limited */ diff --git a/drivers/mux/mux-uclass.c b/drivers/mux/mux-uclass.c index 8870305313a..c98576ceb81 100644 --- a/drivers/mux/mux-uclass.c +++ b/drivers/mux/mux-uclass.c @@ -7,7 +7,7 @@ * Copyright (C) 2017 Axentia Technologies AB * Author: Peter Rosin <peda@axentia.se> * - * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2017-2018 Texas Instruments Incorporated - https://www.ti.com/ * Jean-Jacques Hiblot <jjhiblot@ti.com> */ diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 29304fd7775..23ad2c29d0a 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -471,14 +471,6 @@ config SYS_UNIFY_CACHE depends on MCFFEC bool "Invalidate icache during ethernet operations" -config FSLDMAFEC - bool "ColdFire DMA Ethernet Support" - select PHYLIB - select SYS_DISCOVER_PHY - help - This driver supports the network interface units in the - ColdFire family. - config KS8851_MLL bool "Microchip KS8851-MLL controller driver" help @@ -903,6 +895,23 @@ config MEDIATEK_ETH This Driver support MediaTek Ethernet GMAC Say Y to enable support for the MediaTek Ethernet GMAC. +config HIFEMAC_ETH + bool "HiSilicon Fast Ethernet Controller" + select DM_CLK + select DM_RESET + select PHYLIB + help + This driver supports HIFEMAC Ethernet controller found on + HiSilicon SoCs. + +config HIFEMAC_MDIO + bool "HiSilicon Fast Ethernet Controller MDIO interface" + depends on DM_MDIO + select DM_CLK + help + This driver supports the internal MDIO interface of HIFEMAC + Ethernet controller. + config HIGMACV300_ETH bool "HiSilicon Gigabit Ethernet Controller" select DM_RESET diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 1d444f5b4a6..f9aed1646c9 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -37,7 +37,6 @@ obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw.o obj-$(CONFIG_FEC_MXC) += fec_mxc.o obj-$(CONFIG_FMAN_ENET) += fm/ obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o -obj-$(CONFIG_FSLDMAFEC) += fsl_mcdmafec.o mcfmii.o obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o obj-$(CONFIG_FSL_LS_MDIO) += fsl_ls_mdio.o obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/ @@ -47,6 +46,8 @@ obj-$(CONFIG_FSL_PFE) += pfe_eth/ obj-$(CONFIG_FTGMAC100) += ftgmac100.o obj-$(CONFIG_FTMAC100) += ftmac100.o obj-$(CONFIG_GMAC_ROCKCHIP) += gmac_rockchip.o +obj-$(CONFIG_HIFEMAC_ETH) += hifemac.o +obj-$(CONFIG_HIFEMAC_MDIO) += hifemac_mdio.o obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o obj-$(CONFIG_KSZ9477) += ksz9477.o diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 20b86e74cec..a174344b3ef 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -19,6 +19,7 @@ #include <net.h> #include <pci.h> #include <reset.h> +#include <phys2bus.h> #include <asm/cache.h> #include <dm/device_compat.h> #include <dm/device-internal.h> @@ -232,8 +233,10 @@ static void tx_descs_init(struct dw_eth_dev *priv) for (idx = 0; idx < CFG_TX_DESCR_NUM; idx++) { desc_p = &desc_table_p[idx]; - desc_p->dmamac_addr = (ulong)&txbuffs[idx * CFG_ETH_BUFSIZE]; - desc_p->dmamac_next = (ulong)&desc_table_p[idx + 1]; + desc_p->dmamac_addr = dev_phys_to_bus(priv->dev, + (ulong)&txbuffs[idx * CFG_ETH_BUFSIZE]); + desc_p->dmamac_next = dev_phys_to_bus(priv->dev, + (ulong)&desc_table_p[idx + 1]); #if defined(CONFIG_DW_ALTDESCRIPTOR) desc_p->txrx_status &= ~(DESC_TXSTS_TXINT | DESC_TXSTS_TXLAST | @@ -251,14 +254,15 @@ static void tx_descs_init(struct dw_eth_dev *priv) } /* Correcting the last pointer of the chain */ - desc_p->dmamac_next = (ulong)&desc_table_p[0]; + desc_p->dmamac_next = dev_phys_to_bus(priv->dev, (ulong)&desc_table_p[0]); /* Flush all Tx buffer descriptors at once */ flush_dcache_range((ulong)priv->tx_mac_descrtable, (ulong)priv->tx_mac_descrtable + sizeof(priv->tx_mac_descrtable)); - writel((ulong)&desc_table_p[0], &dma_p->txdesclistaddr); + writel(dev_phys_to_bus(priv->dev, (ulong)&desc_table_p[0]), + &dma_p->txdesclistaddr); priv->tx_currdescnum = 0; } @@ -280,8 +284,10 @@ static void rx_descs_init(struct dw_eth_dev *priv) for (idx = 0; idx < CFG_RX_DESCR_NUM; idx++) { desc_p = &desc_table_p[idx]; - desc_p->dmamac_addr = (ulong)&rxbuffs[idx * CFG_ETH_BUFSIZE]; - desc_p->dmamac_next = (ulong)&desc_table_p[idx + 1]; + desc_p->dmamac_addr = dev_phys_to_bus(priv->dev, + (ulong)&rxbuffs[idx * CFG_ETH_BUFSIZE]); + desc_p->dmamac_next = dev_phys_to_bus(priv->dev, + (ulong)&desc_table_p[idx + 1]); desc_p->dmamac_cntl = (MAC_MAX_FRAME_SZ & DESC_RXCTRL_SIZE1MASK) | @@ -291,14 +297,15 @@ static void rx_descs_init(struct dw_eth_dev *priv) } /* Correcting the last pointer of the chain */ - desc_p->dmamac_next = (ulong)&desc_table_p[0]; + desc_p->dmamac_next = dev_phys_to_bus(priv->dev, (ulong)&desc_table_p[0]); /* Flush all Rx buffer descriptors at once */ flush_dcache_range((ulong)priv->rx_mac_descrtable, (ulong)priv->rx_mac_descrtable + sizeof(priv->rx_mac_descrtable)); - writel((ulong)&desc_table_p[0], &dma_p->rxdesclistaddr); + writel(dev_phys_to_bus(priv->dev, (ulong)&desc_table_p[0]), + &dma_p->rxdesclistaddr); priv->rx_currdescnum = 0; } @@ -448,7 +455,7 @@ static int _dw_eth_send(struct dw_eth_dev *priv, void *packet, int length) ulong desc_start = (ulong)desc_p; ulong desc_end = desc_start + roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN); - ulong data_start = desc_p->dmamac_addr; + ulong data_start = dev_bus_to_phys(priv->dev, desc_p->dmamac_addr); ulong data_end = data_start + roundup(length, ARCH_DMA_MINALIGN); /* * Strictly we only need to invalidate the "txrx_status" field @@ -515,7 +522,7 @@ static int _dw_eth_recv(struct dw_eth_dev *priv, uchar **packetp) ulong desc_start = (ulong)desc_p; ulong desc_end = desc_start + roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN); - ulong data_start = desc_p->dmamac_addr; + ulong data_start = dev_bus_to_phys(priv->dev, desc_p->dmamac_addr); ulong data_end; /* Invalidate entire buffer descriptor */ @@ -532,7 +539,8 @@ static int _dw_eth_recv(struct dw_eth_dev *priv, uchar **packetp) /* Invalidate received data */ data_end = data_start + roundup(length, ARCH_DMA_MINALIGN); invalidate_dcache_range(data_start, data_end); - *packetp = (uchar *)(ulong)desc_p->dmamac_addr; + *packetp = (uchar *)(ulong)dev_bus_to_phys(priv->dev, + desc_p->dmamac_addr); } return length; @@ -757,6 +765,7 @@ int designware_eth_probe(struct udevice *dev) goto mdio_err; } priv->bus = miiphy_get_dev_by_name(dev->name); + priv->dev = dev; ret = dw_phy_init(priv, dev); debug("%s, ret=%d\n", __func__, ret); diff --git a/drivers/net/designware.h b/drivers/net/designware.h index 9da4e902cb0..918a38615ad 100644 --- a/drivers/net/designware.h +++ b/drivers/net/designware.h @@ -241,6 +241,7 @@ struct dw_eth_dev { int clock_count; /* number of clock in clock list */ #endif + struct udevice *dev; struct phy_device *phydev; struct mii_dev *bus; }; diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 18466cfe257..a4e3698c600 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -746,6 +746,7 @@ static int eqos_start(struct udevice *dev) u32 val, tx_fifo_sz, rx_fifo_sz, tqs, rqs, pbl; ulong last_rx_desc; ulong desc_pad; + ulong addr64; debug("%s(dev=%p):\n", __func__, dev); @@ -1039,25 +1040,25 @@ static int eqos_start(struct udevice *dev) for (i = 0; i < EQOS_DESCRIPTORS_RX; i++) { struct eqos_desc *rx_desc = eqos_get_desc(eqos, i, true); - rx_desc->des0 = (u32)(ulong)(eqos->rx_dma_buf + - (i * EQOS_MAX_PACKET_SIZE)); + + addr64 = (ulong)(eqos->rx_dma_buf + (i * EQOS_MAX_PACKET_SIZE)); + rx_desc->des0 = lower_32_bits(addr64); + rx_desc->des1 = upper_32_bits(addr64); rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V; mb(); eqos->config->ops->eqos_flush_desc(rx_desc); - eqos->config->ops->eqos_inval_buffer(eqos->rx_dma_buf + - (i * EQOS_MAX_PACKET_SIZE), - EQOS_MAX_PACKET_SIZE); + eqos->config->ops->eqos_inval_buffer((void *)addr64, EQOS_MAX_PACKET_SIZE); } - writel(0, &eqos->dma_regs->ch0_txdesc_list_haddress); - writel((ulong)eqos_get_desc(eqos, 0, false), - &eqos->dma_regs->ch0_txdesc_list_address); + addr64 = (ulong)eqos_get_desc(eqos, 0, false); + writel(upper_32_bits(addr64), &eqos->dma_regs->ch0_txdesc_list_haddress); + writel(lower_32_bits(addr64), &eqos->dma_regs->ch0_txdesc_list_address); writel(EQOS_DESCRIPTORS_TX - 1, &eqos->dma_regs->ch0_txdesc_ring_length); - writel(0, &eqos->dma_regs->ch0_rxdesc_list_haddress); - writel((ulong)eqos_get_desc(eqos, 0, true), - &eqos->dma_regs->ch0_rxdesc_list_address); + addr64 = (ulong)eqos_get_desc(eqos, 0, true); + writel(upper_32_bits(addr64), &eqos->dma_regs->ch0_rxdesc_list_haddress); + writel(lower_32_bits(addr64), &eqos->dma_regs->ch0_rxdesc_list_address); writel(EQOS_DESCRIPTORS_RX - 1, &eqos->dma_regs->ch0_rxdesc_ring_length); @@ -1162,8 +1163,8 @@ static int eqos_send(struct udevice *dev, void *packet, int length) eqos->tx_desc_idx++; eqos->tx_desc_idx %= EQOS_DESCRIPTORS_TX; - tx_desc->des0 = (ulong)eqos->tx_dma_buf; - tx_desc->des1 = 0; + tx_desc->des0 = lower_32_bits((ulong)eqos->tx_dma_buf); + tx_desc->des1 = upper_32_bits((ulong)eqos->tx_dma_buf); tx_desc->des2 = length; /* * Make sure that if HW sees the _OWN write below, it will see all the @@ -1234,14 +1235,17 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length) for (idx = eqos->rx_desc_idx - idx_mask; idx <= eqos->rx_desc_idx; idx++) { + ulong addr64; + rx_desc = eqos_get_desc(eqos, idx, true); rx_desc->des0 = 0; + rx_desc->des1 = 0; mb(); eqos->config->ops->eqos_flush_desc(rx_desc); eqos->config->ops->eqos_inval_buffer(packet, length); - rx_desc->des0 = (u32)(ulong)(eqos->rx_dma_buf + - (idx * EQOS_MAX_PACKET_SIZE)); - rx_desc->des1 = 0; + addr64 = (ulong)(eqos->rx_dma_buf + (idx * EQOS_MAX_PACKET_SIZE)); + rx_desc->des0 = lower_32_bits(addr64); + rx_desc->des1 = upper_32_bits(addr64); rx_desc->des2 = 0; /* * Make sure that if HW sees the _OWN write below, diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index 41e6ba760e2..84a2a7cf904 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -5718,15 +5718,9 @@ static const struct eth_ops e1000_eth_ops = { .write_hwaddr = e1000_write_hwaddr, }; -static const struct udevice_id e1000_eth_ids[] = { - { .compatible = "intel,e1000" }, - { } -}; - U_BOOT_DRIVER(eth_e1000) = { .name = "eth_e1000", .id = UCLASS_ETH, - .of_match = e1000_eth_ids, .bind = e1000_eth_bind, .probe = e1000_eth_probe, .ops = &e1000_eth_ops, diff --git a/drivers/net/fm/fm.c b/drivers/net/fm/fm.c index 7dfa8219094..a8caa0f0927 100644 --- a/drivers/net/fm/fm.c +++ b/drivers/net/fm/fm.c @@ -3,7 +3,6 @@ * Copyright 2009-2011 Freescale Semiconductor, Inc. * Dave Liu <daveliu@freescale.com> */ -#include <common.h> #include <env.h> #include <fs_loader.h> #include <image.h> diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 984616fb65c..f5c5057bec1 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -30,6 +30,8 @@ #include <fsl-mc/fsl_qbman_portal.h> #include <fsl-mc/ldpaa_wriop.h> #include <net/ldpaa_eth.h> +#include <asm/arch/cpu.h> +#include <asm/arch-fsl-layerscape/fsl_icid.h> #define MC_RAM_BASE_ADDR_ALIGNMENT (512UL * 1024 * 1024) #define MC_RAM_BASE_ADDR_ALIGNMENT_MASK (~(MC_RAM_BASE_ADDR_ALIGNMENT - 1)) @@ -929,6 +931,114 @@ unsigned long mc_get_dram_block_size(void) return dram_block_size; } +/** + * Populate the device tree with MC reserved memory ranges. + */ +void fdt_reserve_mc_mem(void *blob, u32 mc_icid) +{ + u32 phandle, mc_ph; + int noff, ret, i; + char mem_name[16]; + struct fdt_memory mc_mem_ranges[] = { + { + .start = 0, + .end = 0 + }, + { + .start = CFG_SYS_FSL_MC_BASE, + .end = CFG_SYS_FSL_MC_BASE + CFG_SYS_FSL_MC_SIZE - 1 + }, + { + .start = CFG_SYS_FSL_NI_BASE, + .end = CFG_SYS_FSL_NI_BASE + CFG_SYS_FSL_NI_SIZE - 1 + }, + { + .start = CFG_SYS_FSL_QBMAN_BASE, + .end = CFG_SYS_FSL_QBMAN_BASE + + CFG_SYS_FSL_QBMAN_SIZE - 1 + }, + { + .start = CFG_SYS_FSL_PEBUF_BASE, + .end = CFG_SYS_FSL_PEBUF_BASE + + CFG_SYS_FSL_PEBUF_SIZE - 1 + }, + { + .start = CFG_SYS_FSL_CCSR_BASE, + .end = CFG_SYS_FSL_CCSR_BASE + CFG_SYS_FSL_CCSR_SIZE - 1 + } + }; + + mc_mem_ranges[0].start = gd->arch.resv_ram; + mc_mem_ranges[0].end = mc_mem_ranges[0].start + + mc_get_dram_block_size() - 1; + + for (i = 0; i < ARRAY_SIZE(mc_mem_ranges); i++) { + noff = fdt_node_offset_by_compatible(blob, -1, "fsl,qoriq-mc"); + if (noff < 0) { + printf("WARN: failed to get MC node: %d\n", noff); + return; + } + mc_ph = fdt_get_phandle(blob, noff); + if (!mc_ph) { + mc_ph = fdt_create_phandle(blob, noff); + if (!mc_ph) { + printf("WARN: failed to get MC node phandle\n"); + return; + } + } + + sprintf(mem_name, "mc-mem%d", i); + ret = fdtdec_add_reserved_memory(blob, mem_name, + &mc_mem_ranges[i], NULL, 0, + &phandle, 0); + if (ret < 0) { + printf("ERROR: failed to reserve MC memory: %d\n", ret); + return; + } + + noff = fdt_node_offset_by_phandle(blob, phandle); + if (noff < 0) { + printf("ERROR: failed get resvmem node offset: %d\n", + noff); + return; + } + ret = fdt_setprop_u32(blob, noff, "iommu-addresses", mc_ph); + if (ret < 0) { + printf("ERROR: failed to set 'iommu-addresses': %d\n", + ret); + return; + } + ret = fdt_appendprop_u64(blob, noff, "iommu-addresses", + mc_mem_ranges[i].start); + if (ret < 0) { + printf("ERROR: failed to set 'iommu-addresses': %d\n", + ret); + return; + } + ret = fdt_appendprop_u64(blob, noff, "iommu-addresses", + mc_mem_ranges[i].end - + mc_mem_ranges[i].start + 1); + if (ret < 0) { + printf("ERROR: failed to set 'iommu-addresses': %d\n", + ret); + return; + } + + noff = fdt_node_offset_by_phandle(blob, mc_ph); + if (noff < 0) { + printf("ERROR: failed get MC node offset: %d\n", noff); + return; + } + ret = fdt_appendprop_u32(blob, noff, "memory-region", phandle); + if (ret < 0) { + printf("ERROR: failed to set 'memory-region': %d\n", + ret); + } + } + + fdt_set_iommu_prop(blob, noff, fdt_get_smmu_phandle(blob), &mc_icid, 1); +} + int fsl_mc_ldpaa_init(struct bd_info *bis) { int i; diff --git a/drivers/net/fsl_mcdmafec.c b/drivers/net/fsl_mcdmafec.c deleted file mode 100644 index cc61a107403..00000000000 --- a/drivers/net/fsl_mcdmafec.c +++ /dev/null @@ -1,592 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2000-2004 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * (C) Copyright 2007 Freescale Semiconductor, Inc. - * TsiChung Liew (Tsi-Chung.Liew@freescale.com) - * - * Conversion to DM - * (C) 2019 Angelo Dureghello <angelo.dureghello@timesys.com> - */ - -#include <common.h> -#include <env.h> -#include <hang.h> -#include <malloc.h> -#include <command.h> -#include <config.h> -#include <net.h> -#include <miiphy.h> -#include <asm/global_data.h> -#include <linux/delay.h> -#include <linux/mii.h> -#include <asm/immap.h> -#include <asm/fsl_mcdmafec.h> - -#include "MCD_dma.h" - -#undef ET_DEBUG -#undef MII_DEBUG - -/* Ethernet Transmit and Receive Buffers */ -#define DBUF_LENGTH 1520 -#define PKT_MAXBUF_SIZE 1518 -#define FIFO_ERRSTAT (FIFO_STAT_RXW | FIFO_STAT_UF | FIFO_STAT_OF) - -/* RxBD bits definitions */ -#define BD_ENET_RX_ERR (BD_ENET_RX_LG | BD_ENET_RX_NO | BD_ENET_RX_CR | \ - BD_ENET_RX_OV | BD_ENET_RX_TR) - -DECLARE_GLOBAL_DATA_PTR; - -static void init_eth_info(struct fec_info_dma *info) -{ - /* setup Receive and Transmit buffer descriptor */ -#ifdef CFG_SYS_FEC_BUF_USE_SRAM - static u32 tmp; - - if (info->index == 0) - tmp = CFG_SYS_INIT_RAM_ADDR + 0x1000; - else - info->rxbd = (cbd_t *)DBUF_LENGTH; - - info->rxbd = (cbd_t *)((u32)info->rxbd + tmp); - tmp = (u32)info->rxbd; - info->txbd = - (cbd_t *)((u32)info->txbd + tmp + - (PKTBUFSRX * sizeof(cbd_t))); - tmp = (u32)info->txbd; - info->txbuf = - (char *)((u32)info->txbuf + tmp + - (CFG_SYS_TX_ETH_BUFFER * sizeof(cbd_t))); - tmp = (u32)info->txbuf; -#else - info->rxbd = - (cbd_t *)memalign(CONFIG_SYS_CACHELINE_SIZE, - (PKTBUFSRX * sizeof(cbd_t))); - info->txbd = - (cbd_t *)memalign(CONFIG_SYS_CACHELINE_SIZE, - (CFG_SYS_TX_ETH_BUFFER * sizeof(cbd_t))); - info->txbuf = - (char *)memalign(CONFIG_SYS_CACHELINE_SIZE, DBUF_LENGTH); -#endif - -#ifdef ET_DEBUG - printf("rxbd %x txbd %x\n", (int)info->rxbd, (int)info->txbd); -#endif - info->phy_name = (char *)memalign(CONFIG_SYS_CACHELINE_SIZE, 32); -} - -static void fec_halt(struct udevice *dev) -{ - struct fec_info_dma *info = dev_get_priv(dev); - volatile fecdma_t *fecp = (fecdma_t *)info->iobase; - int counter = 0xffff; - - /* issue graceful stop command to the FEC transmitter if necessary */ - fecp->tcr |= FEC_TCR_GTS; - - /* wait for graceful stop to register */ - while ((counter--) && (!(fecp->eir & FEC_EIR_GRA))) - ; - - /* Disable DMA tasks */ - MCD_killDma(info->tx_task); - MCD_killDma(info->rx_task); - - /* Disable the Ethernet Controller */ - fecp->ecr &= ~FEC_ECR_ETHER_EN; - - /* Clear FIFO status registers */ - fecp->rfsr &= FIFO_ERRSTAT; - fecp->tfsr &= FIFO_ERRSTAT; - - fecp->frst = 0x01000000; - - /* Issue a reset command to the FEC chip */ - fecp->ecr |= FEC_ECR_RESET; - - /* wait at least 20 clock cycles */ - mdelay(10); - -#ifdef ET_DEBUG - printf("Ethernet task stopped\n"); -#endif -} - -#ifdef ET_DEBUG -static void dbg_fec_regs(struct eth_device *dev) -{ - struct fec_info_dma *info = dev->priv; - volatile fecdma_t *fecp = (fecdma_t *)info->iobase; - - printf("=====\n"); - printf("ievent %x - %x\n", (int)&fecp->eir, fecp->eir); - printf("imask %x - %x\n", (int)&fecp->eimr, fecp->eimr); - printf("ecntrl %x - %x\n", (int)&fecp->ecr, fecp->ecr); - printf("mii_mframe %x - %x\n", (int)&fecp->mmfr, fecp->mmfr); - printf("mii_speed %x - %x\n", (int)&fecp->mscr, fecp->mscr); - printf("mii_ctrlstat %x - %x\n", (int)&fecp->mibc, fecp->mibc); - printf("r_cntrl %x - %x\n", (int)&fecp->rcr, fecp->rcr); - printf("r hash %x - %x\n", (int)&fecp->rhr, fecp->rhr); - printf("x_cntrl %x - %x\n", (int)&fecp->tcr, fecp->tcr); - printf("padr_l %x - %x\n", (int)&fecp->palr, fecp->palr); - printf("padr_u %x - %x\n", (int)&fecp->paur, fecp->paur); - printf("op_pause %x - %x\n", (int)&fecp->opd, fecp->opd); - printf("iadr_u %x - %x\n", (int)&fecp->iaur, fecp->iaur); - printf("iadr_l %x - %x\n", (int)&fecp->ialr, fecp->ialr); - printf("gadr_u %x - %x\n", (int)&fecp->gaur, fecp->gaur); - printf("gadr_l %x - %x\n", (int)&fecp->galr, fecp->galr); - printf("x_wmrk %x - %x\n", (int)&fecp->tfwr, fecp->tfwr); - printf("r_fdata %x - %x\n", (int)&fecp->rfdr, fecp->rfdr); - printf("r_fstat %x - %x\n", (int)&fecp->rfsr, fecp->rfsr); - printf("r_fctrl %x - %x\n", (int)&fecp->rfcr, fecp->rfcr); - printf("r_flrfp %x - %x\n", (int)&fecp->rlrfp, fecp->rlrfp); - printf("r_flwfp %x - %x\n", (int)&fecp->rlwfp, fecp->rlwfp); - printf("r_frfar %x - %x\n", (int)&fecp->rfar, fecp->rfar); - printf("r_frfrp %x - %x\n", (int)&fecp->rfrp, fecp->rfrp); - printf("r_frfwp %x - %x\n", (int)&fecp->rfwp, fecp->rfwp); - printf("t_fdata %x - %x\n", (int)&fecp->tfdr, fecp->tfdr); - printf("t_fstat %x - %x\n", (int)&fecp->tfsr, fecp->tfsr); - printf("t_fctrl %x - %x\n", (int)&fecp->tfcr, fecp->tfcr); - printf("t_flrfp %x - %x\n", (int)&fecp->tlrfp, fecp->tlrfp); - printf("t_flwfp %x - %x\n", (int)&fecp->tlwfp, fecp->tlwfp); - printf("t_ftfar %x - %x\n", (int)&fecp->tfar, fecp->tfar); - printf("t_ftfrp %x - %x\n", (int)&fecp->tfrp, fecp->tfrp); - printf("t_ftfwp %x - %x\n", (int)&fecp->tfwp, fecp->tfwp); - printf("frst %x - %x\n", (int)&fecp->frst, fecp->frst); - printf("ctcwr %x - %x\n", (int)&fecp->ctcwr, fecp->ctcwr); -} -#endif - -static void set_fec_duplex_speed(volatile fecdma_t *fecp, int dup_spd) -{ - struct bd_info *bd = gd->bd; - - if ((dup_spd >> 16) == FULL) { - /* Set maximum frame length */ - fecp->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | FEC_RCR_MII_MODE | - FEC_RCR_PROM | 0x100; - fecp->tcr = FEC_TCR_FDEN; - } else { - /* Half duplex mode */ - fecp->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | - FEC_RCR_MII_MODE | FEC_RCR_DRT; - fecp->tcr &= ~FEC_TCR_FDEN; - } - - if ((dup_spd & 0xFFFF) == _100BASET) { -#ifdef MII_DEBUG - printf("100Mbps\n"); -#endif - bd->bi_ethspeed = 100; - } else { -#ifdef MII_DEBUG - printf("10Mbps\n"); -#endif - bd->bi_ethspeed = 10; - } -} - -static void fec_set_hwaddr(volatile fecdma_t *fecp, u8 *mac) -{ - u8 curr_byte; /* byte for which to compute the CRC */ - int byte; /* loop - counter */ - int bit; /* loop - counter */ - u32 crc = 0xffffffff; /* initial value */ - - for (byte = 0; byte < 6; byte++) { - curr_byte = mac[byte]; - for (bit = 0; bit < 8; bit++) { - if ((curr_byte & 0x01) ^ (crc & 0x01)) { - crc >>= 1; - crc = crc ^ 0xedb88320; - } else { - crc >>= 1; - } - curr_byte >>= 1; - } - } - - crc = crc >> 26; - - /* Set individual hash table register */ - if (crc >= 32) { - fecp->ialr = (1 << (crc - 32)); - fecp->iaur = 0; - } else { - fecp->ialr = 0; - fecp->iaur = (1 << crc); - } - - /* Set physical address */ - fecp->palr = (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; - fecp->paur = (mac[4] << 24) + (mac[5] << 16) + 0x8808; - - /* Clear multicast address hash table */ - fecp->gaur = 0; - fecp->galr = 0; -} - -static int fec_init(struct udevice *dev) -{ - struct fec_info_dma *info = dev_get_priv(dev); - volatile fecdma_t *fecp = (fecdma_t *)info->iobase; - int rval, i; - uchar enetaddr[6]; - -#ifdef ET_DEBUG - printf("fec_init: iobase 0x%08x ...\n", info->iobase); -#endif - - fecpin_setclear(info, 1); - fec_halt(dev); - - mii_init(); - set_fec_duplex_speed(fecp, info->dup_spd); - - /* We use strictly polling mode only */ - fecp->eimr = 0; - - /* Clear any pending interrupt */ - fecp->eir = 0xffffffff; - - /* Set station address */ - if (info->index == 0) - rval = eth_env_get_enetaddr("ethaddr", enetaddr); - else - rval = eth_env_get_enetaddr("eth1addr", enetaddr); - - if (!rval) { - puts("Please set a valid MAC address\n"); - return -EINVAL; - } - - fec_set_hwaddr(fecp, enetaddr); - - /* Set Opcode/Pause Duration Register */ - fecp->opd = 0x00010020; - - /* Setup Buffers and Buffer Descriptors */ - info->rx_idx = 0; - info->tx_idx = 0; - - /* Setup Receiver Buffer Descriptors (13.14.24.18) - * Settings: Empty, Wrap */ - for (i = 0; i < PKTBUFSRX; i++) { - info->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; - info->rxbd[i].cbd_datlen = PKTSIZE_ALIGN; - info->rxbd[i].cbd_bufaddr = (uint) net_rx_packets[i]; - } - info->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; - - /* Setup Ethernet Transmitter Buffer Descriptors (13.14.24.19) - * Settings: Last, Tx CRC */ - for (i = 0; i < CFG_SYS_TX_ETH_BUFFER; i++) { - info->txbd[i].cbd_sc = 0; - info->txbd[i].cbd_datlen = 0; - info->txbd[i].cbd_bufaddr = (uint) (&info->txbuf[0]); - } - info->txbd[CFG_SYS_TX_ETH_BUFFER - 1].cbd_sc |= BD_ENET_TX_WRAP; - - info->used_tbd_idx = 0; - info->clean_tbd_num = CFG_SYS_TX_ETH_BUFFER; - - /* Set Rx FIFO alarm and granularity value */ - fecp->rfcr = 0x0c000000; - fecp->rfar = 0x0000030c; - - /* Set Tx FIFO granularity value */ - fecp->tfcr = FIFO_CTRL_FRAME | FIFO_CTRL_GR(6) | 0x00040000; - fecp->tfar = 0x00000080; - - fecp->tfwr = 0x2; - fecp->ctcwr = 0x03000000; - - /* Enable DMA receive task */ - MCD_startDma(info->rx_task, - (s8 *)info->rxbd, - 0, - (s8 *)&fecp->rfdr, - 4, - 0, - 4, - info->rx_init, - info->rx_pri, - (MCD_FECRX_DMA | MCD_TT_FLAGS_DEF), - (MCD_NO_CSUM | MCD_NO_BYTE_SWAP) - ); - - /* Enable DMA tx task with no ready buffer descriptors */ - MCD_startDma(info->tx_task, - (s8 *)info->txbd, - 0, - (s8 *)&fecp->tfdr, - 4, - 0, - 4, - info->tx_init, - info->tx_pri, - (MCD_FECTX_DMA | MCD_TT_FLAGS_DEF), - (MCD_NO_CSUM | MCD_NO_BYTE_SWAP) - ); - - /* Now enable the transmit and receive processing */ - fecp->ecr |= FEC_ECR_ETHER_EN; - - return 0; -} - -static int mcdmafec_init(struct udevice *dev) -{ - return fec_init(dev); -} - -static int mcdmafec_send(struct udevice *dev, void *packet, int length) -{ - struct fec_info_dma *info = dev_get_priv(dev); - cbd_t *p_tbd, *p_used_tbd; - u16 phy_status; - - miiphy_read(dev->name, info->phy_addr, MII_BMSR, &phy_status); - - /* process all the consumed TBDs */ - while (info->clean_tbd_num < CFG_SYS_TX_ETH_BUFFER) { - p_used_tbd = &info->txbd[info->used_tbd_idx]; - if (p_used_tbd->cbd_sc & BD_ENET_TX_READY) { -#ifdef ET_DEBUG - printf("Cannot clean TBD %d, in use\n", - info->clean_tbd_num); -#endif - return 0; - } - - /* clean this buffer descriptor */ - if (info->used_tbd_idx == (CFG_SYS_TX_ETH_BUFFER - 1)) - p_used_tbd->cbd_sc = BD_ENET_TX_WRAP; - else - p_used_tbd->cbd_sc = 0; - - /* update some indeces for a correct handling of TBD ring */ - info->clean_tbd_num++; - info->used_tbd_idx = (info->used_tbd_idx + 1) - % CFG_SYS_TX_ETH_BUFFER; - } - - /* Check for valid length of data. */ - if (length > 1500 || length <= 0) - return -1; - - /* Check the number of vacant TxBDs. */ - if (info->clean_tbd_num < 1) { - printf("No available TxBDs ...\n"); - return -1; - } - - /* Get the first TxBD to send the mac header */ - p_tbd = &info->txbd[info->tx_idx]; - p_tbd->cbd_datlen = length; - p_tbd->cbd_bufaddr = (u32)packet; - p_tbd->cbd_sc |= BD_ENET_TX_LAST | BD_ENET_TX_TC | BD_ENET_TX_READY; - info->tx_idx = (info->tx_idx + 1) % CFG_SYS_TX_ETH_BUFFER; - - /* Enable DMA transmit task */ - MCD_continDma(info->tx_task); - - info->clean_tbd_num -= 1; - - /* wait until frame is sent . */ - while (p_tbd->cbd_sc & BD_ENET_TX_READY) - udelay(10); - - return (int)(info->txbd[info->tx_idx].cbd_sc & BD_ENET_TX_STATS); -} - -static int mcdmafec_recv(struct udevice *dev, int flags, uchar **packetp) -{ - struct fec_info_dma *info = dev_get_priv(dev); - volatile fecdma_t *fecp = (fecdma_t *)info->iobase; - - cbd_t *prbd = &info->rxbd[info->rx_idx]; - u32 ievent; - int frame_length, len = 0; - - /* Check if any critical events have happened */ - ievent = fecp->eir; - if (ievent != 0) { - fecp->eir = ievent; - - if (ievent & (FEC_EIR_BABT | FEC_EIR_TXERR | FEC_EIR_RXERR)) { - printf("fec_recv: error\n"); - fec_halt(dev); - fec_init(dev); - return 0; - } - - if (ievent & FEC_EIR_HBERR) { - /* Heartbeat error */ - fecp->tcr |= FEC_TCR_GTS; - } - - if (ievent & FEC_EIR_GRA) { - /* Graceful stop complete */ - if (fecp->tcr & FEC_TCR_GTS) { - printf("fec_recv: tcr_gts\n"); - fec_halt(dev); - fecp->tcr &= ~FEC_TCR_GTS; - fec_init(dev); - } - } - } - - if (!(prbd->cbd_sc & BD_ENET_RX_EMPTY)) { - if ((prbd->cbd_sc & BD_ENET_RX_LAST) && - !(prbd->cbd_sc & BD_ENET_RX_ERR) && - ((prbd->cbd_datlen - 4) > 14)) { - /* Get buffer address and size */ - frame_length = prbd->cbd_datlen - 4; - - /* Fill the buffer and pass it to upper layers */ - net_process_received_packet((uchar *)prbd->cbd_bufaddr, - frame_length); - len = frame_length; - } - - /* Reset buffer descriptor as empty */ - if (info->rx_idx == (PKTBUFSRX - 1)) - prbd->cbd_sc = (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY); - else - prbd->cbd_sc = BD_ENET_RX_EMPTY; - - prbd->cbd_datlen = PKTSIZE_ALIGN; - - /* Now, we have an empty RxBD, restart the DMA receive task */ - MCD_continDma(info->rx_task); - - /* Increment BD count */ - info->rx_idx = (info->rx_idx + 1) % PKTBUFSRX; - } - - return len; -} - -static void mcdmafec_halt(struct udevice *dev) -{ - fec_halt(dev); -} - -static const struct eth_ops mcdmafec_ops = { - .start = mcdmafec_init, - .send = mcdmafec_send, - .recv = mcdmafec_recv, - .stop = mcdmafec_halt, -}; - -/* - * Boot sequence, called just after mcffec_of_to_plat, - * as DM way, it replaces old mcffec_initialize. - */ -static int mcdmafec_probe(struct udevice *dev) -{ - struct fec_info_dma *info = dev_get_priv(dev); - struct eth_pdata *pdata = dev_get_plat(dev); - int node = dev_of_offset(dev); - int retval; - const u32 *val; - - info->index = dev_seq(dev); - info->iobase = pdata->iobase; - info->miibase = pdata->iobase; - info->phy_addr = -1; - - val = fdt_getprop(gd->fdt_blob, node, "rx-task", NULL); - if (val) - info->rx_task = fdt32_to_cpu(*val); - - val = fdt_getprop(gd->fdt_blob, node, "tx-task", NULL); - if (val) - info->tx_task = fdt32_to_cpu(*val); - - val = fdt_getprop(gd->fdt_blob, node, "rx-prioprity", NULL); - if (val) - info->rx_pri = fdt32_to_cpu(*val); - - val = fdt_getprop(gd->fdt_blob, node, "tx-prioprity", NULL); - if (val) - info->tx_pri = fdt32_to_cpu(*val); - - val = fdt_getprop(gd->fdt_blob, node, "rx-init", NULL); - if (val) - info->rx_init = fdt32_to_cpu(*val); - - val = fdt_getprop(gd->fdt_blob, node, "tx-init", NULL); - if (val) - info->tx_init = fdt32_to_cpu(*val); - -#ifdef CFG_SYS_FEC_BUF_USE_SRAM - u32 tmp = CFG_SYS_INIT_RAM_ADDR + 0x1000; -#endif - init_eth_info(info); - -#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) - info->bus = mdio_alloc(); - if (!info->bus) - return -ENOMEM; - strlcpy(info->bus->name, dev->name, MDIO_NAME_LEN); - info->bus->read = mcffec_miiphy_read; - info->bus->write = mcffec_miiphy_write; - - retval = mdio_register(info->bus); - if (retval < 0) - return retval; -#endif - - return 0; -} - -static int mcdmafec_remove(struct udevice *dev) -{ - struct fec_info_dma *priv = dev_get_priv(dev); - - mdio_unregister(priv->bus); - mdio_free(priv->bus); - - return 0; -} - -/* - * Boot sequence, called 1st - */ -static int mcdmafec_of_to_plat(struct udevice *dev) -{ - struct eth_pdata *pdata = dev_get_plat(dev); - const u32 *val; - - pdata->iobase = dev_read_addr(dev); - /* Default to 10Mbit/s */ - pdata->max_speed = 10; - - val = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "max-speed", NULL); - if (val) - pdata->max_speed = fdt32_to_cpu(*val); - - return 0; -} - -static const struct udevice_id mcdmafec_ids[] = { - { .compatible = "fsl,mcf-dma-fec" }, - { } -}; - -U_BOOT_DRIVER(mcffec) = { - .name = "mcdmafec", - .id = UCLASS_ETH, - .of_match = mcdmafec_ids, - .of_to_plat = mcdmafec_of_to_plat, - .probe = mcdmafec_probe, - .remove = mcdmafec_remove, - .ops = &mcdmafec_ops, - .priv_auto = sizeof(struct fec_info_dma), - .plat_auto = sizeof(struct eth_pdata), -}; diff --git a/drivers/net/ftgmac100.c b/drivers/net/ftgmac100.c index 587d3658fa9..9b536fd5ab8 100644 --- a/drivers/net/ftgmac100.c +++ b/drivers/net/ftgmac100.c @@ -13,6 +13,7 @@ #include <common.h> #include <clk.h> +#include <reset.h> #include <cpu_func.h> #include <dm.h> #include <log.h> @@ -91,6 +92,7 @@ struct ftgmac100_data { u32 max_speed; struct clk_bulk clks; + struct reset_ctl *reset_ctl; /* End of RX/TX ring buffer bits. Depend on model */ u32 rxdes0_edorr_mask; @@ -569,6 +571,8 @@ static int ftgmac100_of_to_plat(struct udevice *dev) priv->txdes0_edotr_mask = BIT(15); } + priv->reset_ctl = devm_reset_control_get_optional(dev, NULL); + return clk_get_bulk(dev, &priv->clks); } @@ -594,6 +598,12 @@ static int ftgmac100_probe(struct udevice *dev) if (ret) goto out; + if (priv->reset_ctl) { + ret = reset_deassert(priv->reset_ctl); + if (ret) + goto out; + } + /* * If DM MDIO is enabled, the MDIO bus will be initialized later in * dm_eth_phy_connect @@ -629,6 +639,8 @@ static int ftgmac100_remove(struct udevice *dev) free(priv->phydev); mdio_unregister(priv->bus); mdio_free(priv->bus); + if (priv->reset_ctl) + reset_assert(priv->reset_ctl); clk_release_bulk(&priv->clks); return 0; diff --git a/drivers/net/hifemac.c b/drivers/net/hifemac.c new file mode 100644 index 00000000000..b61a29e6360 --- /dev/null +++ b/drivers/net/hifemac.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hisilicon Fast Ethernet MAC Driver + * Adapted from linux + * + * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. + * Copyright (c) 2023 Yang Xiwen <forbidden405@outlook.com> + */ + +#include <dm.h> +#include <clk.h> +#include <miiphy.h> +#include <net.h> +#include <reset.h> +#include <wait_bit.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/delay.h> +#include <linux/kernel.h> + +/* MAC control register list */ +#define MAC_PORTSEL 0x0200 +#define MAC_PORTSEL_STAT_CPU BIT(0) +#define MAC_PORTSEL_RMII BIT(1) +#define MAC_PORTSET 0x0208 +#define MAC_PORTSET_DUPLEX_FULL BIT(0) +#define MAC_PORTSET_LINKED BIT(1) +#define MAC_PORTSET_SPEED_100M BIT(2) +#define MAC_SET 0x0210 +#define MAX_FRAME_SIZE 1600 +#define MAX_FRAME_SIZE_MASK GENMASK(10, 0) +#define BIT_PAUSE_EN BIT(18) +#define RX_COALESCE_SET 0x0340 +#define RX_COALESCED_FRAME_OFFSET 24 +#define RX_COALESCED_FRAMES 8 +#define RX_COALESCED_TIMER 0x74 +#define QLEN_SET 0x0344 +#define RX_DEPTH_OFFSET 8 +#define MAX_HW_FIFO_DEPTH 64 +#define HW_TX_FIFO_DEPTH 1 +#define MAX_HW_RX_FIFO_DEPTH (MAX_HW_FIFO_DEPTH - HW_TX_FIFO_DEPTH) +#define HW_RX_FIFO_DEPTH min(PKTBUFSRX, MAX_HW_RX_FIFO_DEPTH) +#define IQFRM_DES 0x0354 +#define RX_FRAME_LEN_MASK GENMASK(11, 0) +#define RX_FRAME_IN_INDEX_MASK GENMASK(17, 12) +#define IQ_ADDR 0x0358 +#define EQ_ADDR 0x0360 +#define EQFRM_LEN 0x0364 +#define ADDRQ_STAT 0x036C +#define TX_CNT_INUSE_MASK GENMASK(5, 0) +#define BIT_TX_READY BIT(24) +#define BIT_RX_READY BIT(25) +/* global control register list */ +#define GLB_HOSTMAC_L32 0x0000 +#define GLB_HOSTMAC_H16 0x0004 +#define GLB_SOFT_RESET 0x0008 +#define SOFT_RESET_ALL BIT(0) +#define GLB_FWCTRL 0x0010 +#define FWCTRL_VLAN_ENABLE BIT(0) +#define FWCTRL_FW2CPU_ENA BIT(5) +#define FWCTRL_FWALL2CPU BIT(7) +#define GLB_MACTCTRL 0x0014 +#define MACTCTRL_UNI2CPU BIT(1) +#define MACTCTRL_MULTI2CPU BIT(3) +#define MACTCTRL_BROAD2CPU BIT(5) +#define MACTCTRL_MACT_ENA BIT(7) +#define GLB_IRQ_STAT 0x0030 +#define GLB_IRQ_ENA 0x0034 +#define IRQ_ENA_PORT0_MASK GENMASK(7, 0) +#define IRQ_ENA_PORT0 BIT(18) +#define IRQ_ENA_ALL BIT(19) +#define GLB_IRQ_RAW 0x0038 +#define IRQ_INT_RX_RDY BIT(0) +#define IRQ_INT_TX_PER_PACKET BIT(1) +#define IRQ_INT_TX_FIFO_EMPTY BIT(6) +#define IRQ_INT_MULTI_RXRDY BIT(7) +#define DEF_INT_MASK (IRQ_INT_MULTI_RXRDY | \ + IRQ_INT_TX_PER_PACKET | \ + IRQ_INT_TX_FIFO_EMPTY) +#define GLB_MAC_L32_BASE 0x0100 +#define GLB_MAC_H16_BASE 0x0104 +#define MACFLT_HI16_MASK GENMASK(15, 0) +#define BIT_MACFLT_ENA BIT(17) +#define BIT_MACFLT_FW2CPU BIT(21) +#define GLB_MAC_H16(reg) (GLB_MAC_H16_BASE + ((reg) * 0x8)) +#define GLB_MAC_L32(reg) (GLB_MAC_L32_BASE + ((reg) * 0x8)) +#define MAX_MAC_FILTER_NUM 8 +#define MAX_UNICAST_ADDRESSES 2 +#define MAX_MULTICAST_ADDRESSES (MAX_MAC_FILTER_NUM - \ + MAX_UNICAST_ADDRESSES) +/* software tx and rx queue number, should be power of 2 */ +#define TXQ_NUM 64 +#define RXQ_NUM 128 + +#define PHY_RESET_DELAYS_PROPERTY "hisilicon,phy-reset-delays-us" +#define MAC_RESET_DELAY_PROPERTY "hisilicon,mac-reset-delay-us" +#define MAC_RESET_ASSERT_PERIOD 200000 + +enum phy_reset_delays { + PRE_DELAY, + PULSE, + POST_DELAY, + DELAYS_NUM, +}; + +enum clk_type { + CLK_MAC, + CLK_BUS, + CLK_PHY, + CLK_NUM, +}; + +struct hisi_femac_priv { + void __iomem *port_base; + void __iomem *glb_base; + struct clk *clks[CLK_NUM]; + struct reset_ctl *mac_rst; + struct reset_ctl *phy_rst; + u32 phy_reset_delays[DELAYS_NUM]; + u32 mac_reset_delay; + + struct phy_device *phy; + + u32 link_status; +}; + +static void hisi_femac_irq_enable(struct hisi_femac_priv *priv, int irqs) +{ + u32 val; + + val = readl(priv->glb_base + GLB_IRQ_ENA); + writel(val | irqs, priv->glb_base + GLB_IRQ_ENA); +} + +static void hisi_femac_irq_disable(struct hisi_femac_priv *priv, int irqs) +{ + u32 val; + + val = readl(priv->glb_base + GLB_IRQ_ENA); + writel(val & (~irqs), priv->glb_base + GLB_IRQ_ENA); +} + +static void hisi_femac_port_init(struct hisi_femac_priv *priv) +{ + u32 val; + + /* MAC gets link status info and phy mode by software config */ + val = MAC_PORTSEL_STAT_CPU; + if (priv->phy->interface == PHY_INTERFACE_MODE_RMII) + val |= MAC_PORTSEL_RMII; + writel(val, priv->port_base + MAC_PORTSEL); + + /*clear all interrupt status */ + writel(IRQ_ENA_PORT0_MASK, priv->glb_base + GLB_IRQ_RAW); + hisi_femac_irq_disable(priv, IRQ_ENA_PORT0_MASK | IRQ_ENA_PORT0); + + val = readl(priv->glb_base + GLB_FWCTRL); + val &= ~(FWCTRL_VLAN_ENABLE | FWCTRL_FWALL2CPU); + val |= FWCTRL_FW2CPU_ENA; + writel(val, priv->glb_base + GLB_FWCTRL); + + val = readl(priv->glb_base + GLB_MACTCTRL); + val |= (MACTCTRL_BROAD2CPU | MACTCTRL_MACT_ENA); + writel(val, priv->glb_base + GLB_MACTCTRL); + + val = readl(priv->port_base + MAC_SET); + val &= ~MAX_FRAME_SIZE_MASK; + val |= MAX_FRAME_SIZE; + writel(val, priv->port_base + MAC_SET); + + val = RX_COALESCED_TIMER | + (RX_COALESCED_FRAMES << RX_COALESCED_FRAME_OFFSET); + writel(val, priv->port_base + RX_COALESCE_SET); + + val = (HW_RX_FIFO_DEPTH << RX_DEPTH_OFFSET) | HW_TX_FIFO_DEPTH; + writel(val, priv->port_base + QLEN_SET); +} + +static void hisi_femac_rx_refill(struct hisi_femac_priv *priv) +{ + int i; + ulong addr; + + for (i = 0; i < HW_RX_FIFO_DEPTH; i++) { + addr = (ulong)net_rx_packets[i]; + writel(addr, priv->port_base + IQ_ADDR); + } +} + +static void hisi_femac_adjust_link(struct udevice *dev) +{ + struct hisi_femac_priv *priv = dev_get_priv(dev); + struct phy_device *phy = priv->phy; + u32 status = 0; + + if (phy->link) + status |= MAC_PORTSET_LINKED; + if (phy->duplex == DUPLEX_FULL) + status |= MAC_PORTSET_DUPLEX_FULL; + if (phy->speed == SPEED_100) + status |= MAC_PORTSET_SPEED_100M; + + writel(status, priv->port_base + MAC_PORTSET); +} + +static int hisi_femac_port_reset(struct hisi_femac_priv *priv) +{ + u32 val; + + val = readl(priv->glb_base + GLB_SOFT_RESET); + val |= SOFT_RESET_ALL; + writel(val, priv->glb_base + GLB_SOFT_RESET); + + udelay(800); + + val &= ~SOFT_RESET_ALL; + writel(val, priv->glb_base + GLB_SOFT_RESET); + + return 0; +} + +static int hisi_femac_set_hw_mac_addr(struct udevice *dev) +{ + struct hisi_femac_priv *priv = dev_get_priv(dev); + struct eth_pdata *plat = dev_get_plat(dev); + unsigned char *mac = plat->enetaddr; + u32 reg; + + reg = mac[1] | (mac[0] << 8); + writel(reg, priv->glb_base + GLB_HOSTMAC_H16); + + reg = mac[5] | (mac[4] << 8) | (mac[3] << 16) | (mac[2] << 24); + writel(reg, priv->glb_base + GLB_HOSTMAC_L32); + + return 0; +} + +static int hisi_femac_start(struct udevice *dev) +{ + int ret; + struct hisi_femac_priv *priv = dev_get_priv(dev); + + hisi_femac_port_reset(priv); + hisi_femac_set_hw_mac_addr(dev); + hisi_femac_rx_refill(priv); + + ret = phy_startup(priv->phy); + if (ret) + return log_msg_ret("Failed to startup phy", ret); + + if (!priv->phy->link) { + debug("%s: link down\n", __func__); + return -ENODEV; + } + + hisi_femac_adjust_link(dev); + + writel(IRQ_ENA_PORT0_MASK, priv->glb_base + GLB_IRQ_RAW); + hisi_femac_irq_enable(priv, IRQ_ENA_ALL | IRQ_ENA_PORT0 | DEF_INT_MASK); + + return 0; +} + +static int hisi_femac_send(struct udevice *dev, void *packet, int length) +{ + struct hisi_femac_priv *priv = dev_get_priv(dev); + ulong addr = (ulong)packet; + int ret; + + // clear previous irq + writel(IRQ_INT_TX_PER_PACKET, priv->glb_base + GLB_IRQ_RAW); + + // flush cache + flush_cache(addr, length + ETH_FCS_LEN); + + // write packet address + writel(addr, priv->port_base + EQ_ADDR); + + // write packet length (and send it) + writel(length + ETH_FCS_LEN, priv->port_base + EQFRM_LEN); + + // wait until FIFO is empty + ret = wait_for_bit_le32(priv->glb_base + GLB_IRQ_RAW, IRQ_INT_TX_PER_PACKET, true, 50, false); + if (ret == -ETIMEDOUT) + return log_msg_ret("FIFO timeout", ret); + + return 0; +} + +static int hisi_femac_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct hisi_femac_priv *priv = dev_get_priv(dev); + int val, index, length; + + val = readl(priv->glb_base + GLB_IRQ_RAW); + if (!(val & IRQ_INT_RX_RDY)) + return -EAGAIN; + + val = readl(priv->port_base + IQFRM_DES); + index = (val & RX_FRAME_IN_INDEX_MASK) >> 12; + length = val & RX_FRAME_LEN_MASK; + + // invalidate cache + invalidate_dcache_range((ulong)net_rx_packets[index], (ulong)net_rx_packets[index] + length); + *packetp = net_rx_packets[index]; + + // Tell hardware we will process the packet + writel(IRQ_INT_RX_RDY, priv->glb_base + GLB_IRQ_RAW); + + return length; +} + +static int hisi_femac_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct hisi_femac_priv *priv = dev_get_priv(dev); + ulong addr = (ulong)packet; + + // Tell hardware the packet can be reused + writel(addr, priv->port_base + IQ_ADDR); + + return 0; +} + +static void hisi_femac_stop(struct udevice *dev) +{ + struct hisi_femac_priv *priv = dev_get_priv(dev); + + // assert internal reset + writel(SOFT_RESET_ALL, priv->glb_base + GLB_SOFT_RESET); +} + +int hisi_femac_of_to_plat(struct udevice *dev) +{ + int ret, i; + struct hisi_femac_priv *priv = dev_get_priv(dev); + static const char * const clk_strs[] = { + [CLK_MAC] = "mac", + [CLK_BUS] = "bus", + [CLK_PHY] = "phy", + }; + + priv->port_base = dev_remap_addr_name(dev, "port"); + if (IS_ERR(priv->port_base)) + return log_msg_ret("Failed to remap port address space", PTR_ERR(priv->port_base)); + + priv->glb_base = dev_remap_addr_name(dev, "glb"); + if (IS_ERR(priv->glb_base)) + return log_msg_ret("Failed to remap global address space", PTR_ERR(priv->glb_base)); + + for (i = 0; i < ARRAY_SIZE(clk_strs); i++) { + priv->clks[i] = devm_clk_get(dev, clk_strs[i]); + if (IS_ERR(priv->clks[i])) { + dev_err(dev, "Error getting clock %s\n", clk_strs[i]); + return log_msg_ret("Failed to get clocks", PTR_ERR(priv->clks[i])); + } + } + + priv->mac_rst = devm_reset_control_get(dev, "mac"); + if (IS_ERR(priv->mac_rst)) + return log_msg_ret("Failed to get MAC reset", PTR_ERR(priv->mac_rst)); + + priv->phy_rst = devm_reset_control_get(dev, "phy"); + if (IS_ERR(priv->phy_rst)) + return log_msg_ret("Failed to get PHY reset", PTR_ERR(priv->phy_rst)); + + ret = dev_read_u32_array(dev, + PHY_RESET_DELAYS_PROPERTY, + priv->phy_reset_delays, + DELAYS_NUM); + if (ret < 0) + return log_msg_ret("Failed to get PHY reset delays", ret); + + priv->mac_reset_delay = dev_read_u32_default(dev, + MAC_RESET_DELAY_PROPERTY, + MAC_RESET_ASSERT_PERIOD); + + return 0; +} + +static int hisi_femac_phy_reset(struct hisi_femac_priv *priv) +{ + struct reset_ctl *rst = priv->phy_rst; + u32 *delays = priv->phy_reset_delays; + int ret; + + // Disable MAC clk before phy reset + ret = clk_disable(priv->clks[CLK_MAC]); + if (ret < 0) + return log_msg_ret("Failed to disable MAC clock", ret); + ret = clk_disable(priv->clks[CLK_BUS]); + if (ret < 0) + return log_msg_ret("Failed to disable bus clock", ret); + + udelay(delays[PRE_DELAY]); + + ret = reset_assert(rst); + if (ret < 0) + return log_msg_ret("Failed to assert reset", ret); + + udelay(delays[PULSE]); + + ret = reset_deassert(rst); + if (ret < 0) + return log_msg_ret("Failed to deassert reset", ret); + + udelay(delays[POST_DELAY]); + + ret = clk_enable(priv->clks[CLK_MAC]); + if (ret < 0) + return log_msg_ret("Failed to enable MAC clock", ret); + ret = clk_enable(priv->clks[CLK_BUS]); + if (ret < 0) + return log_msg_ret("Failed to enable MAC bus clock", ret); + + return 0; +} + +int hisi_femac_probe(struct udevice *dev) +{ + struct hisi_femac_priv *priv = dev_get_priv(dev); + int ret, i; + + // Enable clocks + for (i = 0; i < CLK_NUM; i++) { + ret = clk_prepare_enable(priv->clks[i]); + if (ret < 0) + return log_msg_ret("Failed to enable clks", ret); + } + + // Reset MAC + ret = reset_assert(priv->mac_rst); + if (ret < 0) + return log_msg_ret("Failed to assert MAC reset", ret); + + udelay(priv->mac_reset_delay); + + ret = reset_deassert(priv->mac_rst); + if (ret < 0) + return log_msg_ret("Failed to deassert MAC reset", ret); + + // Reset PHY + ret = hisi_femac_phy_reset(priv); + if (ret < 0) + return log_msg_ret("Failed to reset phy", ret); + + // Connect to PHY + priv->phy = dm_eth_phy_connect(dev); + if (!priv->phy) + return log_msg_ret("Failed to connect to phy", -EINVAL); + + hisi_femac_port_init(priv); + return 0; +} + +static const struct eth_ops hisi_femac_ops = { + .start = hisi_femac_start, + .send = hisi_femac_send, + .recv = hisi_femac_recv, + .free_pkt = hisi_femac_free_pkt, + .stop = hisi_femac_stop, + .write_hwaddr = hisi_femac_set_hw_mac_addr, +}; + +static const struct udevice_id hisi_femac_ids[] = { + {.compatible = "hisilicon,hisi-femac-v1",}, + {.compatible = "hisilicon,hisi-femac-v2",}, + {.compatible = "hisilicon,hi3516cv300-femac",}, + {.compatible = "hisilicon,hi3798mv200-femac",}, + {}, +}; + +U_BOOT_DRIVER(hisi_femac_driver) = { + .name = "eth_hisi_femac", + .id = UCLASS_ETH, + .of_match = of_match_ptr(hisi_femac_ids), + .of_to_plat = hisi_femac_of_to_plat, + .ops = &hisi_femac_ops, + .probe = hisi_femac_probe, + .plat_auto = sizeof(struct eth_pdata), + .priv_auto = sizeof(struct hisi_femac_priv), +}; diff --git a/drivers/net/hifemac_mdio.c b/drivers/net/hifemac_mdio.c new file mode 100644 index 00000000000..343c5f3a38a --- /dev/null +++ b/drivers/net/hifemac_mdio.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hisilicon Fast Ethernet MDIO Bus Driver + * + * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. + */ + +#include <dm.h> +#include <clk.h> +#include <miiphy.h> +#include <linux/io.h> +#include <linux/iopoll.h> + +#define MDIO_RWCTRL 0x00 +#define MDIO_RO_DATA 0x04 +#define MDIO_WRITE BIT(13) +#define MDIO_RW_FINISH BIT(15) +#define BIT_PHY_ADDR_OFFSET 8 +#define BIT_WR_DATA_OFFSET 16 + +struct hisi_femac_mdio_data { + struct clk *clk; + void __iomem *membase; +}; + +static int hisi_femac_mdio_wait_ready(struct hisi_femac_mdio_data *data) +{ + u32 val; + + return readl_poll_timeout(data->membase + MDIO_RWCTRL, + val, val & MDIO_RW_FINISH, 10000); +} + +static int hisi_femac_mdio_read(struct udevice *dev, int addr, int devad, int reg) +{ + struct hisi_femac_mdio_data *data = dev_get_priv(dev); + int ret; + + ret = hisi_femac_mdio_wait_ready(data); + if (ret) + return ret; + + writel((addr << BIT_PHY_ADDR_OFFSET) | reg, + data->membase + MDIO_RWCTRL); + + ret = hisi_femac_mdio_wait_ready(data); + if (ret) + return ret; + + return readl(data->membase + MDIO_RO_DATA) & 0xFFFF; +} + +static int hisi_femac_mdio_write(struct udevice *dev, int addr, int devad, int reg, u16 val) +{ + struct hisi_femac_mdio_data *data = dev_get_priv(dev); + int ret; + + ret = hisi_femac_mdio_wait_ready(data); + if (ret) + return ret; + + writel(MDIO_WRITE | (val << BIT_WR_DATA_OFFSET) | + (addr << BIT_PHY_ADDR_OFFSET) | reg, + data->membase + MDIO_RWCTRL); + + return hisi_femac_mdio_wait_ready(data); +} + +static int hisi_femac_mdio_of_to_plat(struct udevice *dev) +{ + struct hisi_femac_mdio_data *data = dev_get_priv(dev); + int ret; + + data->membase = dev_remap_addr(dev); + if (IS_ERR(data->membase)) { + ret = PTR_ERR(data->membase); + return log_msg_ret("Failed to remap base addr", ret); + } + + // clk is optional + data->clk = devm_clk_get_optional(dev, NULL); + + return 0; +} + +static int hisi_femac_mdio_probe(struct udevice *dev) +{ + struct hisi_femac_mdio_data *data = dev_get_priv(dev); + int ret; + + ret = clk_prepare_enable(data->clk); + if (ret) + return log_msg_ret("Failed to enable clk", ret); + + return 0; +} + +static const struct mdio_ops hisi_femac_mdio_ops = { + .read = hisi_femac_mdio_read, + .write = hisi_femac_mdio_write, +}; + +static const struct udevice_id hisi_femac_mdio_dt_ids[] = { + { .compatible = "hisilicon,hisi-femac-mdio" }, + { } +}; + +U_BOOT_DRIVER(hisi_femac_mdio_driver) = { + .name = "hisi-femac-mdio", + .id = UCLASS_MDIO, + .of_match = hisi_femac_mdio_dt_ids, + .of_to_plat = hisi_femac_mdio_of_to_plat, + .probe = hisi_femac_mdio_probe, + .ops = &hisi_femac_mdio_ops, + .priv_auto = sizeof(struct hisi_femac_mdio_data), +}; diff --git a/drivers/net/mv88e6xxx.c b/drivers/net/mv88e6xxx.c index 64e860e324d..c073f81e72d 100644 --- a/drivers/net/mv88e6xxx.c +++ b/drivers/net/mv88e6xxx.c @@ -29,6 +29,7 @@ #include <dm/device-internal.h> #include <dm/lists.h> #include <dm/of_extra.h> +#include <linux/bitfield.h> #include <linux/delay.h> #include <miiphy.h> #include <net/dsa.h> @@ -110,20 +111,35 @@ */ #define SMI_BUSY BIT(15) #define SMI_CMD_CLAUSE_22 BIT(12) -#define SMI_CMD_CLAUSE_22_OP_READ (2 << 10) -#define SMI_CMD_CLAUSE_22_OP_WRITE (1 << 10) -#define SMI_CMD_ADDR_SHIFT 5 -#define SMI_CMD_ADDR_MASK 0x1f -#define SMI_CMD_REG_SHIFT 0 -#define SMI_CMD_REG_MASK 0x1f +#define SMI_CMD_OP_MASK GENMASK(11, 10) +#define SMI_CMD_CLAUSE_22_OP_WRITE 0x1 +#define SMI_CMD_CLAUSE_22_OP_READ 0x2 +#define SMI_CMD_CLAUSE_45_OP_WRITE_ADDR 0x0 +#define SMI_CMD_CLAUSE_45_OP_WRITE 0x1 +#define SMI_CMD_CLAUSE_45_OP_READ 0x3 + +#define SMI_CMD_ADDR_MASK GENMASK(9, 5) +#define SMI_CMD_REG_MASK GENMASK(4, 0) #define SMI_CMD_READ(addr, reg) \ - (SMI_BUSY | SMI_CMD_CLAUSE_22 | SMI_CMD_CLAUSE_22_OP_READ) | \ - (((addr) & SMI_CMD_ADDR_MASK) << SMI_CMD_ADDR_SHIFT) | \ - (((reg) & SMI_CMD_REG_MASK) << SMI_CMD_REG_SHIFT) + (SMI_BUSY | SMI_CMD_CLAUSE_22 | FIELD_PREP(SMI_CMD_OP_MASK, SMI_CMD_CLAUSE_22_OP_READ)) | \ + (FIELD_PREP(SMI_CMD_ADDR_MASK, addr)) | \ + (FIELD_PREP(SMI_CMD_REG_MASK, reg)) #define SMI_CMD_WRITE(addr, reg) \ - (SMI_BUSY | SMI_CMD_CLAUSE_22 | SMI_CMD_CLAUSE_22_OP_WRITE) | \ - (((addr) & SMI_CMD_ADDR_MASK) << SMI_CMD_ADDR_SHIFT) | \ - (((reg) & SMI_CMD_REG_MASK) << SMI_CMD_REG_SHIFT) + (SMI_BUSY | SMI_CMD_CLAUSE_22 | FIELD_PREP(SMI_CMD_OP_MASK, SMI_CMD_CLAUSE_22_OP_WRITE)) | \ + (FIELD_PREP(SMI_CMD_ADDR_MASK, addr)) | \ + (FIELD_PREP(SMI_CMD_REG_MASK, reg)) +#define SMI_CMD_SET_C45_ADDR(phyad, devad) \ + (SMI_BUSY | FIELD_PREP(SMI_CMD_OP_MASK, SMI_CMD_CLAUSE_45_OP_WRITE_ADDR)) | \ + (FIELD_PREP(SMI_CMD_ADDR_MASK, phyad)) | \ + (FIELD_PREP(SMI_CMD_REG_MASK, devad)) +#define SMI_CMD_READ_C45(phyad, devad) \ + (SMI_BUSY | FIELD_PREP(SMI_CMD_OP_MASK, SMI_CMD_CLAUSE_45_OP_READ)) | \ + (FIELD_PREP(SMI_CMD_ADDR_MASK, phyad)) | \ + (FIELD_PREP(SMI_CMD_REG_MASK, devad)) +#define SMI_CMD_WRITE_C45(phyad, devad) \ + (SMI_BUSY | FIELD_PREP(SMI_CMD_OP_MASK, SMI_CMD_CLAUSE_45_OP_WRITE)) | \ + (FIELD_PREP(SMI_CMD_ADDR_MASK, phyad)) | \ + (FIELD_PREP(SMI_CMD_REG_MASK, devad)) /* ID register values for different switch models */ #define PORT_SWITCH_ID_6020 0x0200 @@ -272,12 +288,37 @@ static int mv88e6xxx_phy_wait(struct udevice *dev) static int mv88e6xxx_phy_read_indirect(struct udevice *dev, int phyad, int devad, int reg) { struct mv88e6xxx_priv *priv = dev_get_priv(dev); + u16 smi_cmd; int res; + if (devad >= 0) { + /* + * For C45 we need to write the register address into the + * PHY Data register first and then call the Write Address + * Register OP in the PHY command register. + */ + res = mv88e6xxx_reg_write(dev, priv->global2, + GLOBAL2_REG_PHY_DATA, + reg); + + res = mv88e6xxx_reg_write(dev, priv->global2, + GLOBAL2_REG_PHY_CMD, + SMI_CMD_SET_C45_ADDR(phyad, devad)); + + /* Wait for busy bit to clear */ + res = mv88e6xxx_phy_wait(dev); + if (res < 0) + return res; + + /* Set the actual C45 or C22 OP-s */ + smi_cmd = SMI_CMD_READ_C45(phyad, devad); + } else + smi_cmd = SMI_CMD_READ(phyad, reg); + /* Issue command to read */ res = mv88e6xxx_reg_write(dev, priv->global2, GLOBAL2_REG_PHY_CMD, - SMI_CMD_READ(phyad, reg)); + smi_cmd); /* Wait for data to be read */ res = mv88e6xxx_phy_wait(dev); @@ -293,8 +334,33 @@ static int mv88e6xxx_phy_write_indirect(struct udevice *dev, int phyad, int devad, int reg, u16 data) { struct mv88e6xxx_priv *priv = dev_get_priv(dev); + u16 smi_cmd; int res; + if (devad >= 0) { + /* + * For C45 we need to write the register address into the + * PHY Data register first and then call the Write Address + * Register OP in the PHY command register. + */ + res = mv88e6xxx_reg_write(dev, priv->global2, + GLOBAL2_REG_PHY_DATA, + reg); + + res = mv88e6xxx_reg_write(dev, priv->global2, + GLOBAL2_REG_PHY_CMD, + SMI_CMD_SET_C45_ADDR(phyad, devad)); + + /* Wait for busy bit to clear */ + res = mv88e6xxx_phy_wait(dev); + if (res < 0) + return res; + + /* Set the actual C45 or C22 OP-s */ + smi_cmd = SMI_CMD_WRITE_C45(phyad, devad); + } else + smi_cmd = SMI_CMD_WRITE(phyad, reg); + /* Set the data to write */ res = mv88e6xxx_reg_write(dev, priv->global2, GLOBAL2_REG_PHY_DATA, data); @@ -303,7 +369,7 @@ static int mv88e6xxx_phy_write_indirect(struct udevice *dev, int phyad, /* Issue the write command */ res = mv88e6xxx_reg_write(dev, priv->global2, GLOBAL2_REG_PHY_CMD, - SMI_CMD_WRITE(phyad, reg)); + smi_cmd); if (res < 0) return res; diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c index 8d32d73b07f..f9d4782580e 100644 --- a/drivers/net/phy/dp83869.c +++ b/drivers/net/phy/dp83869.c @@ -81,7 +81,10 @@ /* RGMIIDCTL bits */ #define DP83869_RGMII_TX_CLK_DELAY_SHIFT 4 -#define DP83869_CLK_DELAY_DEF 7 +#define DP83869_CLK_DELAY_STEP 250 +#define DP83869_CLK_DELAY_MIN 250 +#define DP83869_CLK_DELAY_MAX 4000 +#define DP83869_CLK_DELAY_DEFAULT 2000 /* CFG2 bits */ #define MII_DP83869_CFG2_SPEEDOPT_10EN 0x0040 @@ -157,10 +160,6 @@ static int dp83869_config_port_mirroring(struct phy_device *phydev) return 0; } -static const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500, - 1750, 2000, 2250, 2500, 2750, 3000, - 3250, 3500, 3750, 4000}; - static int dp83869_set_strapped_mode(struct phy_device *phydev) { struct dp83869_private *dp83869 = phydev->priv; @@ -183,7 +182,6 @@ static int dp83869_set_strapped_mode(struct phy_device *phydev) static int dp83869_of_init(struct phy_device *phydev) { struct dp83869_private * const dp83869 = phydev->priv; - const int delay_entries = ARRAY_SIZE(dp83869_internal_delay); int ret; ofnode node; @@ -238,32 +236,45 @@ static int dp83869_of_init(struct phy_device *phydev) dp83869->tx_fifo_depth = ofnode_read_s32_default(node, "tx-fifo-depth", DP83869_PHYCR_FIFO_DEPTH_4_B_NIB); + /* Internal clock delay values can be configured in steps of + * 250ps (0.25ns). The register field for clock delay is 4-bits wide, + * the values range from 0b0000 for 0.25ns to 0b1111 for 4ns. + */ + /* RX delay *must* be specified if internal delay of RX is used. */ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { - dp83869->rx_int_delay = ofnode_read_u32_default(node, "rx-internal-delay-ps", - DP83869_CLK_DELAY_DEF); - if (dp83869->rx_int_delay > delay_entries) { - dp83869->rx_int_delay = DP83869_CLK_DELAY_DEF; - pr_debug("rx-internal-delay-ps not set/invalid, default to %ups\n", - dp83869_internal_delay[dp83869->rx_int_delay]); + dp83869->rx_int_delay = ofnode_read_u32_default(node, + "rx-internal-delay-ps", DP83869_CLK_DELAY_DEFAULT); + if (dp83869->rx_int_delay > DP83869_CLK_DELAY_MAX || + dp83869->rx_int_delay < DP83869_CLK_DELAY_MIN || + dp83869->rx_int_delay % DP83869_CLK_DELAY_STEP) { + dp83869->rx_int_delay = DP83869_CLK_DELAY_DEFAULT; + pr_warn("rx-internal-delay-ps not set/invalid, default" + " to %ups\n", DP83869_CLK_DELAY_DEFAULT); } - dp83869->rx_int_delay = dp83869_internal_delay[dp83869->rx_int_delay]; + dp83869->rx_int_delay = + (dp83869->rx_int_delay - DP83869_CLK_DELAY_STEP) + / DP83869_CLK_DELAY_STEP; } - /* TX delay *must* be specified if internal delay of RX is used. */ + /* TX delay *must* be specified if internal delay of TX is used. */ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { - dp83869->tx_int_delay = ofnode_read_u32_default(node, "tx-internal-delay-ps", - DP83869_CLK_DELAY_DEF); - if (dp83869->tx_int_delay > delay_entries) { - dp83869->tx_int_delay = DP83869_CLK_DELAY_DEF; - pr_debug("tx-internal-delay-ps not set/invalid, default to %ups\n", - dp83869_internal_delay[dp83869->tx_int_delay]); + dp83869->tx_int_delay = ofnode_read_u32_default(node, + "tx-internal-delay-ps", DP83869_CLK_DELAY_DEFAULT); + if (dp83869->tx_int_delay > DP83869_CLK_DELAY_MAX || + dp83869->tx_int_delay < DP83869_CLK_DELAY_MIN || + dp83869->tx_int_delay % DP83869_CLK_DELAY_STEP) { + dp83869->tx_int_delay = DP83869_CLK_DELAY_DEFAULT; + pr_warn("tx-internal-delay-ps not set/invalid, default" + " to %ups\n", DP83869_CLK_DELAY_DEFAULT); } - dp83869->tx_int_delay = dp83869_internal_delay[dp83869->tx_int_delay]; + dp83869->tx_int_delay = + (dp83869->tx_int_delay - DP83869_CLK_DELAY_STEP) + / DP83869_CLK_DELAY_STEP; } return 0; diff --git a/drivers/net/phy/et1011c.c b/drivers/net/phy/et1011c.c index fa4831427d5..db879bc73f2 100644 --- a/drivers/net/phy/et1011c.c +++ b/drivers/net/phy/et1011c.c @@ -3,7 +3,7 @@ * ET1011C PHY driver * * Derived from Linux kernel driver by Chaithrika U S - * Copyright (C) 2013, Texas Instruments, Incorporated - http://www.ti.com/ + * Copyright (C) 2013, Texas Instruments, Incorporated - https://www.ti.com/ */ #include <config.h> #include <phy.h> diff --git a/drivers/net/phy/xilinx_phy.c b/drivers/net/phy/xilinx_phy.c index 1df639d6f44..c07c780193f 100644 --- a/drivers/net/phy/xilinx_phy.c +++ b/drivers/net/phy/xilinx_phy.c @@ -99,7 +99,6 @@ static int xilinxphy_startup(struct phy_device *phydev) static int xilinxphy_of_init(struct phy_device *phydev) { - u32 phytype; ofnode node; debug("%s\n", __func__); @@ -107,10 +106,6 @@ static int xilinxphy_of_init(struct phy_device *phydev) if (!ofnode_valid(node)) return -EINVAL; - phytype = ofnode_read_u32_default(node, "xlnx,phy-type", -1); - if (phytype == XAE_PHY_TYPE_1000BASE_X) - phydev->flags |= XAE_PHY_TYPE_1000BASE_X; - return 0; } diff --git a/drivers/net/qe/uccf.c b/drivers/net/qe/uccf.c index 306f1ea1db6..00848a1a37d 100644 --- a/drivers/net/qe/uccf.c +++ b/drivers/net/qe/uccf.c @@ -6,7 +6,6 @@ * based on source code of Shlomi Gridish */ -#include <common.h> #include <malloc.h> #include <linux/errno.h> #include <asm/io.h> diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 04c3274fbe1..a12f7e32e8f 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -29,6 +29,7 @@ #include <net.h> #include <reset.h> #include <wait_bit.h> +#include <power/regulator.h> #define MDIO_CMD_MII_BUSY BIT(0) #define MDIO_CMD_MII_WRITE BIT(1) @@ -167,9 +168,8 @@ struct emac_eth_dev { struct clk ephy_clk; struct reset_ctl tx_rst; struct reset_ctl ephy_rst; -#if CONFIG_IS_ENABLED(DM_GPIO) struct gpio_desc reset_gpio; -#endif + struct udevice *phy_reg; }; @@ -615,7 +615,6 @@ err_tx_clk: return ret; } -#if CONFIG_IS_ENABLED(DM_GPIO) static int sun8i_mdio_reset(struct mii_dev *bus) { struct udevice *dev = bus->priv; @@ -647,7 +646,6 @@ static int sun8i_mdio_reset(struct mii_dev *bus) return 0; } -#endif static int sun8i_mdio_init(const char *name, struct udevice *priv) { @@ -662,9 +660,7 @@ static int sun8i_mdio_init(const char *name, struct udevice *priv) bus->write = sun8i_mdio_write; snprintf(bus->name, sizeof(bus->name), name); bus->priv = (void *)priv; -#if CONFIG_IS_ENABLED(DM_GPIO) bus->reset = sun8i_mdio_reset; -#endif return mdio_register(bus); } @@ -720,6 +716,9 @@ static int sun8i_emac_eth_probe(struct udevice *dev) sun8i_emac_set_syscon(sun8i_pdata, priv); + if (priv->phy_reg) + regulator_set_enable(priv->phy_reg, true); + sun8i_mdio_init(dev->name, dev); priv->bus = miiphy_get_dev_by_name(dev->name); @@ -778,9 +777,7 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev) const fdt32_t *reg; int node = dev_of_offset(dev); int offset = 0; -#if CONFIG_IS_ENABLED(DM_GPIO) int reset_flags = GPIOD_IS_OUT; -#endif int ret; pdata->iobase = dev_read_addr(dev); @@ -829,6 +826,8 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev) priv->sysctl_reg = (void *)syscon_base + priv->variant->syscon_offset; + device_get_supply_regulator(dev, "phy-supply", &priv->phy_reg); + pdata->phy_interface = -1; priv->phyaddr = -1; priv->use_internal_phy = false; @@ -865,7 +864,6 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev) printf("%s: Invalid RX delay value %d\n", __func__, sun8i_pdata->rx_delay_ps); -#if CONFIG_IS_ENABLED(DM_GPIO) if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), "snps,reset-active-low")) reset_flags |= GPIOD_ACTIVE_LOW; @@ -880,7 +878,6 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev) } else if (ret == -ENOENT) { ret = 0; } -#endif return 0; } diff --git a/drivers/net/sunxi_emac.c b/drivers/net/sunxi_emac.c index 4c90d4b4981..f546ad1fe8d 100644 --- a/drivers/net/sunxi_emac.c +++ b/drivers/net/sunxi_emac.c @@ -17,6 +17,7 @@ #include <net.h> #include <asm/io.h> #include <asm/arch/clock.h> +#include <power/regulator.h> /* EMAC register */ struct emac_regs { @@ -165,6 +166,7 @@ struct emac_eth_dev { struct phy_device *phydev; int link_printed; uchar rx_buf[EMAC_RX_BUFSIZE]; + struct udevice *phy_reg; }; struct emac_rxhdr { @@ -572,6 +574,9 @@ static int sunxi_emac_eth_probe(struct udevice *dev) if (ret) return ret; + if (priv->phy_reg) + regulator_set_enable(priv->phy_reg, true); + return sunxi_emac_init_phy(priv, dev); } @@ -585,9 +590,42 @@ static const struct eth_ops sunxi_emac_eth_ops = { static int sunxi_emac_eth_of_to_plat(struct udevice *dev) { struct eth_pdata *pdata = dev_get_plat(dev); + struct emac_eth_dev *priv = dev_get_priv(dev); + struct ofnode_phandle_args args; + ofnode phy_node, mdio_node; + int ret; pdata->iobase = dev_read_addr(dev); + phy_node = dev_get_phy_node(dev); + if (!ofnode_valid(phy_node)) { + dev_err(dev, "failed to get PHY node\n"); + return -ENOENT; + } + /* + * The PHY regulator is in the MDIO node, not the EMAC or PHY node. + * U-Boot does not have (and does not need) a device driver for the + * MDIO device, so just "pass through" that DT node to get to the + * regulator phandle. + * The PHY regulator is optional, though: ignore if we cannot find + * a phy-supply property. + */ + mdio_node = ofnode_get_parent(phy_node); + ret= ofnode_parse_phandle_with_args(mdio_node, "phy-supply", NULL, 0, 0, + &args); + if (ret && ret != -ENOENT) { + dev_err(dev, "failed to get PHY supply node\n"); + return ret; + } + if (!ret) { + ret = uclass_get_device_by_ofnode(UCLASS_REGULATOR, args.node, + &priv->phy_reg); + if (ret) { + dev_err(dev, "failed to get PHY regulator node\n"); + return ret; + } + } + return 0; } diff --git a/drivers/net/ti/Kconfig b/drivers/net/ti/Kconfig index 02660e4fbb4..c75f4186285 100644 --- a/drivers/net/ti/Kconfig +++ b/drivers/net/ti/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -# Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ +# Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ config DRIVER_TI_CPSW bool "TI Common Platform Ethernet Switch" diff --git a/drivers/net/ti/Makefile b/drivers/net/ti/Makefile index 8d3808bb4b6..0ce0cf2828a 100644 --- a/drivers/net/ti/Makefile +++ b/drivers/net/ti/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -# Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ +# Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ obj-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o cpsw-common.o cpsw_mdio.o obj-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o diff --git a/drivers/net/ti/cpsw.c b/drivers/net/ti/cpsw.c index 877be7fca52..9a5e9642df1 100644 --- a/drivers/net/ti/cpsw.c +++ b/drivers/net/ti/cpsw.c @@ -2,7 +2,7 @@ /* * CPSW Ethernet Switch Driver * - * Copyright (C) 2010-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010-2018 Texas Instruments Incorporated - https://www.ti.com/ */ #include <common.h> diff --git a/drivers/net/ti/cpsw_mdio.c b/drivers/net/ti/cpsw_mdio.c index ac791faa813..74cc956785f 100644 --- a/drivers/net/ti/cpsw_mdio.c +++ b/drivers/net/ti/cpsw_mdio.c @@ -2,7 +2,7 @@ /* * CPSW MDIO generic driver for TI AMxx/K2x/EMAC devices. * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ */ #include <common.h> diff --git a/drivers/net/ti/cpsw_mdio.h b/drivers/net/ti/cpsw_mdio.h index 9b98763656f..ddf65a4686d 100644 --- a/drivers/net/ti/cpsw_mdio.h +++ b/drivers/net/ti/cpsw_mdio.h @@ -2,7 +2,7 @@ /* * CPSW MDIO generic driver API for TI AMxx/K2x/EMAC devices. * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef CPSW_MDIO_H_ diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 3377e669f2f..7c57d32614f 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -321,11 +321,38 @@ static int zynq_gem_setup_mac(struct udevice *dev) return 0; } +static u32 gem_mdc_clk_div(struct zynq_gem_priv *priv) +{ + u32 config; + unsigned long pclk_hz; + + pclk_hz = clk_get_rate(&priv->pclk); + if (pclk_hz <= 20000000) + config = GEM_MDC_SET(GEM_CLK_DIV8); + else if (pclk_hz <= 40000000) + config = GEM_MDC_SET(GEM_CLK_DIV16); + else if (pclk_hz <= 80000000) + config = GEM_MDC_SET(GEM_CLK_DIV32); + else if (pclk_hz <= 120000000) + config = GEM_MDC_SET(GEM_CLK_DIV48); + else if (pclk_hz <= 160000000) + config = GEM_MDC_SET(GEM_CLK_DIV64); + else if (pclk_hz <= 240000000) + config = GEM_MDC_SET(GEM_CLK_DIV96); + else if (pclk_hz <= 320000000) + config = GEM_MDC_SET(GEM_CLK_DIV128); + else + config = GEM_MDC_SET(GEM_CLK_DIV224); + + return config; +} + static int zynq_phy_init(struct udevice *dev) { - int ret; + int ret, val; struct zynq_gem_priv *priv = dev_get_priv(dev); struct zynq_gem_regs *regs_mdio = priv->mdiobase; + struct zynq_gem_regs *regs = priv->iobase; const u32 supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | @@ -333,6 +360,10 @@ static int zynq_phy_init(struct udevice *dev) SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full; + val = gem_mdc_clk_div(priv); + if (val) + writel(val, ®s->nwcfg); + /* Enable only MDIO bus */ writel(ZYNQ_GEM_NWCTRL_MDEN_MASK, ®s_mdio->nwctrl); @@ -360,35 +391,10 @@ static int zynq_phy_init(struct udevice *dev) return phy_config(priv->phydev); } -static u32 gem_mdc_clk_div(struct zynq_gem_priv *priv) -{ - u32 config; - unsigned long pclk_hz; - - pclk_hz = clk_get_rate(&priv->pclk); - if (pclk_hz <= 20000000) - config = GEM_MDC_SET(GEM_CLK_DIV8); - else if (pclk_hz <= 40000000) - config = GEM_MDC_SET(GEM_CLK_DIV16); - else if (pclk_hz <= 80000000) - config = GEM_MDC_SET(GEM_CLK_DIV32); - else if (pclk_hz <= 120000000) - config = GEM_MDC_SET(GEM_CLK_DIV48); - else if (pclk_hz <= 160000000) - config = GEM_MDC_SET(GEM_CLK_DIV64); - else if (pclk_hz <= 240000000) - config = GEM_MDC_SET(GEM_CLK_DIV96); - else if (pclk_hz <= 320000000) - config = GEM_MDC_SET(GEM_CLK_DIV128); - else - config = GEM_MDC_SET(GEM_CLK_DIV224); - - return config; -} static int zynq_gem_init(struct udevice *dev) { - u32 i, nwconfig; + u32 i, nwconfig, nwcfg; int ret; unsigned long clk_rate = 0; struct zynq_gem_priv *priv = dev_get_priv(dev); @@ -494,8 +500,7 @@ static int zynq_gem_init(struct udevice *dev) return -1; } - nwconfig = gem_mdc_clk_div(priv); - nwconfig |= ZYNQ_GEM_NWCFG_INIT; + nwconfig = ZYNQ_GEM_NWCFG_INIT; /* * Set SGMII enable PCS selection only if internal PCS/PMA @@ -509,19 +514,21 @@ static int zynq_gem_init(struct udevice *dev) switch (priv->phydev->speed) { case SPEED_1000: - writel(nwconfig | ZYNQ_GEM_NWCFG_SPEED1000, - ®s->nwcfg); + nwconfig |= ZYNQ_GEM_NWCFG_SPEED1000; clk_rate = ZYNQ_GEM_FREQUENCY_1000; break; case SPEED_100: - writel(nwconfig | ZYNQ_GEM_NWCFG_SPEED100, - ®s->nwcfg); + nwconfig |= ZYNQ_GEM_NWCFG_SPEED100; clk_rate = ZYNQ_GEM_FREQUENCY_100; break; case SPEED_10: clk_rate = ZYNQ_GEM_FREQUENCY_10; break; } + nwcfg = readl(®s->nwcfg); + nwcfg |= nwconfig; + if (nwcfg) + writel(nwcfg, ®s->nwcfg); #ifdef CONFIG_ARM64 if (priv->interface == PHY_INTERFACE_MODE_SGMII && diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index 20dc910d8a3..c39cd41aa38 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -906,7 +906,7 @@ int nvme_init(struct udevice *udev) /* The real blksz and size will be set by nvme_blk_probe() */ ret = blk_create_devicef(udev, "nvme-blk", name, UCLASS_NVME, - -1, 512, 0, &ns_udev); + -1, DEFAULT_BLKSZ, 0, &ns_udev); if (ret) goto free_id; diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index ae7350aaff9..e0d01f6a85d 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -123,7 +123,7 @@ static void pci_dev_find_ofnode(struct udevice *bus, phys_addr_t bdf, dev_for_each_subnode(node, bus) { ret = ofnode_read_pci_addr(node, FDT_PCI_SPACE_CONFIG, "reg", - &addr); + &addr, NULL); if (ret) continue; diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index 3697cd8d652..83559550e6f 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -641,7 +641,8 @@ static int mvebu_pcie_port_parse_dt(ofnode node, ofnode parent, struct mvebu_pci pcie->is_x4 = true; /* devfn is in bits [15:8], see PCI_DEV usage */ - ret = ofnode_read_pci_addr(node, FDT_PCI_SPACE_CONFIG, "reg", &pci_addr); + ret = ofnode_read_pci_addr(node, FDT_PCI_SPACE_CONFIG, "reg", &pci_addr, + NULL); if (ret < 0) { printf("%s: property \"reg\" is invalid\n", pcie->name); goto err; diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c index 131c21b7684..d6374a58e33 100644 --- a/drivers/pci/pci_tegra.c +++ b/drivers/pci/pci_tegra.c @@ -462,7 +462,7 @@ static int tegra_pcie_parse_port_info(ofnode node, uint *index, uint *lanes) *lanes = err; - err = ofnode_read_pci_addr(node, 0, "reg", &addr); + err = ofnode_read_pci_addr(node, 0, "reg", &addr, NULL); if (err < 0) { pr_err("failed to parse \"reg\" property\n"); return err; diff --git a/drivers/pci/pcie_mediatek.c b/drivers/pci/pcie_mediatek.c index ed25a10bcf0..f0f34b5d119 100644 --- a/drivers/pci/pcie_mediatek.c +++ b/drivers/pci/pcie_mediatek.c @@ -661,7 +661,7 @@ static int mtk_pcie_probe(struct udevice *dev) if (!ofnode_is_enabled(subnode)) continue; - err = ofnode_read_pci_addr(subnode, 0, "reg", &addr); + err = ofnode_read_pci_addr(subnode, 0, "reg", &addr, NULL); if (err) return err; @@ -700,7 +700,7 @@ static int mtk_pcie_probe_v2(struct udevice *dev) if (!ofnode_is_enabled(subnode)) continue; - err = ofnode_read_pci_addr(subnode, 0, "reg", &addr); + err = ofnode_read_pci_addr(subnode, 0, "reg", &addr, NULL); if (err) return err; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 8ac5769ed9a..60138beca49 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -200,7 +200,7 @@ config MESON_GXL_USB_PHY config MESON_G12A_USB_PHY bool "Amlogic Meson G12A USB PHYs" - depends on PHY && ARCH_MESON && MESON_G12A + depends on PHY && ARCH_MESON && (MESON_G12A || MESON_A1) imply REGMAP help This is the generic phy driver for the Amlogic Meson G12A diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 5d4de86e71a..2e8723186c0 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -# Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ +# Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ # Written by Jean-Jacques Hiblot <jjhiblot@ti.com> obj-y += allwinner/ diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 77dffcad884..6624e9134f4 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -472,9 +472,9 @@ static int sun4i_usb_phy_probe(struct udevice *dev) if (!data->cfg) return -EINVAL; - data->base = (void __iomem *)devfdt_get_addr_name(dev, "phy_ctrl"); - if (IS_ERR(data->base)) - return PTR_ERR(data->base); + data->base = (void __iomem *)dev_read_addr_name_ptr(dev, "phy_ctrl"); + if (!data->base) + return -EINVAL; device_get_supply_regulator(dev, "usb0_vbus_power-supply", &data->vbus_power_supply); @@ -555,9 +555,9 @@ static int sun4i_usb_phy_probe(struct udevice *dev) if (i || data->cfg->phy0_dual_route) { snprintf(name, sizeof(name), "pmu%d", i); - phy->pmu = (void __iomem *)devfdt_get_addr_name(dev, name); - if (IS_ERR(phy->pmu)) - return PTR_ERR(phy->pmu); + phy->pmu = (void __iomem *)dev_read_addr_name_ptr(dev, name); + if (!phy->pmu) + return -EINVAL; } phy->id = i; diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index fc5044fd5d3..4bb8a0ca7f3 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -7,7 +7,7 @@ * Copyright (c) 2018 Cadence Design Systems * Author: Alan Douglas <adouglas@cadence.com> * - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ * Jean-Jacques Hiblot <jjhiblot@ti.com> * */ diff --git a/drivers/phy/keystone-usb-phy.c b/drivers/phy/keystone-usb-phy.c index 6799e232370..3bb9c0814c1 100644 --- a/drivers/phy/keystone-usb-phy.c +++ b/drivers/phy/keystone-usb-phy.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ * Written by Jean-Jacques Hiblot <jjhiblot@ti.com> */ diff --git a/drivers/phy/meson-axg-mipi-dphy.c b/drivers/phy/meson-axg-mipi-dphy.c index cf2a1cd14c7..a69b6c97594 100644 --- a/drivers/phy/meson-axg-mipi-dphy.c +++ b/drivers/phy/meson-axg-mipi-dphy.c @@ -25,6 +25,7 @@ #include <linux/bitops.h> #include <linux/compat.h> #include <linux/bitfield.h> +#include <linux/time.h> /* [31] soft reset for the phy. * 1: reset. 0: dessert the reset. @@ -170,8 +171,6 @@ #define MIPI_DSI_TEST_CTRL0 0x3c #define MIPI_DSI_TEST_CTRL1 0x40 -#define NSEC_PER_MSEC 1000000L - struct phy_meson_axg_mipi_dphy_priv { struct regmap *regmap; #if CONFIG_IS_ENABLED(CLK) diff --git a/drivers/phy/meson-g12a-usb2.c b/drivers/phy/meson-g12a-usb2.c index 8b243225156..3958d2404b8 100644 --- a/drivers/phy/meson-g12a-usb2.c +++ b/drivers/phy/meson-g12a-usb2.c @@ -19,17 +19,34 @@ #include <linux/delay.h> #include <linux/printk.h> #include <power/regulator.h> +#include <power-domain.h> #include <reset.h> #include <clk.h> #include <linux/bitops.h> #include <linux/compat.h> +#include <linux/bitfield.h> #define PHY_CTRL_R0 0x0 #define PHY_CTRL_R1 0x4 #define PHY_CTRL_R2 0x8 + #define PHY_CTRL_R3 0xc + #define PHY_CTRL_R3_SQUELCH_REF GENMASK(1, 0) + #define PHY_CTRL_R3_HSDIC_REF GENMASK(3, 2) + #define PHY_CTRL_R3_DISC_THRESH GENMASK(7, 4) + #define PHY_CTRL_R4 0x10 + #define PHY_CTRL_R4_CALIB_CODE_7_0 GENMASK(7, 0) + #define PHY_CTRL_R4_CALIB_CODE_15_8 GENMASK(15, 8) + #define PHY_CTRL_R4_CALIB_CODE_23_16 GENMASK(23, 16) + #define PHY_CTRL_R4_I_C2L_CAL_EN BIT(24) + #define PHY_CTRL_R4_I_C2L_CAL_RESET_N BIT(25) + #define PHY_CTRL_R4_I_C2L_CAL_DONE BIT(26) + #define PHY_CTRL_R4_TEST_BYPASS_MODE_EN BIT(27) + #define PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0 GENMASK(29, 28) + #define PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2 GENMASK(31, 30) + #define PHY_CTRL_R5 0x14 #define PHY_CTRL_R6 0x18 #define PHY_CTRL_R7 0x1c @@ -38,35 +55,131 @@ #define PHY_CTRL_R10 0x28 #define PHY_CTRL_R11 0x2c #define PHY_CTRL_R12 0x30 + #define PHY_CTRL_R13 0x34 + #define PHY_CTRL_R13_CUSTOM_PATTERN_19 GENMASK(7, 0) + #define PHY_CTRL_R13_LOAD_STAT BIT(14) + #define PHY_CTRL_R13_UPDATE_PMA_SIGNALS BIT(15) + #define PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET GENMASK(20, 16) + #define PHY_CTRL_R13_CLEAR_HOLD_HS_DISCONNECT BIT(21) + #define PHY_CTRL_R13_BYPASS_HOST_DISCONNECT_VAL BIT(22) + #define PHY_CTRL_R13_BYPASS_HOST_DISCONNECT_EN BIT(23) + #define PHY_CTRL_R13_I_C2L_HS_EN BIT(24) + #define PHY_CTRL_R13_I_C2L_FS_EN BIT(25) + #define PHY_CTRL_R13_I_C2L_LS_EN BIT(26) + #define PHY_CTRL_R13_I_C2L_HS_OE BIT(27) + #define PHY_CTRL_R13_I_C2L_FS_OE BIT(28) + #define PHY_CTRL_R13_I_C2L_HS_RX_EN BIT(29) + #define PHY_CTRL_R13_I_C2L_FSLS_RX_EN BIT(30) + #define PHY_CTRL_R14 0x38 #define PHY_CTRL_R15 0x3c + #define PHY_CTRL_R16 0x40 + #define PHY_CTRL_R16_MPLL_M GENMASK(8, 0) + #define PHY_CTRL_R16_MPLL_N GENMASK(14, 10) + #define PHY_CTRL_R16_MPLL_TDC_MODE BIT(20) + #define PHY_CTRL_R16_MPLL_SDM_EN BIT(21) + #define PHY_CTRL_R16_MPLL_LOAD BIT(22) + #define PHY_CTRL_R16_MPLL_DCO_SDM_EN BIT(23) + #define PHY_CTRL_R16_MPLL_LOCK_LONG GENMASK(25, 24) + #define PHY_CTRL_R16_MPLL_LOCK_F BIT(26) + #define PHY_CTRL_R16_MPLL_FAST_LOCK BIT(27) + #define PHY_CTRL_R16_MPLL_EN BIT(28) + #define PHY_CTRL_R16_MPLL_RESET BIT(29) + #define PHY_CTRL_R16_MPLL_LOCK BIT(30) + #define PHY_CTRL_R16_MPLL_LOCK_DIG BIT(31) + #define PHY_CTRL_R17 0x44 + #define PHY_CTRL_R17_MPLL_FRAC_IN GENMASK(13, 0) + #define PHY_CTRL_R17_MPLL_FIX_EN BIT(16) + #define PHY_CTRL_R17_MPLL_LAMBDA1 GENMASK(19, 17) + #define PHY_CTRL_R17_MPLL_LAMBDA0 GENMASK(22, 20) + #define PHY_CTRL_R17_MPLL_FILTER_MODE BIT(23) + #define PHY_CTRL_R17_MPLL_FILTER_PVT2 GENMASK(27, 24) + #define PHY_CTRL_R17_MPLL_FILTER_PVT1 GENMASK(31, 28) + #define PHY_CTRL_R18 0x48 + #define PHY_CTRL_R18_MPLL_LKW_SEL GENMASK(1, 0) + #define PHY_CTRL_R18_MPLL_LK_W GENMASK(5, 2) + #define PHY_CTRL_R18_MPLL_LK_S GENMASK(11, 6) + #define PHY_CTRL_R18_MPLL_DCO_M_EN BIT(12) + #define PHY_CTRL_R18_MPLL_DCO_CLK_SEL BIT(13) + #define PHY_CTRL_R18_MPLL_PFD_GAIN GENMASK(15, 14) + #define PHY_CTRL_R18_MPLL_ROU GENMASK(18, 16) + #define PHY_CTRL_R18_MPLL_DATA_SEL GENMASK(21, 19) + #define PHY_CTRL_R18_MPLL_BIAS_ADJ GENMASK(23, 22) + #define PHY_CTRL_R18_MPLL_BB_MODE GENMASK(25, 24) + #define PHY_CTRL_R18_MPLL_ALPHA GENMASK(28, 26) + #define PHY_CTRL_R18_MPLL_ADJ_LDO GENMASK(30, 29) + #define PHY_CTRL_R18_MPLL_ACG_RANGE BIT(31) + #define PHY_CTRL_R19 0x4c + #define PHY_CTRL_R20 0x50 + #define PHY_CTRL_R20_USB2_IDDET_EN BIT(0) + #define PHY_CTRL_R20_USB2_OTG_VBUS_TRIM_2_0 GENMASK(3, 1) + #define PHY_CTRL_R20_USB2_OTG_VBUSDET_EN BIT(4) + #define PHY_CTRL_R20_USB2_AMON_EN BIT(5) + #define PHY_CTRL_R20_USB2_CAL_CODE_R5 BIT(6) + #define PHY_CTRL_R20_BYPASS_OTG_DET BIT(7) + #define PHY_CTRL_R20_USB2_DMON_EN BIT(8) + #define PHY_CTRL_R20_USB2_DMON_SEL_3_0 GENMASK(12, 9) + #define PHY_CTRL_R20_USB2_EDGE_DRV_EN BIT(13) + #define PHY_CTRL_R20_USB2_EDGE_DRV_TRIM_1_0 GENMASK(15, 14) + #define PHY_CTRL_R20_USB2_BGR_ADJ_4_0 GENMASK(20, 16) + #define PHY_CTRL_R20_USB2_BGR_START BIT(21) + #define PHY_CTRL_R20_USB2_BGR_VREF_4_0 GENMASK(28, 24) + #define PHY_CTRL_R20_USB2_BGR_DBG_1_0 GENMASK(30, 29) + #define PHY_CTRL_R20_BYPASS_CAL_DONE_R5 BIT(31) + #define PHY_CTRL_R21 0x54 + #define PHY_CTRL_R21_USB2_BGR_FORCE BIT(0) + #define PHY_CTRL_R21_USB2_CAL_ACK_EN BIT(1) + #define PHY_CTRL_R21_USB2_OTG_ACA_EN BIT(2) + #define PHY_CTRL_R21_USB2_TX_STRG_PD BIT(3) + #define PHY_CTRL_R21_USB2_OTG_ACA_TRIM_1_0 GENMASK(5, 4) + #define PHY_CTRL_R21_BYPASS_UTMI_CNTR GENMASK(15, 6) + #define PHY_CTRL_R21_BYPASS_UTMI_REG GENMASK(25, 20) + #define PHY_CTRL_R22 0x58 #define PHY_CTRL_R23 0x5c #define RESET_COMPLETE_TIME 1000 #define PLL_RESET_COMPLETE_TIME 100 +enum meson_soc_id { + MESON_SOC_A1, + MESON_SOC_G12A, +}; + struct phy_meson_g12a_usb2_priv { struct regmap *regmap; #if CONFIG_IS_ENABLED(CLK) struct clk clk; #endif struct reset_ctl reset; +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + struct power_domain pwrdm; +#endif + int soc_id; }; static int phy_meson_g12a_usb2_init(struct phy *phy) { struct udevice *dev = phy->dev; struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev); + u32 value; int ret; +#if CONFIG_IS_ENABLED(CLK) + ret = clk_enable(&priv->clk); + if (ret && ret != -ENOSYS && ret != -ENOTSUPP) { + pr_err("failed to enable PHY clock\n"); + return ret; + } +#endif + ret = reset_assert(&priv->reset); udelay(1); ret |= reset_deassert(&priv->reset); @@ -79,25 +192,91 @@ static int phy_meson_g12a_usb2_init(struct phy *phy) regmap_update_bits(priv->regmap, PHY_CTRL_R21, BIT(2), 0); /* PLL Setup : 24MHz * 20 / 1 = 480MHz */ - regmap_write(priv->regmap, PHY_CTRL_R16, 0x39400414); - regmap_write(priv->regmap, PHY_CTRL_R17, 0x927e0000); - regmap_write(priv->regmap, PHY_CTRL_R18, 0xac5f49e5); + regmap_write(priv->regmap, PHY_CTRL_R16, + FIELD_PREP(PHY_CTRL_R16_MPLL_M, 20) | + FIELD_PREP(PHY_CTRL_R16_MPLL_N, 1) | + PHY_CTRL_R16_MPLL_LOAD | + FIELD_PREP(PHY_CTRL_R16_MPLL_LOCK_LONG, 1) | + PHY_CTRL_R16_MPLL_FAST_LOCK | + PHY_CTRL_R16_MPLL_EN | + PHY_CTRL_R16_MPLL_RESET); + + regmap_write(priv->regmap, PHY_CTRL_R17, + FIELD_PREP(PHY_CTRL_R17_MPLL_FRAC_IN, 0) | + FIELD_PREP(PHY_CTRL_R17_MPLL_LAMBDA1, 7) | + FIELD_PREP(PHY_CTRL_R17_MPLL_LAMBDA0, 7) | + FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT2, 2) | + FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT1, 9)); + + value = FIELD_PREP(PHY_CTRL_R18_MPLL_LKW_SEL, 1) | + FIELD_PREP(PHY_CTRL_R18_MPLL_LK_W, 9) | + FIELD_PREP(PHY_CTRL_R18_MPLL_LK_S, 0x27) | + FIELD_PREP(PHY_CTRL_R18_MPLL_PFD_GAIN, 1) | + FIELD_PREP(PHY_CTRL_R18_MPLL_ROU, 7) | + FIELD_PREP(PHY_CTRL_R18_MPLL_DATA_SEL, 3) | + FIELD_PREP(PHY_CTRL_R18_MPLL_BIAS_ADJ, 1) | + FIELD_PREP(PHY_CTRL_R18_MPLL_BB_MODE, 0) | + FIELD_PREP(PHY_CTRL_R18_MPLL_ALPHA, 3) | + FIELD_PREP(PHY_CTRL_R18_MPLL_ADJ_LDO, 1) | + PHY_CTRL_R18_MPLL_ACG_RANGE; + + if (priv->soc_id == MESON_SOC_A1) + value |= PHY_CTRL_R18_MPLL_DCO_CLK_SEL; + + regmap_write(priv->regmap, PHY_CTRL_R18, value); udelay(PLL_RESET_COMPLETE_TIME); /* UnReset PLL */ - regmap_write(priv->regmap, PHY_CTRL_R16, 0x19400414); + regmap_write(priv->regmap, PHY_CTRL_R16, + FIELD_PREP(PHY_CTRL_R16_MPLL_M, 20) | + FIELD_PREP(PHY_CTRL_R16_MPLL_N, 1) | + PHY_CTRL_R16_MPLL_LOAD | + FIELD_PREP(PHY_CTRL_R16_MPLL_LOCK_LONG, 1) | + PHY_CTRL_R16_MPLL_FAST_LOCK | + PHY_CTRL_R16_MPLL_EN); /* PHY Tuning */ - regmap_write(priv->regmap, PHY_CTRL_R20, 0xfe18); - regmap_write(priv->regmap, PHY_CTRL_R4, 0x8000fff); + regmap_write(priv->regmap, PHY_CTRL_R20, + FIELD_PREP(PHY_CTRL_R20_USB2_OTG_VBUS_TRIM_2_0, 4) | + PHY_CTRL_R20_USB2_OTG_VBUSDET_EN | + FIELD_PREP(PHY_CTRL_R20_USB2_DMON_SEL_3_0, 15) | + PHY_CTRL_R20_USB2_EDGE_DRV_EN | + FIELD_PREP(PHY_CTRL_R20_USB2_EDGE_DRV_TRIM_1_0, 3) | + FIELD_PREP(PHY_CTRL_R20_USB2_BGR_ADJ_4_0, 0) | + FIELD_PREP(PHY_CTRL_R20_USB2_BGR_VREF_4_0, 0) | + FIELD_PREP(PHY_CTRL_R20_USB2_BGR_DBG_1_0, 0)); + + if (priv->soc_id == MESON_SOC_G12A) + regmap_write(priv->regmap, PHY_CTRL_R4, + FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_7_0, 0xf) | + FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_15_8, 0xf) | + FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_23_16, 0xf) | + PHY_CTRL_R4_TEST_BYPASS_MODE_EN | + FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0, 0) | + FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2, 0)); + else if (priv->soc_id == MESON_SOC_A1) + regmap_write(priv->regmap, PHY_CTRL_R21, + PHY_CTRL_R21_USB2_CAL_ACK_EN | + PHY_CTRL_R21_USB2_TX_STRG_PD | + FIELD_PREP(PHY_CTRL_R21_USB2_OTG_ACA_TRIM_1_0, 2)); /* Tuning Disconnect Threshold */ - regmap_write(priv->regmap, PHY_CTRL_R3, 0x34); + regmap_write(priv->regmap, PHY_CTRL_R3, + FIELD_PREP(PHY_CTRL_R3_SQUELCH_REF, 0) | + FIELD_PREP(PHY_CTRL_R3_HSDIC_REF, 1) | + FIELD_PREP(PHY_CTRL_R3_DISC_THRESH, 3)); /* Analog Settings */ - regmap_write(priv->regmap, PHY_CTRL_R14, 0); - regmap_write(priv->regmap, PHY_CTRL_R13, 0x78000); + if (priv->soc_id == MESON_SOC_G12A) { + regmap_write(priv->regmap, PHY_CTRL_R14, 0); + regmap_write(priv->regmap, PHY_CTRL_R13, + PHY_CTRL_R13_UPDATE_PMA_SIGNALS | + FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7)); + } else if (priv->soc_id == MESON_SOC_A1) { + regmap_write(priv->regmap, PHY_CTRL_R13, + FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7)); + } return 0; } @@ -108,6 +287,10 @@ static int phy_meson_g12a_usb2_exit(struct phy *phy) struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev); int ret; +#if CONFIG_IS_ENABLED(CLK) + clk_disable(&priv->clk); +#endif + ret = reset_assert(&priv->reset); if (ret) return ret; @@ -125,6 +308,8 @@ int meson_g12a_usb2_phy_probe(struct udevice *dev) struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev); int ret; + priv->soc_id = (enum meson_soc_id)dev_get_driver_data(dev); + ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); if (ret) return ret; @@ -141,24 +326,40 @@ int meson_g12a_usb2_phy_probe(struct udevice *dev) return ret; } +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + ret = power_domain_get(dev, &priv->pwrdm); + if (ret < 0 && ret != -ENODEV && ret != -ENOENT) { + pr_err("failed to get power domain : %d\n", ret); + return ret; + } + + if (ret != -ENODEV && ret != -ENOENT) { + ret = power_domain_on(&priv->pwrdm); + if (ret < 0) { + pr_err("failed to enable power domain\n"); + return ret; + } + } +#endif + #if CONFIG_IS_ENABLED(CLK) ret = clk_get_by_index(dev, 0, &priv->clk); if (ret < 0) return ret; - - ret = clk_enable(&priv->clk); - if (ret && ret != -ENOSYS && ret != -ENOTSUPP) { - pr_err("failed to enable PHY clock\n"); - clk_free(&priv->clk); - return ret; - } #endif return 0; } static const struct udevice_id meson_g12a_usb2_phy_ids[] = { - { .compatible = "amlogic,g12a-usb2-phy" }, + { + .compatible = "amlogic,g12a-usb2-phy", + .data = (ulong)MESON_SOC_G12A, + }, + { + .compatible = "amlogic,a1-usb2-phy", + .data = (ulong)MESON_SOC_A1, + }, { } }; diff --git a/drivers/phy/nop-phy.c b/drivers/phy/nop-phy.c index d0904f4f075..c53e3216d0f 100644 --- a/drivers/phy/nop-phy.c +++ b/drivers/phy/nop-phy.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ * Written by Jean-Jacques Hiblot <jjhiblot@ti.com> */ diff --git a/drivers/phy/omap-usb2-phy.c b/drivers/phy/omap-usb2-phy.c index 2a9604cdcc4..d3d38062ecf 100644 --- a/drivers/phy/omap-usb2-phy.c +++ b/drivers/phy/omap-usb2-phy.c @@ -2,7 +2,7 @@ /* * OMAP USB2 PHY LAYER * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com * Written by Jean-Jacques Hiblot <jjhiblot@ti.com> */ diff --git a/drivers/phy/phy-bcm-sr-pcie.c b/drivers/phy/phy-bcm-sr-pcie.c index f0e795333b9..cf33bab3707 100644 --- a/drivers/phy/phy-bcm-sr-pcie.c +++ b/drivers/phy/phy-bcm-sr-pcie.c @@ -143,8 +143,8 @@ static int sr_pcie_phy_probe(struct udevice *dev) core->dev = dev; - core->base = (void __iomem *)devfdt_get_addr_name(dev, "reg_base"); - core->cdru = (void __iomem *)devfdt_get_addr_name(dev, "cdru_base"); + core->base = (void __iomem *)dev_read_addr_name_ptr(dev, "reg_base"); + core->cdru = (void __iomem *)dev_read_addr_name_ptr(dev, "cdru_base"); debug("ip base %p\n", core->base); debug("cdru base %p\n", core->cdru); diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c index ba5f6486126..bb61816add2 100644 --- a/drivers/phy/phy-core-mipi-dphy.c +++ b/drivers/phy/phy-core-mipi-dphy.c @@ -6,11 +6,10 @@ #include <common.h> #include <div64.h> +#include <linux/time.h> #include <phy-mipi-dphy.h> -#define PSEC_PER_SEC 1000000000000LL - /* * Minimum D-PHY timings based on MIPI D-PHY specification. Derived * from the valid ranges specified in Section 6.9, Table 14, Page 41 diff --git a/drivers/phy/phy-uclass.c b/drivers/phy/phy-uclass.c index 22f2fe91487..0dcfe258bc4 100644 --- a/drivers/phy/phy-uclass.c +++ b/drivers/phy/phy-uclass.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ * Written by Jean-Jacques Hiblot <jjhiblot@ti.com> */ diff --git a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c index 9ed7af0d6ef..5be76e05339 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c @@ -15,6 +15,7 @@ #include <linux/clk-provider.h> #include <linux/delay.h> #include <linux/math64.h> +#include <linux/time.h> #include <phy-mipi-dphy.h> #include <reset.h> @@ -186,8 +187,6 @@ #define DSI_PHY_STATUS 0xb0 #define PHY_LOCK BIT(0) -#define PSEC_PER_SEC 1000000000000LL - #define msleep(a) udelay(a * 1000) enum phy_max_rate { diff --git a/drivers/phy/sandbox-phy.c b/drivers/phy/sandbox-phy.c index 7b3d988613b..7e123da25fb 100644 --- a/drivers/phy/sandbox-phy.c +++ b/drivers/phy/sandbox-phy.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ * Written by Jean-Jacques Hiblot <jjhiblot@ti.com> */ diff --git a/drivers/phy/ti-pipe3-phy.c b/drivers/phy/ti-pipe3-phy.c index 313735844ab..29a35ae5ffb 100644 --- a/drivers/phy/ti-pipe3-phy.c +++ b/drivers/phy/ti-pipe3-phy.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ * Written by Jean-Jacques Hiblot <jjhiblot@ti.com> */ diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c index 72613399073..daf62f5deda 100644 --- a/drivers/phy/ti/phy-j721e-wiz.c +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2017-2018 Texas Instruments Incorporated - https://www.ti.com/ * Jean-Jacques Hiblot <jjhiblot@ti.com> */ diff --git a/drivers/pinctrl/nuvoton/pinctrl-npcm8xx.c b/drivers/pinctrl/nuvoton/pinctrl-npcm8xx.c index 7976e3b3ed5..ff49819b58d 100644 --- a/drivers/pinctrl/nuvoton/pinctrl-npcm8xx.c +++ b/drivers/pinctrl/nuvoton/pinctrl-npcm8xx.c @@ -329,6 +329,7 @@ struct group_info { static const struct group_info npcm8xx_groups[] = { FUNC_LIST + {FN_gpio, "GPIO", NULL, 0, 0, 0} }; /* Pin flags */ diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index d80281fd3dd..d1db377c137 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -5,6 +5,7 @@ */ #include <common.h> +#include <mapmem.h> #include <dm.h> #include <dm/device_compat.h> #include <dm/devres.h> @@ -24,7 +25,7 @@ * @bits_per_mux: true if one register controls more than one pin */ struct single_pdata { - fdt_addr_t base; + void *base; int offset; u32 mask; u32 width; @@ -97,7 +98,7 @@ struct single_fdt_bits_cfg { #if (!IS_ENABLED(CONFIG_SANDBOX)) -static unsigned int single_read(struct udevice *dev, fdt_addr_t reg) +static unsigned int single_read(struct udevice *dev, void *reg) { struct single_pdata *pdata = dev_get_plat(dev); @@ -113,7 +114,7 @@ static unsigned int single_read(struct udevice *dev, fdt_addr_t reg) return readb(reg); } -static void single_write(struct udevice *dev, unsigned int val, fdt_addr_t reg) +static void single_write(struct udevice *dev, unsigned int val, void *reg) { struct single_pdata *pdata = dev_get_plat(dev); @@ -131,18 +132,18 @@ static void single_write(struct udevice *dev, unsigned int val, fdt_addr_t reg) #else /* CONFIG_SANDBOX */ -static unsigned int single_read(struct udevice *dev, fdt_addr_t reg) +static unsigned int single_read(struct udevice *dev, void *reg) { struct single_priv *priv = dev_get_priv(dev); - return priv->sandbox_regs[reg]; + return priv->sandbox_regs[map_to_sysmem(reg)]; } -static void single_write(struct udevice *dev, unsigned int val, fdt_addr_t reg) +static void single_write(struct udevice *dev, unsigned int val, void *reg) { struct single_priv *priv = dev_get_priv(dev); - priv->sandbox_regs[reg] = val; + priv->sandbox_regs[map_to_sysmem(reg)] = val; } #endif /* CONFIG_SANDBOX */ @@ -214,7 +215,8 @@ static int single_get_pin_muxing(struct udevice *dev, unsigned int pin, { struct single_pdata *pdata = dev_get_plat(dev); struct single_priv *priv = dev_get_priv(dev); - fdt_addr_t reg; + phys_addr_t phys_reg; + void *reg; const char *fname; unsigned int val; int offset, pin_shift = 0; @@ -226,13 +228,15 @@ static int single_get_pin_muxing(struct udevice *dev, unsigned int pin, reg = pdata->base + offset; val = single_read(dev, reg); + phys_reg = map_to_sysmem(reg); + if (pdata->bits_per_mux) pin_shift = pin % (pdata->width / priv->bits_per_pin) * priv->bits_per_pin; val &= (pdata->mask << pin_shift); fname = single_get_pin_function(dev, pin); - snprintf(buf, size, "%pa 0x%08x %s", ®, val, + snprintf(buf, size, "%pa 0x%08x %s", &phys_reg, val, fname ? fname : "UNCLAIMED"); return 0; } @@ -243,7 +247,7 @@ static int single_request(struct udevice *dev, int pin, int flags) struct single_pdata *pdata = dev_get_plat(dev); struct single_gpiofunc_range *frange = NULL; struct list_head *pos, *tmp; - phys_addr_t reg; + void *reg; int mux_bytes = 0; u32 data; @@ -321,7 +325,7 @@ static int single_configure_pins(struct udevice *dev, int stride = pdata->args_count + 1; int n, pin, count = size / sizeof(u32); struct single_func *func; - phys_addr_t reg; + void *reg; u32 offset, val, mux; /* If function mask is null, needn't enable it. */ @@ -379,7 +383,7 @@ static int single_configure_bits(struct udevice *dev, int n, pin, count = size / sizeof(struct single_fdt_bits_cfg); int npins_in_reg, pin_num_from_lsb; struct single_func *func; - phys_addr_t reg; + void *reg; u32 offset, val, mask, bit_pos, val_pos, mask_pos, submask; /* If function mask is null, needn't enable it. */ @@ -570,7 +574,7 @@ static int single_probe(struct udevice *dev) static int single_of_to_plat(struct udevice *dev) { - fdt_addr_t addr; + void *addr; fdt_size_t size; struct single_pdata *pdata = dev_get_plat(dev); int ret; @@ -591,8 +595,8 @@ static int single_of_to_plat(struct udevice *dev) return -EINVAL; } - addr = dev_read_addr_size_index(dev, 0, &size); - if (addr == FDT_ADDR_T_NONE) { + addr = dev_read_addr_size_index_ptr(dev, 0, &size); + if (!addr) { dev_err(dev, "failed to get base register address\n"); return -EINVAL; } diff --git a/drivers/pinctrl/pinctrl-zynqmp.c b/drivers/pinctrl/pinctrl-zynqmp.c index 517035961da..eb17a4290b7 100644 --- a/drivers/pinctrl/pinctrl-zynqmp.c +++ b/drivers/pinctrl/pinctrl-zynqmp.c @@ -548,6 +548,8 @@ static int zynqmp_pinctrl_get_pin_muxing(struct udevice *dev, &pinmux.drive_strength); zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_VOLTAGE_STATUS, &pinmux.volt_sts); + zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_TRI_STATE, + &pinmux.tri_state); switch (pinmux.drive_strength) { case PM_PINCTRL_DRIVE_STRENGTH_2MA: @@ -568,13 +570,15 @@ static int zynqmp_pinctrl_get_pin_muxing(struct udevice *dev, return -EINVAL; } - snprintf(buf, size, "slew:%s\tbias:%s\tpull:%s\tinput:%s\tdrive:%dmA\tvolt:%s", + snprintf(buf, size, + "slew:%s\tbias:%s\tpull:%s\tinput:%s\tdrive:%dmA\tvolt:%s\ttri_state:%s", pinmux.slew ? "slow" : "fast", pinmux.bias ? "enabled" : "disabled", pinmux.pull_ctrl ? "up" : "down", pinmux.input_type ? "schmitt" : "cmos", pinmux.drive_strength, - pinmux.volt_sts ? "1.8" : "3.3"); + pinmux.volt_sts ? "1.8" : "3.3", + pinmux.tri_state ? "enabled" : "disabled"); return 0; } diff --git a/drivers/pinctrl/pinctrl_stm32.c b/drivers/pinctrl/pinctrl_stm32.c index 8bb7588714a..7120b8edba0 100644 --- a/drivers/pinctrl/pinctrl_stm32.c +++ b/drivers/pinctrl/pinctrl_stm32.c @@ -505,6 +505,8 @@ static const struct udevice_id stm32_pinctrl_ids[] = { { .compatible = "st,stm32mp157-pinctrl" }, { .compatible = "st,stm32mp157-z-pinctrl" }, { .compatible = "st,stm32mp135-pinctrl" }, + { .compatible = "st,stm32mp257-pinctrl" }, + { .compatible = "st,stm32mp257-z-pinctrl" }, { } }; diff --git a/drivers/pinctrl/renesas/Kconfig b/drivers/pinctrl/renesas/Kconfig index 32f44e5bbd7..4c8ec9fcf18 100644 --- a/drivers/pinctrl/renesas/Kconfig +++ b/drivers/pinctrl/renesas/Kconfig @@ -138,6 +138,15 @@ config PINCTRL_RZA1 help Support pin multiplexing control on Renesas RZ/A1 R7S72100 SoCs. +config PINCTRL_RZG2L + bool "Renesas RZ/G2L family pin control driver" + depends on PINCTRL + depends on PINCTRL_GENERIC + depends on PINCONF + help + Support the pinctrl functionality of the pin function controller (PFC) + on the Renesas RZ/G2L SoC family. + endif config PINCTRL_RZN1 diff --git a/drivers/pinctrl/renesas/Makefile b/drivers/pinctrl/renesas/Makefile index f9a68794eb9..cf7ec109681 100644 --- a/drivers/pinctrl/renesas/Makefile +++ b/drivers/pinctrl/renesas/Makefile @@ -21,3 +21,4 @@ obj-$(CONFIG_PINCTRL_PFC_R8A779F0) += pfc-r8a779f0.o obj-$(CONFIG_PINCTRL_PFC_R8A779G0) += pfc-r8a779g0.o obj-$(CONFIG_PINCTRL_RZA1) += pinctrl-rza1.o obj-$(CONFIG_PINCTRL_RZN1) += pinctrl-rzn1.o +obj-$(CONFIG_PINCTRL_RZG2L) += rzg2l-pfc.o diff --git a/drivers/pinctrl/renesas/rzg2l-pfc.c b/drivers/pinctrl/renesas/rzg2l-pfc.c new file mode 100644 index 00000000000..e88ec1c1837 --- /dev/null +++ b/drivers/pinctrl/renesas/rzg2l-pfc.c @@ -0,0 +1,624 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G2L Pin Function Controller + * + * Copyright (C) 2021-2023 Renesas Electronics Corp. + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/device.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <dm/lists.h> +#include <dm/pinctrl.h> +#include <renesas/rzg2l-pfc.h> +#include <reset.h> + +struct rzg2l_pfc_driver_data { + uint num_dedicated_pins; + uint num_ports; + const u32 *gpio_configs; +}; + +struct rzg2l_dedicated_configs { + const char *name; + u32 config; +}; + +/* + * We need to ensure that the module clock is enabled and all resets are + * de-asserted before using either the gpio or pinctrl functionality. Error + * handling can be quite simple here as if the PFC cannot be enabled then we + * will not be able to progress with the boot anyway. + */ +int rzg2l_pfc_enable(struct udevice *dev) +{ + struct reset_ctl_bulk rsts; + struct clk clk; + int ret; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) { + dev_err(dev, "failed to get gpio module clock\n"); + return ret; + } + + ret = clk_enable(&clk); + if (ret < 0) { + dev_err(dev, "failed to enable gpio module clock\n"); + return ret; + } + + ret = reset_get_bulk(dev, &rsts); + if (ret < 0) { + dev_err(dev, "failed to get reset lines\n"); + return ret; + } + + ret = reset_deassert_bulk(&rsts); + if (ret < 0) { + dev_err(dev, "failed to de-assert reset lines\n"); + return ret; + } + + return 0; +} + +bool rzg2l_port_validate(const struct rzg2l_pfc_data *data, u32 port, u8 pin) +{ + return (port < data->num_ports) && + (pin < RZG2L_GPIO_PORT_GET_PINCNT(data->gpio_configs[port])); +} + +/* Decode a pin selector, returning the port index and setting *pin to the pin + * index. Returns -1 on error, which can be checked directly or by calling + * rzg2l_port_validate(). + */ +static int rzg2l_selector_decode(const struct rzg2l_pfc_data *data, + unsigned int selector, + u8 *pin) +{ + int port; + + selector -= data->num_dedicated_pins; + for (port = 0; port < data->num_ports; port++) { + u8 num_pins = RZG2L_GPIO_PORT_GET_PINCNT(data->gpio_configs[port]); + if (selector < num_pins) { + *pin = (u8)selector; + return port; + } + selector -= num_pins; + } + return -EINVAL; +} + +static unsigned int rzg2l_selector_encode(const struct rzg2l_pfc_data *data, + u32 port, u8 pin) +{ + unsigned int selector = data->num_dedicated_pins + pin; + u32 i; + + for (i = 0; i < port; i++) + selector += RZG2L_GPIO_PORT_GET_PINCNT(data->gpio_configs[i]); + + return selector; +} + +static const char * const rzg2l_gpio_names[] = { + "P0_0", "P0_1", "P0_2", "P0_3", "P0_4", "P0_5", "P0_6", "P0_7", + "P1_0", "P1_1", "P1_2", "P1_3", "P1_4", "P1_5", "P1_6", "P1_7", + "P2_0", "P2_1", "P2_2", "P2_3", "P2_4", "P2_5", "P2_6", "P2_7", + "P3_0", "P3_1", "P3_2", "P3_3", "P3_4", "P3_5", "P3_6", "P3_7", + "P4_0", "P4_1", "P4_2", "P4_3", "P4_4", "P4_5", "P4_6", "P4_7", + "P5_0", "P5_1", "P5_2", "P5_3", "P5_4", "P5_5", "P5_6", "P5_7", + "P6_0", "P6_1", "P6_2", "P6_3", "P6_4", "P6_5", "P6_6", "P6_7", + "P7_0", "P7_1", "P7_2", "P7_3", "P7_4", "P7_5", "P7_6", "P7_7", + "P8_0", "P8_1", "P8_2", "P8_3", "P8_4", "P8_5", "P8_6", "P8_7", + "P9_0", "P9_1", "P9_2", "P9_3", "P9_4", "P9_5", "P9_6", "P9_7", + "P10_0", "P10_1", "P10_2", "P10_3", "P10_4", "P10_5", "P10_6", "P10_7", + "P11_0", "P11_1", "P11_2", "P11_3", "P11_4", "P11_5", "P11_6", "P11_7", + "P12_0", "P12_1", "P12_2", "P12_3", "P12_4", "P12_5", "P12_6", "P12_7", + "P13_0", "P13_1", "P13_2", "P13_3", "P13_4", "P13_5", "P13_6", "P13_7", + "P14_0", "P14_1", "P14_2", "P14_3", "P14_4", "P14_5", "P14_6", "P14_7", + "P15_0", "P15_1", "P15_2", "P15_3", "P15_4", "P15_5", "P15_6", "P15_7", + "P16_0", "P16_1", "P16_2", "P16_3", "P16_4", "P16_5", "P16_6", "P16_7", + "P17_0", "P17_1", "P17_2", "P17_3", "P17_4", "P17_5", "P17_6", "P17_7", + "P18_0", "P18_1", "P18_2", "P18_3", "P18_4", "P18_5", "P18_6", "P18_7", + "P19_0", "P19_1", "P19_2", "P19_3", "P19_4", "P19_5", "P19_6", "P19_7", + "P20_0", "P20_1", "P20_2", "P20_3", "P20_4", "P20_5", "P20_6", "P20_7", + "P21_0", "P21_1", "P21_2", "P21_3", "P21_4", "P21_5", "P21_6", "P21_7", + "P22_0", "P22_1", "P22_2", "P22_3", "P22_4", "P22_5", "P22_6", "P22_7", + "P23_0", "P23_1", "P23_2", "P23_3", "P23_4", "P23_5", "P23_6", "P23_7", + "P24_0", "P24_1", "P24_2", "P24_3", "P24_4", "P24_5", "P24_6", "P24_7", + "P25_0", "P25_1", "P25_2", "P25_3", "P25_4", "P25_5", "P25_6", "P25_7", + "P26_0", "P26_1", "P26_2", "P26_3", "P26_4", "P26_5", "P26_6", "P26_7", + "P27_0", "P27_1", "P27_2", "P27_3", "P27_4", "P27_5", "P27_6", "P27_7", + "P28_0", "P28_1", "P28_2", "P28_3", "P28_4", "P28_5", "P28_6", "P28_7", + "P29_0", "P29_1", "P29_2", "P29_3", "P29_4", "P29_5", "P29_6", "P29_7", + "P30_0", "P30_1", "P30_2", "P30_3", "P30_4", "P30_5", "P30_6", "P30_7", + "P31_0", "P31_1", "P31_2", "P31_3", "P31_4", "P31_5", "P31_6", "P31_7", + "P32_0", "P32_1", "P32_2", "P32_3", "P32_4", "P32_5", "P32_6", "P32_7", + "P33_0", "P33_1", "P33_2", "P33_3", "P33_4", "P33_5", "P33_6", "P33_7", + "P34_0", "P34_1", "P34_2", "P34_3", "P34_4", "P34_5", "P34_6", "P34_7", + "P35_0", "P35_1", "P35_2", "P35_3", "P35_4", "P35_5", "P35_6", "P35_7", + "P36_0", "P36_1", "P36_2", "P36_3", "P36_4", "P36_5", "P36_6", "P36_7", + "P37_0", "P37_1", "P37_2", "P37_3", "P37_4", "P37_5", "P37_6", "P37_7", + "P38_0", "P38_1", "P38_2", "P38_3", "P38_4", "P38_5", "P38_6", "P38_7", + "P39_0", "P39_1", "P39_2", "P39_3", "P39_4", "P39_5", "P39_6", "P39_7", + "P40_0", "P40_1", "P40_2", "P40_3", "P40_4", "P40_5", "P40_6", "P40_7", + "P41_0", "P41_1", "P41_2", "P41_3", "P41_4", "P41_5", "P41_6", "P41_7", + "P42_0", "P42_1", "P42_2", "P42_3", "P42_4", "P42_5", "P42_6", "P42_7", + "P43_0", "P43_1", "P43_2", "P43_3", "P43_4", "P43_5", "P43_6", "P43_7", + "P44_0", "P44_1", "P44_2", "P44_3", "P44_4", "P44_5", "P44_6", "P44_7", + "P45_0", "P45_1", "P45_2", "P45_3", "P45_4", "P45_5", "P45_6", "P45_7", + "P46_0", "P46_1", "P46_2", "P46_3", "P46_4", "P46_5", "P46_6", "P46_7", + "P47_0", "P47_1", "P47_2", "P47_3", "P47_4", "P47_5", "P47_6", "P47_7", + "P48_0", "P48_1", "P48_2", "P48_3", "P48_4", "P48_5", "P48_6", "P48_7", +}; + +static const u32 r9a07g044_gpio_configs[] = { + RZG2L_GPIO_PORT_PACK(2, 0x10, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x11, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x12, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x13, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x14, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(3, 0x15, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x16, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(3, 0x17, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(3, 0x18, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x19, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x1a, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x1b, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x1c, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(3, 0x1d, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x1e, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x1f, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x20, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(3, 0x21, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x22, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x23, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(3, 0x24, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH0)), + RZG2L_GPIO_PORT_PACK(2, 0x25, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH0)), + RZG2L_GPIO_PORT_PACK(2, 0x26, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH0)), + RZG2L_GPIO_PORT_PACK(2, 0x27, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH0)), + RZG2L_GPIO_PORT_PACK(2, 0x28, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH0)), + RZG2L_GPIO_PORT_PACK(2, 0x29, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH0)), + RZG2L_GPIO_PORT_PACK(2, 0x2a, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH0)), + RZG2L_GPIO_PORT_PACK(2, 0x2b, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH0)), + RZG2L_GPIO_PORT_PACK(2, 0x2c, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH0)), + RZG2L_GPIO_PORT_PACK(2, 0x2d, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH1)), + RZG2L_GPIO_PORT_PACK(2, 0x2e, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH1)), + RZG2L_GPIO_PORT_PACK(2, 0x2f, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH1)), + RZG2L_GPIO_PORT_PACK(2, 0x30, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH1)), + RZG2L_GPIO_PORT_PACK(2, 0x31, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH1)), + RZG2L_GPIO_PORT_PACK(2, 0x32, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH1)), + RZG2L_GPIO_PORT_PACK(2, 0x33, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH1)), + RZG2L_GPIO_PORT_PACK(2, 0x34, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH1)), + RZG2L_GPIO_PORT_PACK(3, 0x35, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IO_VMC_ETH1)), + RZG2L_GPIO_PORT_PACK(2, 0x36, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(3, 0x37, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(3, 0x38, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(2, 0x39, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(5, 0x3a, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(4, 0x3b, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(4, 0x3c, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(4, 0x3d, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(4, 0x3e, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(4, 0x3f, RZG2L_MPXED_PIN_FUNCS), + RZG2L_GPIO_PORT_PACK(5, 0x40, RZG2L_MPXED_PIN_FUNCS), +}; + +static const struct { + struct rzg2l_dedicated_configs common[35]; + struct rzg2l_dedicated_configs rzg2l_pins[7]; +} rzg2l_dedicated_pins = { + .common = { + { "NMI", RZG2L_SINGLE_PIN_PACK(0x1, 0, + (PIN_CFG_FILONOFF | PIN_CFG_FILNUM | PIN_CFG_FILCLKSEL)) }, + { "TMS/SWDIO", RZG2L_SINGLE_PIN_PACK(0x2, 0, + (PIN_CFG_IOLH_A | PIN_CFG_SR | PIN_CFG_IEN)) }, + { "TDO", RZG2L_SINGLE_PIN_PACK(0x3, 0, + (PIN_CFG_IOLH_A | PIN_CFG_SR | PIN_CFG_IEN)) }, + { "AUDIO_CLK1", RZG2L_SINGLE_PIN_PACK(0x4, 0, PIN_CFG_IEN) }, + { "AUDIO_CLK2", RZG2L_SINGLE_PIN_PACK(0x4, 1, PIN_CFG_IEN) }, + { "SD0_CLK", RZG2L_SINGLE_PIN_PACK(0x6, 0, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_SD0)) }, + { "SD0_CMD", RZG2L_SINGLE_PIN_PACK(0x6, 1, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD0)) }, + { "SD0_RST#", RZG2L_SINGLE_PIN_PACK(0x6, 2, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_SD0)) }, + { "SD0_DATA0", RZG2L_SINGLE_PIN_PACK(0x7, 0, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD0)) }, + { "SD0_DATA1", RZG2L_SINGLE_PIN_PACK(0x7, 1, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD0)) }, + { "SD0_DATA2", RZG2L_SINGLE_PIN_PACK(0x7, 2, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD0)) }, + { "SD0_DATA3", RZG2L_SINGLE_PIN_PACK(0x7, 3, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD0)) }, + { "SD0_DATA4", RZG2L_SINGLE_PIN_PACK(0x7, 4, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD0)) }, + { "SD0_DATA5", RZG2L_SINGLE_PIN_PACK(0x7, 5, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD0)) }, + { "SD0_DATA6", RZG2L_SINGLE_PIN_PACK(0x7, 6, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD0)) }, + { "SD0_DATA7", RZG2L_SINGLE_PIN_PACK(0x7, 7, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD0)) }, + { "SD1_CLK", RZG2L_SINGLE_PIN_PACK(0x8, 0, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_SD1)) }, + { "SD1_CMD", RZG2L_SINGLE_PIN_PACK(0x8, 1, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD1)) }, + { "SD1_DATA0", RZG2L_SINGLE_PIN_PACK(0x9, 0, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD1)) }, + { "SD1_DATA1", RZG2L_SINGLE_PIN_PACK(0x9, 1, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD1)) }, + { "SD1_DATA2", RZG2L_SINGLE_PIN_PACK(0x9, 2, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD1)) }, + { "SD1_DATA3", RZG2L_SINGLE_PIN_PACK(0x9, 3, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD1)) }, + { "QSPI0_SPCLK", RZG2L_SINGLE_PIN_PACK(0xa, 0, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI0_IO0", RZG2L_SINGLE_PIN_PACK(0xa, 1, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI0_IO1", RZG2L_SINGLE_PIN_PACK(0xa, 2, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI0_IO2", RZG2L_SINGLE_PIN_PACK(0xa, 3, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI0_IO3", RZG2L_SINGLE_PIN_PACK(0xa, 4, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI0_SSL", RZG2L_SINGLE_PIN_PACK(0xa, 5, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI_RESET#", RZG2L_SINGLE_PIN_PACK(0xc, 0, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI_WP#", RZG2L_SINGLE_PIN_PACK(0xc, 1, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "WDTOVF_PERROUT#", RZG2L_SINGLE_PIN_PACK(0xd, 0, (PIN_CFG_IOLH_A | PIN_CFG_SR)) }, + { "RIIC0_SDA", RZG2L_SINGLE_PIN_PACK(0xe, 0, PIN_CFG_IEN) }, + { "RIIC0_SCL", RZG2L_SINGLE_PIN_PACK(0xe, 1, PIN_CFG_IEN) }, + { "RIIC1_SDA", RZG2L_SINGLE_PIN_PACK(0xe, 2, PIN_CFG_IEN) }, + { "RIIC1_SCL", RZG2L_SINGLE_PIN_PACK(0xe, 3, PIN_CFG_IEN) }, + }, + .rzg2l_pins = { + { "QSPI_INT#", RZG2L_SINGLE_PIN_PACK(0xc, 2, (PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI1_SPCLK", RZG2L_SINGLE_PIN_PACK(0xb, 0, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI1_IO0", RZG2L_SINGLE_PIN_PACK(0xb, 1, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI1_IO1", RZG2L_SINGLE_PIN_PACK(0xb, 2, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI1_IO2", RZG2L_SINGLE_PIN_PACK(0xb, 3, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI1_IO3", RZG2L_SINGLE_PIN_PACK(0xb, 4, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + { "QSPI1_SSL", RZG2L_SINGLE_PIN_PACK(0xb, 5, + (PIN_CFG_IOLH_B | PIN_CFG_SR | PIN_CFG_IO_VMC_QSPI)) }, + } +}; + +static void rzg2l_rmw_pin_config(const struct rzg2l_pfc_data *data, u32 offset, + u8 pin, u32 mask, u32 val) +{ + void __iomem *addr = data->base + offset; + + /* handle _L/_H for 32-bit register read/write */ + if (pin >= 4) { + pin -= 4; + addr += 4; + } + + clrsetbits_le32(addr, mask << (pin * 8), val << (pin * 8)); +} + +static int rzg2l_get_pins_count(struct udevice *dev) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + + return data->num_dedicated_pins + data->num_pins; +} + +static const char *rzg2l_get_pin_name(struct udevice *dev, unsigned int selector) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + int port; + u8 pin; + + if (selector < data->num_dedicated_pins) { + if (selector >= ARRAY_SIZE(rzg2l_dedicated_pins.common)) { + unsigned int u = selector - ARRAY_SIZE(rzg2l_dedicated_pins.common); + return rzg2l_dedicated_pins.rzg2l_pins[u].name; + } else { + return rzg2l_dedicated_pins.common[selector].name; + } + } + + port = rzg2l_selector_decode(data, selector, &pin); + if (port < 0) + return "(invalid pin)"; + return rzg2l_gpio_names[pin + 8 * port]; +} + +static int rzg2l_pinconf_set(struct udevice *dev, unsigned int pin_selector, + unsigned int param, unsigned int argument) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + u32 cfg, port_offset; + u8 pin; + + if (pin_selector >= data->num_dedicated_pins) { + /* The pin selector refers to a multiplexed pin */ + int port = rzg2l_selector_decode(data, pin_selector, &pin); + if (port < 0) { + dev_err(dev, "Invalid pin selector %u:%u\n", port, pin); + return port; + } + + cfg = data->gpio_configs[port]; + port_offset = P(port); + } else { + /* The pin selector refers to a dedicated function pin */ + const struct rzg2l_dedicated_configs *dedicated_config; + + if (pin_selector >= data->num_dedicated_pins) { + dev_err(dev, "Invalid dedicated pin %u\n", pin_selector); + return -EINVAL; + } + + if (pin_selector >= ARRAY_SIZE(rzg2l_dedicated_pins.common)) { + pin_selector -= ARRAY_SIZE(rzg2l_dedicated_pins.common); + dedicated_config = &rzg2l_dedicated_pins.rzg2l_pins[pin_selector]; + } else { + dedicated_config = &rzg2l_dedicated_pins.common[pin_selector]; + } + + port_offset = RZG2L_SINGLE_PIN_GET_PORT_OFFSET(dedicated_config->config); + pin = RZG2L_SINGLE_PIN_GET_BIT(dedicated_config->config); + cfg = RZG2L_SINGLE_PIN_GET_CFGS(dedicated_config->config); + } + + switch (param) { + case PIN_CONFIG_INPUT_ENABLE: { + if (!(cfg & PIN_CFG_IEN)) { + dev_err(dev, "pin does not support IEN\n"); + return -EINVAL; + } + + dev_dbg(dev, "port off %u:%u set IEN=%u\n", + port_offset, pin, argument); + rzg2l_rmw_pin_config(data, IEN(port_offset), pin, IEN_MASK, !!argument); + break; + } + + case PIN_CONFIG_POWER_SOURCE: { + u32 pwr_reg = 0x0; + + /* argument is in mV */ + if (argument != 1800 && argument != 3300) { + dev_err(dev, "Invalid mV %u\n", argument); + return -EINVAL; + } + + /* + * TODO: PIN_CFG_IO_VMC_ETH0 & PIN_CFG_IO_VMC_ETH1 will be + * handled when the RZ/G2L Ethernet driver is added. + */ + if (cfg & PIN_CFG_IO_VMC_SD0) { + dev_dbg(dev, "port off %u:%u set SD_CH 0 PVDD=%u\n", + port_offset, pin, argument); + pwr_reg = SD_CH(0); + } else if (cfg & PIN_CFG_IO_VMC_SD1) { + dev_dbg(dev, "port off %u:%u set SD_CH 1 PVDD=%u\n", + port_offset, pin, argument); + pwr_reg = SD_CH(1); + } else if (cfg & PIN_CFG_IO_VMC_QSPI) { + dev_dbg(dev, "port off %u:%u set QSPI PVDD=%u\n", + port_offset, pin, argument); + pwr_reg = QSPI; + } else { + dev_dbg(dev, "pin power source is not selectable\n"); + return -EINVAL; + } + + writel((argument == 1800) ? PVDD_1800 : PVDD_3300, + data->base + pwr_reg); + break; + } + + default: + dev_err(dev, "Invalid pinconf parameter\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int rzg2l_pinmux_property_set(struct udevice *dev, u32 pinmux_group) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + u32 port, pin, func, pfc_state; + u8 pmc_state; + + func = RZG2L_PINMUX_TO_FUNC(pinmux_group); + if (func > 5) { + dev_err(dev, "Invalid pin function %u\n", func); + return -EINVAL; + } + + port = RZG2L_PINMUX_TO_PORT(pinmux_group); + pin = RZG2L_PINMUX_TO_PIN(pinmux_group); + if (!rzg2l_port_validate(data, port, pin)) { + dev_err(dev, "Invalid pin selector %u:%u\n", port, pin); + return -EINVAL; + } + + /* Check current PMC & PFC to decide if we need to change anything. */ + pmc_state = readb(data->base + PMC(port)) & BIT(pin); + pfc_state = (readl(data->base + PFC(port)) >> (pin * 4)) & PFC_MASK; + if (pmc_state && pfc_state == func) + return 0; + + dev_dbg(dev, "pinmux port %u pin %u func %u\n", port, pin, func); + + /* Set pin to 'Non-use (Hi-Z input protection)' */ + clrbits_le16(data->base + PM(port), PM_MASK << (pin * 2)); + + /* Temporarily switch to GPIO mode with PMC register */ + clrbits_8(data->base + PMC(port), BIT(pin)); + + /* Set the PWPR register to allow PFC register to write */ + writel(0x0, data->base + PWPR); /* B0WI=0, PFCWE=0 */ + writel(PWPR_PFCWE, data->base + PWPR); /* B0WI=0, PFCWE=1 */ + + /* Select Pin function mode with PFC register */ + clrsetbits_le32(data->base + PFC(port), PFC_MASK << (pin * 4), + func << (pin * 4)); + + /* Set the PWPR register to be write-protected */ + writel(0x0, data->base + PWPR); /* B0WI=0, PFCWE=0 */ + writel(PWPR_B0WI, data->base + PWPR); /* B0WI=1, PFCWE=0 */ + + /* Switch to Peripheral pin function with PMC register */ + setbits_8(data->base + PMC(port), BIT(pin)); + + return rzg2l_selector_encode(data, port, pin); +} + +static int rzg2l_get_pin_muxing(struct udevice *dev, unsigned int selector, + char *buf, int size) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + u32 pmc_state; + int port; + u8 pin; + + if (selector < data->num_dedicated_pins) { + snprintf(buf, size, rzg2l_get_pin_name(dev, selector)); + return 0; + } + + port = rzg2l_selector_decode(data, selector, &pin); + if (port < 0) { + dev_err(dev, "Invalid pin selector %u:%u\n", port, pin); + return port; + } + + pmc_state = readb(data->base + PMC(port)) & BIT(pin); + if (pmc_state) { + u32 pfc_state = (readl(data->base + PFC(port)) >> (pin * 4)) & PFC_MASK; + snprintf(buf, size, "Function %d", pfc_state); + return 0; + } + + snprintf(buf, size, "GPIO"); + return 0; +} + +static const struct pinconf_param rzg2l_pinconf_params[] = { + { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 }, + { "power-source", PIN_CONFIG_POWER_SOURCE, 3300 /* mV */ }, +}; + +static const struct pinctrl_ops rzg2l_pinctrl_ops = { + .get_pins_count = rzg2l_get_pins_count, + .get_pin_name = rzg2l_get_pin_name, + + .pinconf_num_params = ARRAY_SIZE(rzg2l_pinconf_params), + .pinconf_params = rzg2l_pinconf_params, + .pinconf_set = rzg2l_pinconf_set, + + .pinmux_property_set = rzg2l_pinmux_property_set, + .set_state = pinctrl_generic_set_state, + .get_pin_muxing = rzg2l_get_pin_muxing, +}; + +static int rzg2l_pinctrl_probe(struct udevice *dev) +{ + return rzg2l_pfc_enable(dev); +} + +U_BOOT_DRIVER(rzg2l_pfc_pinctrl) = { + .name = "rzg2l-pfc-pinctrl", + .id = UCLASS_PINCTRL, + .ops = &rzg2l_pinctrl_ops, + .probe = rzg2l_pinctrl_probe, +}; + +static const struct rzg2l_pfc_driver_data r9a07g044_driver_data = { + .num_dedicated_pins = ARRAY_SIZE(rzg2l_dedicated_pins.common) + + ARRAY_SIZE(rzg2l_dedicated_pins.rzg2l_pins), + .num_ports = ARRAY_SIZE(r9a07g044_gpio_configs), + .gpio_configs = r9a07g044_gpio_configs, +}; + +static const struct udevice_id rzg2l_pfc_ids[] = { + { .compatible = "renesas,r9a07g044-pinctrl", .data = (ulong)&r9a07g044_driver_data }, + { /* sentinel */ } +}; + +static int rzg2l_pfc_bind(struct udevice *parent) +{ + struct rzg2l_pfc_driver_data *driver_data; + struct rzg2l_pfc_data *data; + struct udevice *pinctrl_dev; + struct driver *drv; + unsigned int i; + int ret; + + driver_data = + (struct rzg2l_pfc_driver_data *)dev_get_driver_data(parent); + if (!driver_data) + return -EINVAL; + data = devm_kmalloc(parent, sizeof(*data), 0); + if (!data) + return -ENOMEM; + + data->base = dev_read_addr_ptr(parent); + if (!data->base) + return -EINVAL; + data->num_dedicated_pins = driver_data->num_dedicated_pins; + data->num_ports = driver_data->num_ports; + data->gpio_configs = driver_data->gpio_configs; + + data->num_pins = 0; + for (i = 0; i < data->num_ports; i++) + data->num_pins += RZG2L_GPIO_PORT_GET_PINCNT(data->gpio_configs[i]); + dev_dbg(parent, "%u dedicated pins, %u muxed ports, %u muxed pins\n", + data->num_dedicated_pins, data->num_ports, data->num_pins); + + drv = lists_driver_lookup_name("rzg2l-pfc-pinctrl"); + if (!drv) + return -ENOENT; + + ret = device_bind_with_driver_data(parent, drv, parent->name, + (ulong)data, dev_ofnode(parent), + &pinctrl_dev); + + if (!ret && IS_ENABLED(CONFIG_RZG2L_GPIO)) { + drv = lists_driver_lookup_name("rzg2l-pfc-gpio"); + if (!drv) { + device_unbind(pinctrl_dev); + return -ENOENT; + } + + ret = device_bind_with_driver_data(parent, drv, parent->name, + (ulong)data, + dev_ofnode(parent), NULL); + if (ret) + device_unbind(pinctrl_dev); + } + + return ret; +} + +U_BOOT_DRIVER(rzg2l_pfc) = { + .name = "rzg2l-pfc", + .id = UCLASS_NOP, + .of_match = rzg2l_pfc_ids, + .bind = rzg2l_pfc_bind, +}; diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig index 77da90836b6..cbd61795986 100644 --- a/drivers/pinctrl/sunxi/Kconfig +++ b/drivers/pinctrl/sunxi/Kconfig @@ -124,4 +124,9 @@ config PINCTRL_SUN50I_H616_R default MACH_SUN50I_H616 select PINCTRL_SUNXI +config PINCTRL_SUN20I_D1 + bool "Support for the Allwinner D1/R528 PIO" + default MACH_SUN8I_R528 + select PINCTRL_SUNXI + endif diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index e5102180902..37ea93715d1 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -7,6 +7,7 @@ #include <dm/pinctrl.h> #include <errno.h> #include <malloc.h> +#include <sunxi_gpio.h> #include <asm/gpio.h> @@ -35,7 +36,7 @@ struct sunxi_pinctrl_desc { }; struct sunxi_pinctrl_plat { - struct sunxi_gpio __iomem *base; + void __iomem *base; }; static int sunxi_pinctrl_get_pins_count(struct udevice *dev) @@ -49,7 +50,7 @@ static const char *sunxi_pinctrl_get_pin_name(struct udevice *dev, uint pin_selector) { const struct sunxi_pinctrl_desc *desc = dev_get_priv(dev); - static char pin_name[sizeof("PN31")]; + static char pin_name[sizeof("PN31")] __section(".data"); snprintf(pin_name, sizeof(pin_name), "P%c%d", pin_selector / SUNXI_GPIOS_PER_BANK + desc->first_bank + 'A', @@ -86,8 +87,8 @@ static int sunxi_pinctrl_pinmux_set(struct udevice *dev, uint pin_selector, sunxi_pinctrl_get_function_name(dev, func_selector), desc->functions[func_selector].mux); - sunxi_gpio_set_cfgbank(plat->base + bank, pin, - desc->functions[func_selector].mux); + sunxi_gpio_set_cfgbank(plat->base + bank * SUNXI_PINCTRL_BANK_SIZE, + pin, desc->functions[func_selector].mux); return 0; } @@ -102,7 +103,7 @@ static const struct pinconf_param sunxi_pinctrl_pinconf_params[] = { static int sunxi_pinctrl_pinconf_set_pull(struct sunxi_pinctrl_plat *plat, uint bank, uint pin, uint bias) { - struct sunxi_gpio *regs = &plat->base[bank]; + void *regs = plat->base + bank * SUNXI_PINCTRL_BANK_SIZE; sunxi_gpio_set_pull_bank(regs, pin, bias); @@ -112,7 +113,7 @@ static int sunxi_pinctrl_pinconf_set_pull(struct sunxi_pinctrl_plat *plat, static int sunxi_pinctrl_pinconf_set_drive(struct sunxi_pinctrl_plat *plat, uint bank, uint pin, uint drive) { - struct sunxi_gpio *regs = &plat->base[bank]; + void *regs = plat->base + bank * SUNXI_PINCTRL_BANK_SIZE; if (drive < 10 || drive > 40) return -EINVAL; @@ -148,7 +149,7 @@ static int sunxi_pinctrl_get_pin_muxing(struct udevice *dev, uint pin_selector, struct sunxi_pinctrl_plat *plat = dev_get_plat(dev); int bank = pin_selector / SUNXI_GPIOS_PER_BANK; int pin = pin_selector % SUNXI_GPIOS_PER_BANK; - int mux = sunxi_gpio_get_cfgbank(plat->base + bank, pin); + int mux = sunxi_gpio_get_cfgbank(plat->base + bank * SUNXI_PINCTRL_BANK_SIZE, pin); switch (mux) { case SUNXI_GPIO_INPUT: @@ -206,7 +207,7 @@ static int sunxi_pinctrl_bind(struct udevice *dev) if (!gpio_plat) return -ENOMEM; - gpio_plat->regs = plat->base + i; + gpio_plat->regs = plat->base + i * SUNXI_PINCTRL_BANK_SIZE; gpio_plat->bank_name[0] = 'P'; gpio_plat->bank_name[1] = 'A' + desc->first_bank + i; gpio_plat->bank_name[2] = '\0'; @@ -597,6 +598,32 @@ static const struct sunxi_pinctrl_desc __maybe_unused sun9i_a80_r_pinctrl_desc = .num_banks = 3, }; +static const struct sunxi_pinctrl_function sun20i_d1_pinctrl_functions[] = { + { "emac", 8 }, /* PE0-PE15 */ + { "gpio_in", 0 }, + { "gpio_out", 1 }, + { "i2c0", 4 }, /* PB10-PB11 */ + { "mmc0", 2 }, /* PF0-PF5 */ + { "mmc1", 2 }, /* PG0-PG5 */ + { "mmc2", 3 }, /* PC2-PC7 */ + { "spi0", 2 }, /* PC2-PC7 */ +#if IS_ENABLED(CONFIG_UART0_PORT_F) + { "uart0", 3 }, /* PF2,PF4 */ +#else + { "uart0", 6 }, /* PB0-PB1, PB8-PB9, PE2-PE3 */ +#endif + { "uart1", 2 }, /* PG6-PG7 */ + { "uart2", 7 }, /* PB0-PB1 */ + { "uart3", 7 }, /* PB6-PB7 */ +}; + +static const struct sunxi_pinctrl_desc __maybe_unused sun20i_d1_pinctrl_desc = { + .functions = sun20i_d1_pinctrl_functions, + .num_functions = ARRAY_SIZE(sun20i_d1_pinctrl_functions), + .first_bank = SUNXI_GPIO_A, + .num_banks = 7, +}; + static const struct sunxi_pinctrl_function sun50i_a64_pinctrl_functions[] = { { "emac", 4 }, /* PD8-PD23 */ { "gpio_in", 0 }, @@ -862,6 +889,12 @@ static const struct udevice_id sunxi_pinctrl_ids[] = { .data = (ulong)&sun9i_a80_r_pinctrl_desc, }, #endif +#ifdef CONFIG_PINCTRL_SUN20I_D1 + { + .compatible = "allwinner,sun20i-d1-pinctrl", + .data = (ulong)&sun20i_d1_pinctrl_desc, + }, +#endif #ifdef CONFIG_PINCTRL_SUN50I_A64 { .compatible = "allwinner,sun50i-a64-pinctrl", diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 7f3b990d231..2395720c99c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -101,6 +101,15 @@ config AXP305_POWER Select this to enable support for the axp305 pmic found on most H616 boards. +config AXP313_POWER + bool "axp313 pmic support" + depends on MACH_SUN50I_H616 + select AXP_PMIC_BUS + select CMD_POWEROFF + ---help--- + Select this to enable support for the AXP313 PMIC found on some + H616 boards. + config AXP809_POWER bool "axp809 pmic support" depends on MACH_SUN9I @@ -143,9 +152,10 @@ config AXP_DCDC1_VOLT config AXP_DCDC2_VOLT int "axp pmic dcdc2 voltage" - depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER + depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER default 900 if AXP818_POWER default 1400 if AXP152_POWER || AXP209_POWER + default 1000 if AXP313_POWER default 1200 if MACH_SUN6I default 1100 if MACH_SUN8I default 0 if MACH_SUN9I @@ -158,13 +168,15 @@ config AXP_DCDC2_VOLT On A80 boards dcdc2 powers the GPU and can be left off. On A83T boards dcdc2 is used for VDD-CPUA(cluster 0) and should be 0.9V. On R40 boards dcdc2 is VDD-CPU and should be 1.1V + On boards using the AXP313 it's often VDD-CPU. config AXP_DCDC3_VOLT int "axp pmic dcdc3 voltage" - depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER + depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER default 900 if AXP809_POWER || AXP818_POWER default 1500 if AXP152_POWER default 1250 if AXP209_POWER + default 1100 if AXP313_POWER default 1100 if MACH_SUN8I_R40 default 1200 if MACH_SUN6I || MACH_SUN8I ---help--- @@ -177,10 +189,11 @@ config AXP_DCDC3_VOLT On A80 boards dcdc3 is used for VDD-CPUA(cluster 0) and should be 0.9V. On A83T boards dcdc3 is used for VDD-CPUB(cluster 1) and should be 0.9V. On R40 boards dcdc3 is VDD-SYS and VDD-GPU and should be 1.1V. + On boards using the AXP313 it's often VDD-DRAM and should be 1.1V for LPDDR4. config AXP_DCDC4_VOLT int "axp pmic dcdc4 voltage" - depends on AXP152_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP305_POWER + depends on AXP152_POWER || AXP221_POWER || AXP809_POWER || AXP305_POWER default 1250 if AXP152_POWER default 1200 if MACH_SUN6I default 0 if MACH_SUN8I diff --git a/drivers/power/Makefile b/drivers/power/Makefile index ba64b2c5938..c7ee4595fc8 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_AXP152_POWER) += axp152.o obj-$(CONFIG_AXP209_POWER) += axp209.o obj-$(CONFIG_AXP221_POWER) += axp221.o obj-$(CONFIG_AXP305_POWER) += axp305.o +obj-$(CONFIG_AXP313_POWER) += axp313.o obj-$(CONFIG_AXP809_POWER) += axp809.o obj-$(CONFIG_AXP818_POWER) += axp818.o obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o diff --git a/drivers/power/axp313.c b/drivers/power/axp313.c new file mode 100644 index 00000000000..bbc9e911115 --- /dev/null +++ b/drivers/power/axp313.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AXP313(a) driver + * + * (C) Copyright 2023 Arm Ltd. + * + * Based on axp305.c + * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net> + * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * (C) Copyright 2013 Oliver Schinagl <oliver@schinagl.nl> + */ + +#include <common.h> +#include <command.h> +#include <errno.h> +#include <asm/arch/pmic_bus.h> +#include <axp_pmic.h> + +enum axp313_reg { + AXP313_CHIP_VERSION = 0x03, + AXP313_OUTPUT_CTRL = 0x10, + AXP313_DCDC1_CTRL = 0x13, + AXP313_SHUTDOWN = 0x1a, +}; + +#define AXP313_CHIP_VERSION_MASK 0xcf +#define AXP313_CHIP_VERSION_AXP1530 0x48 +#define AXP313_CHIP_VERSION_AXP313A 0x4b +#define AXP313_CHIP_VERSION_AXP313B 0x4c + +#define AXP313_DCDC_SPLIT_OFFSET 71 +#define AXP313_DCDC_SPLIT_MVOLT 1200 + +#define AXP313_POWEROFF BIT(7) + +static u8 mvolt_to_cfg(int mvolt, int min, int max, int div) +{ + if (mvolt < min) + mvolt = min; + else if (mvolt > max) + mvolt = max; + + return (mvolt - min) / div; +} + +static int axp_set_dcdc(int dcdc_num, unsigned int mvolt) +{ + int ret; + u8 cfg, enable_mask = 1U << (dcdc_num - 1); + int volt_reg = AXP313_DCDC1_CTRL + dcdc_num - 1; + int max_mV; + + switch (dcdc_num) { + case 1: + case 2: + max_mV = 1540; + break; + case 3: + /* + * The manual defines a different split point, but tests + * show that it's the same 1200mV as for DCDC1/2. + */ + max_mV = 1840; + break; + default: + return -EINVAL; + } + + if (mvolt > AXP313_DCDC_SPLIT_MVOLT) + cfg = AXP313_DCDC_SPLIT_OFFSET + mvolt_to_cfg(mvolt, + AXP313_DCDC_SPLIT_MVOLT + 20, max_mV, 20); + else + cfg = mvolt_to_cfg(mvolt, 500, AXP313_DCDC_SPLIT_MVOLT, 10); + + if (mvolt == 0) + return pmic_bus_clrbits(AXP313_OUTPUT_CTRL, enable_mask); + + debug("DCDC%d: writing 0x%x to reg 0x%x\n", dcdc_num, cfg, volt_reg); + ret = pmic_bus_write(volt_reg, cfg); + if (ret) + return ret; + + return pmic_bus_setbits(AXP313_OUTPUT_CTRL, enable_mask); +} + +int axp_set_dcdc2(unsigned int mvolt) +{ + return axp_set_dcdc(2, mvolt); +} + +int axp_set_dcdc3(unsigned int mvolt) +{ + return axp_set_dcdc(3, mvolt); +} + +int axp_init(void) +{ + u8 axp_chip_id; + int ret; + + ret = pmic_bus_init(); + if (ret) + return ret; + + ret = pmic_bus_read(AXP313_CHIP_VERSION, &axp_chip_id); + if (ret) + return ret; + + axp_chip_id &= AXP313_CHIP_VERSION_MASK; + switch (axp_chip_id) { + case AXP313_CHIP_VERSION_AXP1530: + case AXP313_CHIP_VERSION_AXP313A: + case AXP313_CHIP_VERSION_AXP313B: + break; + default: + debug("unknown PMIC: 0x%x\n", axp_chip_id); + return -EINVAL; + } + + return ret; +} + +#if !CONFIG_IS_ENABLED(ARM_PSCI_FW) && !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) +int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + pmic_bus_write(AXP313_SHUTDOWN, AXP313_POWEROFF); + + /* infinite loop during shutdown */ + while (1) {} + + /* not reached */ + return 0; +} +#endif diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index 411c210756a..bd82d2f7044 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -83,6 +83,13 @@ config SANDBOX_POWER_DOMAIN simply accepts requests to power on/off various HW modules without actually doing anything beyond a little error checking. +config SCMI_POWER_DOMAIN + bool "Enable SCMI power domain driver" + depends on POWER_DOMAIN && SCMI_FIRMWARE + help + Enable power domain implementation based on SCMI power domain + management protocol. + config TEGRA186_POWER_DOMAIN bool "Enable Tegra186 BPMP-based power domain driver" depends on TEGRA186_BPMP diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index aa5a4ba57cd..2daab73eb75 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_MESON_EE_POWER_DOMAIN) += meson-ee-pwrc.o obj-$(CONFIG_MESON_SECURE_POWER_DOMAIN) += meson-secure-pwrc.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o +obj-$(CONFIG_SCMI_POWER_DOMAIN) += scmi-power-domain.o obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o obj-$(CONFIG_TI_SCI_POWER_DOMAIN) += ti-sci-power-domain.o obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o diff --git a/drivers/power/domain/scmi-power-domain.c b/drivers/power/domain/scmi-power-domain.c new file mode 100644 index 00000000000..3cd0f075d95 --- /dev/null +++ b/drivers/power/domain/scmi-power-domain.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SCMI Power domain driver + * + * Copyright (C) 2023 Linaro Limited + * author: AKASHI Takahiro <takahiro.akashi@linaro.org> + */ + +#include <dm.h> +#include <malloc.h> +#include <power-domain.h> +#include <power-domain-uclass.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include <dm/device_compat.h> + +/** + * struct scmi_pwd_properties + * @attributes: Power domain attributes + * @name: Name of the domain + */ +struct scmi_pwd_properties { + u32 attributes; + u8 *name; /* not used now */ +}; + +/** + * struct scmi_power_domain_priv + * @num_pwdoms: Number of power domains + * @prop: Pointer to domain's properties + * @stats_addr: Address of statistics memory region + * @stats_len: Length of statistics memory region + */ +struct scmi_power_domain_priv { + int num_pwdoms; + struct scmi_pwd_properties *prop; + u64 stats_addr; + size_t stats_len; +}; + +/** + * async_is_supported - check asynchronous transition + * @attributes: Power domain attributes + * + * Determine if the power transition can be done asynchronously. + * + * Return: true if supported, false if not + */ +static bool async_is_supported(u32 attributes) +{ + if (attributes & SCMI_PWD_ATTR_PSTATE_ASYNC) + return true; + + /* TODO: check attributes && SCMI_PWD_ATTR_PSTATE_SYNC */ + return false; +} + +/** + * scmi_power_domain_on - Enable the power domain + * @power_domain: Power domain + * + * Turn on the power domain. + * + * Return: 0 on success, error code on failure + */ +static int scmi_power_domain_on(struct power_domain *power_domain) +{ + struct scmi_power_domain_priv *priv = dev_get_priv(power_domain->dev); + u32 flags, pstate; + int ret; + + if (power_domain->id > priv->num_pwdoms) + return -EINVAL; + + if (async_is_supported(priv->prop[power_domain->id].attributes)) + flags = SCMI_PWD_SET_FLAGS_ASYNC; + else + flags = 0; + + /* ON */ + pstate = 0; + + ret = scmi_pwd_state_set(power_domain->dev, flags, power_domain->id, + pstate); + if (ret) { + dev_err(power_domain->dev, "failed to set the state on (%d)\n", + ret); + return ret; + } + + return 0; +} + +/** + * scmi_power_domain_off - Disable the power domain + * @power_domain: Power domain + * + * Turn off the power domain. + * + * Return: 0 on success, error code on failure + */ +static int scmi_power_domain_off(struct power_domain *power_domain) +{ + struct scmi_power_domain_priv *priv = dev_get_priv(power_domain->dev); + u32 flags, pstate; + int ret; + + if (power_domain->id > priv->num_pwdoms) + return -EINVAL; + + if (async_is_supported(priv->prop[power_domain->id].attributes)) + flags = SCMI_PWD_SET_FLAGS_ASYNC; + else + flags = 0; + + /* OFF */ + pstate = SCMI_PWD_PSTATE_TYPE_LOST; + + ret = scmi_pwd_state_set(power_domain->dev, flags, power_domain->id, + pstate); + if (ret) { + dev_err(power_domain->dev, "failed to set the state off (%d)\n", + ret); + return ret; + } + + return 0; +} + +/** + * scmi_power_domain_probe - Probe the power domain + * @dev: Power domain device + * + * Probe the power domain and initialize the properties. + * + * Return: 0 on success, error code on failure + */ +static int scmi_power_domain_probe(struct udevice *dev) +{ + struct scmi_power_domain_priv *priv = dev_get_priv(dev); + u32 version; + int i, ret; + + ret = devm_scmi_of_get_channel(dev); + if (ret) { + dev_err(dev, "failed to get channel (%d)\n", ret); + return ret; + } + + ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_POWER_DOMAIN, + &version); + + ret = scmi_pwd_protocol_attrs(dev, &priv->num_pwdoms, &priv->stats_addr, + &priv->stats_len); + if (ret) { + dev_err(dev, "failed to get protocol attributes (%d)\n", ret); + return ret; + } + + priv->prop = calloc(sizeof(*priv->prop), priv->num_pwdoms); + if (!priv->prop) + return -ENOMEM; + + for (i = 0; i < priv->num_pwdoms; i++) { + ret = scmi_pwd_attrs(dev, i, &priv->prop[i].attributes, + &priv->prop[i].name); + if (ret) { + dev_err(dev, "failed to get attributes pwd:%d (%d)\n", + i, ret); + for (i--; i >= 0; i--) + free(priv->prop[i].name); + free(priv->prop); + + return ret; + } + } + + return 0; +} + +struct power_domain_ops scmi_power_domain_ops = { + .on = scmi_power_domain_on, + .off = scmi_power_domain_off, +}; + +U_BOOT_DRIVER(scmi_power_domain) = { + .name = "scmi_power_domain", + .id = UCLASS_POWER_DOMAIN, + .ops = &scmi_power_domain_ops, + .probe = scmi_power_domain_probe, + .priv_auto = sizeof(struct scmi_power_domain_priv), +}; diff --git a/drivers/power/domain/ti-power-domain.c b/drivers/power/domain/ti-power-domain.c index 9e7151307c8..b34c982f4f5 100644 --- a/drivers/power/domain/ti-power-domain.c +++ b/drivers/power/domain/ti-power-domain.c @@ -2,7 +2,7 @@ /* * Texas Instruments power domain driver * - * Copyright (C) 2020-2021 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ * Tero Kristo <t-kristo@ti.com> */ diff --git a/drivers/power/domain/ti-sci-power-domain.c b/drivers/power/domain/ti-sci-power-domain.c index 0140e5e5217..8d6abe13dbc 100644 --- a/drivers/power/domain/ti-sci-power-domain.c +++ b/drivers/power/domain/ti-sci-power-domain.c @@ -2,7 +2,7 @@ /* * Texas Instruments System Control Interface (TI SCI) power domain driver * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Andreas Dannenberg <dannenberg@ti.com> * * Loosely based on Linux kernel ti_sci_pm_domains.c... diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 4a6f0ce093a..454a6e0cf87 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -184,6 +184,15 @@ config SPL_DM_PMIC_PFUZE100 This config enables implementation of driver-model pmic uclass features for PMIC PFUZE100 in SPL. The driver implements read/write operations. +config DM_PMIC_MAX77663 + bool "Enable Driver Model for PMIC MAX77663" + ---help--- + This config enables implementation of driver-model pmic uclass features + for PMIC MAX77663. The driver implements read/write operations. + This is a Power Management IC with a decent set of peripherals from which + 4 DC-to-DC Step-Down (SD) Regulators, 9 Low-Dropout Linear (LDO) Regulators, + 8 GPIOs, Real-Time Clock (RTC) and more with I2C Compatible Interface. + config DM_PMIC_MAX77686 bool "Enable Driver Model for PMIC MAX77686" ---help--- @@ -342,6 +351,17 @@ config DM_PMIC_TPS65910 DC-DC converter, 8 LDOs and a RTC. This driver binds the SMPS and LDO pmic children. +config DM_PMIC_TPS80031 + bool "Enable driver for Texas Instruments TPS80031/TPS80032 PMIC" + ---help--- + This config enables implementation of driver-model pmic uclass features + for TPS80031/TPS80032 PMICs. The driver implements read/write operations. + This is a Power Management IC with a decent set of peripherals from which + 5 Buck Converters refered as Switched-mode power supply (SMPS), 11 General- + Purpose Low-Dropout Voltage Regulators (LDO), USB OTG Module, Real-Time + Clock (RTC) with Timer and Alarm Wake-Up, Two Digital PWM Outputs and more + with I2C Compatible Interface. PMIC occupies 4 I2C addresses. + config PMIC_STPMIC1 bool "Enable support for STMicroelectronics STPMIC1 PMIC" depends on DM_I2C diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 0b3b3d62d0e..55ee614364b 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_$(SPL_TPL_)DM_PMIC) += pmic-uclass.o obj-$(CONFIG_$(SPL_)DM_PMIC_FAN53555) += fan53555.o obj-$(CONFIG_$(SPL_)DM_PMIC_DA9063) += da9063.o +obj-$(CONFIG_$(SPL_)DM_PMIC_MAX77663) += max77663.o obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o obj-$(CONFIG_DM_PMIC_MAX8998) += max8998.o obj-$(CONFIG_DM_PMIC_MC34708) += mc34708.o @@ -26,6 +27,7 @@ obj-$(CONFIG_$(SPL_)PMIC_RN5T567) += rn5t567.o obj-$(CONFIG_PMIC_TPS65090) += tps65090.o obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o obj-$(CONFIG_DM_PMIC_TPS65910) += pmic_tps65910_dm.o +obj-$(CONFIG_$(SPL_)DM_PMIC_TPS80031) += tps80031.o obj-$(CONFIG_$(SPL_)PMIC_PALMAS) += palmas.o obj-$(CONFIG_$(SPL_)PMIC_LP873X) += lp873x.o obj-$(CONFIG_$(SPL_)PMIC_LP87565) += lp87565.o diff --git a/drivers/power/pmic/axp.c b/drivers/power/pmic/axp.c index 025dac24f28..0e1e45fba74 100644 --- a/drivers/power/pmic/axp.c +++ b/drivers/power/pmic/axp.c @@ -87,6 +87,7 @@ static const struct udevice_id axp_pmic_ids[] = { { .compatible = "x-powers,axp209", .data = AXP209_ID }, { .compatible = "x-powers,axp221", .data = AXP221_ID }, { .compatible = "x-powers,axp223", .data = AXP223_ID }, + { .compatible = "x-powers,axp313a", .data = AXP313_ID }, { .compatible = "x-powers,axp803", .data = AXP803_ID }, { .compatible = "x-powers,axp806", .data = AXP806_ID }, { .compatible = "x-powers,axp809", .data = AXP809_ID }, diff --git a/drivers/power/pmic/max77663.c b/drivers/power/pmic/max77663.c new file mode 100644 index 00000000000..68c3cbbc646 --- /dev/null +++ b/drivers/power/pmic/max77663.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <dm/lists.h> +#include <power/pmic.h> +#include <power/max77663.h> + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "ldo", .driver = MAX77663_LDO_DRIVER }, + { .prefix = "sd", .driver = MAX77663_SD_DRIVER }, + { }, +}; + +static int max77663_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + int ret; + + ret = dm_i2c_write(dev, reg, buff, len); + if (ret) { + log_debug("write error to device: %p register: %#x!\n", dev, reg); + return ret; + } + + return 0; +} + +static int max77663_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + int ret; + + ret = dm_i2c_read(dev, reg, buff, len); + if (ret) { + log_debug("read error from device: %p register: %#x!\n", dev, reg); + return ret; + } + + return 0; +} + +static int max77663_bind(struct udevice *dev) +{ + ofnode regulators_node; + int children, ret; + + if (IS_ENABLED(CONFIG_SYSRESET_MAX77663)) { + ret = device_bind_driver(dev, MAX77663_RST_DRIVER, + "sysreset", NULL); + if (ret) { + log_err("cannot bind SYSRESET (ret = %d)\n", ret); + return ret; + } + } + + regulators_node = dev_read_subnode(dev, "regulators"); + if (!ofnode_valid(regulators_node)) { + log_err("%s regulators subnode not found!\n", dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, pmic_children_info); + if (!children) + log_err("%s - no child found\n", dev->name); + + /* Always return success for this device */ + return 0; +} + +static struct dm_pmic_ops max77663_ops = { + .read = max77663_read, + .write = max77663_write, +}; + +static const struct udevice_id max77663_ids[] = { + { .compatible = "maxim,max77663" }, + { } +}; + +U_BOOT_DRIVER(pmic_max77663) = { + .name = "max77663_pmic", + .id = UCLASS_PMIC, + .of_match = max77663_ids, + .bind = max77663_bind, + .ops = &max77663_ops, +}; diff --git a/drivers/power/pmic/palmas.c b/drivers/power/pmic/palmas.c index eb83c88d564..32f2a938b28 100644 --- a/drivers/power/pmic/palmas.c +++ b/drivers/power/pmic/palmas.c @@ -8,13 +8,13 @@ #include <fdtdec.h> #include <errno.h> #include <dm.h> +#include <dm/lists.h> #include <i2c.h> #include <log.h> #include <linux/printk.h> #include <power/pmic.h> #include <power/regulator.h> #include <power/palmas.h> -#include <dm/device.h> static const struct pmic_child_info pmic_children_info[] = { { .prefix = "ldo", .driver = PALMAS_LDO_DRIVER }, @@ -47,7 +47,16 @@ static int palmas_bind(struct udevice *dev) { ofnode pmic_node = ofnode_null(), regulators_node; ofnode subnode; - int children; + int children, ret; + + if (IS_ENABLED(CONFIG_SYSRESET_PALMAS)) { + ret = device_bind_driver(dev, PALMAS_RST_DRIVER, + "sysreset", NULL); + if (ret) { + log_err("cannot bind SYSRESET (ret = %d)\n", ret); + return ret; + } + } dev_for_each_subnode(subnode, dev) { const char *name; @@ -81,6 +90,24 @@ static int palmas_bind(struct udevice *dev) return 0; } +static int palmas_probe(struct udevice *dev) +{ + struct dm_i2c_chip *chip = dev_get_parent_plat(dev); + struct palmas_priv *priv = dev_get_priv(dev); + struct udevice *bus = dev_get_parent(dev); + u32 chip2_addr = chip->chip_addr + 1; + int ret; + + /* Palmas PMIC is multi chip and chips are located in a row */ + ret = i2c_get_chip(bus, chip2_addr, 1, &priv->chip2); + if (ret) { + log_err("cannot get second PMIC I2C chip (err %d)\n", ret); + return ret; + } + + return 0; +} + static struct dm_pmic_ops palmas_ops = { .read = palmas_read, .write = palmas_write, @@ -88,6 +115,7 @@ static struct dm_pmic_ops palmas_ops = { static const struct udevice_id palmas_ids[] = { { .compatible = "ti,tps659038", .data = TPS659038 }, + { .compatible = "ti,tps65913" , .data = TPS659038 }, { .compatible = "ti,tps65917" , .data = TPS65917 }, { } }; @@ -97,5 +125,7 @@ U_BOOT_DRIVER(pmic_palmas) = { .id = UCLASS_PMIC, .of_match = palmas_ids, .bind = palmas_bind, + .probe = palmas_probe, .ops = &palmas_ops, + .priv_auto = sizeof(struct palmas_priv), }; diff --git a/drivers/power/pmic/pmic_tps62362.c b/drivers/power/pmic/pmic_tps62362.c index 59190d6f672..6426d1488a5 100644 --- a/drivers/power/pmic/pmic_tps62362.c +++ b/drivers/power/pmic/pmic_tps62362.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * (C) Copyright 2014 Texas Instruments Incorporated - http://www.ti.com + * (C) Copyright 2014 Texas Instruments Incorporated - https://www.ti.com * Author: Felipe Balbi <balbi@ti.com> */ diff --git a/drivers/power/pmic/pmic_tps65910_dm.c b/drivers/power/pmic/pmic_tps65910_dm.c index 8ead1db802a..ecf836eb0e6 100644 --- a/drivers/power/pmic/pmic_tps65910_dm.c +++ b/drivers/power/pmic/pmic_tps65910_dm.c @@ -5,6 +5,7 @@ #include <common.h> #include <dm.h> +#include <dm/lists.h> #include <i2c.h> #include <log.h> #include <linux/printk.h> @@ -12,13 +13,19 @@ #include <power/regulator.h> #include <power/tps65910_pmic.h> -static const struct pmic_child_info pmic_children_info[] = { +static const struct pmic_child_info tps65910_children_info[] = { { .prefix = "ldo_", .driver = TPS65910_LDO_DRIVER }, { .prefix = "buck_", .driver = TPS65910_BUCK_DRIVER }, { .prefix = "boost_", .driver = TPS65910_BOOST_DRIVER }, { }, }; +static const struct pmic_child_info tps65911_children_info[] = { + { .prefix = "ldo", .driver = TPS65911_LDO_DRIVER }, + { .prefix = "vdd", .driver = TPS65911_VDD_DRIVER }, + { }, +}; + static int pmic_tps65910_reg_count(struct udevice *dev) { return TPS65910_NUM_REGS; @@ -50,8 +57,19 @@ static int pmic_tps65910_read(struct udevice *dev, uint reg, u8 *buffer, static int pmic_tps65910_bind(struct udevice *dev) { + const struct pmic_child_info *tps6591x_children_info = + (struct pmic_child_info *)dev_get_driver_data(dev); ofnode regulators_node; - int children; + int children, ret; + + if (IS_ENABLED(CONFIG_SYSRESET_TPS65910)) { + ret = device_bind_driver(dev, TPS65910_RST_DRIVER, + "sysreset", NULL); + if (ret) { + log_err("cannot bind SYSRESET (ret = %d)\n", ret); + return ret; + } + } regulators_node = dev_read_subnode(dev, "regulators"); if (!ofnode_valid(regulators_node)) { @@ -59,7 +77,7 @@ static int pmic_tps65910_bind(struct udevice *dev) return -EINVAL; } - children = pmic_bind_children(dev, regulators_node, pmic_children_info); + children = pmic_bind_children(dev, regulators_node, tps6591x_children_info); if (!children) debug("%s has no children (regulators)\n", dev->name); @@ -83,7 +101,8 @@ static struct dm_pmic_ops pmic_tps65910_ops = { }; static const struct udevice_id pmic_tps65910_match[] = { - { .compatible = "ti,tps65910" }, + { .compatible = "ti,tps65910", .data = (ulong)&tps65910_children_info }, + { .compatible = "ti,tps65911", .data = (ulong)&tps65911_children_info }, { /* sentinel */ } }; diff --git a/drivers/power/pmic/tps80031.c b/drivers/power/pmic/tps80031.c new file mode 100644 index 00000000000..a2f935b0c6d --- /dev/null +++ b/drivers/power/pmic/tps80031.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <dm/lists.h> +#include <power/pmic.h> +#include <power/tps80031.h> + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "ldo", .driver = TPS80031_LDO_DRIVER }, + { .prefix = "smps", .driver = TPS80031_SMPS_DRIVER }, + { }, +}; + +static int tps80031_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + int ret; + + ret = dm_i2c_write(dev, reg, buff, len); + if (ret) { + log_debug("write error to device: %p register: %#x!\n", dev, reg); + return ret; + } + + return 0; +} + +static int tps80031_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + int ret; + + ret = dm_i2c_read(dev, reg, buff, len); + if (ret) { + log_debug("read error from device: %p register: %#x!\n", dev, reg); + return ret; + } + + return 0; +} + +static int tps80031_bind(struct udevice *dev) +{ + ofnode regulators_node; + int children, ret; + + if (IS_ENABLED(CONFIG_SYSRESET_TPS80031)) { + ret = device_bind_driver(dev, TPS80031_RST_DRIVER, + "sysreset", NULL); + if (ret) { + log_err("cannot bind SYSRESET (ret = %d)\n", ret); + return ret; + } + } + + regulators_node = dev_read_subnode(dev, "regulators"); + if (!ofnode_valid(regulators_node)) { + log_err("%s regulators subnode not found!\n", dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, pmic_children_info); + if (!children) + log_err("%s - no child found\n", dev->name); + + /* Always return success for this device */ + return 0; +} + +static struct dm_pmic_ops tps80031_ops = { + .read = tps80031_read, + .write = tps80031_write, +}; + +static const struct udevice_id tps80031_ids[] = { + { .compatible = "ti,tps80031" }, + { .compatible = "ti,tps80032" }, + { } +}; + +U_BOOT_DRIVER(pmic_tps80031) = { + .name = "tps80031_pmic", + .id = UCLASS_PMIC, + .of_match = tps80031_ids, + .bind = tps80031_bind, + .ops = &tps80031_ops, +}; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index eb5aa38c1cc..102ec7bc5f8 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -141,6 +141,15 @@ config SPL_REGULATOR_PWM This config enables implementation of driver-model regulator uclass features for PWM regulators in SPL. +config DM_REGULATOR_MAX77663 + bool "Enable Driver Model for REGULATOR MAX77663" + depends on DM_REGULATOR && DM_PMIC_MAX77663 + ---help--- + This config enables implementation of driver-model regulator uclass + features for REGULATOR MAX77663. The driver supports both DC-to-DC + Step-Down (SD) Regulators and Low-Dropout Linear (LDO) Regulators + found in MAX77663 PMIC and implements get/set api for value and enable. + config DM_REGULATOR_MAX77686 bool "Enable Driver Model for REGULATOR MAX77686" depends on DM_REGULATOR && DM_PMIC_MAX77686 @@ -337,6 +346,17 @@ config DM_REGULATOR_TPS65910 regulator types of the TPS65910 (BUCK, BOOST and LDO). It implements the get/set api for value and enable. +config DM_REGULATOR_TPS65911 + bool "Enable driver for TPS65911 PMIC regulators" + depends on DM_PMIC_TPS65910 + ---help--- + This config enables implementation of driver-model regulator + uclass features for the TPS65911 PMIC. The driver supports Step-Down + DC-DC Converters for Processor Cores (VDD1 and VDD2), Step-Down DC-DC + Converter for I/O Power (VIO), Controller for External FETs (VDDCtrl) + and LDO Voltage Regulators found in TPS65911 PMIC and implements + get/set api for value and enable. + config DM_REGULATOR_TPS62360 bool "Enable driver for TPS6236x Power Regulator" depends on DM_REGULATOR @@ -347,6 +367,14 @@ config DM_REGULATOR_TPS62360 implements the get/set api for value only, as the power line is always on. +config DM_REGULATOR_TPS80031 + bool "Enable driver for TPS80031/TPS80032 PMIC regulators" + depends on DM_PMIC_TPS80031 + ---help--- + This enables implementation of driver-model regulator uclass + features for TPS80031/TPS80032 PMICs. The driver implements + get/set api for: value and enable. + config DM_REGULATOR_STPMIC1 bool "Enable driver for STPMIC1 regulators" depends on DM_REGULATOR && PMIC_STPMIC1 diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index d9e0cd5949c..f79932d8330 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o obj-$(CONFIG_$(SPL_)REGULATOR_AXP) += axp_regulator.o obj-$(CONFIG_$(SPL_)REGULATOR_AXP_USB_POWER) += axp_usb_power.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_DA9063) += da9063.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_MAX77663) += max77663_regulator.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_DM_REGULATOR_NPCM8XX) += npcm8xx_regulator.o obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o @@ -31,7 +32,9 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP873X) += lp873x_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_TPS65911) += tps65911_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_TPS80031) += tps80031_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMIC1) += stpmic1.o obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o diff --git a/drivers/power/regulator/axp_regulator.c b/drivers/power/regulator/axp_regulator.c index 02f320eac1e..d27e09538e0 100644 --- a/drivers/power/regulator/axp_regulator.c +++ b/drivers/power/regulator/axp_regulator.c @@ -173,6 +173,22 @@ static const struct axp_regulator_plat axp22x_regulators[] = { { } }; +/* + * The "dcdc1" regulator has another range, beyond 1.54V up to 3.4V, in + * steps of 100mV. We cannot model this easily, but also don't need that, + * since it's typically only used for ~1.1V anyway, so just ignore it. + * Also the DCDC3 regulator is described wrongly in the (available) manual, + * experiments show that the split point is at 1200mV, as for DCDC1/2. + */ +static const struct axp_regulator_plat axp313_regulators[] = { + { "dcdc1", 0x10, BIT(0), 0x13, 0x7f, 500, 1540, 10, 70 }, + { "dcdc2", 0x10, BIT(1), 0x14, 0x7f, 500, 1540, 10, 70 }, + { "dcdc3", 0x10, BIT(2), 0x15, 0x7f, 500, 1840, 10, 70 }, + { "aldo1", 0x10, BIT(3), 0x16, 0x1f, 500, 3500, 100, NA }, + { "dldo1", 0x10, BIT(4), 0x17, 0x1f, 500, 3500, 100, NA }, + { } +}; + static const struct axp_regulator_plat axp803_regulators[] = { { "dcdc1", 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA }, { "dcdc2", 0x10, BIT(1), 0x21, 0x7f, 500, 1300, 10, 70 }, @@ -274,6 +290,7 @@ static const struct axp_regulator_plat *const axp_regulators[] = { [AXP209_ID] = axp20x_regulators, [AXP221_ID] = axp22x_regulators, [AXP223_ID] = axp22x_regulators, + [AXP313_ID] = axp313_regulators, [AXP803_ID] = axp803_regulators, [AXP806_ID] = axp806_regulators, [AXP809_ID] = axp809_regulators, diff --git a/drivers/power/regulator/max77663_regulator.c b/drivers/power/regulator/max77663_regulator.c new file mode 100644 index 00000000000..ea4b7c63e5a --- /dev/null +++ b/drivers/power/regulator/max77663_regulator.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/max77663.h> + +/* fist row is control registers, second is voltage registers */ +static const char max77663_sd_reg[][MAX77663_SD_NUM] = { + { 0x1d, 0x1e, 0x1f, 0x20, 0x21 }, + { 0x16, 0x17, 0x18, 0x19, 0x2a }, +}; + +static const char max77663_ldo_reg[MAX77663_LDO_NUM] = { + 0x23, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x2f, 0x31, 0x33 +}; + +static int max77663_sd_enable(struct udevice *dev, int op, bool *enable) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int val, ret; + + val = pmic_reg_read(dev->parent, adr); + if (val < 0) + return val; + + if (op == PMIC_OP_GET) { + if (val & SD_STATUS_MASK) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + val &= ~SD_STATUS_MASK; + + if (*enable) + val |= SD_STATUS_MASK; + + ret = pmic_reg_write(dev->parent, adr, val); + if (ret) + return ret; + } + + return 0; +} + +/** + * max77663_*_volt2hex() - convert voltage in uV into + * applicable to register hex value + * + * @idx: regulator index + * @uV: voltage in uV + * + * Return: voltage in hex on success, -ve on failure + */ +static int max77663_sd_volt2hex(int idx, int uV) +{ + switch (idx) { + case 0: + /* SD0 has max voltage 1.4V */ + if (uV > SD0_VOLT_MAX) + return -EINVAL; + break; + case 1: + /* SD1 has max voltage 1.55V */ + if (uV > SD1_VOLT_MAX) + return -EINVAL; + break; + default: + /* SD2 and SD3 have max voltage 3.79V */ + if (uV > SD_VOLT_MAX) + return -EINVAL; + break; + }; + + if (uV < SD_VOLT_MIN) + uV = SD_VOLT_MIN; + + return (uV - SD_VOLT_BASE) / 12500; +} + +/** + * max77663_*_hex2volt() - convert register hex value into + * actual voltage in uV + * + * @idx: regulator index + * @hex: hex value of register + * + * Return: voltage in uV on success, -ve on failure + */ +static int max77663_sd_hex2volt(int idx, int hex) +{ + switch (idx) { + case 0: + /* SD0 has max voltage 1.4V */ + if (hex > SD0_VOLT_MAX_HEX) + return -EINVAL; + break; + case 1: + /* SD1 has max voltage 1.55V */ + if (hex > SD1_VOLT_MAX_HEX) + return -EINVAL; + break; + default: + /* SD2 and SD3 have max voltage 3.79V */ + if (hex > SD_VOLT_MAX_HEX) + return -EINVAL; + break; + }; + + if (hex < SD_VOLT_MIN_HEX) + hex = SD_VOLT_MIN_HEX; + + return SD_VOLT_BASE + hex * 12500; +} + +static int max77663_sd_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int idx = dev->driver_data; + int hex, ret; + + if (op == PMIC_OP_GET) { + hex = pmic_reg_read(dev->parent, adr); + if (hex < 0) + return hex; + + *uV = 0; + + ret = max77663_sd_hex2volt(idx, hex); + if (ret < 0) + return ret; + *uV = ret; + + return 0; + } + + /* SD regulators use entire register for voltage */ + hex = max77663_sd_volt2hex(idx, *uV); + if (hex < 0) + return hex; + + return pmic_reg_write(dev->parent, adr, hex); +} + +static int max77663_sd_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + int idx = dev->driver_data; + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->ctrl_reg = max77663_sd_reg[0][idx]; + uc_pdata->volt_reg = max77663_sd_reg[1][idx]; + + return 0; +} + +static int sd_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = max77663_sd_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int sd_set_value(struct udevice *dev, int uV) +{ + return max77663_sd_val(dev, PMIC_OP_SET, &uV); +} + +static int sd_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = max77663_sd_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int sd_set_enable(struct udevice *dev, bool enable) +{ + return max77663_sd_enable(dev, PMIC_OP_SET, &enable); +} + +static const struct dm_regulator_ops max77663_sd_ops = { + .get_value = sd_get_value, + .set_value = sd_set_value, + .get_enable = sd_get_enable, + .set_enable = sd_set_enable, +}; + +U_BOOT_DRIVER(max77663_sd) = { + .name = MAX77663_SD_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &max77663_sd_ops, + .probe = max77663_sd_probe, +}; + +static int max77663_ldo_enable(struct udevice *dev, int op, bool *enable) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int val, ret; + + val = pmic_reg_read(dev->parent, adr); + if (val < 0) + return val; + + if (op == PMIC_OP_GET) { + if (val & LDO_STATUS_MASK) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + val &= ~LDO_STATUS_MASK; + + if (*enable) + val |= LDO_STATUS_MASK; + + ret = pmic_reg_write(dev->parent, adr, val); + if (ret) + return ret; + } + + return 0; +} + +static int max77663_ldo_volt2hex(int idx, int uV) +{ + switch (idx) { + case 0: + case 1: + if (uV > LDO01_VOLT_MAX) + return -EINVAL; + + return (uV - LDO_VOLT_BASE) / 25000; + case 4: + if (uV > LDO4_VOLT_MAX) + return -EINVAL; + + return (uV - LDO_VOLT_BASE) / 12500; + default: + if (uV > LDO_VOLT_MAX) + return -EINVAL; + + return (uV - LDO_VOLT_BASE) / 50000; + }; +} + +static int max77663_ldo_hex2volt(int idx, int hex) +{ + if (hex > LDO_VOLT_MAX_HEX) + return -EINVAL; + + switch (idx) { + case 0: + case 1: + return (hex * 25000) + LDO_VOLT_BASE; + case 4: + return (hex * 12500) + LDO_VOLT_BASE; + default: + return (hex * 50000) + LDO_VOLT_BASE; + }; +} + +static int max77663_ldo_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int idx = dev->driver_data; + int hex, val, ret; + + val = pmic_reg_read(dev->parent, adr); + if (val < 0) + return val; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret = max77663_ldo_hex2volt(idx, val & LDO_VOLT_MASK); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = max77663_ldo_volt2hex(idx, *uV); + if (hex < 0) + return hex; + + val &= ~LDO_VOLT_MASK; + + return pmic_reg_write(dev->parent, adr, val | hex); +} + +static int max77663_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + int idx = dev->driver_data; + + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->ctrl_reg = max77663_ldo_reg[idx]; + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = max77663_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return max77663_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static int ldo_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = max77663_ldo_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + return max77663_ldo_enable(dev, PMIC_OP_SET, &enable); +} + +static const struct dm_regulator_ops max77663_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = ldo_get_enable, + .set_enable = ldo_set_enable, +}; + +U_BOOT_DRIVER(max77663_ldo) = { + .name = MAX77663_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &max77663_ldo_ops, + .probe = max77663_ldo_probe, +}; diff --git a/drivers/power/regulator/palmas_regulator.c b/drivers/power/regulator/palmas_regulator.c index 3c4eb83be77..d615e947340 100644 --- a/drivers/power/regulator/palmas_regulator.c +++ b/drivers/power/regulator/palmas_regulator.c @@ -301,19 +301,23 @@ static int palmas_ldo_probe(struct udevice *dev) uc_pdata->type = REGULATOR_TYPE_LDO; - if (dev->driver_data) { + /* check for ldoln and ldousb cases */ + if (!strcmp("ldoln", dev->name)) { + uc_pdata->ctrl_reg = palmas_ldo_ctrl[type][9]; + uc_pdata->volt_reg = palmas_ldo_volt[type][9]; + return 0; + } + + if (!strcmp("ldousb", dev->name)) { + uc_pdata->ctrl_reg = palmas_ldo_ctrl[type][10]; + uc_pdata->volt_reg = palmas_ldo_volt[type][10]; + return 0; + } + + if (dev->driver_data > 0) { u8 idx = dev->driver_data - 1; uc_pdata->ctrl_reg = palmas_ldo_ctrl[type][idx]; uc_pdata->volt_reg = palmas_ldo_volt[type][idx]; - } else { - /* check for ldoln and ldousb cases */ - if (!strcmp("ldoln", dev->name)) { - uc_pdata->ctrl_reg = palmas_ldo_ctrl[type][9]; - uc_pdata->volt_reg = palmas_ldo_volt[type][9]; - } else if (!strcmp("ldousb", dev->name)) { - uc_pdata->ctrl_reg = palmas_ldo_ctrl[type][10]; - uc_pdata->volt_reg = palmas_ldo_volt[type][10]; - } } return 0; diff --git a/drivers/power/regulator/scmi_regulator.c b/drivers/power/regulator/scmi_regulator.c index 801148036ff..9c72c35d039 100644 --- a/drivers/power/regulator/scmi_regulator.c +++ b/drivers/power/regulator/scmi_regulator.c @@ -25,18 +25,9 @@ struct scmi_regulator_platdata { u32 domain_id; }; -/** - * struct scmi_regulator_priv - Private data for SCMI voltage regulator - * @channel: Reference to the SCMI channel to use - */ -struct scmi_regulator_priv { - struct scmi_channel *channel; -}; - static int scmi_voltd_set_enable(struct udevice *dev, bool enable) { struct scmi_regulator_platdata *pdata = dev_get_plat(dev); - struct scmi_regulator_priv *priv = dev_get_priv(dev); struct scmi_voltd_config_set_in in = { .domain_id = pdata->domain_id, .config = enable ? SCMI_VOLTD_CONFIG_ON : SCMI_VOLTD_CONFIG_OFF, @@ -47,7 +38,7 @@ static int scmi_voltd_set_enable(struct udevice *dev, bool enable) in, out); int ret; - ret = devm_scmi_process_msg(dev, priv->channel, &msg); + ret = devm_scmi_process_msg(dev, &msg); if (ret) return ret; @@ -57,7 +48,6 @@ static int scmi_voltd_set_enable(struct udevice *dev, bool enable) static int scmi_voltd_get_enable(struct udevice *dev) { struct scmi_regulator_platdata *pdata = dev_get_plat(dev); - struct scmi_regulator_priv *priv = dev_get_priv(dev); struct scmi_voltd_config_get_in in = { .domain_id = pdata->domain_id, }; @@ -67,7 +57,7 @@ static int scmi_voltd_get_enable(struct udevice *dev) in, out); int ret; - ret = devm_scmi_process_msg(dev, priv->channel, &msg); + ret = devm_scmi_process_msg(dev, &msg); if (ret < 0) return ret; @@ -80,7 +70,6 @@ static int scmi_voltd_get_enable(struct udevice *dev) static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV) { - struct scmi_regulator_priv *priv = dev_get_priv(dev); struct scmi_regulator_platdata *pdata = dev_get_plat(dev); struct scmi_voltd_level_set_in in = { .domain_id = pdata->domain_id, @@ -92,7 +81,7 @@ static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV) in, out); int ret; - ret = devm_scmi_process_msg(dev, priv->channel, &msg); + ret = devm_scmi_process_msg(dev, &msg); if (ret < 0) return ret; @@ -101,7 +90,6 @@ static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV) static int scmi_voltd_get_voltage_level(struct udevice *dev) { - struct scmi_regulator_priv *priv = dev_get_priv(dev); struct scmi_regulator_platdata *pdata = dev_get_plat(dev); struct scmi_voltd_level_get_in in = { .domain_id = pdata->domain_id, @@ -112,7 +100,7 @@ static int scmi_voltd_get_voltage_level(struct udevice *dev) in, out); int ret; - ret = devm_scmi_process_msg(dev, priv->channel, &msg); + ret = devm_scmi_process_msg(dev, &msg); if (ret < 0) return ret; @@ -140,7 +128,6 @@ static int scmi_regulator_of_to_plat(struct udevice *dev) static int scmi_regulator_probe(struct udevice *dev) { struct scmi_regulator_platdata *pdata = dev_get_plat(dev); - struct scmi_regulator_priv *priv = dev_get_priv(dev); struct scmi_voltd_attr_in in = { 0 }; struct scmi_voltd_attr_out out = { 0 }; struct scmi_msg scmi_msg = { @@ -153,14 +140,14 @@ static int scmi_regulator_probe(struct udevice *dev) }; int ret; - ret = devm_scmi_of_get_channel(dev->parent, &priv->channel); + ret = devm_scmi_of_get_channel(dev); if (ret) return ret; /* Check voltage domain is known from SCMI server */ in.domain_id = pdata->domain_id; - ret = devm_scmi_process_msg(dev, priv->channel, &scmi_msg); + ret = devm_scmi_process_msg(dev, &scmi_msg); if (ret) { dev_err(dev, "Failed to query voltage domain %u: %d\n", pdata->domain_id, ret); @@ -184,7 +171,6 @@ U_BOOT_DRIVER(scmi_regulator) = { .probe = scmi_regulator_probe, .of_to_plat = scmi_regulator_of_to_plat, .plat_auto = sizeof(struct scmi_regulator_platdata), - .priv_auto = sizeof(struct scmi_regulator_priv *), }; static int scmi_regulator_bind(struct udevice *dev) diff --git a/drivers/power/regulator/tps62360_regulator.c b/drivers/power/regulator/tps62360_regulator.c index b9f4504539e..7014b1982d0 100644 --- a/drivers/power/regulator/tps62360_regulator.c +++ b/drivers/power/regulator/tps62360_regulator.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ * Tero Kristo <t-kristo@ti.com> */ diff --git a/drivers/power/regulator/tps65911_regulator.c b/drivers/power/regulator/tps65911_regulator.c new file mode 100644 index 00000000000..2b5acdfcbb0 --- /dev/null +++ b/drivers/power/regulator/tps65911_regulator.c @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/tps65910_pmic.h> + +/* fist row is control registers, second is voltage registers */ +static const char tps65911_vdd_reg[][TPS65911_VDD_NUM] = { + { TPS65911_REG_VDD1, TPS65911_REG_VDD2, + TPS65911_REG_VDDCTRL, TPS65911_REG_VIO }, + { TPS65911_REG_VDD1_OP, TPS65911_REG_VDD2_OP, + TPS65911_REG_VDDCTRL_OP, 0x00 }, +}; + +static const char tps65911_ldo_reg[TPS65911_LDO_NUM] = { + TPS65911_REG_LDO1, TPS65911_REG_LDO2, TPS65911_REG_LDO3, + TPS65911_REG_LDO4, TPS65911_REG_LDO5, TPS65911_REG_LDO6, + TPS65911_REG_LDO7, TPS65911_REG_LDO8 +}; + +static int tps65911_regulator_enable(struct udevice *dev, int op, bool *enable) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int val, ret; + + val = pmic_reg_read(dev->parent, adr); + if (val < 0) + return val; + + if (op == PMIC_OP_GET) { + if (val & TPS65910_SUPPLY_STATE_ON) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + val &= ~TPS65910_SUPPLY_STATE_MASK; + + if (*enable) + val |= TPS65910_SUPPLY_STATE_ON; + + ret = pmic_reg_write(dev->parent, adr, val); + if (ret) + return ret; + } + + return 0; +} + +static int tps65911_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = tps65911_regulator_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int tps65911_set_enable(struct udevice *dev, bool enable) +{ + return tps65911_regulator_enable(dev, PMIC_OP_SET, &enable); +} + +/** + * tps65911_vdd_volt2hex() - convert voltage in uV into + * applicable to register hex value + * + * @uV: voltage in uV + * + * Return: voltage in hex on success, -ve on failure + */ +static int tps65911_vdd_volt2hex(int uV) +{ + if (uV > TPS65911_VDD_VOLT_MAX) + return -EINVAL; + + if (uV < TPS65911_VDD_VOLT_MIN) + uV = TPS65911_VDD_VOLT_MIN; + + return (uV - TPS65911_VDD_VOLT_BASE) / 12500; +} + +/** + * tps65911_vdd_hex2volt() - convert register hex value into + * actual voltage in uV + * + * @hex: hex value of register + * + * Return: voltage in uV on success, -ve on failure + */ +static int tps65911_vdd_hex2volt(int hex) +{ + if (hex > TPS65910_VDD_SEL_MAX) + return -EINVAL; + + if (hex < TPS65910_VDD_SEL_MIN) + hex = TPS65910_VDD_SEL_MIN; + + return TPS65911_VDD_VOLT_BASE + hex * 12500; +} + +static int tps65911_vio_range[4] = { + 1500000, 1800000, 2500000, 3300000 +}; + +static int tps65911_vio_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int i, val; + + val = pmic_reg_read(dev->parent, adr); + if (val < 0) + return val; + + if (op == PMIC_OP_GET) { + *uV = 0; + + val &= TPS65910_SEL_MASK; + + *uV = tps65911_vio_range[val >> 2]; + + return 0; + } + + val &= ~TPS65910_SEL_MASK; + + for (i = 0; i < ARRAY_SIZE(tps65911_vio_range); i++) + if (*uV <= tps65911_vio_range[i]) + break; + + return pmic_reg_write(dev->parent, adr, val | i << 2); +} + +static int tps65911_vdd_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int val, ret; + + /* in case vdd is vio */ + if (!adr) + return tps65911_vio_val(dev, op, uV); + + val = pmic_reg_read(dev->parent, adr); + if (val < 0) + return val; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret = tps65911_vdd_hex2volt(val); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + val = tps65911_vdd_volt2hex(*uV); + if (val < 0) + return val; + + return pmic_reg_write(dev->parent, adr, val); +} + +static int tps65911_vdd_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + + /* check for vddctrl and vddio cases */ + if (!strcmp("vddctrl", dev->name)) { + uc_pdata->ctrl_reg = tps65911_vdd_reg[0][2]; + uc_pdata->volt_reg = tps65911_vdd_reg[1][2]; + return 0; + } + + if (!strcmp("vddio", dev->name)) { + uc_pdata->ctrl_reg = tps65911_vdd_reg[0][3]; + uc_pdata->volt_reg = tps65911_vdd_reg[1][3]; + return 0; + } + + if (dev->driver_data > 0) { + u8 idx = dev->driver_data - 1; + + uc_pdata->ctrl_reg = tps65911_vdd_reg[0][idx]; + uc_pdata->volt_reg = tps65911_vdd_reg[1][idx]; + } + + return 0; +} + +static int vdd_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps65911_vdd_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int vdd_set_value(struct udevice *dev, int uV) +{ + return tps65911_vdd_val(dev, PMIC_OP_SET, &uV); +} + +static const struct dm_regulator_ops tps65911_vdd_ops = { + .get_value = vdd_get_value, + .set_value = vdd_set_value, + .get_enable = tps65911_get_enable, + .set_enable = tps65911_set_enable, +}; + +U_BOOT_DRIVER(tps65911_vdd) = { + .name = TPS65911_VDD_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65911_vdd_ops, + .probe = tps65911_vdd_probe, +}; + +/** + * tps65911_ldo_volt2hex() - convert voltage in uV into + * applicable to register hex value + * + * @idx: regulator index + * @uV: voltage in uV + * + * Return: voltage in hex on success, -ve on failure + */ +static int tps65911_ldo_volt2hex(int idx, int uV) +{ + int step; + + if (uV > TPS65911_LDO_VOLT_MAX) + return -EINVAL; + + if (uV < TPS65911_LDO_VOLT_BASE) + uV = TPS65911_LDO_VOLT_BASE; + + switch (idx) { + case 1: + case 2: + case 4: + step = TPS65911_LDO124_VOLT_STEP; + break; + case 3: + case 5: + case 6: + case 7: + case 8: + step = TPS65911_LDO358_VOLT_STEP; + break; + default: + return -EINVAL; + }; + + return ((uV - TPS65911_LDO_VOLT_BASE) / step) << 2; +} + +/** + * tps65911_ldo_hex2volt() - convert register hex value into + * actual voltage in uV + * + * @idx: regulator index + * @hex: hex value of register + * + * Return: voltage in uV on success, -ve on failure + */ +static int tps65911_ldo_hex2volt(int idx, int hex) +{ + int step; + + switch (idx) { + case 1: + case 2: + case 4: + if (hex > TPS65911_LDO124_VOLT_MAX_HEX) + return -EINVAL; + + step = TPS65911_LDO124_VOLT_STEP; + break; + case 3: + case 5: + case 6: + case 7: + case 8: + if (hex > TPS65911_LDO358_VOLT_MAX_HEX) + return -EINVAL; + + if (hex < TPS65911_LDO358_VOLT_MIN_HEX) + hex = TPS65911_LDO358_VOLT_MIN_HEX; + + step = TPS65911_LDO358_VOLT_STEP; + break; + default: + return -EINVAL; + }; + + return TPS65911_LDO_VOLT_BASE + hex * step; +} + +static int tps65911_ldo_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int idx = dev->driver_data; + int val, hex, ret; + + val = pmic_reg_read(dev->parent, adr); + if (val < 0) + return val; + + if (op == PMIC_OP_GET) { + *uV = 0; + val &= TPS65911_LDO_SEL_MASK; + + ret = tps65911_ldo_hex2volt(idx, val >> 2); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = tps65911_ldo_volt2hex(idx, *uV); + if (hex < 0) + return hex; + + val &= ~TPS65911_LDO_SEL_MASK; + + return pmic_reg_write(dev->parent, adr, val | hex); +} + +static int tps65911_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u8 idx = dev->driver_data - 1; + + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->ctrl_reg = tps65911_ldo_reg[idx]; + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps65911_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return tps65911_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static const struct dm_regulator_ops tps65911_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = tps65911_get_enable, + .set_enable = tps65911_set_enable, +}; + +U_BOOT_DRIVER(tps65911_ldo) = { + .name = TPS65911_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65911_ldo_ops, + .probe = tps65911_ldo_probe, +}; diff --git a/drivers/power/regulator/tps80031_regulator.c b/drivers/power/regulator/tps80031_regulator.c new file mode 100644 index 00000000000..87696662e11 --- /dev/null +++ b/drivers/power/regulator/tps80031_regulator.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/tps80031.h> + +static const char tps80031_smps_reg[][TPS80031_SMPS_NUM] = { + { 0x54, 0x5a, 0x66, 0x42, 0x48 }, + { 0x56, 0x5c, 0x68, 0x44, 0x4a }, + { BIT(3), BIT(4), BIT(6), BIT(0), BIT(1) }, +}; + +static const char tps80031_ldo_reg[][TPS80031_LDO_NUM] = { + { 0x9e, 0x86, 0x8e, 0x8a, 0x9a, 0x92, 0xa6, 0x96, 0xa2 }, + { 0x9f, 0x87, 0x8f, 0x8b, 0x9b, 0x93, 0xa7, 0x97, 0xa3 }, +}; + +static int tps80031_regulator_enable(struct udevice *dev, int op, bool *enable) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int val, ret; + + val = pmic_reg_read(dev->parent, adr); + if (val < 0) + return val; + + if (op == PMIC_OP_GET) { + if (val & REGULATOR_MODE_ON) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + val &= ~REGULATOR_STATUS_MASK; + + if (*enable) + val |= REGULATOR_MODE_ON; + + ret = pmic_reg_write(dev->parent, adr, val); + if (ret) + return ret; + } + + return 0; +} + +static int tps80031_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = tps80031_regulator_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int tps80031_set_enable(struct udevice *dev, bool enable) +{ + return tps80031_regulator_enable(dev, PMIC_OP_SET, &enable); +} + +/** + * tps80031_ldo_volt2hex() - convert voltage in uV into + * applicable to register hex value + * + * @uV: voltage in uV + * + * Return: voltage in hex on success, -ve on failure + */ +static int tps80031_ldo_volt2hex(int uV) +{ + if (uV > LDO_VOLT_MAX) + return -EINVAL; + + if (uV < LDO_VOLT_MIN) + uV = LDO_VOLT_MIN; + + return DIV_ROUND_UP(uV - LDO_VOLT_BASE, 102000); +} + +/** + * tps80031_ldo_hex2volt() - convert register hex value into + * actual voltage in uV + * + * @hex: hex value of register + * + * Return: voltage in uV on success, -ve on failure + */ +static int tps80031_ldo_hex2volt(int hex) +{ + if (hex > LDO_VOLT_MAX_HEX) + return -EINVAL; + + if (hex < LDO_VOLT_MIN_HEX) + hex = LDO_VOLT_MIN_HEX; + + return LDO_VOLT_BASE + hex * 102000; +} + +static int tps80031_ldo_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int val, hex, ret; + + val = pmic_reg_read(dev->parent, adr); + if (val < 0) + return val; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret = tps80031_ldo_hex2volt(val & LDO_VOLT_MASK); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = tps80031_ldo_volt2hex(*uV); + if (hex < 0) + return hex; + + val &= ~LDO_VOLT_MASK; + + return pmic_reg_write(dev->parent, adr, val | hex); +} + +static int tps80031_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + + /* check for ldoln and ldousb cases */ + if (!strcmp("ldoln", dev->name)) { + uc_pdata->ctrl_reg = tps80031_ldo_reg[CTRL][7]; + uc_pdata->volt_reg = tps80031_ldo_reg[VOLT][7]; + return 0; + } + + if (!strcmp("ldousb", dev->name)) { + uc_pdata->ctrl_reg = tps80031_ldo_reg[CTRL][8]; + uc_pdata->volt_reg = tps80031_ldo_reg[VOLT][8]; + return 0; + } + + if (dev->driver_data > 0) { + u8 idx = dev->driver_data - 1; + + uc_pdata->ctrl_reg = tps80031_ldo_reg[CTRL][idx]; + uc_pdata->volt_reg = tps80031_ldo_reg[VOLT][idx]; + } + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps80031_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return tps80031_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static const struct dm_regulator_ops tps80031_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = tps80031_get_enable, + .set_enable = tps80031_set_enable, +}; + +U_BOOT_DRIVER(tps80031_ldo) = { + .name = TPS80031_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps80031_ldo_ops, + .probe = tps80031_ldo_probe, +}; + +struct tps80031_smps_priv { + int flags; +}; + +/* DCDC voltages for the selector of 0x39 to 0x3F */ +static int tps80031_dcdc_voltages[5] = { + 1350000, 1500000, 1800000, 1900000, 2100000 +}; + +/** + * tps80031_smps_volt2hex() - convert voltage in uV into + * applicable to register hex value + * + * @base: base voltage in uV + * @uV: voltage in uV + * + * Return: voltage in hex on success, -ve on failure + */ +static int tps80031_smps_volt2hex(u32 base, int uV) +{ + int i; + + if (uV < base) + return 1; + + if (uV > SMPS_VOLT_LINEAR) { + for (i = 0; i < ARRAY_SIZE(tps80031_dcdc_voltages); i++) + if (uV <= tps80031_dcdc_voltages[i]) + break; + + return SMPS_VOLT_NLINEAR_HEX + i; + } + + return DIV_ROUND_UP(uV - base, 12500); +} + +/** + * tps80031_smps_hex2volt() - convert register hex value into + * actual voltage in uV + * + * @base: base voltage in uV + * @hex: hex value of register + * + * Return: voltage in uV on success, -ve on failure + */ +static int tps80031_smps_hex2volt(u32 base, int hex) +{ + if (!hex) + return 0; + + /* if reg value exceeds linear scale use table */ + if (hex > SMPS_VOLT_LINEAR_HEX) + return tps80031_dcdc_voltages[hex - SMPS_VOLT_LINEAR_HEX]; + else + return base + hex * 12500; +} + +static int tps80031_smps_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + struct tps80031_smps_priv *priv = dev_get_priv(dev); + u32 adr = uc_pdata->volt_reg; + int base, val, hex, ret; + + /* If offset flag was set then base voltage is higher */ + if (priv->flags & TPS80031_OFFSET_FLAG) + base = SMPS_VOLT_BASE_OFFSET; + else + base = SMPS_VOLT_BASE; + + val = pmic_reg_read(dev->parent, adr); + if (val < 0) + return val; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret = tps80031_smps_hex2volt(base, val & SMPS_VOLT_MASK); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = tps80031_smps_volt2hex(base, *uV); + if (hex < 0) + return hex; + + val &= ~SMPS_VOLT_MASK; + + return pmic_reg_write(dev->parent, adr, val | hex); +} + +static int tps80031_smps_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + struct tps80031_smps_priv *priv = dev_get_priv(dev); + int idx = dev->driver_data - 1; + int val; + + uc_pdata->type = REGULATOR_TYPE_BUCK; + + uc_pdata->ctrl_reg = tps80031_smps_reg[CTRL][idx]; + uc_pdata->volt_reg = tps80031_smps_reg[VOLT][idx]; + + /* Determine if smps regulator uses higher voltage */ + val = pmic_reg_read(dev->parent, TPS80031_SMPS_OFFSET); + if (val & tps80031_smps_reg[OFFSET][idx]) + priv->flags |= TPS80031_OFFSET_FLAG; + + return 0; +} + +static int smps_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps80031_smps_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int smps_set_value(struct udevice *dev, int uV) +{ + return tps80031_smps_val(dev, PMIC_OP_SET, &uV); +} + +static const struct dm_regulator_ops tps80031_smps_ops = { + .get_value = smps_get_value, + .set_value = smps_set_value, + .get_enable = tps80031_get_enable, + .set_enable = tps80031_set_enable, +}; + +U_BOOT_DRIVER(tps80031_smps) = { + .name = TPS80031_SMPS_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps80031_smps_ops, + .probe = tps80031_smps_probe, + .priv_auto = sizeof(struct tps80031_smps_priv), +}; diff --git a/drivers/pwm/pwm-aspeed.c b/drivers/pwm/pwm-aspeed.c index ba98641c866..b03472d2345 100644 --- a/drivers/pwm/pwm-aspeed.c +++ b/drivers/pwm/pwm-aspeed.c @@ -49,6 +49,7 @@ #include <dm/device_compat.h> #include <linux/math64.h> #include <linux/bitfield.h> +#include <linux/time.h> #include <asm/io.h> /* The channel number of Aspeed pwm controller */ @@ -77,8 +78,6 @@ /* PWM fixed value */ #define PWM_ASPEED_FIXED_PERIOD 0xff -#define NSEC_PER_SEC 1000000000L - struct aspeed_pwm_priv { struct clk clk; struct regmap *regmap; diff --git a/drivers/pwm/pwm-at91.c b/drivers/pwm/pwm-at91.c index 95597aee557..3ff1fb6d5c3 100644 --- a/drivers/pwm/pwm-at91.c +++ b/drivers/pwm/pwm-at91.c @@ -14,11 +14,11 @@ #include <dm.h> #include <linux/bitops.h> #include <linux/io.h> +#include <linux/time.h> #include <pwm.h> #define PERIOD_BITS 16 #define PWM_MAX_PRES 10 -#define NSEC_PER_SEC 1000000000L #define PWM_ENA 0x04 #define PWM_CHANNEL_OFFSET 0x20 diff --git a/drivers/pwm/pwm-cadence-ttc.c b/drivers/pwm/pwm-cadence-ttc.c index dc3b314b0cc..d9f6736a7ae 100644 --- a/drivers/pwm/pwm-cadence-ttc.c +++ b/drivers/pwm/pwm-cadence-ttc.c @@ -17,6 +17,7 @@ #include <linux/bitfield.h> #include <linux/math64.h> #include <linux/log2.h> +#include <linux/time.h> #include <dm/device_compat.h> #define CLOCK_CONTROL 0 @@ -37,8 +38,6 @@ #define COUNTER_INTERVAL_ENABLE BIT(1) #define COUNTER_COUNTING_DISABLE BIT(0) -#define NSEC_PER_SEC 1000000000L - #define TTC_REG(reg, channel) ((reg) + (channel) * sizeof(u32)) #define TTC_CLOCK_CONTROL(reg, channel) \ TTC_REG((reg) + CLOCK_CONTROL, (channel)) diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index 2311910a636..60959720dac 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -26,8 +26,7 @@ #include <linux/math64.h> #include <linux/bitfield.h> #include <linux/clk-provider.h> - -#define NSEC_PER_SEC 1000000000L +#include <linux/time.h> #define REG_PWM_A 0x0 #define REG_PWM_B 0x4 diff --git a/drivers/pwm/pwm-mtk.c b/drivers/pwm/pwm-mtk.c index 11e74440197..ad845ed9662 100644 --- a/drivers/pwm/pwm-mtk.c +++ b/drivers/pwm/pwm-mtk.c @@ -12,6 +12,7 @@ #include <div64.h> #include <linux/bitops.h> #include <linux/io.h> +#include <linux/time.h> /* PWM registers and bits definitions */ #define PWMCON 0x00 @@ -27,8 +28,6 @@ #define PWM_CLK_DIV_MAX 7 #define MAX_PWM_NUM 8 -#define NSEC_PER_SEC 1000000000L - enum mtk_pwm_reg_ver { PWM_REG_V1, PWM_REG_V2, diff --git a/drivers/pwm/pwm-ti-ehrpwm.c b/drivers/pwm/pwm-ti-ehrpwm.c index f09914519bd..fefa3c65ec4 100644 --- a/drivers/pwm/pwm-ti-ehrpwm.c +++ b/drivers/pwm/pwm-ti-ehrpwm.c @@ -14,8 +14,7 @@ #include <dm/device_compat.h> #include <pwm.h> #include <asm/io.h> - -#define NSEC_PER_SEC 1000000000L +#include <linux/time.h> /* Time base module registers */ #define TI_EHRPWM_TBCTL 0x00 diff --git a/drivers/qe/fdt.c b/drivers/qe/fdt.c index 6195c7c4442..fa9e4065560 100644 --- a/drivers/qe/fdt.c +++ b/drivers/qe/fdt.c @@ -6,7 +6,6 @@ * Wolfgang Denk, DENX Software Engineering, wd@denx.de. */ -#include <common.h> #include <asm/global_data.h> #include <linux/libfdt.h> #include <fdt_support.h> diff --git a/drivers/qe/qe.c b/drivers/qe/qe.c index 2825dc6f9aa..9631337b8d9 100644 --- a/drivers/qe/qe.c +++ b/drivers/qe/qe.c @@ -6,7 +6,6 @@ * based on source code of Shlomi Gridish */ -#include <common.h> #include <malloc.h> #include <command.h> #include <asm/global_data.h> @@ -24,6 +23,9 @@ #include <asm/armv8/mmu.h> #include <asm/arch/cpu.h> #endif +#ifdef CONFIG_PPC +#include <asm/ppc.h> +#endif #define MPC85xx_DEVDISR_QE_DISABLE 0x1 diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig index bf999645774..5b07e920301 100644 --- a/drivers/ram/Kconfig +++ b/drivers/ram/Kconfig @@ -109,8 +109,9 @@ config IMXRT_SDRAM source "drivers/ram/aspeed/Kconfig" source "drivers/ram/cadence/Kconfig" +source "drivers/ram/octeon/Kconfig" source "drivers/ram/rockchip/Kconfig" source "drivers/ram/sifive/Kconfig" source "drivers/ram/stm32mp1/Kconfig" -source "drivers/ram/octeon/Kconfig" source "drivers/ram/starfive/Kconfig" +source "drivers/ram/sunxi/Kconfig" diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile index 6eb1a241359..985990ab5ac 100644 --- a/drivers/ram/Makefile +++ b/drivers/ram/Makefile @@ -23,6 +23,9 @@ obj-$(CONFIG_RAM_SIFIVE) += sifive/ ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_STARFIVE_DDR) += starfive/ endif + +obj-$(CONFIG_DRAM_SUN20I_D1) += sunxi/ + obj-$(CONFIG_ARCH_OCTEON) += octeon/ obj-$(CONFIG_ARCH_RMOBILE) += renesas/ diff --git a/drivers/ram/k3-am654-ddrss.c b/drivers/ram/k3-am654-ddrss.c index b8338f84a3d..cff8ffc8929 100644 --- a/drivers/ram/k3-am654-ddrss.c +++ b/drivers/ram/k3-am654-ddrss.c @@ -2,7 +2,7 @@ /* * Texas Instruments' AM654 DDRSS driver * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Lokesh Vutla <lokeshvutla@ti.com> */ @@ -903,7 +903,7 @@ static int am654_ddrss_power_on(struct am654_ddrss_desc *ddrss) static int am654_ddrss_ofdata_to_priv(struct udevice *dev) { struct am654_ddrss_desc *ddrss = dev_get_priv(dev); - phys_addr_t reg; + void *reg; int ret; debug("%s(dev=%p)\n", __func__, dev); @@ -926,26 +926,26 @@ static int am654_ddrss_ofdata_to_priv(struct udevice *dev) return ret; } - reg = devfdt_get_addr_name(dev, "ss"); - if (reg == FDT_ADDR_T_NONE) { + reg = dev_read_addr_name_ptr(dev, "ss"); + if (!reg) { dev_err(dev, "No reg property for DDRSS wrapper logic\n"); return -EINVAL; } - ddrss->ddrss_ss_cfg = (void *)reg; + ddrss->ddrss_ss_cfg = reg; - reg = devfdt_get_addr_name(dev, "ctl"); - if (reg == FDT_ADDR_T_NONE) { + reg = dev_read_addr_name_ptr(dev, "ctl"); + if (!reg) { dev_err(dev, "No reg property for Controller region\n"); return -EINVAL; } - ddrss->ddrss_ctl_cfg = (void *)reg; + ddrss->ddrss_ctl_cfg = reg; - reg = devfdt_get_addr_name(dev, "phy"); - if (reg == FDT_ADDR_T_NONE) { + reg = dev_read_addr_name_ptr(dev, "phy"); + if (!reg) { dev_err(dev, "No reg property for PHY region\n"); return -EINVAL; } - ddrss->ddrss_phy_cfg = (void *)reg; + ddrss->ddrss_phy_cfg = reg; ret = dev_read_u32_array(dev, "ti,ss-reg", (u32 *)&ddrss->params.ss_reg, diff --git a/drivers/ram/k3-am654-ddrss.h b/drivers/ram/k3-am654-ddrss.h index c87f186291e..9d03ae10750 100644 --- a/drivers/ram/k3-am654-ddrss.h +++ b/drivers/ram/k3-am654-ddrss.h @@ -2,7 +2,7 @@ /* * AM654: DDRSS Register definitions and structures. * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Lokesh Vutla <lokeshvutla@ti.com> * */ diff --git a/drivers/ram/k3-ddrss/Makefile b/drivers/ram/k3-ddrss/Makefile index ba5d9a2f4d3..823d1887178 100644 --- a/drivers/ram/k3-ddrss/Makefile +++ b/drivers/ram/k3-ddrss/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -# Copyright (C) 2019-2022 Texas Instruments Incorporated - http://www.ti.com/ +# Copyright (C) 2019-2022 Texas Instruments Incorporated - https://www.ti.com/ # obj-$(CONFIG_K3_DDRSS) += k3-ddrss.o diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index b54557f02cc..a5c9b82cf1d 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -2,7 +2,7 @@ /* * Texas Instruments' K3 DDRSS driver * - * Copyright (C) 2020-2021 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ */ #include <common.h> @@ -331,32 +331,29 @@ static int k3_ddrss_ofdata_to_priv(struct udevice *dev) { struct k3_ddrss_desc *ddrss = dev_get_priv(dev); struct k3_ddrss_data *ddrss_data = (struct k3_ddrss_data *)dev_get_driver_data(dev); - phys_addr_t reg; + void *reg; int ret; debug("%s(dev=%p)\n", __func__, dev); - reg = dev_read_addr_name(dev, "cfg"); - if (reg == FDT_ADDR_T_NONE) { + reg = dev_read_addr_name_ptr(dev, "cfg"); + if (!reg) { dev_err(dev, "No reg property for DDRSS wrapper logic\n"); return -EINVAL; } - ddrss->ddrss_ctl_cfg = (void *)reg; + ddrss->ddrss_ctl_cfg = reg; - reg = dev_read_addr_name(dev, "ctrl_mmr_lp4"); - if (reg == FDT_ADDR_T_NONE) { + reg = dev_read_addr_name_ptr(dev, "ctrl_mmr_lp4"); + if (!reg) { dev_err(dev, "No reg property for CTRL MMR\n"); return -EINVAL; } - ddrss->ddrss_ctrl_mmr = (void *)reg; + ddrss->ddrss_ctrl_mmr = reg; - reg = dev_read_addr_name(dev, "ss_cfg"); - if (reg == FDT_ADDR_T_NONE) { + reg = dev_read_addr_name_ptr(dev, "ss_cfg"); + if (!reg) dev_dbg(dev, "No reg property for SS Config region, but this is optional so continuing.\n"); - ddrss->ddrss_ss_cfg = NULL; - } else { - ddrss->ddrss_ss_cfg = (void *)reg; - } + ddrss->ddrss_ss_cfg = reg; ret = power_domain_get_by_index(dev, &ddrss->ddrcfg_pwrdmn, 0); if (ret) { diff --git a/drivers/ram/sunxi/Kconfig b/drivers/ram/sunxi/Kconfig new file mode 100644 index 00000000000..1775cb0d780 --- /dev/null +++ b/drivers/ram/sunxi/Kconfig @@ -0,0 +1,60 @@ +config DRAM_SUN20I_D1 + bool + depends on ARCH_SUNXI + help + This enables support for the DRAM controller driver covering + the Allwinner D1/R528/T113s SoCs. + +if DRAM_SUN20I_D1 + +config DRAM_SUNXI_ODT_EN + hex "DRAM ODT EN parameter" + help + ODT EN value from vendor DRAM settings. + +config DRAM_SUNXI_TPR0 + hex "DRAM TPR0 parameter" + help + TPR0 value from vendor DRAM settings. + +config DRAM_SUNXI_TPR11 + hex "DRAM TPR11 parameter" + help + TPR11 value from vendor DRAM settings. + +config DRAM_SUNXI_TPR12 + hex "DRAM TPR12 parameter" + help + TPR12 value from vendor DRAM settings. + +config DRAM_SUNXI_TPR13 + hex "DRAM TPR13 parameter" + help + TPR13 value from vendor DRAM settings. It tells which features + should be configured. + +choice + prompt "DRAM chip type" + default SUNXI_DRAM_TYPE_DDR3 if DRAM_SUN20I_D1 + +config SUNXI_DRAM_TYPE_DDR2 + bool "DDR2 chips" + +config SUNXI_DRAM_TYPE_DDR3 + bool "DDR3 chips" + +config SUNXI_DRAM_TYPE_LPDDR2 + bool "LPDDR2 chips" + +config SUNXI_DRAM_TYPE_LPDDR3 + bool "LPDDR3 chips" +endchoice + +config SUNXI_DRAM_TYPE + int + default 2 if SUNXI_DRAM_TYPE_DDR2 + default 3 if SUNXI_DRAM_TYPE_DDR3 + default 6 if SUNXI_DRAM_TYPE_LPDDR2 + default 7 if SUNXI_DRAM_TYPE_LPDDR3 + +endif diff --git a/drivers/ram/sunxi/Makefile b/drivers/ram/sunxi/Makefile new file mode 100644 index 00000000000..86ea0b9ae98 --- /dev/null +++ b/drivers/ram/sunxi/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_DRAM_SUN20I_D1) += dram_sun20i_d1.o diff --git a/drivers/ram/sunxi/dram_sun20i_d1.c b/drivers/ram/sunxi/dram_sun20i_d1.c new file mode 100644 index 00000000000..38379281d73 --- /dev/null +++ b/drivers/ram/sunxi/dram_sun20i_d1.c @@ -0,0 +1,1441 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Allwinner D1/D1s/R528/T113-sx DRAM initialisation + * + * As usual there is no documentation for the memory controller or PHY IP + * used here. The baseline of this code was lifted from awboot[1], which + * seems to be based on some form of de-compilation of some original Allwinner + * code bits (with a GPL2 license tag from the very beginning). + * This version here is a reworked version, to match the U-Boot coding style + * and style of the other Allwinner DRAM drivers. + * + * [1] https://github.com/szemzoa/awboot.git + */ + +#include <asm/io.h> +#include <common.h> +#ifdef CONFIG_RAM + #include <dm.h> + #include <ram.h> +#endif +#include <linux/delay.h> + +#include "dram_sun20i_d1.h" + +#ifndef SUNXI_SID_BASE +#define SUNXI_SID_BASE 0x3006200 +#endif + +#ifndef SUNXI_CCM_BASE +#define SUNXI_CCM_BASE 0x2001000 +#endif + +static void sid_read_ldoB_cal(const dram_para_t *para) +{ + uint32_t reg; + + reg = (readl(SUNXI_SID_BASE + 0x1c) & 0xff00) >> 8; + + if (reg == 0) + return; + + switch (para->dram_type) { + case SUNXI_DRAM_TYPE_DDR2: + break; + case SUNXI_DRAM_TYPE_DDR3: + if (reg > 0x20) + reg -= 0x16; + break; + default: + reg = 0; + break; + } + + clrsetbits_le32(0x3000150, 0xff00, reg << 8); +} + +static void dram_voltage_set(const dram_para_t *para) +{ + int vol; + + switch (para->dram_type) { + case SUNXI_DRAM_TYPE_DDR2: + vol = 47; + break; + case SUNXI_DRAM_TYPE_DDR3: + vol = 25; + break; + default: + vol = 0; + break; + } + + clrsetbits_le32(0x3000150, 0x20ff00, vol << 8); + + udelay(1); + + sid_read_ldoB_cal(para); +} + +static void dram_enable_all_master(void) +{ + writel(~0, 0x3102020); + writel(0xff, 0x3102024); + writel(0xffff, 0x3102028); + udelay(10); +} + +static void dram_disable_all_master(void) +{ + writel(1, 0x3102020); + writel(0, 0x3102024); + writel(0, 0x3102028); + udelay(10); +} + +static void eye_delay_compensation(const dram_para_t *para) +{ + uint32_t delay; + unsigned long ptr; + + // DATn0IOCR, n = 0...7 + delay = (para->dram_tpr11 & 0xf) << 9; + delay |= (para->dram_tpr12 & 0xf) << 1; + for (ptr = 0x3103310; ptr < 0x3103334; ptr += 4) + setbits_le32(ptr, delay); + + // DATn1IOCR, n = 0...7 + delay = (para->dram_tpr11 & 0xf0) << 5; + delay |= (para->dram_tpr12 & 0xf0) >> 3; + for (ptr = 0x3103390; ptr != 0x31033b4; ptr += 4) + setbits_le32(ptr, delay); + + // PGCR0: assert AC loopback FIFO reset + clrbits_le32(0x3103100, 0x04000000); + + // ?? + + delay = (para->dram_tpr11 & 0xf0000) >> 7; + delay |= (para->dram_tpr12 & 0xf0000) >> 15; + setbits_le32(0x3103334, delay); + setbits_le32(0x3103338, delay); + + delay = (para->dram_tpr11 & 0xf00000) >> 11; + delay |= (para->dram_tpr12 & 0xf00000) >> 19; + setbits_le32(0x31033b4, delay); + setbits_le32(0x31033b8, delay); + + setbits_le32(0x310333c, (para->dram_tpr11 & 0xf0000) << 9); + setbits_le32(0x31033bc, (para->dram_tpr11 & 0xf00000) << 5); + + // PGCR0: release AC loopback FIFO reset + setbits_le32(0x3103100, BIT(26)); + + udelay(1); + + delay = (para->dram_tpr10 & 0xf0) << 4; + for (ptr = 0x3103240; ptr != 0x310327c; ptr += 4) + setbits_le32(ptr, delay); + for (ptr = 0x3103228; ptr != 0x3103240; ptr += 4) + setbits_le32(ptr, delay); + + setbits_le32(0x3103218, (para->dram_tpr10 & 0x0f) << 8); + setbits_le32(0x310321c, (para->dram_tpr10 & 0x0f) << 8); + + setbits_le32(0x3103280, (para->dram_tpr10 & 0xf00) >> 4); +} + +/* + * Main purpose of the auto_set_timing routine seems to be to calculate all + * timing settings for the specific type of sdram used. Read together with + * an sdram datasheet for context on the various variables. + */ +static void mctl_set_timing_params(const dram_para_t *para, + const dram_config_t *config) +{ + /* DRAM_TPR0 */ + u8 tccd = 2; + u8 tfaw; + u8 trrd; + u8 trcd; + u8 trc; + + /* DRAM_TPR1 */ + u8 txp; + u8 twtr; + u8 trtp = 4; + u8 twr; + u8 trp; + u8 tras; + + /* DRAM_TPR2 */ + u16 trefi; + u16 trfc; + + u8 tcksrx; + u8 tckesr; + u8 trd2wr; + u8 twr2rd; + u8 trasmax; + u8 twtp; + u8 tcke; + u8 tmod; + u8 tmrd; + u8 tmrw; + + u8 tcl; + u8 tcwl; + u8 t_rdata_en; + u8 wr_latency; + + u32 mr0; + u32 mr1; + u32 mr2; + u32 mr3; + + u32 tdinit0; + u32 tdinit1; + u32 tdinit2; + u32 tdinit3; + + switch (para->dram_type) { + case SUNXI_DRAM_TYPE_DDR2: + /* DRAM_TPR0 */ + tfaw = ns_to_t(50); + trrd = ns_to_t(10); + trcd = ns_to_t(20); + trc = ns_to_t(65); + + /* DRAM_TPR1 */ + txp = 2; + twtr = ns_to_t(8); + twr = ns_to_t(15); + trp = ns_to_t(15); + tras = ns_to_t(45); + + /* DRAM_TRP2 */ + trfc = ns_to_t(328); + trefi = ns_to_t(7800) / 32; + + trasmax = CONFIG_DRAM_CLK / 30; + if (CONFIG_DRAM_CLK < 409) { + t_rdata_en = 1; + tcl = 3; + mr0 = 0x06a3; + } else { + t_rdata_en = 2; + tcl = 4; + mr0 = 0x0e73; + } + tmrd = 2; + twtp = twr + 5; + tcksrx = 5; + tckesr = 4; + trd2wr = 4; + tcke = 3; + tmod = 12; + wr_latency = 1; + tmrw = 0; + twr2rd = twtr + 5; + tcwl = 0; + + mr1 = para->dram_mr1; + mr2 = 0; + mr3 = 0; + + tdinit0 = 200 * CONFIG_DRAM_CLK + 1; + tdinit1 = 100 * CONFIG_DRAM_CLK / 1000 + 1; + tdinit2 = 200 * CONFIG_DRAM_CLK + 1; + tdinit3 = 1 * CONFIG_DRAM_CLK + 1; + + break; + case SUNXI_DRAM_TYPE_DDR3: + trfc = ns_to_t(350); + trefi = ns_to_t(7800) / 32 + 1; // XXX + + twtr = ns_to_t(8) + 2; // + 2 ? XXX + /* Only used by trd2wr calculation, which gets discard below */ +// twr = max(ns_to_t(15), 2); + trrd = max(ns_to_t(10), 2); + txp = max(ns_to_t(10), 2); + + if (CONFIG_DRAM_CLK <= 800) { + tfaw = ns_to_t(50); + trcd = ns_to_t(15); + trp = ns_to_t(15); + trc = ns_to_t(53); + tras = ns_to_t(38); + + mr0 = 0x1c70; + mr2 = 0x18; + tcl = 6; + wr_latency = 2; + tcwl = 4; + t_rdata_en = 4; + } else { + tfaw = ns_to_t(35); + trcd = ns_to_t(14); + trp = ns_to_t(14); + trc = ns_to_t(48); + tras = ns_to_t(34); + + mr0 = 0x1e14; + mr2 = 0x20; + tcl = 7; + wr_latency = 3; + tcwl = 5; + t_rdata_en = 5; + } + + trasmax = CONFIG_DRAM_CLK / 30; + twtp = tcwl + 2 + twtr; // WL+BL/2+tWTR + /* Gets overwritten below */ +// trd2wr = tcwl + 2 + twr; // WL+BL/2+tWR + twr2rd = tcwl + twtr; // WL+tWTR + + tdinit0 = 500 * CONFIG_DRAM_CLK + 1; // 500 us + tdinit1 = 360 * CONFIG_DRAM_CLK / 1000 + 1; // 360 ns + tdinit2 = 200 * CONFIG_DRAM_CLK + 1; // 200 us + tdinit3 = 1 * CONFIG_DRAM_CLK + 1; // 1 us + + mr1 = para->dram_mr1; + mr3 = 0; + tcke = 3; + tcksrx = 5; + tckesr = 4; + if (((config->dram_tpr13 & 0xc) == 0x04) || CONFIG_DRAM_CLK < 912) + trd2wr = 5; + else + trd2wr = 6; + + tmod = 12; + tmrd = 4; + tmrw = 0; + + break; + case SUNXI_DRAM_TYPE_LPDDR2: + tfaw = max(ns_to_t(50), 4); + trrd = max(ns_to_t(10), 1); + trcd = max(ns_to_t(24), 2); + trc = ns_to_t(70); + txp = ns_to_t(8); + if (txp < 2) { + txp++; + twtr = 2; + } else { + twtr = txp; + } + twr = max(ns_to_t(15), 2); + trp = ns_to_t(17); + tras = ns_to_t(42); + trefi = ns_to_t(3900) / 32; + trfc = ns_to_t(210); + + trasmax = CONFIG_DRAM_CLK / 60; + mr3 = para->dram_mr3; + twtp = twr + 5; + mr2 = 6; + mr1 = 5; + tcksrx = 5; + tckesr = 5; + trd2wr = 10; + tcke = 2; + tmod = 5; + tmrd = 5; + tmrw = 3; + tcl = 4; + wr_latency = 1; + t_rdata_en = 1; + + tdinit0 = 200 * CONFIG_DRAM_CLK + 1; + tdinit1 = 100 * CONFIG_DRAM_CLK / 1000 + 1; + tdinit2 = 11 * CONFIG_DRAM_CLK + 1; + tdinit3 = 1 * CONFIG_DRAM_CLK + 1; + twr2rd = twtr + 5; + tcwl = 2; + mr1 = 195; + mr0 = 0; + + break; + case SUNXI_DRAM_TYPE_LPDDR3: + tfaw = max(ns_to_t(50), 4); + trrd = max(ns_to_t(10), 1); + trcd = max(ns_to_t(24), 2); + trc = ns_to_t(70); + twtr = max(ns_to_t(8), 2); + twr = max(ns_to_t(15), 2); + trp = ns_to_t(17); + tras = ns_to_t(42); + trefi = ns_to_t(3900) / 32; + trfc = ns_to_t(210); + txp = twtr; + + trasmax = CONFIG_DRAM_CLK / 60; + if (CONFIG_DRAM_CLK < 800) { + tcwl = 4; + wr_latency = 3; + t_rdata_en = 6; + mr2 = 12; + } else { + tcwl = 3; + tcke = 6; + wr_latency = 2; + t_rdata_en = 5; + mr2 = 10; + } + twtp = tcwl + 5; + tcl = 7; + mr3 = para->dram_mr3; + tcksrx = 5; + tckesr = 5; + trd2wr = 13; + tcke = 3; + tmod = 12; + tdinit0 = 400 * CONFIG_DRAM_CLK + 1; + tdinit1 = 500 * CONFIG_DRAM_CLK / 1000 + 1; + tdinit2 = 11 * CONFIG_DRAM_CLK + 1; + tdinit3 = 1 * CONFIG_DRAM_CLK + 1; + tmrd = 5; + tmrw = 5; + twr2rd = tcwl + twtr + 5; + mr1 = 195; + mr0 = 0; + + break; + default: + trfc = 128; + trp = 6; + trefi = 98; + txp = 10; + twr = 8; + twtr = 3; + tras = 14; + tfaw = 16; + trc = 20; + trcd = 6; + trrd = 3; + + twr2rd = 8; + tcksrx = 4; + tckesr = 3; + trd2wr = 4; + trasmax = 27; + twtp = 12; + tcke = 2; + tmod = 6; + tmrd = 2; + tmrw = 0; + tcwl = 3; + tcl = 3; + wr_latency = 1; + t_rdata_en = 1; + mr3 = 0; + mr2 = 0; + mr1 = 0; + mr0 = 0; + tdinit3 = 0; + tdinit2 = 0; + tdinit1 = 0; + tdinit0 = 0; + + break; + } + + /* Set mode registers */ + writel(mr0, 0x3103030); + writel(mr1, 0x3103034); + writel(mr2, 0x3103038); + writel(mr3, 0x310303c); + /* TODO: dram_odt_en is either 0x0 or 0x1, so right shift looks weird */ + writel((para->dram_odt_en >> 4) & 0x3, 0x310302c); + + /* Set dram timing DRAMTMG0 - DRAMTMG5 */ + writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | (tras << 0), + 0x3103058); + writel((txp << 16) | (trtp << 8) | (trc << 0), + 0x310305c); + writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | (twr2rd << 0), + 0x3103060); + writel((tmrw << 16) | (tmrd << 12) | (tmod << 0), + 0x3103064); + writel((trcd << 24) | (tccd << 16) | (trrd << 8) | (trp << 0), + 0x3103068); + writel((tcksrx << 24) | (tcksrx << 16) | (tckesr << 8) | (tcke << 0), + 0x310306c); + + /* Set dual rank timing */ + clrsetbits_le32(0x3103078, 0xf000ffff, + (CONFIG_DRAM_CLK < 800) ? 0xf0006610 : 0xf0007610); + + /* Set phy interface time PITMG0, PTR3, PTR4 */ + writel((0x2 << 24) | (t_rdata_en << 16) | BIT(8) | (wr_latency << 0), + 0x3103080); + writel(((tdinit0 << 0) | (tdinit1 << 20)), 0x3103050); + writel(((tdinit2 << 0) | (tdinit3 << 20)), 0x3103054); + + /* Set refresh timing and mode */ + writel((trefi << 16) | (trfc << 0), 0x3103090); + writel((trefi << 15) & 0x0fff0000, 0x3103094); +} + +// Purpose of this routine seems to be to initialize the PLL driving +// the MBUS and sdram. +// +static int ccu_set_pll_ddr_clk(int index, const dram_para_t *para, + const dram_config_t *config) +{ + unsigned int val, clk, n; + + if (config->dram_tpr13 & BIT(6)) + clk = para->dram_tpr9; + else + clk = para->dram_clk; + + // set VCO clock divider + n = (clk * 2) / 24; + + val = readl(SUNXI_CCM_BASE + 0x10); + val &= ~0x0007ff03; // clear dividers + val |= (n - 1) << 8; // set PLL division + val |= BIT(31) | BIT(30); // enable PLL and LDO + writel(val | BIT(29), SUNXI_CCM_BASE + 0x10); + + // wait for PLL to lock + while ((readl(SUNXI_CCM_BASE + 0x10) & BIT(28)) == 0) + ; + + udelay(20); + + // enable PLL output + setbits_le32(SUNXI_CCM_BASE + 0x0, BIT(27)); + + // turn clock gate on + val = readl(SUNXI_CCM_BASE + 0x800); + val &= ~0x03000303; // select DDR clk source, n=1, m=1 + val |= BIT(31); // turn clock on + writel(val, SUNXI_CCM_BASE + 0x800); + + return n * 24; +} + +/* Set up the PLL and clock gates for the DRAM controller and MBUS clocks. */ +static void mctl_sys_init(const dram_para_t *para, const dram_config_t *config) +{ + // assert MBUS reset + clrbits_le32(SUNXI_CCM_BASE + 0x540, BIT(30)); + + // turn off sdram clock gate, assert sdram reset + clrbits_le32(SUNXI_CCM_BASE + 0x80c, 0x10001); + clrsetbits_le32(SUNXI_CCM_BASE + 0x800, BIT(31) | BIT(30), BIT(27)); + udelay(10); + + // set ddr pll clock + ccu_set_pll_ddr_clk(0, para, config); + udelay(100); + dram_disable_all_master(); + + // release sdram reset + setbits_le32(SUNXI_CCM_BASE + 0x80c, BIT(16)); + + // release MBUS reset + setbits_le32(SUNXI_CCM_BASE + 0x540, BIT(30)); + setbits_le32(SUNXI_CCM_BASE + 0x800, BIT(30)); + + udelay(5); + + // turn on sdram clock gate + setbits_le32(SUNXI_CCM_BASE + 0x80c, BIT(0)); + + // turn dram clock gate on, trigger sdr clock update + setbits_le32(SUNXI_CCM_BASE + 0x800, BIT(31) | BIT(27)); + udelay(5); + + // mCTL clock enable + writel(0x8000, 0x310300c); + udelay(10); +} + +// The main purpose of this routine seems to be to copy an address configuration +// from the dram_para1 and dram_para2 fields to the PHY configuration registers +// (0x3102000, 0x3102004). +// +static void mctl_com_init(const dram_para_t *para, const dram_config_t *config) +{ + uint32_t val, width; + unsigned long ptr; + int i; + + // purpose ?? + clrsetbits_le32(0x3102008, 0x3f00, 0x2000); + + // set SDRAM type and word width + val = readl(0x3102000) & ~0x00fff000; + val |= (para->dram_type & 0x7) << 16; // DRAM type + val |= (~config->dram_para2 & 0x1) << 12; // DQ width + val |= BIT(22); // ?? + if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR2 || + para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) { + val |= BIT(19); // type 6 and 7 must use 1T + } else { + if (config->dram_tpr13 & BIT(5)) + val |= BIT(19); + } + writel(val, 0x3102000); + + // init rank / bank / row for single/dual or two different ranks + if ((config->dram_para2 & BIT(8)) && + ((config->dram_para2 & 0xf000) != 0x1000)) + width = 32; + else + width = 16; + + ptr = 0x3102000; + for (i = 0; i < width; i += 16) { + val = readl(ptr) & 0xfffff000; + + val |= (config->dram_para2 >> 12) & 0x3; // rank + val |= ((config->dram_para1 >> (i + 12)) << 2) & 0x4; // bank - 2 + val |= (((config->dram_para1 >> (i + 4)) - 1) << 4) & 0xff; // row - 1 + + // convert from page size to column addr width - 3 + switch ((config->dram_para1 >> i) & 0xf) { + case 8: val |= 0xa00; break; + case 4: val |= 0x900; break; + case 2: val |= 0x800; break; + case 1: val |= 0x700; break; + default: val |= 0x600; break; + } + writel(val, ptr); + ptr += 4; + } + + // set ODTMAP based on number of ranks in use + val = (readl(0x3102000) & 0x1) ? 0x303 : 0x201; + writel(val, 0x3103120); + + // set mctl reg 3c4 to zero when using half DQ + if (config->dram_para2 & BIT(0)) + writel(0, 0x31033c4); + + // purpose ?? + if (para->dram_tpr4) { + setbits_le32(0x3102000, (para->dram_tpr4 & 0x3) << 25); + setbits_le32(0x3102004, (para->dram_tpr4 & 0x7fc) << 10); + } +} + +static const uint8_t ac_remapping_tables[][22] = { + [0] = { 0 }, + [1] = { 1, 9, 3, 7, 8, 18, 4, 13, 5, 6, 10, + 2, 14, 12, 0, 0, 21, 17, 20, 19, 11, 22 }, + [2] = { 4, 9, 3, 7, 8, 18, 1, 13, 2, 6, 10, + 5, 14, 12, 0, 0, 21, 17, 20, 19, 11, 22 }, + [3] = { 1, 7, 8, 12, 10, 18, 4, 13, 5, 6, 3, + 2, 9, 0, 0, 0, 21, 17, 20, 19, 11, 22 }, + [4] = { 4, 12, 10, 7, 8, 18, 1, 13, 2, 6, 3, + 5, 9, 0, 0, 0, 21, 17, 20, 19, 11, 22 }, + [5] = { 13, 2, 7, 9, 12, 19, 5, 1, 6, 3, 4, + 8, 10, 0, 0, 0, 21, 22, 18, 17, 11, 20 }, + [6] = { 3, 10, 7, 13, 9, 11, 1, 2, 4, 6, 8, + 5, 12, 0, 0, 0, 20, 1, 0, 21, 22, 17 }, + [7] = { 3, 2, 4, 7, 9, 1, 17, 12, 18, 14, 13, + 8, 15, 6, 10, 5, 19, 22, 16, 21, 20, 11 }, +}; + +/* + * This routine chooses one of several remapping tables for 22 lines. + * It is unclear which lines are being remapped. It seems to pick + * table cfg7 for the Nezha board. + */ +static void mctl_phy_ac_remapping(const dram_para_t *para, + const dram_config_t *config) +{ + const uint8_t *cfg; + uint32_t fuse, val; + + /* + * It is unclear whether the LPDDRx types don't need any remapping, + * or whether the original code just didn't provide tables. + */ + if (para->dram_type != SUNXI_DRAM_TYPE_DDR2 && + para->dram_type != SUNXI_DRAM_TYPE_DDR3) + return; + + fuse = (readl(SUNXI_SID_BASE + 0x28) & 0xf00) >> 8; + debug("DDR efuse: 0x%x\n", fuse); + + if (para->dram_type == SUNXI_DRAM_TYPE_DDR2) { + if (fuse == 15) + return; + cfg = ac_remapping_tables[6]; + } else { + if (config->dram_tpr13 & 0xc0000) { + cfg = ac_remapping_tables[7]; + } else { + switch (fuse) { + case 8: cfg = ac_remapping_tables[2]; break; + case 9: cfg = ac_remapping_tables[3]; break; + case 10: cfg = ac_remapping_tables[5]; break; + case 11: cfg = ac_remapping_tables[4]; break; + default: + case 12: cfg = ac_remapping_tables[1]; break; + case 13: + case 14: cfg = ac_remapping_tables[0]; break; + } + } + } + + val = (cfg[4] << 25) | (cfg[3] << 20) | (cfg[2] << 15) | + (cfg[1] << 10) | (cfg[0] << 5); + writel(val, 0x3102500); + + val = (cfg[10] << 25) | (cfg[9] << 20) | (cfg[8] << 15) | + (cfg[ 7] << 10) | (cfg[6] << 5) | cfg[5]; + writel(val, 0x3102504); + + val = (cfg[15] << 20) | (cfg[14] << 15) | (cfg[13] << 10) | + (cfg[12] << 5) | cfg[11]; + writel(val, 0x3102508); + + val = (cfg[21] << 25) | (cfg[20] << 20) | (cfg[19] << 15) | + (cfg[18] << 10) | (cfg[17] << 5) | cfg[16]; + writel(val, 0x310250c); + + val = (cfg[4] << 25) | (cfg[3] << 20) | (cfg[2] << 15) | + (cfg[1] << 10) | (cfg[0] << 5) | 1; + writel(val, 0x3102500); +} + +// Init the controller channel. The key part is placing commands in the main +// command register (PIR, 0x3103000) and checking command status (PGSR0, 0x3103010). +// +static unsigned int mctl_channel_init(unsigned int ch_index, + const dram_para_t *para, + const dram_config_t *config) +{ + unsigned int val, dqs_gating_mode; + + dqs_gating_mode = (config->dram_tpr13 & 0xc) >> 2; + + // set DDR clock to half of CPU clock + clrsetbits_le32(0x310200c, 0xfff, (para->dram_clk / 2) - 1); + + // MRCTRL0 nibble 3 undocumented + clrsetbits_le32(0x3103108, 0xf00, 0x300); + + if (para->dram_odt_en) + val = 0; + else + val = BIT(5); + + // DX0GCR0 + if (para->dram_clk > 672) + clrsetbits_le32(0x3103344, 0xf63e, val); + else + clrsetbits_le32(0x3103344, 0xf03e, val); + + // DX1GCR0 + if (para->dram_clk > 672) { + setbits_le32(0x3103344, 0x400); + clrsetbits_le32(0x31033c4, 0xf63e, val); + } else { + clrsetbits_le32(0x31033c4, 0xf03e, val); + } + + // 0x3103208 undocumented + setbits_le32(0x3103208, BIT(1)); + + eye_delay_compensation(para); + + // set PLL SSCG ? + val = readl(0x3103108); + if (dqs_gating_mode == 1) { + clrsetbits_le32(0x3103108, 0xc0, 0); + clrbits_le32(0x31030bc, 0x107); + } else if (dqs_gating_mode == 2) { + clrsetbits_le32(0x3103108, 0xc0, 0x80); + + clrsetbits_le32(0x31030bc, 0x107, + (((config->dram_tpr13 >> 16) & 0x1f) - 2) | 0x100); + clrsetbits_le32(0x310311c, BIT(31), BIT(27)); + } else { + clrbits_le32(0x3103108, 0x40); + udelay(10); + setbits_le32(0x3103108, 0xc0); + } + + if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR2 || + para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) { + if (dqs_gating_mode == 1) + clrsetbits_le32(0x310311c, 0x080000c0, 0x80000000); + else + clrsetbits_le32(0x310311c, 0x77000000, 0x22000000); + } + + clrsetbits_le32(0x31030c0, 0x0fffffff, + (config->dram_para2 & BIT(12)) ? 0x03000001 : 0x01000007); + + if (readl(0x70005d4) & BIT(16)) { + clrbits_le32(0x7010250, 0x2); + udelay(10); + } + + // Set ZQ config + clrsetbits_le32(0x3103140, 0x3ffffff, + (para->dram_zq & 0x00ffffff) | BIT(25)); + + // Initialise DRAM controller + if (dqs_gating_mode == 1) { + //writel(0x52, 0x3103000); // prep PHY reset + PLL init + z-cal + writel(0x53, 0x3103000); // Go + + while ((readl(0x3103010) & 0x1) == 0) { + } // wait for IDONE + udelay(10); + + // 0x520 = prep DQS gating + DRAM init + d-cal + if (para->dram_type == SUNXI_DRAM_TYPE_DDR3) + writel(0x5a0, 0x3103000); // + DRAM reset + else + writel(0x520, 0x3103000); + } else { + if ((readl(0x70005d4) & (1 << 16)) == 0) { + // prep DRAM init + PHY reset + d-cal + PLL init + z-cal + if (para->dram_type == SUNXI_DRAM_TYPE_DDR3) + writel(0x1f2, 0x3103000); // + DRAM reset + else + writel(0x172, 0x3103000); + } else { + // prep PHY reset + d-cal + z-cal + writel(0x62, 0x3103000); + } + } + + setbits_le32(0x3103000, 0x1); // GO + + udelay(10); + while ((readl(0x3103010) & 0x1) == 0) { + } // wait for IDONE + + if (readl(0x70005d4) & BIT(16)) { + clrsetbits_le32(0x310310c, 0x06000000, 0x04000000); + udelay(10); + + setbits_le32(0x3103004, 0x1); + + while ((readl(0x3103018) & 0x7) != 0x3) { + } + + clrbits_le32(0x7010250, 0x1); + udelay(10); + + clrbits_le32(0x3103004, 0x1); + + while ((readl(0x3103018) & 0x7) != 0x1) { + } + + udelay(15); + + if (dqs_gating_mode == 1) { + clrbits_le32(0x3103108, 0xc0); + clrsetbits_le32(0x310310c, 0x06000000, 0x02000000); + udelay(1); + writel(0x401, 0x3103000); + + while ((readl(0x3103010) & 0x1) == 0) { + } + } + } + + // Check for training error + if (readl(0x3103010) & BIT(20)) { + printf("ZQ calibration error, check external 240 ohm resistor\n"); + return 0; + } + + // STATR = Zynq STAT? Wait for status 'normal'? + while ((readl(0x3103018) & 0x1) == 0) { + } + + setbits_le32(0x310308c, BIT(31)); + udelay(10); + clrbits_le32(0x310308c, BIT(31)); + udelay(10); + setbits_le32(0x3102014, BIT(31)); + udelay(10); + + clrbits_le32(0x310310c, 0x06000000); + + if (dqs_gating_mode == 1) + clrsetbits_le32(0x310311c, 0xc0, 0x40); + + return 1; +} + +static unsigned int calculate_rank_size(uint32_t regval) +{ + unsigned int bits; + + bits = (regval >> 8) & 0xf; /* page size - 3 */ + bits += (regval >> 4) & 0xf; /* row width - 1 */ + bits += (regval >> 2) & 0x3; /* bank count - 2 */ + bits -= 14; /* 1MB = 20 bits, minus above 6 = 14 */ + + return 1U << bits; +} + +/* + * The below routine reads the dram config registers and extracts + * the number of address bits in each rank available. It then calculates + * total memory size in MB. + */ +static unsigned int DRAMC_get_dram_size(void) +{ + uint32_t val; + unsigned int size; + + val = readl(0x3102000); /* MC_WORK_MODE0 */ + size = calculate_rank_size(val); + if ((val & 0x3) == 0) /* single rank? */ + return size; + + val = readl(0x3102004); /* MC_WORK_MODE1 */ + if ((val & 0x3) == 0) /* two identical ranks? */ + return size * 2; + + /* add sizes of both ranks */ + return size + calculate_rank_size(val); +} + +/* + * The below routine reads the command status register to extract + * DQ width and rank count. This follows the DQS training command in + * channel_init. If error bit 22 is reset, we have two ranks and full DQ. + * If there was an error, figure out whether it was half DQ, single rank, + * or both. Set bit 12 and 0 in dram_para2 with the results. + */ +static int dqs_gate_detect(dram_config_t *config) +{ + uint32_t dx0, dx1; + + if ((readl(0x3103010) & BIT(22)) == 0) { + config->dram_para2 = (config->dram_para2 & ~0xf) | BIT(12); + debug("dual rank and full DQ\n"); + + return 1; + } + + dx0 = (readl(0x3103348) & 0x3000000) >> 24; + if (dx0 == 0) { + config->dram_para2 = (config->dram_para2 & ~0xf) | 0x1001; + debug("dual rank and half DQ\n"); + + return 1; + } + + if (dx0 == 2) { + dx1 = (readl(0x31033c8) & 0x3000000) >> 24; + if (dx1 == 2) { + config->dram_para2 = config->dram_para2 & ~0xf00f; + debug("single rank and full DQ\n"); + } else { + config->dram_para2 = (config->dram_para2 & ~0xf00f) | BIT(0); + debug("single rank and half DQ\n"); + } + + return 1; + } + + if ((config->dram_tpr13 & BIT(29)) == 0) + return 0; + + debug("DX0 state: %d\n", dx0); + debug("DX1 state: %d\n", dx1); + + return 0; +} + +static int dramc_simple_wr_test(unsigned int mem_mb, int len) +{ + unsigned int offs = (mem_mb / 2) << 18; // half of memory size + unsigned int patt1 = 0x01234567; + unsigned int patt2 = 0xfedcba98; + unsigned int *addr, v1, v2, i; + + addr = (unsigned int *)CFG_SYS_SDRAM_BASE; + for (i = 0; i != len; i++, addr++) { + writel(patt1 + i, (unsigned long)addr); + writel(patt2 + i, (unsigned long)(addr + offs)); + } + + addr = (unsigned int *)CFG_SYS_SDRAM_BASE; + for (i = 0; i != len; i++) { + v1 = readl((unsigned long)(addr + i)); + v2 = patt1 + i; + if (v1 != v2) { + printf("DRAM: simple test FAIL\n"); + printf("%x != %x at address %p\n", v1, v2, addr + i); + return 1; + } + v1 = readl((unsigned long)(addr + offs + i)); + v2 = patt2 + i; + if (v1 != v2) { + printf("DRAM: simple test FAIL\n"); + printf("%x != %x at address %p\n", v1, v2, addr + offs + i); + return 1; + } + } + + debug("DRAM: simple test OK\n"); + return 0; +} + +// Set the Vref mode for the controller +// +static void mctl_vrefzq_init(const dram_para_t *para, const dram_config_t *config) +{ + if (config->dram_tpr13 & BIT(17)) + return; + + clrsetbits_le32(0x3103110, 0x7f7f7f7f, para->dram_tpr5); + + // IOCVR1 + if ((config->dram_tpr13 & BIT(16)) == 0) + clrsetbits_le32(0x3103114, 0x7f, para->dram_tpr6 & 0x7f); +} + +// Perform an init of the controller. This is actually done 3 times. The first +// time to establish the number of ranks and DQ width. The second time to +// establish the actual ram size. The third time is final one, with the final +// settings. +// +static int mctl_core_init(const dram_para_t *para, const dram_config_t *config) +{ + mctl_sys_init(para, config); + + mctl_vrefzq_init(para, config); + + mctl_com_init(para, config); + + mctl_phy_ac_remapping(para, config); + + mctl_set_timing_params(para, config); + + return mctl_channel_init(0, para, config); +} + +/* + * This routine sizes a DRAM device by cycling through address lines and + * figuring out if they are connected to a real address line, or if the + * address is a mirror. + * First the column and bank bit allocations are set to low values (2 and 9 + * address lines). Then a maximum allocation (16 lines) is set for rows and + * this is tested. + * Next the BA2 line is checked. This seems to be placed above the column, + * BA0-1 and row addresses. Finally, the column address is allocated 13 lines + * and these are tested. The results are placed in dram_para1 and dram_para2. + */ + +static uint32_t get_payload(bool odd, unsigned long int ptr) +{ + if (odd) + return (uint32_t)ptr; + else + return ~((uint32_t)ptr); +} + +static int auto_scan_dram_size(const dram_para_t *para, dram_config_t *config) +{ + unsigned int rval, i, j, rank, maxrank, offs; + unsigned int shft; + unsigned long ptr, mc_work_mode, chk; + + if (mctl_core_init(para, config) == 0) { + printf("DRAM initialisation error : 0\n"); + return 0; + } + + maxrank = (config->dram_para2 & 0xf000) ? 2 : 1; + mc_work_mode = 0x3102000; + offs = 0; + + /* write test pattern */ + for (i = 0, ptr = CFG_SYS_SDRAM_BASE; i < 64; i++, ptr += 4) + writel(get_payload(i & 0x1, ptr), ptr); + + for (rank = 0; rank < maxrank;) { + /* set row mode */ + clrsetbits_le32(mc_work_mode, 0xf0c, 0x6f0); + udelay(1); + + // Scan per address line, until address wraps (i.e. see shadow) + for (i = 11; i < 17; i++) { + chk = CFG_SYS_SDRAM_BASE + (1U << (i + 11)); + ptr = CFG_SYS_SDRAM_BASE; + for (j = 0; j < 64; j++) { + if (readl(chk) != get_payload(j & 0x1, ptr)) + break; + ptr += 4; + chk += 4; + } + if (j == 64) + break; + } + if (i > 16) + i = 16; + debug("rank %d row = %d\n", rank, i); + + /* Store rows in para 1 */ + shft = offs + 4; + rval = config->dram_para1; + rval &= ~(0xff << shft); + rval |= i << shft; + config->dram_para1 = rval; + + if (rank == 1) /* Set bank mode for rank0 */ + clrsetbits_le32(0x3102000, 0xffc, 0x6a4); + + /* Set bank mode for current rank */ + clrsetbits_le32(mc_work_mode, 0xffc, 0x6a4); + udelay(1); + + // Test if bit A23 is BA2 or mirror XXX A22? + chk = CFG_SYS_SDRAM_BASE + (1U << 22); + ptr = CFG_SYS_SDRAM_BASE; + for (i = 0, j = 0; i < 64; i++) { + if (readl(chk) != get_payload(i & 1, ptr)) { + j = 1; + break; + } + ptr += 4; + chk += 4; + } + + debug("rank %d bank = %d\n", rank, (j + 1) << 2); /* 4 or 8 */ + + /* Store banks in para 1 */ + shft = 12 + offs; + rval = config->dram_para1; + rval &= ~(0xf << shft); + rval |= j << shft; + config->dram_para1 = rval; + + if (rank == 1) /* Set page mode for rank0 */ + clrsetbits_le32(0x3102000, 0xffc, 0xaa0); + + /* Set page mode for current rank */ + clrsetbits_le32(mc_work_mode, 0xffc, 0xaa0); + udelay(1); + + // Scan per address line, until address wraps (i.e. see shadow) + for (i = 9; i < 14; i++) { + chk = CFG_SYS_SDRAM_BASE + (1U << i); + ptr = CFG_SYS_SDRAM_BASE; + for (j = 0; j < 64; j++) { + if (readl(chk) != get_payload(j & 1, ptr)) + break; + ptr += 4; + chk += 4; + } + if (j == 64) + break; + } + if (i > 13) + i = 13; + + unsigned int pgsize = (i == 9) ? 0 : (1 << (i - 10)); + debug("rank %d page size = %d KB\n", rank, pgsize); + + /* Store page size */ + shft = offs; + rval = config->dram_para1; + rval &= ~(0xf << shft); + rval |= pgsize << shft; + config->dram_para1 = rval; + + // Move to next rank + rank++; + if (rank != maxrank) { + if (rank == 1) { + /* MC_WORK_MODE */ + clrsetbits_le32(0x3202000, 0xffc, 0x6f0); + + /* MC_WORK_MODE2 */ + clrsetbits_le32(0x3202004, 0xffc, 0x6f0); + } + /* store rank1 config in upper half of para1 */ + offs += 16; + mc_work_mode += 4; /* move to MC_WORK_MODE2 */ + } + } + if (maxrank == 2) { + config->dram_para2 &= 0xfffff0ff; + /* note: rval is equal to para->dram_para1 here */ + if ((rval & 0xffff) == (rval >> 16)) { + debug("rank1 config same as rank0\n"); + } else { + config->dram_para2 |= BIT(8); + debug("rank1 config different from rank0\n"); + } + } + + return 1; +} + +/* + * This routine sets up parameters with dqs_gating_mode equal to 1 and two + * ranks enabled. It then configures the core and tests for 1 or 2 ranks and + * full or half DQ width. It then resets the parameters to the original values. + * dram_para2 is updated with the rank and width findings. + */ +static int auto_scan_dram_rank_width(const dram_para_t *para, + dram_config_t *config) +{ + unsigned int s1 = config->dram_tpr13; + unsigned int s2 = config->dram_para1; + + config->dram_para1 = 0x00b000b0; + config->dram_para2 = (config->dram_para2 & ~0xf) | BIT(12); + + /* set DQS probe mode */ + config->dram_tpr13 = (config->dram_tpr13 & ~0x8) | BIT(2) | BIT(0); + + mctl_core_init(para, config); + + if (readl(0x3103010) & BIT(20)) + return 0; + + if (dqs_gate_detect(config) == 0) + return 0; + + config->dram_tpr13 = s1; + config->dram_para1 = s2; + + return 1; +} + +/* + * This routine determines the SDRAM topology. It first establishes the number + * of ranks and the DQ width. Then it scans the SDRAM address lines to establish + * the size of each rank. It then updates dram_tpr13 to reflect that the sizes + * are now known: a re-init will not repeat the autoscan. + */ +static int auto_scan_dram_config(const dram_para_t *para, + dram_config_t *config) +{ + if (((config->dram_tpr13 & BIT(14)) == 0) && + (auto_scan_dram_rank_width(para, config) == 0)) { + printf("ERROR: auto scan dram rank & width failed\n"); + return 0; + } + + if (((config->dram_tpr13 & BIT(0)) == 0) && + (auto_scan_dram_size(para, config) == 0)) { + printf("ERROR: auto scan dram size failed\n"); + return 0; + } + + if ((config->dram_tpr13 & BIT(15)) == 0) + config->dram_tpr13 |= BIT(14) | BIT(13) | BIT(1) | BIT(0); + + return 1; +} + +static int init_DRAM(int type, const dram_para_t *para) +{ + dram_config_t config = { + .dram_para1 = 0x000010d2, + .dram_para2 = 0, + .dram_tpr13 = CONFIG_DRAM_SUNXI_TPR13, + }; + u32 rc, mem_size_mb; + + debug("DRAM BOOT DRIVE INFO: %s\n", "V0.24"); + debug("DRAM CLK = %d MHz\n", para->dram_clk); + debug("DRAM Type = %d (2:DDR2,3:DDR3)\n", para->dram_type); + if ((para->dram_odt_en & 0x1) == 0) + debug("DRAMC read ODT off\n"); + else + debug("DRAMC ZQ value: 0x%x\n", para->dram_zq); + + /* Test ZQ status */ + if (config.dram_tpr13 & BIT(16)) { + debug("DRAM only have internal ZQ\n"); + setbits_le32(0x3000160, BIT(8)); + writel(0, 0x3000168); + udelay(10); + } else { + clrbits_le32(0x3000160, 0x3); + writel(config.dram_tpr13 & BIT(16), 0x7010254); + udelay(10); + clrsetbits_le32(0x3000160, 0x108, BIT(1)); + udelay(10); + setbits_le32(0x3000160, BIT(0)); + udelay(20); + debug("ZQ value = 0x%x\n", readl(0x300016c)); + } + + dram_voltage_set(para); + + /* Set SDRAM controller auto config */ + if ((config.dram_tpr13 & BIT(0)) == 0) { + if (auto_scan_dram_config(para, &config) == 0) { + printf("auto_scan_dram_config() FAILED\n"); + return 0; + } + } + + /* report ODT */ + rc = para->dram_mr1; + if ((rc & 0x44) == 0) + debug("DRAM ODT off\n"); + else + debug("DRAM ODT value: 0x%x\n", rc); + + /* Init core, final run */ + if (mctl_core_init(para, &config) == 0) { + printf("DRAM initialisation error: 1\n"); + return 0; + } + + /* Get SDRAM size */ + /* TODO: who ever puts a negative number in the top half? */ + rc = config.dram_para2; + if (rc & BIT(31)) { + rc = (rc >> 16) & ~BIT(15); + } else { + rc = DRAMC_get_dram_size(); + debug("DRAM: size = %dMB\n", rc); + config.dram_para2 = (config.dram_para2 & 0xffffU) | rc << 16; + } + mem_size_mb = rc; + + /* Purpose ?? */ + if (config.dram_tpr13 & BIT(30)) { + rc = para->dram_tpr8; + if (rc == 0) + rc = 0x10000200; + writel(rc, 0x31030a0); + writel(0x40a, 0x310309c); + setbits_le32(0x3103004, BIT(0)); + debug("Enable Auto SR\n"); + } else { + clrbits_le32(0x31030a0, 0xffff); + clrbits_le32(0x3103004, 0x1); + } + + /* Purpose ?? */ + if (config.dram_tpr13 & BIT(9)) { + clrsetbits_le32(0x3103100, 0xf000, 0x5000); + } else { + if (para->dram_type != SUNXI_DRAM_TYPE_LPDDR2) + clrbits_le32(0x3103100, 0xf000); + } + + setbits_le32(0x3103140, BIT(31)); + + /* CHECK: is that really writing to a different register? */ + if (config.dram_tpr13 & BIT(8)) + writel(readl(0x3103140) | 0x300, 0x31030b8); + + if (config.dram_tpr13 & BIT(16)) + clrbits_le32(0x3103108, BIT(13)); + else + setbits_le32(0x3103108, BIT(13)); + + /* Purpose ?? */ + if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) + clrsetbits_le32(0x310307c, 0xf0000, 0x1000); + + dram_enable_all_master(); + if (config.dram_tpr13 & BIT(28)) { + if ((readl(0x70005d4) & BIT(16)) || + dramc_simple_wr_test(mem_size_mb, 4096)) + return 0; + } + + return mem_size_mb; +} + +static const dram_para_t para = { + .dram_clk = CONFIG_DRAM_CLK, + .dram_type = CONFIG_SUNXI_DRAM_TYPE, + .dram_zq = CONFIG_DRAM_ZQ, + .dram_odt_en = CONFIG_DRAM_SUNXI_ODT_EN, + .dram_mr0 = 0x1c70, + .dram_mr1 = 0x42, + .dram_mr2 = 0x18, + .dram_mr3 = 0, + .dram_tpr0 = 0x004a2195, + .dram_tpr1 = 0x02423190, + .dram_tpr2 = 0x0008b061, + .dram_tpr3 = 0xb4787896, // unused + .dram_tpr4 = 0, + .dram_tpr5 = 0x48484848, + .dram_tpr6 = 0x00000048, + .dram_tpr7 = 0x1620121e, // unused + .dram_tpr8 = 0, + .dram_tpr9 = 0, // clock? + .dram_tpr10 = 0, + .dram_tpr11 = CONFIG_DRAM_SUNXI_TPR11, + .dram_tpr12 = CONFIG_DRAM_SUNXI_TPR12, +}; + +unsigned long sunxi_dram_init(void) +{ + return init_DRAM(0, ¶) * 1024UL * 1024; +}; + +#ifdef CONFIG_RAM /* using the driver model */ +struct sunxi_ram_priv { + size_t size; +}; + +static int sunxi_ram_probe(struct udevice *dev) +{ + struct sunxi_ram_priv *priv = dev_get_priv(dev); + unsigned long dram_size; + + debug("%s: %s: probing\n", __func__, dev->name); + + dram_size = sunxi_dram_init(); + if (!dram_size) { + printf("DRAM init failed\n"); + return -ENODEV; + } + + priv->size = dram_size; + + return 0; +} + +static int sunxi_ram_get_info(struct udevice *dev, struct ram_info *info) +{ + struct sunxi_ram_priv *priv = dev_get_priv(dev); + + debug("%s: %s: getting info\n", __func__, dev->name); + + info->base = CFG_SYS_SDRAM_BASE; + info->size = priv->size; + + return 0; +} + +static struct ram_ops sunxi_ram_ops = { + .get_info = sunxi_ram_get_info, +}; + +static const struct udevice_id sunxi_ram_ids[] = { + { .compatible = "allwinner,sun20i-d1-mbus" }, + { } +}; + +U_BOOT_DRIVER(sunxi_ram) = { + .name = "sunxi_ram", + .id = UCLASS_RAM, + .of_match = sunxi_ram_ids, + .ops = &sunxi_ram_ops, + .probe = sunxi_ram_probe, + .priv_auto = sizeof(struct sunxi_ram_priv), +}; +#endif /* CONFIG_RAM (using driver model) */ diff --git a/drivers/ram/sunxi/dram_sun20i_d1.h b/drivers/ram/sunxi/dram_sun20i_d1.h new file mode 100644 index 00000000000..91383f6cf10 --- /dev/null +++ b/drivers/ram/sunxi/dram_sun20i_d1.h @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * D1/R528/T113 DRAM controller register and constant defines + * + * (C) Copyright 2022 Arm Ltd. + * Based on H6 and H616 header, which are: + * (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io> + * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net> + * + */ + +#ifndef _SUNXI_DRAM_SUN20I_D1_H +#define _SUNXI_DRAM_SUN20I_D1_H + +enum sunxi_dram_type { + SUNXI_DRAM_TYPE_DDR2 = 2, + SUNXI_DRAM_TYPE_DDR3 = 3, + SUNXI_DRAM_TYPE_LPDDR2 = 6, + SUNXI_DRAM_TYPE_LPDDR3 = 7, +}; + +/* + * This structure contains a mixture of fixed configuration settings, + * variables that are used at runtime to communicate settings between + * different stages and functions, and unused values. + * This is copied from Allwinner's boot0 data structure, which can be + * found at offset 0x38 in any boot0 binary. To allow matching up some + * board specific settings, this struct is kept compatible, even though + * we don't need all members in our code. + */ +typedef struct dram_para { + /* normal configuration */ + const u32 dram_clk; + const u32 dram_type; + const u32 dram_zq; + const u32 dram_odt_en; + + /* timing configuration */ + const u32 dram_mr0; + const u32 dram_mr1; + const u32 dram_mr2; + const u32 dram_mr3; + const u32 dram_tpr0; //DRAMTMG0 + const u32 dram_tpr1; //DRAMTMG1 + const u32 dram_tpr2; //DRAMTMG2 + const u32 dram_tpr3; //DRAMTMG3 + const u32 dram_tpr4; //DRAMTMG4 + const u32 dram_tpr5; //DRAMTMG5 + const u32 dram_tpr6; //DRAMTMG8 + const u32 dram_tpr7; + const u32 dram_tpr8; + const u32 dram_tpr9; + const u32 dram_tpr10; + const u32 dram_tpr11; + const u32 dram_tpr12; +} dram_para_t; + +typedef struct dram_config { + /* control configuration */ + u32 dram_para1; + u32 dram_para2; + /* contains a bitfield of DRAM setup settings */ + u32 dram_tpr13; +} dram_config_t; + +static inline int ns_to_t(int nanoseconds) +{ + const unsigned int ctrl_freq = CONFIG_DRAM_CLK / 2; + + return DIV_ROUND_UP(ctrl_freq * nanoseconds, 1000); +} + +#endif /* _SUNXI_DRAM_SUN20I_D1_H */ diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 27e4a60ff5b..781de530aff 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # (C) Copyright 2015 -# Texas Instruments Incorporated - http://www.ti.com/ +# Texas Instruments Incorporated - https://www.ti.com/ # menu "Remote Processor drivers" diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index fbe9c172bc0..e09ed1aa4d4 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # (C) Copyright 2015 -# Texas Instruments Incorporated - http://www.ti.com/ +# Texas Instruments Incorporated - https://www.ti.com/ # obj-$(CONFIG_$(SPL_)REMOTEPROC) += rproc-uclass.o rproc-elf-loader.o diff --git a/drivers/remoteproc/ipu_rproc.c b/drivers/remoteproc/ipu_rproc.c index b4a06bc955a..996e658e871 100644 --- a/drivers/remoteproc/ipu_rproc.c +++ b/drivers/remoteproc/ipu_rproc.c @@ -2,7 +2,7 @@ /* * IPU remoteproc driver for various SoCs * - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ * Angela Stegmaier <angelabaker@ti.com> * Venkateswara Rao Mandela <venkat.mandela@ti.com> * Keerthy <j-keerthy@ti.com> diff --git a/drivers/remoteproc/k3_system_controller.c b/drivers/remoteproc/k3_system_controller.c index e2affe69c67..071de40fbd6 100644 --- a/drivers/remoteproc/k3_system_controller.c +++ b/drivers/remoteproc/k3_system_controller.c @@ -2,7 +2,7 @@ /* * Texas Instruments' K3 System Controller Driver * - * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2017-2018 Texas Instruments Incorporated - https://www.ti.com/ * Lokesh Vutla <lokeshvutla@ti.com> */ diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c index 924070a76b5..6ec55e27d9d 100644 --- a/drivers/remoteproc/pru_rproc.c +++ b/drivers/remoteproc/pru_rproc.c @@ -2,7 +2,7 @@ /* * PRU-RTU remoteproc driver for various SoCs * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Keerthy <j-keerthy@ti.com> */ diff --git a/drivers/remoteproc/rproc-uclass.c b/drivers/remoteproc/rproc-uclass.c index ece534c3c0e..28b362c887a 100644 --- a/drivers/remoteproc/rproc-uclass.c +++ b/drivers/remoteproc/rproc-uclass.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2015 - * Texas Instruments Incorporated - http://www.ti.com/ + * Texas Instruments Incorporated - https://www.ti.com/ */ #define LOG_CATEGORY UCLASS_REMOTEPROC diff --git a/drivers/remoteproc/sandbox_testproc.c b/drivers/remoteproc/sandbox_testproc.c index d360cf3169f..f76f68ebeb4 100644 --- a/drivers/remoteproc/sandbox_testproc.c +++ b/drivers/remoteproc/sandbox_testproc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2015 - * Texas Instruments Incorporated - http://www.ti.com/ + * Texas Instruments Incorporated - https://www.ti.com/ */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include <common.h> diff --git a/drivers/remoteproc/ti_k3_arm64_rproc.c b/drivers/remoteproc/ti_k3_arm64_rproc.c index 99f11000dfb..767493c1383 100644 --- a/drivers/remoteproc/ti_k3_arm64_rproc.c +++ b/drivers/remoteproc/ti_k3_arm64_rproc.c @@ -2,7 +2,7 @@ /* * Texas Instruments' K3 ARM64 Remoteproc driver * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Lokesh Vutla <lokeshvutla@ti.com> * */ diff --git a/drivers/remoteproc/ti_k3_dsp_rproc.c b/drivers/remoteproc/ti_k3_dsp_rproc.c index 1a2e9dd1f58..576de4bb26e 100644 --- a/drivers/remoteproc/ti_k3_dsp_rproc.c +++ b/drivers/remoteproc/ti_k3_dsp_rproc.c @@ -2,7 +2,7 @@ /* * Texas Instruments' K3 DSP Remoteproc driver * - * Copyright (C) 2018-2020 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018-2020 Texas Instruments Incorporated - https://www.ti.com/ * Lokesh Vutla <lokeshvutla@ti.com> * Suman Anna <s-anna@ti.com> */ diff --git a/drivers/remoteproc/ti_power_proc.c b/drivers/remoteproc/ti_power_proc.c index 6887a3c8541..f55df4a9119 100644 --- a/drivers/remoteproc/ti_power_proc.c +++ b/drivers/remoteproc/ti_power_proc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2015-2016 - * Texas Instruments Incorporated - http://www.ti.com/ + * Texas Instruments Incorporated - https://www.ti.com/ */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include <common.h> diff --git a/drivers/remoteproc/ti_sci_proc.h b/drivers/remoteproc/ti_sci_proc.h index 36351da63fc..167a7143e83 100644 --- a/drivers/remoteproc/ti_sci_proc.h +++ b/drivers/remoteproc/ti_sci_proc.h @@ -2,7 +2,7 @@ /* * Texas Instruments TI-SCI Processor Controller Helper Functions * - * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018-2019 Texas Instruments Incorporated - https://www.ti.com/ * Lokesh Vutla <lokeshvutla@ti.com> * Suman Anna <s-anna@ti.com> */ diff --git a/drivers/reset/reset-dra7.c b/drivers/reset/reset-dra7.c index a9589d448dc..05101a94f9b 100644 --- a/drivers/reset/reset-dra7.c +++ b/drivers/reset/reset-dra7.c @@ -2,7 +2,7 @@ /* * Texas Instruments DRA7 reset driver * - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ * Author: Keerthy <j-keerthy@ti.com> */ diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c index 64bc696f137..9d0c8b354f4 100644 --- a/drivers/reset/reset-meson.c +++ b/drivers/reset/reset-meson.c @@ -13,18 +13,26 @@ #include <reset-uclass.h> #include <regmap.h> #include <linux/bitops.h> +#include <linux/delay.h> -#define REG_COUNT 8 #define BITS_PER_REG 32 -#define LEVEL_OFFSET 0x7c + +struct meson_reset_drvdata { + unsigned int reg_count; + unsigned int level_offset; +}; struct meson_reset_priv { struct regmap *regmap; + struct meson_reset_drvdata *drvdata; }; static int meson_reset_request(struct reset_ctl *reset_ctl) { - if (reset_ctl->id > (REG_COUNT * BITS_PER_REG)) + struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev); + struct meson_reset_drvdata *data = priv->drvdata; + + if (reset_ctl->id > (data->reg_count * BITS_PER_REG)) return -EINVAL; return 0; @@ -33,9 +41,10 @@ static int meson_reset_request(struct reset_ctl *reset_ctl) static int meson_reset_level(struct reset_ctl *reset_ctl, bool assert) { struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev); + struct meson_reset_drvdata *data = priv->drvdata; uint bank = reset_ctl->id / BITS_PER_REG; uint offset = reset_ctl->id % BITS_PER_REG; - uint reg_offset = LEVEL_OFFSET + (bank << 2); + uint reg_offset = data->level_offset + (bank << 2); uint val; regmap_read(priv->regmap, reg_offset, &val); @@ -64,15 +73,36 @@ struct reset_ops meson_reset_ops = { .rst_deassert = meson_reset_deassert, }; +static const struct meson_reset_drvdata meson_gxbb_data = { + .reg_count = 8, + .level_offset = 0x7c, +}; + +static const struct meson_reset_drvdata meson_a1_data = { + .reg_count = 3, + .level_offset = 0x40, +}; + static const struct udevice_id meson_reset_ids[] = { - { .compatible = "amlogic,meson-gxbb-reset" }, - { .compatible = "amlogic,meson-axg-reset" }, + { + .compatible = "amlogic,meson-gxbb-reset", + .data = (ulong)&meson_gxbb_data, + }, + { + .compatible = "amlogic,meson-axg-reset", + .data = (ulong)&meson_gxbb_data, + }, + { + .compatible = "amlogic,meson-a1-reset", + .data = (ulong)&meson_a1_data, + }, { } }; static int meson_reset_probe(struct udevice *dev) { struct meson_reset_priv *priv = dev_get_priv(dev); + priv->drvdata = (struct meson_reset_drvdata *)dev_get_driver_data(dev); return regmap_init_mem(dev_ofnode(dev), &priv->regmap); } diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c index 122556162ec..b76711f0a8f 100644 --- a/drivers/reset/reset-scmi.c +++ b/drivers/reset/reset-scmi.c @@ -13,17 +13,8 @@ #include <scmi_protocols.h> #include <asm/types.h> -/** - * struct scmi_reset_priv - Private data for SCMI reset controller - * @channel: Reference to the SCMI channel to use - */ -struct scmi_reset_priv { - struct scmi_channel *channel; -}; - static int scmi_reset_set_level(struct reset_ctl *rst, bool assert_not_deassert) { - struct scmi_reset_priv *priv = dev_get_priv(rst->dev); struct scmi_rd_reset_in in = { .domain_id = rst->id, .flags = assert_not_deassert ? SCMI_RD_RESET_FLAG_ASSERT : 0, @@ -35,7 +26,7 @@ static int scmi_reset_set_level(struct reset_ctl *rst, bool assert_not_deassert) in, out); int ret; - ret = devm_scmi_process_msg(rst->dev, priv->channel, &msg); + ret = devm_scmi_process_msg(rst->dev, &msg); if (ret) return ret; @@ -54,7 +45,6 @@ static int scmi_reset_deassert(struct reset_ctl *rst) static int scmi_reset_request(struct reset_ctl *rst) { - struct scmi_reset_priv *priv = dev_get_priv(rst->dev); struct scmi_rd_attr_in in = { .domain_id = rst->id, }; @@ -68,7 +58,7 @@ static int scmi_reset_request(struct reset_ctl *rst) * We don't really care about the attribute, just check * the reset domain exists. */ - ret = devm_scmi_process_msg(rst->dev, priv->channel, &msg); + ret = devm_scmi_process_msg(rst->dev, &msg); if (ret) return ret; @@ -83,9 +73,7 @@ static const struct reset_ops scmi_reset_domain_ops = { static int scmi_reset_probe(struct udevice *dev) { - struct scmi_reset_priv *priv = dev_get_priv(dev); - - return devm_scmi_of_get_channel(dev, &priv->channel); + return devm_scmi_of_get_channel(dev); } U_BOOT_DRIVER(scmi_reset_domain) = { @@ -93,5 +81,4 @@ U_BOOT_DRIVER(scmi_reset_domain) = { .id = UCLASS_RESET, .ops = &scmi_reset_domain_ops, .probe = scmi_reset_probe, - .priv_auto = sizeof(struct scmi_reset_priv *), }; diff --git a/drivers/reset/reset-ti-sci.c b/drivers/reset/reset-ti-sci.c index f35332767b2..fd654a08f13 100644 --- a/drivers/reset/reset-ti-sci.c +++ b/drivers/reset/reset-ti-sci.c @@ -2,7 +2,7 @@ /* * Texas Instruments System Control Interface (TI SCI) reset driver * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Andreas Dannenberg <dannenberg@ti.com> * * Loosely based on Linux kernel reset-ti-sci.c... diff --git a/drivers/rng/Kconfig b/drivers/rng/Kconfig index 24666bff987..a89c8995682 100644 --- a/drivers/rng/Kconfig +++ b/drivers/rng/Kconfig @@ -48,6 +48,14 @@ config RNG_OPTEE accessible to normal world but reserved and used by the OP-TEE to avoid the weakness of a software PRNG. +config RNG_RISCV_ZKR + bool "RISC-V Zkr random number generator" + depends on RISCV_SMODE + help + This driver provides a Random Number Generator based on the + Zkr RISC-V ISA extension which provides an interface to an + NIST SP 800-90B or BSI AIS-31 compliant physical entropy source. + config RNG_STM32 bool "Enable random number generator for STM32" depends on ARCH_STM32 || ARCH_STM32MP @@ -76,6 +84,12 @@ config RNG_SMCCC_TRNG Enable random number generator for platforms that support Arm SMCCC TRNG interface. +config RNG_ARM_RNDR + bool "Generic ARMv8.5 RNDR register" + depends on DM_RNG && ARM64 + help + Use the ARMv8.5 RNDR register to provide random numbers. + config TPM_RNG bool "Enable random number generator on TPM device" depends on TPM @@ -85,4 +99,10 @@ config TPM_RNG functionality. Enable random number generator on TPM devices. +config RNG_JH7110 + bool "StarFive JH7110 Random Number Generator support" + depends on DM_RNG && STARFIVE_JH7110 + help + Enable True Random Number Generator in StarFive JH7110 SoCs. + endif diff --git a/drivers/rng/Makefile b/drivers/rng/Makefile index 192f911e155..7e64c4cdfc3 100644 --- a/drivers/rng/Makefile +++ b/drivers/rng/Makefile @@ -10,7 +10,10 @@ obj-$(CONFIG_RNG_MSM) += msm_rng.o obj-$(CONFIG_RNG_NPCM) += npcm_rng.o obj-$(CONFIG_RNG_OPTEE) += optee_rng.o obj-$(CONFIG_RNG_STM32) += stm32_rng.o +obj-$(CONFIG_RNG_RISCV_ZKR) += riscv_zkr_rng.o obj-$(CONFIG_RNG_ROCKCHIP) += rockchip_rng.o obj-$(CONFIG_RNG_IPROC200) += iproc_rng200.o obj-$(CONFIG_RNG_SMCCC_TRNG) += smccc_trng.o +obj-$(CONFIG_RNG_ARM_RNDR) += arm_rndr.o obj-$(CONFIG_TPM_RNG) += tpm_rng.o +obj-$(CONFIG_RNG_JH7110) += jh7110_rng.o diff --git a/drivers/rng/arm_rndr.c b/drivers/rng/arm_rndr.c new file mode 100644 index 00000000000..55989743eae --- /dev/null +++ b/drivers/rng/arm_rndr.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Arm Ltd. + * + * Use the (optional) ARMv8.5 RNDR register to provide random numbers to + * U-Boot's UCLASS_RNG users. + * Detection is done at runtime using the CPU ID registers. + */ + +#define LOG_CATEGORY UCLASS_RNG + +#include <common.h> +#include <dm.h> +#include <linux/kernel.h> +#include <rng.h> +#include <asm/system.h> + +#define DRIVER_NAME "arm-rndr" + +static bool cpu_has_rndr(void) +{ + uint64_t reg; + + __asm__ volatile("mrs %0, ID_AA64ISAR0_EL1\n" : "=r" (reg)); + return !!(reg & ID_AA64ISAR0_EL1_RNDR); +} + +/* + * The system register name is RNDR, but this isn't widely known among older + * toolchains, and also triggers errors because of it being an architecture + * extension. Since we check the availability of the register before, it's + * fine to use here, though. + */ +#define RNDR "S3_3_C2_C4_0" + +static uint64_t read_rndr(void) +{ + uint64_t reg; + + __asm__ volatile("mrs %0, " RNDR "\n" : "=r" (reg)); + + return reg; +} + +static int arm_rndr_read(struct udevice *dev, void *data, size_t len) +{ + uint64_t random; + + while (len) { + int tocopy = min(sizeof(uint64_t), len); + + random = read_rndr(); + memcpy(data, &random, tocopy); + len -= tocopy; + data += tocopy; + } + + return 0; +} + +static const struct dm_rng_ops arm_rndr_ops = { + .read = arm_rndr_read, +}; + +static int arm_rndr_probe(struct udevice *dev) +{ + if (!cpu_has_rndr()) + return -ENODEV; + + return 0; +} + +U_BOOT_DRIVER(arm_rndr) = { + .name = DRIVER_NAME, + .id = UCLASS_RNG, + .ops = &arm_rndr_ops, + .probe = arm_rndr_probe, +}; + +U_BOOT_DRVINFO(cpu_arm_rndr) = { + .name = DRIVER_NAME, +}; diff --git a/drivers/rng/jh7110_rng.c b/drivers/rng/jh7110_rng.c new file mode 100644 index 00000000000..eb21afe4e7c --- /dev/null +++ b/drivers/rng/jh7110_rng.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * TRNG driver for the StarFive JH7110 SoC + * + */ + +#include <clk.h> +#include <dm.h> +#include <reset.h> +#include <rng.h> +#include <asm/io.h> +#include <linux/iopoll.h> + +/* trng register offset */ +#define STARFIVE_CTRL 0x00 +#define STARFIVE_STAT 0x04 +#define STARFIVE_MODE 0x08 +#define STARFIVE_SMODE 0x0C +#define STARFIVE_IE 0x10 +#define STARFIVE_ISTAT 0x14 +#define STARFIVE_RAND0 0x20 +#define STARFIVE_RAND1 0x24 +#define STARFIVE_RAND2 0x28 +#define STARFIVE_RAND3 0x2C +#define STARFIVE_RAND4 0x30 +#define STARFIVE_RAND5 0x34 +#define STARFIVE_RAND6 0x38 +#define STARFIVE_RAND7 0x3C +#define STARFIVE_AUTO_RQSTS 0x60 +#define STARFIVE_AUTO_AGE 0x64 + +/* CTRL CMD */ +#define STARFIVE_CTRL_EXEC_NOP 0x0 +#define STARFIVE_CTRL_GENE_RANDNUM 0x1 +#define STARFIVE_CTRL_EXEC_RANDRESEED 0x2 + +/* STAT */ +#define STARFIVE_STAT_NONCE_MODE BIT(2) +#define STARFIVE_STAT_R256 BIT(3) +#define STARFIVE_STAT_MISSION_MODE BIT(8) +#define STARFIVE_STAT_SEEDED BIT(9) +#define STARFIVE_STAT_LAST_RESEED(x) ((x) << 16) +#define STARFIVE_STAT_SRVC_RQST BIT(27) +#define STARFIVE_STAT_RAND_GENERATING BIT(30) +#define STARFIVE_STAT_RAND_SEEDING BIT(31) +#define STARFIVE_STAT_RUNNING (STARFIVE_STAT_RAND_GENERATING | \ + STARFIVE_STAT_RAND_SEEDING) + +/* MODE */ +#define STARFIVE_MODE_R256 BIT(3) + +/* SMODE */ +#define STARFIVE_SMODE_NONCE_MODE BIT(2) +#define STARFIVE_SMODE_MISSION_MODE BIT(8) +#define STARFIVE_SMODE_MAX_REJECTS(x) ((x) << 16) + +/* IE */ +#define STARFIVE_IE_RAND_RDY_EN BIT(0) +#define STARFIVE_IE_SEED_DONE_EN BIT(1) +#define STARFIVE_IE_LFSR_LOCKUP_EN BIT(4) +#define STARFIVE_IE_GLBL_EN BIT(31) + +#define STARFIVE_IE_ALL (STARFIVE_IE_GLBL_EN | \ + STARFIVE_IE_RAND_RDY_EN | \ + STARFIVE_IE_SEED_DONE_EN | \ + STARFIVE_IE_LFSR_LOCKUP_EN) + +/* ISTAT */ +#define STARFIVE_ISTAT_RAND_RDY BIT(0) +#define STARFIVE_ISTAT_SEED_DONE BIT(1) +#define STARFIVE_ISTAT_LFSR_LOCKUP BIT(4) + +#define STARFIVE_RAND_LEN sizeof(u32) + +enum mode { + PRNG_128BIT, + PRNG_256BIT, +}; + +struct starfive_trng_plat { + void *base; + struct clk *hclk; + struct clk *ahb; + struct reset_ctl *rst; + u32 mode; +}; + +static inline int starfive_trng_wait_idle(struct starfive_trng_plat *trng) +{ + u32 stat; + + return readl_relaxed_poll_timeout(trng->base + STARFIVE_STAT, stat, + !(stat & STARFIVE_STAT_RUNNING), + 100000); +} + +static inline void starfive_trng_irq_mask_clear(struct starfive_trng_plat *trng) +{ + /* clear register: ISTAT */ + u32 data = readl(trng->base + STARFIVE_ISTAT); + + writel(data, trng->base + STARFIVE_ISTAT); +} + +static int starfive_trng_cmd(struct starfive_trng_plat *trng, u32 cmd) +{ + u32 stat, flg; + int ret; + + switch (cmd) { + case STARFIVE_CTRL_GENE_RANDNUM: + writel(cmd, trng->base + STARFIVE_CTRL); + flg = STARFIVE_ISTAT_RAND_RDY; + break; + case STARFIVE_CTRL_EXEC_RANDRESEED: + writel(cmd, trng->base + STARFIVE_CTRL); + flg = STARFIVE_ISTAT_SEED_DONE; + break; + default: + return -EINVAL; + } + + ret = readl_relaxed_poll_timeout(trng->base + STARFIVE_ISTAT, stat, + (stat & flg), 1000); + writel(flg, trng->base + STARFIVE_ISTAT); + + return ret; +} + +static int starfive_trng_read(struct udevice *dev, void *data, size_t len) +{ + struct starfive_trng_plat *trng = dev_get_plat(dev); + u8 *buffer = data; + int iter_mask; + + if (trng->mode == PRNG_256BIT) + iter_mask = 7; + else + iter_mask = 3; + + for (int i = 0; len; ++i, i &= iter_mask) { + u32 val; + size_t step; + int ret; + + if (!i) { + ret = starfive_trng_cmd(trng, + STARFIVE_CTRL_GENE_RANDNUM); + if (ret) + return ret; + } + + val = readl(trng->base + STARFIVE_RAND0 + + (i * STARFIVE_RAND_LEN)); + step = min_t(size_t, len, STARFIVE_RAND_LEN); + memcpy(buffer, &val, step); + buffer += step; + len -= step; + } + + return 0; +} + +static int starfive_trng_init(struct starfive_trng_plat *trng) +{ + u32 mode, intr = 0; + + /* setup Auto Request/Age register */ + writel(0, trng->base + STARFIVE_AUTO_AGE); + writel(0, trng->base + STARFIVE_AUTO_RQSTS); + + /* clear register: ISTAT */ + starfive_trng_irq_mask_clear(trng); + + intr |= STARFIVE_IE_ALL; + writel(intr, trng->base + STARFIVE_IE); + + mode = readl(trng->base + STARFIVE_MODE); + + switch (trng->mode) { + case PRNG_128BIT: + mode &= ~STARFIVE_MODE_R256; + break; + case PRNG_256BIT: + mode |= STARFIVE_MODE_R256; + break; + default: + mode |= STARFIVE_MODE_R256; + break; + } + + writel(mode, trng->base + STARFIVE_MODE); + + return starfive_trng_cmd(trng, STARFIVE_CTRL_EXEC_RANDRESEED); +} + +static int starfive_trng_probe(struct udevice *dev) +{ + struct starfive_trng_plat *pdata = dev_get_plat(dev); + int err; + + err = clk_enable(pdata->hclk); + if (err) + return err; + + err = clk_enable(pdata->ahb); + if (err) + goto err_ahb; + + err = reset_deassert(pdata->rst); + if (err) + goto err_reset; + + pdata->mode = PRNG_256BIT; + + err = starfive_trng_init(pdata); + if (err) + goto err_trng_init; + + return 0; + +err_trng_init: + reset_assert(pdata->rst); +err_reset: + clk_disable(pdata->ahb); +err_ahb: + clk_disable(pdata->hclk); + + return err; +} + +static int starfive_trng_of_to_plat(struct udevice *dev) +{ + struct starfive_trng_plat *pdata = dev_get_plat(dev); + + pdata->base = (void *)dev_read_addr(dev); + if (!pdata->base) + return -ENODEV; + + pdata->hclk = devm_clk_get(dev, "hclk"); + if (IS_ERR(pdata->hclk)) + return -ENODEV; + + pdata->ahb = devm_clk_get(dev, "ahb"); + if (IS_ERR(pdata->ahb)) + return -ENODEV; + + pdata->rst = devm_reset_control_get(dev, NULL); + if (IS_ERR(pdata->rst)) + return -ENODEV; + + return 0; +} + +static const struct dm_rng_ops starfive_trng_ops = { + .read = starfive_trng_read, +}; + +static const struct udevice_id starfive_trng_match[] = { + { + .compatible = "starfive,jh7110-trng", + }, + {}, +}; + +U_BOOT_DRIVER(starfive_trng) = { + .name = "jh7110-trng", + .id = UCLASS_RNG, + .of_match = starfive_trng_match, + .probe = starfive_trng_probe, + .ops = &starfive_trng_ops, + .plat_auto = sizeof(struct starfive_trng_plat), + .of_to_plat = starfive_trng_of_to_plat, +}; diff --git a/drivers/rng/meson-rng.c b/drivers/rng/meson-rng.c index e0a1e8c7e04..fd2988e91b5 100644 --- a/drivers/rng/meson-rng.c +++ b/drivers/rng/meson-rng.c @@ -10,10 +10,23 @@ #include <dm.h> #include <rng.h> #include <asm/io.h> +#include <linux/iopoll.h> + +#define RNG_DATA 0x00 +#define RNG_S4_DATA 0x08 +#define RNG_S4_CFG 0x00 + +#define RUN_BIT BIT(0) +#define SEED_READY_STS_BIT BIT(31) + +struct meson_rng_priv { + u32 (*read)(fdt_addr_t base); +}; struct meson_rng_plat { fdt_addr_t base; struct clk clk; + struct meson_rng_priv *priv; }; /** @@ -27,10 +40,11 @@ struct meson_rng_plat { static int meson_rng_read(struct udevice *dev, void *data, size_t len) { struct meson_rng_plat *pdata = dev_get_plat(dev); + struct meson_rng_priv *priv = pdata->priv; char *buffer = (char *)data; while (len) { - u32 rand = readl(pdata->base); + u32 rand = priv->read(pdata->base); size_t step; if (len >= 4) @@ -44,6 +58,47 @@ static int meson_rng_read(struct udevice *dev, void *data, size_t len) return 0; } +static int meson_rng_wait_status(void __iomem *cfg_addr, int bit) +{ + u32 status = 0; + int ret; + + ret = readl_relaxed_poll_timeout(cfg_addr, + status, !(status & bit), + 10000); + if (ret) + return -EBUSY; + + return 0; +} + +static u32 meson_common_rng_read(fdt_addr_t base) +{ + return readl(base); +} + +static u32 meson_s4_rng_read(fdt_addr_t base) +{ + void __iomem *cfg_addr = (void *)base + RNG_S4_CFG; + int err; + + writel_relaxed(readl_relaxed(cfg_addr) | SEED_READY_STS_BIT, cfg_addr); + + err = meson_rng_wait_status(cfg_addr, SEED_READY_STS_BIT); + if (err) { + pr_err("Seed isn't ready, try again\n"); + return err; + } + + err = meson_rng_wait_status(cfg_addr, RUN_BIT); + if (err) { + pr_err("Can't get random number, try again\n"); + return err; + } + + return readl_relaxed(base + RNG_S4_DATA); +} + /** * meson_rng_probe() - probe rng device * @@ -59,6 +114,8 @@ static int meson_rng_probe(struct udevice *dev) if (err) return err; + pdata->priv = (struct meson_rng_priv *)dev_get_driver_data(dev); + return 0; } @@ -102,9 +159,22 @@ static const struct dm_rng_ops meson_rng_ops = { .read = meson_rng_read, }; +static const struct meson_rng_priv meson_rng_priv = { + .read = meson_common_rng_read, +}; + +static const struct meson_rng_priv meson_rng_priv_s4 = { + .read = meson_s4_rng_read, +}; + static const struct udevice_id meson_rng_match[] = { { .compatible = "amlogic,meson-rng", + .data = (ulong)&meson_rng_priv, + }, + { + .compatible = "amlogic,meson-s4-rng", + .data = (ulong)&meson_rng_priv_s4, }, {}, }; diff --git a/drivers/rng/riscv_zkr_rng.c b/drivers/rng/riscv_zkr_rng.c new file mode 100644 index 00000000000..8c9e111e2e0 --- /dev/null +++ b/drivers/rng/riscv_zkr_rng.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * The RISC-V Zkr extension provides CSR seed which provides access to a + * random number generator. + */ + +#define LOG_CATEGORY UCLASS_RNG + +#include <dm.h> +#include <interrupt.h> +#include <log.h> +#include <rng.h> + +#define DRIVER_NAME "riscv_zkr" + +enum opst { + /** @BIST: built in self test running */ + BIST = 0b00, + /** @WAIT: sufficient amount of entropy is not yet available */ + WAIT = 0b01, + /** @ES16: 16bits of entropy available */ + ES16 = 0b10, + /** @DEAD: unrecoverable self-test error */ + DEAD = 0b11, +}; + +static unsigned long read_seed(void) +{ + unsigned long ret; + + __asm__ __volatile__("csrrw %0, seed, x0" : "=r" (ret) : : "memory"); + + return ret; +} + +static int riscv_zkr_read(struct udevice *dev, void *data, size_t len) +{ + u8 *ptr = data; + + while (len) { + u32 val; + + val = read_seed(); + + switch (val >> 30) { + case BIST: + continue; + case WAIT: + continue; + case ES16: + *ptr++ = val & 0xff; + if (--len) { + *ptr++ = val >> 8; + --len; + } + break; + case DEAD: + return -ENODEV; + } + } + + return 0; +} + +/** + * riscv_zkr_probe() - check if the seed register is available + * + * If the SBI software has not set mseccfg.sseed=1 or the Zkr + * extension is not available this probe function will result + * in an exception. Currently we cannot recover from this. + * + * @dev: RNG device + * Return: 0 if successfully probed + */ +static int riscv_zkr_probe(struct udevice *dev) +{ + struct resume_data resume; + int ret; + u32 val; + + /* Check if reading seed leads to interrupt */ + set_resume(&resume); + ret = setjmp(resume.jump); + if (ret) + log_debug("Exception %ld reading seed CSR\n", resume.code); + else + val = read_seed(); + set_resume(NULL); + if (ret) + return -ENODEV; + + do { + val = read_seed(); + val >>= 30; + } while (val == BIST || val == WAIT); + + if (val == DEAD) + return -ENODEV; + + return 0; +} + +static const struct dm_rng_ops riscv_zkr_ops = { + .read = riscv_zkr_read, +}; + +U_BOOT_DRIVER(riscv_zkr) = { + .name = DRIVER_NAME, + .id = UCLASS_RNG, + .ops = &riscv_zkr_ops, + .probe = riscv_zkr_probe, +}; + +U_BOOT_DRVINFO(cpu_riscv_zkr) = { + .name = DRIVER_NAME, +}; diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index a8014129d33..7e21c4ae2bb 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1,46 +1,9 @@ config SCSI - bool "Support SCSI controllers" + bool "Support SCSI controllers with driver model" help This enables support for SCSI (Small Computer System Interface), a parallel interface widely used with storage peripherals such as hard drives and optical drives. The SCSI standards define physical interfaces as well as protocols for controlling devices and - tranferring data. - -config DM_SCSI - bool "Support SCSI controllers with driver model" - help - This option enables the SCSI (Small Computer System Interface) uclass - which supports SCSI and SATA HDDs. For every device configuration - (IDs/LUNs) a block device is created with RAW read/write and - filesystem support. - -if SCSI && !DM_SCSI - -config SCSI_AHCI_PLAT - bool "Platform-specific init of AHCI" - help - This enables a way for boards to set up an AHCI device manually, by - called ahci_init() and providing an ahci_reset() mechanism. - - This is deprecated. An AHCI driver should be provided instead. - -config SYS_SCSI_MAX_SCSI_ID - int "Maximum supported SCSI ID" - default 1 - help - Sets the maximum number of SCSI IDs to scan when looking for devices. - IDs from 0 to (this value - 1) are scanned. - - This is deprecated and is not needed when BLK is enabled. - -config SYS_SCSI_MAX_LUN - int "Maximum support SCSI LUN" - default 1 - help - Sets the maximum number of SCSI Logical Unit Numbers (LUNs) to scan on - devices. LUNs from 0 to (this value - 1) are scanned. - - This is deprecated and is not needed when CONFIG_DM_SCSI is enabled. - -endif + tranferring data. For every device configuration (IDs/LUNs) a block + device is created with RAW read/write and filesystem support. diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index d8d6de59090..628be4c89fb 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -4,25 +4,16 @@ # Wolfgang Denk, DENX Software Engineering, wd@denx.de. ifndef CONFIG_SPL_BUILD -obj-$(CONFIG_DM_SCSI) += scsi-uclass.o -obj-$(CONFIG_SCSI) += scsi.o - +obj-$(CONFIG_SCSI) += scsi.o scsi-uclass.o ifdef CONFIG_SCSI -ifdef CONFIG_DM_SCSI obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += scsi_bootdev.o +obj-$(CONFIG_SANDBOX) += sandbox_scsi.o +obj-$(CONFIG_SANDBOX) += scsi_emul.o endif endif -endif - ifdef CONFIG_SPL_BUILD ifdef CONFIG_SPL_SATA -obj-$(CONFIG_DM_SCSI) += scsi-uclass.o -obj-$(CONFIG_SCSI) += scsi.o +obj-$(CONFIG_SCSI) += scsi.o scsi-uclass.o endif endif - -ifdef CONFIG_SCSI -obj-$(CONFIG_SANDBOX) += sandbox_scsi.o -obj-$(CONFIG_SANDBOX) += scsi_emul.o -endif diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 7411660d465..79ee400d12f 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -21,38 +21,10 @@ #include <dm/device-internal.h> #include <dm/uclass-internal.h> -#if !defined(CONFIG_DM_SCSI) -# ifdef CFG_SCSI_DEV_LIST -# define SCSI_DEV_LIST CFG_SCSI_DEV_LIST -# else -# ifdef CONFIG_SATA_ULI5288 - -# define SCSI_VEND_ID 0x10b9 -# define SCSI_DEV_ID 0x5288 - -# elif !defined(CONFIG_SCSI_AHCI_PLAT) -# error no scsi device defined -# endif -# define SCSI_DEV_LIST {SCSI_VEND_ID, SCSI_DEV_ID} -# endif -#endif - -#if defined(CONFIG_PCI) && !defined(CONFIG_SCSI_AHCI_PLAT) && \ - !defined(CONFIG_DM_SCSI) -const struct pci_device_id scsi_device_list[] = { SCSI_DEV_LIST }; -#endif static struct scsi_cmd tempccb; /* temporary scsi command buffer */ DEFINE_CACHE_ALIGN_BUFFER(u8, tempbuff, 512); /* temporary data buffer */ -#if !defined(CONFIG_DM_SCSI) -static int scsi_max_devs; /* number of highest available scsi device */ - -static int scsi_curr_dev; /* current device */ - -static struct blk_desc scsi_dev_desc[SCSI_MAX_DEVICE]; -#endif - /* almost the maximum amount of the scsi_ext command.. */ #define SCSI_MAX_BLK 0xFFFF #define SCSI_LBA48_READ 0xFFFFFFF @@ -107,7 +79,6 @@ static void scsi_setup_inquiry(struct scsi_cmd *pccb) pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ } -#ifdef CONFIG_BLK static void scsi_setup_read_ext(struct scsi_cmd *pccb, lbaint_t start, unsigned short blocks) { @@ -286,59 +257,6 @@ static int scsi_buffer_aligned(struct udevice *dev, struct bounce_buffer *state) return 1; } #endif /* CONFIG_BOUNCE_BUFFER */ -#endif - -#if defined(CONFIG_PCI) && !defined(CONFIG_SCSI_AHCI_PLAT) && \ - !defined(CONFIG_DM_SCSI) -void scsi_init(void) -{ - int busdevfunc = -1; - int i; - /* - * Find a device from the list, this driver will support a single - * controller. - */ - for (i = 0; i < ARRAY_SIZE(scsi_device_list); i++) { - /* get PCI Device ID */ - struct udevice *dev; - int ret; - - ret = dm_pci_find_device(scsi_device_list[i].vendor, - scsi_device_list[i].device, 0, &dev); - if (!ret) { - busdevfunc = dm_pci_get_bdf(dev); - break; - } - if (busdevfunc != -1) - break; - } - - if (busdevfunc == -1) { - printf("Error: SCSI Controller(s) "); - for (i = 0; i < ARRAY_SIZE(scsi_device_list); i++) { - printf("%04X:%04X ", - scsi_device_list[i].vendor, - scsi_device_list[i].device); - } - printf("not found\n"); - return; - } -#ifdef DEBUG - else { - printf("SCSI Controller (%04X,%04X) found (%d:%d:%d)\n", - scsi_device_list[i].vendor, - scsi_device_list[i].device, - (busdevfunc >> 16) & 0xFF, - (busdevfunc >> 11) & 0x1F, - (busdevfunc >> 8) & 0x7); - } -#endif - bootstage_start(BOOTSTAGE_ID_ACCUM_SCSI, "ahci"); - scsi_low_level_init(busdevfunc); - scsi_scan(true); - bootstage_accum(BOOTSTAGE_ID_ACCUM_SCSI); -} -#endif /* copy src to dest, skipping leading and trailing blanks * and null terminate the string @@ -374,6 +292,7 @@ static int scsi_read_capacity(struct udevice *dev, struct scsi_cmd *pccb, pccb->cmd[0] = SCSI_RD_CAPAC10; pccb->cmd[1] = pccb->lun << 5; pccb->cmdlen = 10; + pccb->dma_dir = DMA_FROM_DEVICE; pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ pccb->datalen = 8; @@ -450,35 +369,16 @@ static void scsi_setup_test_unit_ready(struct scsi_cmd *pccb) */ static void scsi_init_dev_desc_priv(struct blk_desc *dev_desc) { + memset(dev_desc, 0, sizeof(struct blk_desc)); dev_desc->target = 0xff; dev_desc->lun = 0xff; dev_desc->log2blksz = LOG2_INVALID(typeof(dev_desc->log2blksz)); dev_desc->type = DEV_TYPE_UNKNOWN; - dev_desc->vendor[0] = 0; - dev_desc->product[0] = 0; - dev_desc->revision[0] = 0; - dev_desc->removable = false; -} - -#if !defined(CONFIG_DM_SCSI) -/** - * scsi_init_dev_desc - initialize all SCSI specific blk_desc properties - * - * @dev_desc: Block device description pointer - * @devnum: Device number - */ -static void scsi_init_dev_desc(struct blk_desc *dev_desc, int devnum) -{ - dev_desc->lba = 0; - dev_desc->blksz = 0; - dev_desc->uclass_id = UCLASS_SCSI; - dev_desc->devnum = devnum; - dev_desc->part_type = PART_TYPE_UNKNOWN; - - scsi_init_dev_desc_priv(dev_desc); +#if IS_ENABLED(CONFIG_BOUNCE_BUFFER) + dev_desc->bb = true; +#endif /* CONFIG_BOUNCE_BUFFER */ } -#endif /** * scsi_detect_dev - Detect scsi device @@ -538,6 +438,7 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, for (count = 0; count < 3; count++) { pccb->datalen = 0; + pccb->dma_dir = DMA_NONE; scsi_setup_test_unit_ready(pccb); err = scsi_exec(dev, pccb); if (!err) @@ -567,7 +468,6 @@ removable: * (re)-scan the scsi bus and reports scsi device info * to the user if mode = 1 */ -#if defined(CONFIG_DM_SCSI) static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) { int ret; @@ -606,6 +506,7 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) bdesc->lun = lun; bdesc->removable = bd.removable; bdesc->type = bd.type; + bdesc->bb = bd.bb; memcpy(&bdesc->vendor, &bd.vendor, sizeof(bd.vendor)); memcpy(&bdesc->product, &bd.product, sizeof(bd.product)); memcpy(&bdesc->revision, &bd.revision, sizeof(bd.revision)); @@ -687,48 +588,7 @@ int scsi_scan(bool verbose) return 0; } -#else -int scsi_scan(bool verbose) -{ - unsigned char i, lun; - int ret; - if (verbose) - printf("scanning bus for devices...\n"); - for (i = 0; i < SCSI_MAX_DEVICE; i++) - scsi_init_dev_desc(&scsi_dev_desc[i], i); - - scsi_max_devs = 0; - for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) { - for (lun = 0; lun < CONFIG_SYS_SCSI_MAX_LUN; lun++) { - struct blk_desc *bdesc = &scsi_dev_desc[scsi_max_devs]; - - ret = scsi_detect_dev(NULL, i, lun, bdesc); - if (ret) - continue; - part_init(bdesc); - - if (verbose) { - printf(" Device %d: ", bdesc->devnum); - dev_print(bdesc); - } - scsi_max_devs++; - } /* next LUN */ - } - if (scsi_max_devs > 0) - scsi_curr_dev = 0; - else - scsi_curr_dev = -1; - - printf("Found %d device(s).\n", scsi_max_devs); -#ifndef CONFIG_SPL_BUILD - env_set_ulong("scsidevs", scsi_max_devs); -#endif - return 0; -} -#endif - -#ifdef CONFIG_BLK static const struct blk_ops scsi_blk_ops = { .read = scsi_read, .write = scsi_write, @@ -742,11 +602,3 @@ U_BOOT_DRIVER(scsi_blk) = { .id = UCLASS_BLK, .ops = &scsi_blk_ops, }; -#else -U_BOOT_LEGACY_BLK(scsi) = { - .uclass_idname = "scsi", - .uclass_id = UCLASS_SCSI, - .max_devs = SCSI_MAX_DEVICE, - .desc = scsi_dev_desc, -}; -#endif diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 6cb6598f3d2..6628a887de7 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -24,6 +24,21 @@ config BAUDRATE in the SPL stage (most drivers) or for choosing a default baudrate in the absence of an environment setting (serial_mxc.c). +config OF_SERIAL_BAUD + bool "Fetch serial baudrate from device tree" + depends on DM_SERIAL && SPL_ENV_SUPPORT + select DEFAULT_ENV_IS_RW + help + Select this to enable fetching and setting of the baudrate + configured in the DT. Replace the default baudrate with the DT + baudrate and also set it to the environment. + +config DEFAULT_ENV_IS_RW + bool "Make default environment as writable" + help + Select this to enable to make default environment writable. This + allows modifying the default environment. + config REQUIRE_SERIAL_CONSOLE bool "Require a serial port for console" # Running without a serial console is not supported by the @@ -160,7 +175,6 @@ config SERIAL_PROBE_ALL config SPL_DM_SERIAL bool "Enable Driver Model for serial drivers in SPL" depends on DM_SERIAL && SPL_DM - select SYS_SPL_MALLOC_F default y help Enable driver model for serial in SPL. This replaces @@ -171,7 +185,6 @@ config SPL_DM_SERIAL config TPL_DM_SERIAL bool "Enable Driver Model for serial drivers in TPL" depends on DM_SERIAL && TPL_DM - select SYS_TPL_MALLOC_F default y if TPL && DM_SERIAL help Enable driver model for serial in TPL. This replaces diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index eab9537fbae..6deb1d8ddc5 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -13,6 +13,7 @@ #include <ns16550.h> #include <reset.h> #include <serial.h> +#include <spl.h> #include <watchdog.h> #include <asm/global_data.h> #include <linux/err.h> @@ -472,6 +473,10 @@ static int ns16550_serial_getinfo(struct udevice *dev, struct ns16550 *const com_port = dev_get_priv(dev); struct ns16550_plat *plat = com_port->plat; + /* save code size */ + if (!spl_in_proper()) + return -ENOSYS; + info->type = SERIAL_CHIP_16550_COMPATIBLE; #ifdef CONFIG_SYS_NS16550_PORT_MAPPED info->addr_space = SERIAL_ADDRESS_SPACE_IO; @@ -479,6 +484,7 @@ static int ns16550_serial_getinfo(struct udevice *dev, info->addr_space = SERIAL_ADDRESS_SPACE_MEMORY; #endif info->addr = plat->base; + info->size = plat->size; info->reg_width = plat->reg_width; info->reg_shift = plat->reg_shift; info->reg_offset = plat->reg_offset; @@ -487,7 +493,8 @@ static int ns16550_serial_getinfo(struct udevice *dev, return 0; } -static int ns16550_serial_assign_base(struct ns16550_plat *plat, fdt_addr_t base) +static int ns16550_serial_assign_base(struct ns16550_plat *plat, + fdt_addr_t base, fdt_size_t size) { if (base == FDT_ADDR_T_NONE) return -EINVAL; @@ -497,6 +504,7 @@ static int ns16550_serial_assign_base(struct ns16550_plat *plat, fdt_addr_t base #else plat->base = (unsigned long)map_physmem(base, 0, MAP_NOCACHE); #endif + plat->size = size; return 0; } @@ -507,6 +515,7 @@ int ns16550_serial_probe(struct udevice *dev) struct ns16550 *const com_port = dev_get_priv(dev); struct reset_ctl_bulk reset_bulk; fdt_addr_t addr; + fdt_addr_t size; int ret; /* @@ -514,8 +523,8 @@ int ns16550_serial_probe(struct udevice *dev) * or via a PCI bridge, assign plat->base before probing hardware. */ if (device_is_on_pci_bus(dev)) { - addr = devfdt_get_addr_pci(dev); - ret = ns16550_serial_assign_base(plat, addr); + addr = devfdt_get_addr_pci(dev, &size); + ret = ns16550_serial_assign_base(plat, addr, size); if (ret) return ret; } @@ -542,12 +551,14 @@ int ns16550_serial_of_to_plat(struct udevice *dev) { struct ns16550_plat *plat = dev_get_plat(dev); const u32 port_type = dev_get_driver_data(dev); + fdt_size_t size = 0; fdt_addr_t addr; struct clk clk; int err; - addr = dev_read_addr(dev); - err = ns16550_serial_assign_base(plat, addr); + addr = spl_in_proper() ? dev_read_addr_size(dev, &size) : + dev_read_addr(dev); + err = ns16550_serial_assign_base(plat, addr, size); if (err && !device_is_on_pci_bus(dev)) return err; diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c index f4003811ee7..f6ac3d22852 100644 --- a/drivers/serial/sandbox.c +++ b/drivers/serial/sandbox.c @@ -280,7 +280,7 @@ U_BOOT_DRIVER(sandbox_serial) = { .flags = DM_FLAG_PRE_RELOC, }; -#if CONFIG_IS_ENABLED(OF_REAL) +#if CONFIG_IS_ENABLED(OF_REAL) && !CONFIG_IS_ENABLED(OF_PLATDATA) static const struct sandbox_serial_plat platdata_non_fdt = { .colour = -1, }; diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index 5e2e7dfbcb3..e4fa3933bc8 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -155,12 +155,61 @@ static void serial_find_console_or_panic(void) } #endif /* CONFIG_SERIAL_PRESENT */ +/** + * check_valid_baudrate() - Check whether baudrate is valid or not + * + * @baud: baud rate to check + * Return: 0 if OK, -ve on error + */ +static int check_valid_baudrate(int baud) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) { + if (baud == baudrate_table[i]) + return 0; + } + + return -EINVAL; +} + +int fetch_baud_from_dtb(void) +{ + int baud_value, ret; + + baud_value = ofnode_read_baud(); + ret = check_valid_baudrate(baud_value); + if (ret) + return ret; + + return baud_value; +} + /* Called prior to relocation */ int serial_init(void) { #if CONFIG_IS_ENABLED(SERIAL_PRESENT) serial_find_console_or_panic(); gd->flags |= GD_FLG_SERIAL_READY; + + if (IS_ENABLED(CONFIG_OF_SERIAL_BAUD)) { + int ret = 0; + char *ptr = (char*)&default_environment[0]; + + /* + * Fetch the baudrate from the dtb and update the value in the + * default environment. + */ + ret = fetch_baud_from_dtb(); + if (ret != -EINVAL && ret != -EFAULT) { + gd->baudrate = ret; + + while (*ptr != '\0' && *(ptr + 1) != '\0') + ptr++; + ptr += 2; + sprintf(ptr, "baudrate=%d", gd->baudrate); + } + } serial_setbrg(); #endif @@ -182,6 +231,16 @@ int serial_initialize(void) return serial_init(); } +static void _serial_flush(struct udevice *dev) +{ + struct dm_serial_ops *ops = serial_get_ops(dev); + + if (!ops->pending) + return; + while (ops->pending(dev, false) > 0) + ; +} + static void _serial_putc(struct udevice *dev, char ch) { struct dm_serial_ops *ops = serial_get_ops(dev); @@ -193,6 +252,9 @@ static void _serial_putc(struct udevice *dev, char ch) do { err = ops->putc(dev, ch); } while (err == -EAGAIN); + + if (IS_ENABLED(CONFIG_CONSOLE_FLUSH_ON_NEWLINE) && ch == '\n') + _serial_flush(dev); } static int __serial_puts(struct udevice *dev, const char *str, size_t len) @@ -231,22 +293,13 @@ static void _serial_puts(struct udevice *dev, const char *str) if (*newline && __serial_puts(dev, "\r\n", 2)) return; + if (IS_ENABLED(CONFIG_CONSOLE_FLUSH_ON_NEWLINE) && *newline) + _serial_flush(dev); + str += len + !!*newline; } while (*str); } -#ifdef CONFIG_CONSOLE_FLUSH_SUPPORT -static void _serial_flush(struct udevice *dev) -{ - struct dm_serial_ops *ops = serial_get_ops(dev); - - if (!ops->pending) - return; - while (ops->pending(dev, false) > 0) - ; -} -#endif - static int __serial_getc(struct udevice *dev) { struct dm_serial_ops *ops = serial_get_ops(dev); @@ -520,7 +573,7 @@ static int serial_post_probe(struct udevice *dev) return 0; memset(&sdev, '\0', sizeof(sdev)); - strncpy(sdev.name, dev->name, sizeof(sdev.name)); + strlcpy(sdev.name, dev->name, sizeof(sdev.name)); sdev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_DM; sdev.priv = dev; sdev.putc = serial_stub_putc; diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c index 51e66abdbc1..ce08a6b4486 100644 --- a/drivers/serial/serial_lpuart.c +++ b/drivers/serial/serial_lpuart.c @@ -480,18 +480,30 @@ static int lpuart_serial_probe(struct udevice *dev) { #if CONFIG_IS_ENABLED(CLK) struct clk per_clk; + struct clk ipg_clk; int ret; ret = clk_get_by_name(dev, "per", &per_clk); if (!ret) { ret = clk_enable(&per_clk); if (ret) { - dev_err(dev, "Failed to get per clk: %d\n", ret); + dev_err(dev, "Failed to enable per clk: %d\n", ret); return ret; } } else { debug("%s: Failed to get per clk: %d\n", __func__, ret); } + + ret = clk_get_by_name(dev, "ipg", &ipg_clk); + if (!ret) { + ret = clk_enable(&ipg_clk); + if (ret) { + dev_err(dev, "Failed to enable ipg clk: %d\n", ret); + return ret; + } + } else { + debug("%s: Failed to get ipg clk: %d\n", __func__, ret); + } #endif if (is_lpuart32(dev)) diff --git a/drivers/serial/serial_meson.c b/drivers/serial/serial_meson.c index 934de2ab235..be5f380f850 100644 --- a/drivers/serial/serial_meson.c +++ b/drivers/serial/serial_meson.c @@ -232,6 +232,7 @@ static const struct dm_serial_ops meson_serial_ops = { static const struct udevice_id meson_serial_ids[] = { { .compatible = "amlogic,meson-uart" }, { .compatible = "amlogic,meson-gx-uart" }, + { .compatible = "amlogic,meson-a1-uart" }, { } }; diff --git a/drivers/serial/serial_msm_geni.c b/drivers/serial/serial_msm_geni.c index 78fd9389c03..b8bc61451a0 100644 --- a/drivers/serial/serial_msm_geni.c +++ b/drivers/serial/serial_msm_geni.c @@ -13,14 +13,13 @@ #include <dm.h> #include <errno.h> #include <linux/delay.h> +#include <linux/time.h> #include <misc.h> #include <serial.h> #define UART_OVERSAMPLING 32 #define STALE_TIMEOUT 160 -#define USEC_PER_SEC 1000000L - /* Registers*/ #define GENI_FORCE_DEFAULT_REG 0x20 #define GENI_SER_M_CLK_CFG 0x48 diff --git a/drivers/serial/serial_npcm.c b/drivers/serial/serial_npcm.c index 76ac7cb80db..6bf3a943a2f 100644 --- a/drivers/serial/serial_npcm.c +++ b/drivers/serial/serial_npcm.c @@ -83,8 +83,11 @@ static int npcm_serial_setbrg(struct udevice *dev, int baudrate) struct npcm_uart *uart = plat->reg; u16 divisor; + if (IS_ENABLED(CONFIG_SYS_SKIP_UART_INIT)) + return 0; + /* BaudOut = UART Clock / (16 * [Divisor + 2]) */ - divisor = DIV_ROUND_CLOSEST(plat->uart_clk, 16 * baudrate + 2) - 2; + divisor = DIV_ROUND_CLOSEST(plat->uart_clk, 16 * baudrate) - 2; setbits_8(&uart->lcr, LCR_DLAB); writeb(divisor & 0xff, &uart->dll); @@ -97,29 +100,35 @@ static int npcm_serial_setbrg(struct udevice *dev, int baudrate) static int npcm_serial_probe(struct udevice *dev) { struct npcm_serial_plat *plat = dev_get_plat(dev); - struct npcm_uart *uart = plat->reg; + struct npcm_uart *uart; struct clk clk, parent; u32 freq; int ret; plat->reg = dev_read_addr_ptr(dev); - freq = dev_read_u32_default(dev, "clock-frequency", 0); + uart = plat->reg; - ret = clk_get_by_index(dev, 0, &clk); - if (ret < 0) - return ret; + if (!IS_ENABLED(CONFIG_SYS_SKIP_UART_INIT)) { + freq = dev_read_u32_default(dev, "clock-frequency", 24000000); - ret = clk_get_by_index(dev, 1, &parent); - if (!ret) { - ret = clk_set_parent(&clk, &parent); - if (ret) + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) return ret; - } - ret = clk_set_rate(&clk, freq); - if (ret < 0) - return ret; - plat->uart_clk = ret; + ret = clk_get_by_index(dev, 1, &parent); + if (!ret) { + ret = clk_set_parent(&clk, &parent); + if (ret) + return ret; + } + + if (freq) { + ret = clk_set_rate(&clk, freq); + if (ret < 0) + return ret; + } + plat->uart_clk = clk_get_rate(&clk); + } /* Disable all interrupt */ writeb(0, &uart->ier); diff --git a/drivers/serial/serial_omap.c b/drivers/serial/serial_omap.c index 26310b0b746..49ced8f9fae 100644 --- a/drivers/serial/serial_omap.c +++ b/drivers/serial/serial_omap.c @@ -2,7 +2,7 @@ /* * Texas Instruments' OMAP serial driver * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Lokesh Vutla <lokeshvutla@ti.com> */ diff --git a/drivers/serial/serial_s5p.c b/drivers/serial/serial_s5p.c index 7aeb8c0f8cb..7d04dcff54f 100644 --- a/drivers/serial/serial_s5p.c +++ b/drivers/serial/serial_s5p.c @@ -7,7 +7,6 @@ * based on drivers/serial/s3c64xx.c */ -#include <common.h> #include <dm.h> #include <errno.h> #include <fdtdec.h> @@ -21,32 +20,39 @@ #include <serial.h> #include <clk.h> -DECLARE_GLOBAL_DATA_PTR; - enum { PORT_S5P = 0, PORT_S5L }; +#define UFCON_FIFO_EN BIT(0) +#define UFCON_RX_FIFO_RESET BIT(1) +#define UMCON_RESET_VAL 0x0 +#define ULCON_WORD_8_BIT 0x3 +#define UCON_RX_IRQ_OR_POLLING BIT(0) +#define UCON_TX_IRQ_OR_POLLING BIT(2) +#define UCON_RX_ERR_IRQ_EN BIT(6) +#define UCON_TX_IRQ_LEVEL BIT(9) + #define S5L_RX_FIFO_COUNT_SHIFT 0 #define S5L_RX_FIFO_COUNT_MASK (0xf << S5L_RX_FIFO_COUNT_SHIFT) -#define S5L_RX_FIFO_FULL (1 << 8) +#define S5L_RX_FIFO_FULL BIT(8) #define S5L_TX_FIFO_COUNT_SHIFT 4 #define S5L_TX_FIFO_COUNT_MASK (0xf << S5L_TX_FIFO_COUNT_SHIFT) -#define S5L_TX_FIFO_FULL (1 << 9) +#define S5L_TX_FIFO_FULL BIT(9) #define S5P_RX_FIFO_COUNT_SHIFT 0 #define S5P_RX_FIFO_COUNT_MASK (0xff << S5P_RX_FIFO_COUNT_SHIFT) -#define S5P_RX_FIFO_FULL (1 << 8) +#define S5P_RX_FIFO_FULL BIT(8) #define S5P_TX_FIFO_COUNT_SHIFT 16 #define S5P_TX_FIFO_COUNT_MASK (0xff << S5P_TX_FIFO_COUNT_SHIFT) -#define S5P_TX_FIFO_FULL (1 << 24) +#define S5P_TX_FIFO_FULL BIT(24) /* Information about a serial port */ struct s5p_serial_plat { - struct s5p_uart *reg; /* address of registers in physical memory */ - u8 reg_width; /* register width */ - u8 port_id; /* uart port number */ + struct s5p_uart *reg; /* address of registers in physical memory */ + u8 reg_width; /* register width */ + u8 port_id; /* uart port number */ u8 rx_fifo_count_shift; u8 tx_fifo_count_shift; u32 rx_fifo_count_mask; @@ -59,7 +65,7 @@ struct s5p_serial_plat { * The coefficient, used to calculate the baudrate on S5P UARTs is * calculated as * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT - * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1, + * however, section 31.6.11 of the datasheet doesn't recommend using 1 for 1, * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants: */ static const int udivslot[] = { @@ -83,13 +89,15 @@ static const int udivslot[] = { static void __maybe_unused s5p_serial_init(struct s5p_uart *uart) { - /* enable FIFOs, auto clear Rx FIFO */ - writel(0x3, &uart->ufcon); - writel(0, &uart->umcon); - /* 8N1 */ - writel(0x3, &uart->ulcon); + /* Enable FIFOs, auto clear Rx FIFO */ + writel(UFCON_FIFO_EN | UFCON_RX_FIFO_RESET, &uart->ufcon); + /* No auto flow control, disable nRTS signal */ + writel(UMCON_RESET_VAL, &uart->umcon); + /* 8N1, no parity bit */ + writel(ULCON_WORD_8_BIT, &uart->ulcon); /* No interrupts, no DMA, pure polling */ - writel(0x245, &uart->ucon); + writel(UCON_RX_IRQ_OR_POLLING | UCON_TX_IRQ_OR_POLLING | + UCON_RX_ERR_IRQ_EN | UCON_TX_IRQ_LEVEL, &uart->ucon); } static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, u8 reg_width, @@ -118,7 +126,7 @@ int s5p_serial_setbrg(struct udevice *dev, int baudrate) #if IS_ENABLED(CONFIG_CLK_EXYNOS) || IS_ENABLED(CONFIG_ARCH_APPLE) struct clk clk; - u32 ret; + int ret; ret = clk_get_by_index(dev, 1, &clk); if (ret < 0) @@ -213,16 +221,13 @@ static int s5p_serial_of_to_plat(struct udevice *dev) { struct s5p_serial_plat *plat = dev_get_plat(dev); const ulong port_type = dev_get_driver_data(dev); - fdt_addr_t addr; - addr = dev_read_addr(dev); - if (addr == FDT_ADDR_T_NONE) + plat->reg = dev_read_addr_ptr(dev); + if (!plat->reg) return -EINVAL; - plat->reg = (struct s5p_uart *)addr; plat->reg_width = dev_read_u32_default(dev, "reg-io-width", 1); - plat->port_id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), - "id", dev_seq(dev)); + plat->port_id = dev_read_u8_default(dev, "id", dev_seq(dev)); if (port_type == PORT_S5L) { plat->rx_fifo_count_shift = S5L_RX_FIFO_COUNT_SHIFT; @@ -244,10 +249,10 @@ static int s5p_serial_of_to_plat(struct udevice *dev) } static const struct dm_serial_ops s5p_serial_ops = { - .putc = s5p_serial_putc, - .pending = s5p_serial_pending, - .getc = s5p_serial_getc, - .setbrg = s5p_serial_setbrg, + .putc = s5p_serial_putc, + .pending = s5p_serial_pending, + .getc = s5p_serial_getc, + .setbrg = s5p_serial_setbrg, }; static const struct udevice_id s5p_serial_ids[] = { @@ -257,13 +262,13 @@ static const struct udevice_id s5p_serial_ids[] = { }; U_BOOT_DRIVER(serial_s5p) = { - .name = "serial_s5p", - .id = UCLASS_SERIAL, - .of_match = s5p_serial_ids, - .of_to_plat = s5p_serial_of_to_plat, + .name = "serial_s5p", + .id = UCLASS_SERIAL, + .of_match = s5p_serial_ids, + .of_to_plat = s5p_serial_of_to_plat, .plat_auto = sizeof(struct s5p_serial_plat), - .probe = s5p_serial_probe, - .ops = &s5p_serial_ops, + .probe = s5p_serial_probe, + .ops = &s5p_serial_ops, }; #endif @@ -291,10 +296,12 @@ static inline void _debug_uart_putc(int ch) struct s5p_uart *uart = (struct s5p_uart *)CONFIG_VAL(DEBUG_UART_BASE); #if IS_ENABLED(CONFIG_ARCH_APPLE) - while (readl(&uart->ufstat) & S5L_TX_FIFO_FULL); + while (readl(&uart->ufstat) & S5L_TX_FIFO_FULL) + ; writel(ch, &uart->utxh); #else - while (readl(&uart->ufstat) & S5P_TX_FIFO_FULL); + while (readl(&uart->ufstat) & S5P_TX_FIFO_FULL) + ; writeb(ch, &uart->utxh); #endif } diff --git a/drivers/serial/serial_sh.c b/drivers/serial/serial_sh.c index 20cda5dbe27..e4cc4ee4260 100644 --- a/drivers/serial/serial_sh.c +++ b/drivers/serial/serial_sh.c @@ -6,17 +6,18 @@ * Copyright (C) 2002 - 2008 Paul Mundt */ -#include <common.h> -#include <errno.h> -#include <clk.h> -#include <dm.h> #include <asm/global_data.h> #include <asm/io.h> #include <asm/processor.h> -#include <serial.h> -#include <linux/compiler.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> #include <dm/platform_data/serial_sh.h> +#include <errno.h> +#include <linux/compiler.h> #include <linux/delay.h> +#include <reset.h> +#include <serial.h> #include "serial_sh.h" DECLARE_GLOBAL_DATA_PTR; @@ -58,8 +59,10 @@ static void sh_serial_init_generic(struct uart_port *port) sci_out(port, SCSPTR, 0x0003); #endif +#if IS_ENABLED(CONFIG_RCAR_GEN2) || IS_ENABLED(CONFIG_RCAR_GEN3) || IS_ENABLED(CONFIG_RCAR_GEN4) if (port->type == PORT_HSCIF) sci_out(port, HSSRR, HSSRR_SRE | HSSRR_SRCYC8); +#endif } static void @@ -77,10 +80,22 @@ sh_serial_setbrg_generic(struct uart_port *port, int clk, int baudrate) static void handle_error(struct uart_port *port) { - sci_in(port, SCxSR); - sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); + /* + * Most errors are cleared by resetting the relevant error bits to zero + * in the FSR & LSR registers. For each register, a read followed by a + * write is needed according to the relevant datasheets. + */ + unsigned short status = sci_in(port, SCxSR); + sci_out(port, SCxSR, status & ~SCxSR_ERRORS(port)); sci_in(port, SCLSR); sci_out(port, SCLSR, 0x00); + + /* + * To clear framing errors, we also need to read and discard a + * character. + */ + if ((port->type != PORT_SCI) && (status & SCIF_FER)) + sci_in(port, SCxRDR); } static int serial_raw_putc(struct uart_port *port, const char c) @@ -185,12 +200,24 @@ static int sh_serial_probe(struct udevice *dev) { struct sh_serial_plat *plat = dev_get_plat(dev); struct uart_port *priv = dev_get_priv(dev); + struct reset_ctl rst; + int ret; priv->membase = (unsigned char *)plat->base; priv->mapbase = plat->base; priv->type = plat->type; priv->clk_mode = plat->clk_mode; + /* De-assert the module reset if it is defined. */ + ret = reset_get_by_index(dev, 0, &rst); + if (!ret) { + ret = reset_deassert(&rst); + if (ret < 0) { + dev_err(dev, "failed to de-assert reset line\n"); + return ret; + } + } + sh_serial_init_generic(priv); return 0; @@ -207,6 +234,7 @@ static const struct dm_serial_ops sh_serial_ops = { static const struct udevice_id sh_serial_id[] ={ {.compatible = "renesas,sci", .data = PORT_SCI}, {.compatible = "renesas,scif", .data = PORT_SCIF}, + {.compatible = "renesas,scif-r9a07g044", .data = PORT_SCIFA}, {.compatible = "renesas,scifa", .data = PORT_SCIFA}, {.compatible = "renesas,hscif", .data = PORT_HSCIF}, {} diff --git a/drivers/serial/serial_sh.h b/drivers/serial/serial_sh.h index 149ec1fe739..58c2d22bc75 100644 --- a/drivers/serial/serial_sh.h +++ b/drivers/serial/serial_sh.h @@ -90,7 +90,7 @@ struct uart_port { # define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ # define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_RCAR_GEN2) || defined(CONFIG_RCAR_64) || \ - defined(CONFIG_R7S72100) + defined(CONFIG_R7S72100) || defined(CONFIG_RZG2L) # if defined(CFG_SCIF_A) # define SCIF_ORER 0x0200 # else @@ -312,6 +312,9 @@ static inline void sci_##name##_out(struct uart_port *port,\ sh4_scif_offset, sh4_scif_size) #define SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) \ CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) +#elif defined(CONFIG_RZG2L) +#define SCIF_FNS(reg_name, reg_offset, reg_size) \ + CPU_SCIF_FNS(reg_name, reg_offset, reg_size) #else #define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size,\ sh4_sci_offset, sh4_sci_size, \ @@ -387,6 +390,20 @@ SCIF_FNS(SCLSR, 0, 0, 0x14, 16) #else SCIF_FNS(SCLSR, 0, 0, 0x24, 16) #endif +#elif defined(CONFIG_RZG2L) +SCIF_FNS(SCSMR, 0x00, 16) +SCIF_FNS(SCBRR, 0x02, 8) +SCIF_FNS(SCSCR, 0x04, 16) +SCIF_FNS(SCxTDR, 0x06, 8) +SCIF_FNS(SCxSR, 0x08, 16) +SCIF_FNS(SCxRDR, 0x0A, 8) +SCIF_FNS(SCFCR, 0x0C, 16) +SCIF_FNS(SCFDR, 0x0E, 16) +SCIF_FNS(SCSPTR, 0x10, 16) +SCIF_FNS(SCLSR, 0x12, 16) +SCIF_FNS(SCSEMR, 0x14, 8) +SCIF_FNS(SCxTCR, 0x16, 16) +SCIF_FNS(DL, 0x00, 0) #else /* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 SCI/H8*/ /* name off sz off sz off sz off sz off sz*/ diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c index 23d476fba28..fb039546a41 100644 --- a/drivers/serial/serial_stm32.c +++ b/drivers/serial/serial_stm32.c @@ -30,7 +30,7 @@ */ #define ONE_BYTE_B115200_US 87 -static void _stm32_serial_setbrg(fdt_addr_t base, +static void _stm32_serial_setbrg(void __iomem *base, struct stm32_uart_info *uart_info, u32 clock_rate, int baudrate) @@ -75,7 +75,7 @@ static int stm32_serial_setconfig(struct udevice *dev, uint serial_config) struct stm32x7_serial_plat *plat = dev_get_plat(dev); bool stm32f4 = plat->uart_info->stm32f4; u8 uart_enable_bit = plat->uart_info->uart_enable_bit; - u32 cr1 = plat->base + CR1_OFFSET(stm32f4); + void __iomem *cr1 = plat->base + CR1_OFFSET(stm32f4); u32 config = 0; uint parity = SERIAL_GET_PARITY(serial_config); uint bits = SERIAL_GET_BITS(serial_config); @@ -122,7 +122,7 @@ static int stm32_serial_getc(struct udevice *dev) { struct stm32x7_serial_plat *plat = dev_get_plat(dev); bool stm32f4 = plat->uart_info->stm32f4; - fdt_addr_t base = plat->base; + void __iomem *base = plat->base; u32 isr = readl(base + ISR_OFFSET(stm32f4)); if ((isr & USART_ISR_RXNE) == 0) @@ -141,7 +141,7 @@ static int stm32_serial_getc(struct udevice *dev) return readl(base + RDR_OFFSET(stm32f4)); } -static int _stm32_serial_putc(fdt_addr_t base, +static int _stm32_serial_putc(void __iomem *base, struct stm32_uart_info *uart_info, const char c) { @@ -166,7 +166,7 @@ static int stm32_serial_pending(struct udevice *dev, bool input) { struct stm32x7_serial_plat *plat = dev_get_plat(dev); bool stm32f4 = plat->uart_info->stm32f4; - fdt_addr_t base = plat->base; + void __iomem *base = plat->base; if (input) return readl(base + ISR_OFFSET(stm32f4)) & @@ -176,7 +176,7 @@ static int stm32_serial_pending(struct udevice *dev, bool input) USART_ISR_TXE ? 0 : 1; } -static void _stm32_serial_init(fdt_addr_t base, +static void _stm32_serial_init(void __iomem *base, struct stm32_uart_info *uart_info) { bool stm32f4 = uart_info->stm32f4; @@ -250,11 +250,14 @@ static const struct udevice_id stm32_serial_id[] = { static int stm32_serial_of_to_plat(struct udevice *dev) { struct stm32x7_serial_plat *plat = dev_get_plat(dev); + fdt_addr_t addr; - plat->base = dev_read_addr(dev); - if (plat->base == FDT_ADDR_T_NONE) + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) return -EINVAL; + plat->base = (void __iomem *)addr; + return 0; } @@ -297,7 +300,7 @@ static inline struct stm32_uart_info *_debug_uart_info(void) static inline void _debug_uart_init(void) { - fdt_addr_t base = CONFIG_VAL(DEBUG_UART_BASE); + void __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE); struct stm32_uart_info *uart_info = _debug_uart_info(); _stm32_serial_init(base, uart_info); @@ -308,7 +311,7 @@ static inline void _debug_uart_init(void) static inline void _debug_uart_putc(int c) { - fdt_addr_t base = CONFIG_VAL(DEBUG_UART_BASE); + void __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE); struct stm32_uart_info *uart_info = _debug_uart_info(); while (_stm32_serial_putc(base, uart_info, c) == -EAGAIN) diff --git a/drivers/serial/serial_stm32.h b/drivers/serial/serial_stm32.h index b7e7a90b931..d2c92ba48ea 100644 --- a/drivers/serial/serial_stm32.h +++ b/drivers/serial/serial_stm32.h @@ -49,7 +49,7 @@ struct stm32_uart_info stm32h7_info = { /* Information about a serial port */ struct stm32x7_serial_plat { - fdt_addr_t base; /* address of registers in physical memory */ + void __iomem *base; /* address of registers in physical memory */ struct stm32_uart_info *uart_info; unsigned long int clock_rate; }; diff --git a/drivers/sm/Kconfig b/drivers/sm/Kconfig new file mode 100644 index 00000000000..926af286330 --- /dev/null +++ b/drivers/sm/Kconfig @@ -0,0 +1,8 @@ +config SM + bool "Enable Secure Monitor driver support" + +config MESON_SM + bool "Amlogic Secure Monitor driver" + select SM + help + Say y here to enable the Amlogic secure monitor driver. diff --git a/drivers/sm/Makefile b/drivers/sm/Makefile new file mode 100644 index 00000000000..da81ee898ab --- /dev/null +++ b/drivers/sm/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-y += sm-uclass.o +obj-$(CONFIG_SANDBOX) += sandbox-sm.o +obj-$(CONFIG_MESON_SM) += meson-sm.o diff --git a/drivers/sm/meson-sm.c b/drivers/sm/meson-sm.c new file mode 100644 index 00000000000..15b3b0e2672 --- /dev/null +++ b/drivers/sm/meson-sm.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 SberDevices, Inc. + * + * Author: Alexey Romanov <avromanov@salutedevices.com> + */ + +#include <common.h> +#include <dm.h> +#include <regmap.h> +#include <sm.h> +#include <sm-uclass.h> +#include <stdlib.h> +#include <syscon.h> +#include <asm/ptrace.h> +#include <asm/system.h> +#include <meson/sm.h> +#include <linux/bitfield.h> +#include <linux/err.h> +#include <linux/sizes.h> + +struct meson_sm_cmd { + u32 smc_id; +}; + +#define SET_CMD(index, id) \ + [index] = { \ + .smc_id = (id), \ + } + +struct meson_sm_data { + u32 cmd_get_shmem_in; + u32 cmd_get_shmem_out; + unsigned int shmem_size; + struct meson_sm_cmd cmd[]; +}; + +struct meson_sm_priv { + void *sm_shmem_in; + void *sm_shmem_out; + const struct meson_sm_data *data; +}; + +static unsigned long __meson_sm_call(u32 cmd, const struct pt_regs *args) +{ + struct pt_regs r = *args; + + r.regs[0] = cmd; + smc_call(&r); + + return r.regs[0]; +}; + +static u32 meson_sm_get_cmd(const struct meson_sm_data *data, + u32 cmd_index) +{ + struct meson_sm_cmd cmd; + + if (cmd_index >= MESON_SMC_CMD_COUNT) + return 0; + + cmd = data->cmd[cmd_index]; + return cmd.smc_id; +} + +static int meson_sm_call(struct udevice *dev, u32 cmd_index, s32 *retval, + struct pt_regs *args) +{ + struct meson_sm_priv *priv = dev_get_priv(dev); + u32 cmd, ret; + + cmd = meson_sm_get_cmd(priv->data, cmd_index); + if (!cmd) + return -ENOENT; + + ret = __meson_sm_call(cmd, args); + if (retval) + *retval = ret; + + return 0; +} + +static int meson_sm_call_read(struct udevice *dev, void *buffer, size_t size, + u32 cmd_index, struct pt_regs *args) +{ + struct meson_sm_priv *priv = dev_get_priv(dev); + s32 nbytes; + int ret; + + if (!buffer || size > priv->data->shmem_size) + return -EINVAL; + + ret = meson_sm_call(dev, cmd_index, &nbytes, args); + if (ret) + return ret; + + if (nbytes < 0 || nbytes > size) + return -ENOBUFS; + + /* In some cases (for example GET_CHIP_ID command), + * SMC doesn't return the number of bytes read, even + * though the bytes were actually read into sm_shmem_out. + * So this check is needed. + */ + ret = nbytes; + if (!nbytes) + nbytes = size; + + memcpy(buffer, priv->sm_shmem_out, nbytes); + + return ret; +} + +static int meson_sm_call_write(struct udevice *dev, void *buffer, size_t size, + u32 cmd_index, struct pt_regs *args) +{ + struct meson_sm_priv *priv = dev_get_priv(dev); + s32 nbytes; + int ret; + + if (!buffer || size > priv->data->shmem_size) + return -EINVAL; + + memcpy(priv->sm_shmem_in, buffer, size); + + ret = meson_sm_call(dev, cmd_index, &nbytes, args); + if (ret) + return ret; + + if (nbytes <= 0 || nbytes > size) + return -EIO; + + return nbytes; +} + +static int meson_sm_probe(struct udevice *dev) +{ + struct meson_sm_priv *priv = dev_get_priv(dev); + struct pt_regs regs = { 0 }; + + priv->data = (struct meson_sm_data *)dev_get_driver_data(dev); + if (!priv->data) + return -EINVAL; + + priv->sm_shmem_in = + (void *)__meson_sm_call(priv->data->cmd_get_shmem_in, ®s); + + if (!priv->sm_shmem_in) + return -ENOMEM; + + priv->sm_shmem_out = + (void *)__meson_sm_call(priv->data->cmd_get_shmem_out, ®s); + + if (!priv->sm_shmem_out) + return -ENOMEM; + + pr_debug("meson sm driver probed\n" + "shmem_in addr: 0x%p, shmem_out addr: 0x%p\n", + priv->sm_shmem_in, + priv->sm_shmem_out); + + return 0; +} + +static const struct meson_sm_data meson_sm_gxbb_data = { + .cmd_get_shmem_in = 0x82000020, + .cmd_get_shmem_out = 0x82000021, + .shmem_size = SZ_4K, + .cmd = { + SET_CMD(MESON_SMC_CMD_EFUSE_READ, 0x82000030), + SET_CMD(MESON_SMC_CMD_EFUSE_WRITE, 0x82000031), + SET_CMD(MESON_SMC_CMD_CHIP_ID_GET, 0x82000044), + SET_CMD(MESON_SMC_CMD_PWRDM_SET, 0x82000093), + }, +}; + +static const struct udevice_id meson_sm_ids[] = { + { + .compatible = "amlogic,meson-gxbb-sm", + .data = (ulong)&meson_sm_gxbb_data, + }, + { } +}; + +static const struct sm_ops sm_ops = { + .sm_call = meson_sm_call, + .sm_call_read = meson_sm_call_read, + .sm_call_write = meson_sm_call_write, +}; + +U_BOOT_DRIVER(meson_sm) = { + .name = "meson_sm", + .id = UCLASS_SM, + .of_match = meson_sm_ids, + .probe = meson_sm_probe, + .bind = dm_scan_fdt_dev, + .priv_auto = sizeof(struct meson_sm_priv), + .ops = &sm_ops, +}; diff --git a/drivers/sm/sandbox-sm.c b/drivers/sm/sandbox-sm.c new file mode 100644 index 00000000000..109ddb2af55 --- /dev/null +++ b/drivers/sm/sandbox-sm.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 SberDevices, Inc. + * + * Author: Alexey Romanov <avromanov@salutedevices.com> + */ + +#include <common.h> +#include <sm.h> +#include <sm-uclass.h> +#include <sandbox-sm.h> +#include <asm/ptrace.h> +#include <dm/device.h> +#include <linux/sizes.h> + +static u8 test_buffer[SZ_4K]; + +static int sandbox_sm_call(struct udevice *dev, u32 cmd_index, s32 *smc_ret, + struct pt_regs *args) +{ + if (cmd_index >= SANDBOX_SMC_CMD_COUNT) + return -EINVAL; + + if (smc_ret) + *smc_ret = 0; + + return 0; +} + +static int sandbox_sm_call_read(struct udevice *dev, void *buffer, size_t size, + u32 cmd_index, struct pt_regs *args) +{ + if (cmd_index >= SANDBOX_SMC_CMD_COUNT || !buffer) + return -EINVAL; + + if (size > sizeof(test_buffer)) + return -EINVAL; + + memcpy(buffer, test_buffer, size); + + return size; +} + +static int sandbox_sm_call_write(struct udevice *dev, void *buffer, size_t size, + u32 cmd_index, struct pt_regs *args) +{ + if (cmd_index >= SANDBOX_SMC_CMD_COUNT || !buffer) + return -EINVAL; + + if (size > sizeof(test_buffer)) + return -EINVAL; + + memcpy(test_buffer, buffer, size); + + return size; +} + +static const struct udevice_id sandbox_sm_ids[] = { + { + .compatible = "sandbox,sm", + }, + {}, +}; + +static const struct sm_ops sandbox_sm_ops = { + .sm_call = sandbox_sm_call, + .sm_call_read = sandbox_sm_call_read, + .sm_call_write = sandbox_sm_call_write, +}; + +U_BOOT_DRIVER(sm) = { + .name = "sm", + .id = UCLASS_SM, + .of_match = sandbox_sm_ids, + .ops = &sandbox_sm_ops, +}; diff --git a/drivers/sm/sm-uclass.c b/drivers/sm/sm-uclass.c new file mode 100644 index 00000000000..6a8b7026293 --- /dev/null +++ b/drivers/sm/sm-uclass.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 SberDevices, Inc. + * + * Author: Alexey Romanov <avromanov@salutedevices.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <sm-uclass.h> + +static const struct sm_ops *get_sm_ops(struct udevice *dev) +{ + return (const struct sm_ops *)dev->driver->ops; +} + +int sm_call(struct udevice *dev, u32 cmd, s32 *ret, struct pt_regs *args) +{ + const struct sm_ops *ops = get_sm_ops(dev); + + if (ops->sm_call) + return ops->sm_call(dev, cmd, ret, args); + + return -ENOSYS; +} + +int sm_call_read(struct udevice *dev, void *buffer, size_t size, + u32 cmd, struct pt_regs *args) +{ + const struct sm_ops *ops = get_sm_ops(dev); + + if (ops->sm_call_read) + return ops->sm_call_read(dev, buffer, size, cmd, + args); + + return -ENOSYS; +} + +int sm_call_write(struct udevice *dev, void *buffer, size_t size, + u32 cmd, struct pt_regs *args) +{ + const struct sm_ops *ops = get_sm_ops(dev); + + if (ops->sm_call_write) + return ops->sm_call_write(dev, buffer, size, cmd, + args); + + return -ENOSYS; +} + +UCLASS_DRIVER(sm) = { + .name = "sm", + .id = UCLASS_SM, +}; diff --git a/drivers/soc/soc-uclass.c b/drivers/soc/soc-uclass.c index dfad32d80db..8b3044fed8d 100644 --- a/drivers/soc/soc-uclass.c +++ b/drivers/soc/soc-uclass.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * (C) Copyright 2020 - Texas Instruments Incorporated - http://www.ti.com/ + * (C) Copyright 2020 - Texas Instruments Incorporated - https://www.ti.com/ * Dave Gerlach <d-gerlach@ti.com> */ diff --git a/drivers/soc/soc_sandbox.c b/drivers/soc/soc_sandbox.c index 5c82ad84fc2..15fdd9930cb 100644 --- a/drivers/soc/soc_sandbox.c +++ b/drivers/soc/soc_sandbox.c @@ -2,7 +2,7 @@ /* * Sandbox driver for the SOC uclass * - * (C) Copyright 2020 - Texas Instruments Incorporated - http://www.ti.com/ + * (C) Copyright 2020 - Texas Instruments Incorporated - https://www.ti.com/ * Dave Gerlach <d-gerlach@ti.com> */ diff --git a/drivers/soc/soc_ti_k3.c b/drivers/soc/soc_ti_k3.c index b720131ae5d..355a5368dd4 100644 --- a/drivers/soc/soc_ti_k3.c +++ b/drivers/soc/soc_ti_k3.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ * Dave Gerlach <d-gerlach@ti.com> */ diff --git a/drivers/soc/ti/k3-navss-ringacc.c b/drivers/soc/ti/k3-navss-ringacc.c index 9881bffc8e1..7a2fbb0db6e 100644 --- a/drivers/soc/ti/k3-navss-ringacc.c +++ b/drivers/soc/ti/k3-navss-ringacc.c @@ -2,7 +2,7 @@ /* * TI K3 AM65x NAVSS Ring accelerator Manager (RA) subsystem driver * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com */ #include <common.h> @@ -987,10 +987,10 @@ static int k3_nav_ringacc_init(struct udevice *dev, struct k3_nav_ringacc *ringa if (!base_cfg) return -EINVAL; - base_rt = (uint32_t *)devfdt_get_addr_name(dev, "rt"); + base_rt = dev_read_addr_name_ptr(dev, "rt"); pr_debug("rt %p\n", base_rt); - if (IS_ERR(base_rt)) - return PTR_ERR(base_rt); + if (!base_rt) + return -EINVAL; ringacc->rings = devm_kzalloc(dev, sizeof(*ringacc->rings) * @@ -1045,9 +1045,9 @@ struct k3_nav_ringacc *k3_ringacc_dmarings_init(struct udevice *dev, ringacc->tisci = data->tisci; ringacc->tisci_dev_id = data->tisci_dev_id; - base_rt = (uint32_t *)devfdt_get_addr_name(dev, "ringrt"); - if (IS_ERR(base_rt)) - return base_rt; + base_rt = dev_read_addr_name_ptr(dev, "ringrt"); + if (!base_rt) + return ERR_PTR(-EINVAL); ringacc->rings = devm_kzalloc(dev, sizeof(*ringacc->rings) * diff --git a/drivers/soc/ti/keystone_serdes.c b/drivers/soc/ti/keystone_serdes.c index 2ece1a8f647..0e1bf8ff39d 100644 --- a/drivers/soc/ti/keystone_serdes.c +++ b/drivers/soc/ti/keystone_serdes.c @@ -8,6 +8,7 @@ #include <errno.h> #include <common.h> +#include <asm/io.h> #include <asm/ti-common/keystone_serdes.h> #include <linux/bitops.h> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 854b8b88daf..69b184b0d9e 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -251,6 +251,15 @@ config MICROCHIP_COREQSPI Enable the QSPI driver for Microchip FPGA QSPI controllers. This driver can be used on Polarfire SoC. +config MESON_SPIFC_A1 + bool "Amlogic Meson A1 SPI Flash Controller driver" + depends on ARCH_MESON + help + Enable the Amlogic A1 SPI Flash Controller (SPIFC) driver. + This driver can be used to access the SPI NOR/NAND flash chips + with STR mode frequency up to 98MHz. Dual and quad modes are + supported by controller. + config MPC8XX_SPI bool "MPC8XX SPI Driver" depends on MPC8xx @@ -451,7 +460,6 @@ config SANDBOX_SPI_MAX_CS config SPI_ASPEED_SMC bool "ASPEED SPI flash controller driver" depends on DM_SPI && SPI_MEM - default n help Enable ASPEED SPI flash controller driver for AST2500 and AST2600 SoCs. diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c27b3327c33..14bdb97f189 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_ICH_SPI) += ich.o obj-$(CONFIG_IPROC_QSPI) += iproc_qspi.o obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o +obj-$(CONFIG_MESON_SPIFC_A1) += meson_spifc_a1.o obj-$(CONFIG_MICROCHIP_COREQSPI) += microchip_coreqspi.o obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o diff --git a/drivers/spi/cadence_ospi_versal.c b/drivers/spi/cadence_ospi_versal.c index a7685a2f512..e02a3b3de37 100644 --- a/drivers/spi/cadence_ospi_versal.c +++ b/drivers/spi/cadence_ospi_versal.c @@ -44,8 +44,10 @@ int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv, priv->regbase + CQSPI_REG_INDIR_TRIG_ADDR_RANGE); writel(CQSPI_DFLT_DMA_PERIPH_CFG, priv->regbase + CQSPI_REG_DMA_PERIPH_CFG); - writel((unsigned long)rxbuf, priv->regbase + + writel(lower_32_bits((unsigned long)rxbuf), priv->regbase + CQSPI_DMA_DST_ADDR_REG); + writel(upper_32_bits((unsigned long)rxbuf), priv->regbase + + CQSPI_DMA_DST_ADDR_MSB_REG); writel(priv->trigger_address, priv->regbase + CQSPI_DMA_SRC_RD_ADDR_REG); writel(bytes_to_dma, priv->regbase + diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index cc3a54f2958..2efd626852e 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -7,7 +7,6 @@ #include <common.h> #include <clk.h> #include <log.h> -#include <asm-generic/io.h> #include <dm.h> #include <fdtdec.h> #include <malloc.h> @@ -17,13 +16,13 @@ #include <dm/device_compat.h> #include <linux/err.h> #include <linux/errno.h> +#include <linux/io.h> #include <linux/sizes.h> +#include <linux/time.h> #include <zynqmp_firmware.h> #include "cadence_qspi.h" #include <dt-bindings/power/xlnx-versal-power.h> -#define NSEC_PER_SEC 1000000000L - #define CQSPI_STIG_READ 0 #define CQSPI_STIG_WRITE 1 #define CQSPI_READ 2 diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 9ebc4ed48f0..25f5e9fdebd 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * * Driver for SPI controller on DaVinci. Based on atmel_spi.c * by Atmel Corporation diff --git a/drivers/spi/fsl_dspi.c b/drivers/spi/fsl_dspi.c index f8ec268812c..89907cbbb02 100644 --- a/drivers/spi/fsl_dspi.c +++ b/drivers/spi/fsl_dspi.c @@ -27,9 +27,7 @@ #include <linux/bitops.h> #include <linux/delay.h> #include <linux/printk.h> - -/* linux/include/time.h */ -#define NSEC_PER_SEC 1000000000L +#include <linux/time.h> DECLARE_GLOBAL_DATA_PTR; diff --git a/drivers/spi/meson_spifc_a1.c b/drivers/spi/meson_spifc_a1.c new file mode 100644 index 00000000000..cca4debb412 --- /dev/null +++ b/drivers/spi/meson_spifc_a1.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Amlogic A1 SPI flash controller (SPIFC) + * + * Copyright (c) 2023, SberDevices. All Rights Reserved. + * + * Author: Martin Kurbanov <mmkurbanov@sberdevices.ru> + * + * Ported to u-boot: + * Author: Igor Prusov <ivprusov@sberdevices.ru> + */ + +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <spi-mem.h> +#include <asm/io.h> +#include <linux/log2.h> +#include <linux/time.h> +#include <linux/iopoll.h> +#include <linux/bitfield.h> + +#define SPIFC_A1_AHB_CTRL_REG 0x0 +#define SPIFC_A1_AHB_BUS_EN BIT(31) + +#define SPIFC_A1_USER_CTRL0_REG 0x200 +#define SPIFC_A1_USER_REQUEST_ENABLE BIT(31) +#define SPIFC_A1_USER_REQUEST_FINISH BIT(30) +#define SPIFC_A1_USER_DATA_UPDATED BIT(0) + +#define SPIFC_A1_USER_CTRL1_REG 0x204 +#define SPIFC_A1_USER_CMD_ENABLE BIT(30) +#define SPIFC_A1_USER_CMD_MODE GENMASK(29, 28) +#define SPIFC_A1_USER_CMD_CODE GENMASK(27, 20) +#define SPIFC_A1_USER_ADDR_ENABLE BIT(19) +#define SPIFC_A1_USER_ADDR_MODE GENMASK(18, 17) +#define SPIFC_A1_USER_ADDR_BYTES GENMASK(16, 15) +#define SPIFC_A1_USER_DOUT_ENABLE BIT(14) +#define SPIFC_A1_USER_DOUT_MODE GENMASK(11, 10) +#define SPIFC_A1_USER_DOUT_BYTES GENMASK(9, 0) + +#define SPIFC_A1_USER_CTRL2_REG 0x208 +#define SPIFC_A1_USER_DUMMY_ENABLE BIT(31) +#define SPIFC_A1_USER_DUMMY_MODE GENMASK(30, 29) +#define SPIFC_A1_USER_DUMMY_CLK_SYCLES GENMASK(28, 23) + +#define SPIFC_A1_USER_CTRL3_REG 0x20c +#define SPIFC_A1_USER_DIN_ENABLE BIT(31) +#define SPIFC_A1_USER_DIN_MODE GENMASK(28, 27) +#define SPIFC_A1_USER_DIN_BYTES GENMASK(25, 16) + +#define SPIFC_A1_USER_ADDR_REG 0x210 + +#define SPIFC_A1_AHB_REQ_CTRL_REG 0x214 +#define SPIFC_A1_AHB_REQ_ENABLE BIT(31) + +#define SPIFC_A1_ACTIMING0_REG (0x0088 << 2) +#define SPIFC_A1_TSLCH GENMASK(31, 30) +#define SPIFC_A1_TCLSH GENMASK(29, 28) +#define SPIFC_A1_TSHWL GENMASK(20, 16) +#define SPIFC_A1_TSHSL2 GENMASK(15, 12) +#define SPIFC_A1_TSHSL1 GENMASK(11, 8) +#define SPIFC_A1_TWHSL GENMASK(7, 0) + +#define SPIFC_A1_DBUF_CTRL_REG 0x240 +#define SPIFC_A1_DBUF_DIR BIT(31) +#define SPIFC_A1_DBUF_AUTO_UPDATE_ADDR BIT(30) +#define SPIFC_A1_DBUF_ADDR GENMASK(7, 0) + +#define SPIFC_A1_DBUF_DATA_REG 0x244 + +#define SPIFC_A1_USER_DBUF_ADDR_REG 0x248 + +#define SPIFC_A1_BUFFER_SIZE 512U + +#define SPIFC_A1_MAX_HZ 200000000 +#define SPIFC_A1_MIN_HZ 1000000 + +#define SPIFC_A1_USER_CMD(op) ( \ + SPIFC_A1_USER_CMD_ENABLE | \ + FIELD_PREP(SPIFC_A1_USER_CMD_CODE, (op)->cmd.opcode) | \ + FIELD_PREP(SPIFC_A1_USER_CMD_MODE, ilog2((op)->cmd.buswidth))) + +#define SPIFC_A1_USER_ADDR(op) ( \ + SPIFC_A1_USER_ADDR_ENABLE | \ + FIELD_PREP(SPIFC_A1_USER_ADDR_MODE, ilog2((op)->addr.buswidth)) | \ + FIELD_PREP(SPIFC_A1_USER_ADDR_BYTES, (op)->addr.nbytes - 1)) + +#define SPIFC_A1_USER_DUMMY(op) ( \ + SPIFC_A1_USER_DUMMY_ENABLE | \ + FIELD_PREP(SPIFC_A1_USER_DUMMY_MODE, ilog2((op)->dummy.buswidth)) | \ + FIELD_PREP(SPIFC_A1_USER_DUMMY_CLK_SYCLES, (op)->dummy.nbytes << 3)) + +#define SPIFC_A1_TSLCH_VAL FIELD_PREP(SPIFC_A1_TSLCH, 1) +#define SPIFC_A1_TCLSH_VAL FIELD_PREP(SPIFC_A1_TCLSH, 1) +#define SPIFC_A1_TSHWL_VAL FIELD_PREP(SPIFC_A1_TSHWL, 7) +#define SPIFC_A1_TSHSL2_VAL FIELD_PREP(SPIFC_A1_TSHSL2, 7) +#define SPIFC_A1_TSHSL1_VAL FIELD_PREP(SPIFC_A1_TSHSL1, 7) +#define SPIFC_A1_TWHSL_VAL FIELD_PREP(SPIFC_A1_TWHSL, 2) +#define SPIFC_A1_ACTIMING0_VAL (SPIFC_A1_TSLCH_VAL | SPIFC_A1_TCLSH_VAL | \ + SPIFC_A1_TSHWL_VAL | SPIFC_A1_TSHSL2_VAL | \ + SPIFC_A1_TSHSL1_VAL | SPIFC_A1_TWHSL_VAL) + +struct amlogic_spifc_a1 { + struct clk clk; + void __iomem *base; + u32 curr_speed_hz; +}; + +static int amlogic_spifc_a1_request(struct amlogic_spifc_a1 *spifc, bool read) +{ + u32 mask = SPIFC_A1_USER_REQUEST_FINISH | + (read ? SPIFC_A1_USER_DATA_UPDATED : 0); + u32 val; + + writel(SPIFC_A1_USER_REQUEST_ENABLE, + spifc->base + SPIFC_A1_USER_CTRL0_REG); + + return readl_poll_timeout(spifc->base + SPIFC_A1_USER_CTRL0_REG, + val, (val & mask) == mask, + 200 * USEC_PER_MSEC); +} + +static void amlogic_spifc_a1_drain_buffer(struct amlogic_spifc_a1 *spifc, + char *buf, u32 len) +{ + u32 data; + const u32 count = len / sizeof(data); + const u32 pad = len % sizeof(data); + + writel(SPIFC_A1_DBUF_AUTO_UPDATE_ADDR, + spifc->base + SPIFC_A1_DBUF_CTRL_REG); + ioread32_rep(spifc->base + SPIFC_A1_DBUF_DATA_REG, buf, count); + + if (pad) { + data = readl(spifc->base + SPIFC_A1_DBUF_DATA_REG); + memcpy(buf + len - pad, &data, pad); + } +} + +static void amlogic_spifc_a1_fill_buffer(struct amlogic_spifc_a1 *spifc, + const char *buf, u32 len) +{ + u32 data; + const u32 count = len / sizeof(data); + const u32 pad = len % sizeof(data); + + writel(SPIFC_A1_DBUF_DIR | SPIFC_A1_DBUF_AUTO_UPDATE_ADDR, + spifc->base + SPIFC_A1_DBUF_CTRL_REG); + iowrite32_rep(spifc->base + SPIFC_A1_DBUF_DATA_REG, buf, count); + + if (pad) { + memcpy(&data, buf + len - pad, pad); + writel(data, spifc->base + SPIFC_A1_DBUF_DATA_REG); + } +} + +static void amlogic_spifc_a1_user_init(struct amlogic_spifc_a1 *spifc) +{ + writel(0, spifc->base + SPIFC_A1_USER_CTRL0_REG); + writel(0, spifc->base + SPIFC_A1_USER_CTRL1_REG); + writel(0, spifc->base + SPIFC_A1_USER_CTRL2_REG); + writel(0, spifc->base + SPIFC_A1_USER_CTRL3_REG); +} + +static void amlogic_spifc_a1_set_cmd(struct amlogic_spifc_a1 *spifc, + u32 cmd_cfg) +{ + u32 val; + + val = readl(spifc->base + SPIFC_A1_USER_CTRL1_REG); + val &= ~(SPIFC_A1_USER_CMD_MODE | SPIFC_A1_USER_CMD_CODE); + val |= cmd_cfg; + writel(val, spifc->base + SPIFC_A1_USER_CTRL1_REG); +} + +static void amlogic_spifc_a1_set_addr(struct amlogic_spifc_a1 *spifc, u32 addr, + u32 addr_cfg) +{ + u32 val; + + writel(addr, spifc->base + SPIFC_A1_USER_ADDR_REG); + + val = readl(spifc->base + SPIFC_A1_USER_CTRL1_REG); + val &= ~(SPIFC_A1_USER_ADDR_MODE | SPIFC_A1_USER_ADDR_BYTES); + val |= addr_cfg; + writel(val, spifc->base + SPIFC_A1_USER_CTRL1_REG); +} + +static void amlogic_spifc_a1_set_dummy(struct amlogic_spifc_a1 *spifc, + u32 dummy_cfg) +{ + u32 val = readl(spifc->base + SPIFC_A1_USER_CTRL2_REG); + + val &= ~(SPIFC_A1_USER_DUMMY_MODE | SPIFC_A1_USER_DUMMY_CLK_SYCLES); + val |= dummy_cfg; + writel(val, spifc->base + SPIFC_A1_USER_CTRL2_REG); +} + +static int amlogic_spifc_a1_read(struct amlogic_spifc_a1 *spifc, void *buf, + u32 size, u32 mode) +{ + u32 val = readl(spifc->base + SPIFC_A1_USER_CTRL3_REG); + int ret; + + val &= ~(SPIFC_A1_USER_DIN_MODE | SPIFC_A1_USER_DIN_BYTES); + val |= SPIFC_A1_USER_DIN_ENABLE; + val |= FIELD_PREP(SPIFC_A1_USER_DIN_MODE, mode); + val |= FIELD_PREP(SPIFC_A1_USER_DIN_BYTES, size); + writel(val, spifc->base + SPIFC_A1_USER_CTRL3_REG); + + ret = amlogic_spifc_a1_request(spifc, true); + if (!ret) + amlogic_spifc_a1_drain_buffer(spifc, buf, size); + + return ret; +} + +static int amlogic_spifc_a1_write(struct amlogic_spifc_a1 *spifc, + const void *buf, u32 size, u32 mode) +{ + u32 val; + + amlogic_spifc_a1_fill_buffer(spifc, buf, size); + + val = readl(spifc->base + SPIFC_A1_USER_CTRL1_REG); + val &= ~(SPIFC_A1_USER_DOUT_MODE | SPIFC_A1_USER_DOUT_BYTES); + val |= FIELD_PREP(SPIFC_A1_USER_DOUT_MODE, mode); + val |= FIELD_PREP(SPIFC_A1_USER_DOUT_BYTES, size); + val |= SPIFC_A1_USER_DOUT_ENABLE; + writel(val, spifc->base + SPIFC_A1_USER_CTRL1_REG); + + return amlogic_spifc_a1_request(spifc, false); +} + +static int amlogic_spifc_a1_set_freq(struct amlogic_spifc_a1 *spifc, u32 freq) +{ + int ret; + + if (freq == spifc->curr_speed_hz) + return 0; + + ret = clk_set_rate(&spifc->clk, freq); + if (ret) + return ret; + + spifc->curr_speed_hz = freq; + return 0; +} + +static int amlogic_spifc_a1_exec_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + struct amlogic_spifc_a1 *spifc = dev_get_priv(slave->dev->parent); + size_t data_size = op->data.nbytes; + int ret; + + ret = amlogic_spifc_a1_set_freq(spifc, slave->max_hz); + if (ret) + return ret; + + amlogic_spifc_a1_user_init(spifc); + amlogic_spifc_a1_set_cmd(spifc, SPIFC_A1_USER_CMD(op)); + + if (op->addr.nbytes) + amlogic_spifc_a1_set_addr(spifc, op->addr.val, + SPIFC_A1_USER_ADDR(op)); + + if (op->dummy.nbytes) + amlogic_spifc_a1_set_dummy(spifc, SPIFC_A1_USER_DUMMY(op)); + + if (data_size) { + u32 mode = ilog2(op->data.buswidth); + + writel(0, spifc->base + SPIFC_A1_USER_DBUF_ADDR_REG); + + if (op->data.dir == SPI_MEM_DATA_IN) + ret = amlogic_spifc_a1_read(spifc, op->data.buf.in, + data_size, mode); + else + ret = amlogic_spifc_a1_write(spifc, op->data.buf.out, + data_size, mode); + } else { + ret = amlogic_spifc_a1_request(spifc, false); + } + + return ret; +} + +static int amlogic_spifc_a1_adjust_op_size(struct spi_slave *slave, + struct spi_mem_op *op) +{ + op->data.nbytes = min(op->data.nbytes, SPIFC_A1_BUFFER_SIZE); + return 0; +} + +static void amlogic_spifc_a1_hw_init(struct amlogic_spifc_a1 *spifc) +{ + u32 regv; + + regv = readl(spifc->base + SPIFC_A1_AHB_REQ_CTRL_REG); + regv &= ~(SPIFC_A1_AHB_REQ_ENABLE); + writel(regv, spifc->base + SPIFC_A1_AHB_REQ_CTRL_REG); + + regv = readl(spifc->base + SPIFC_A1_AHB_CTRL_REG); + regv &= ~(SPIFC_A1_AHB_BUS_EN); + writel(regv, spifc->base + SPIFC_A1_AHB_CTRL_REG); + + writel(SPIFC_A1_ACTIMING0_VAL, spifc->base + SPIFC_A1_ACTIMING0_REG); + + writel(0, spifc->base + SPIFC_A1_USER_DBUF_ADDR_REG); +} + +static const struct spi_controller_mem_ops amlogic_spifc_a1_mem_ops = { + .exec_op = amlogic_spifc_a1_exec_op, + .adjust_op_size = amlogic_spifc_a1_adjust_op_size, +}; + +static int amlogic_spifc_a1_probe(struct udevice *dev) +{ + struct amlogic_spifc_a1 *spifc = dev_get_priv(dev); + int ret; + struct udevice *bus = dev; + + spifc->base = dev_read_addr_ptr(dev); + if (!spifc->base) + return -EINVAL; + + ret = clk_get_by_index(bus, 0, &spifc->clk); + if (ret) { + pr_err("can't get clk spifc_gate!\n"); + return ret; + } + + ret = clk_enable(&spifc->clk); + if (ret) { + pr_err("enable clk fail\n"); + return ret; + } + + amlogic_spifc_a1_hw_init(spifc); + + return 0; +} + +static int amlogic_spifc_a1_remove(struct udevice *dev) +{ + struct amlogic_spifc_a1 *spifc = dev_get_priv(dev); + + clk_free(&spifc->clk); + + return 0; +} + +static const struct udevice_id meson_spifc_ids[] = { + { .compatible = "amlogic,a1-spifc", }, + { } +}; + +int amlogic_spifc_a1_set_speed(struct udevice *bus, uint hz) +{ + return 0; +} + +int amlogic_spifc_a1_set_mode(struct udevice *bus, uint mode) +{ + return 0; +} + +static const struct dm_spi_ops amlogic_spifc_a1_ops = { + .mem_ops = &amlogic_spifc_a1_mem_ops, + .set_speed = amlogic_spifc_a1_set_speed, + .set_mode = amlogic_spifc_a1_set_mode, +}; + +U_BOOT_DRIVER(meson_spifc_a1) = { + .name = "meson_spifc_a1", + .id = UCLASS_SPI, + .of_match = meson_spifc_ids, + .ops = &amlogic_spifc_a1_ops, + .probe = amlogic_spifc_a1_probe, + .remove = amlogic_spifc_a1_remove, + .priv_auto = sizeof(struct amlogic_spifc_a1), +}; diff --git a/drivers/spi/mtk_spim.c b/drivers/spi/mtk_spim.c index 418e586b91d..90f4c3cecb9 100644 --- a/drivers/spi/mtk_spim.c +++ b/drivers/spi/mtk_spim.c @@ -409,7 +409,7 @@ static int mtk_spim_transfer_wait(struct spi_slave *slave, { struct udevice *bus = dev_get_parent(slave->dev); struct mtk_spim_priv *priv = dev_get_priv(bus); - u32 sck_l, sck_h, clk_count, reg; + u32 pll_clk, sck_l, sck_h, clk_count, reg; ulong us = 1; int ret = 0; @@ -418,11 +418,12 @@ static int mtk_spim_transfer_wait(struct spi_slave *slave, else clk_count = op->data.nbytes; + pll_clk = priv->pll_clk_rate; sck_l = readl(priv->base + SPI_CFG2_REG) >> SPI_CFG2_SCK_LOW_OFFSET; sck_h = readl(priv->base + SPI_CFG2_REG) & SPI_CFG2_SCK_HIGH_MASK; - do_div(priv->pll_clk_rate, sck_l + sck_h + 2); + do_div(pll_clk, sck_l + sck_h + 2); - us = CLK_TO_US(priv->pll_clk_rate, clk_count * 8); + us = CLK_TO_US(pll_clk, clk_count * 8); us += 1000 * 1000; /* 1s tolerance */ if (us > UINT_MAX) diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c index 579d6bac9b1..5db27f9ae2c 100644 --- a/drivers/spi/nxp_fspi.c +++ b/drivers/spi/nxp_fspi.c @@ -927,6 +927,13 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f) fspi_writel(f, FSPI_AHBCR_PREF_EN | FSPI_AHBCR_RDADDROPT, base + FSPI_AHBCR); + /* Reset the flashx control1 registers */ + reg = FSPI_FLSHXCR1_TCSH(0x3) | FSPI_FLSHXCR1_TCSS(0x3); + fspi_writel(f, reg, base + FSPI_FLSHA1CR1); + fspi_writel(f, reg, base + FSPI_FLSHA2CR1); + fspi_writel(f, reg, base + FSPI_FLSHB1CR1); + fspi_writel(f, reg, base + FSPI_FLSHB2CR1); + /* AHB Read - Set lut sequence ID for all CS. */ fspi_writel(f, SEQID_LUT, base + FSPI_FLSHA1CR2); fspi_writel(f, SEQID_LUT, base + FSPI_FLSHA2CR2); diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c index ff7b55f8707..5cce6baa621 100644 --- a/drivers/spi/omap3_spi.c +++ b/drivers/spi/omap3_spi.c @@ -6,7 +6,7 @@ * Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com> * * Driver for McSPI controller on OMAP3. Based on davinci_spi.c - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * * Copyright (C) 2007 Atmel Corporation * diff --git a/drivers/spi/spi-mem-nodm.c b/drivers/spi/spi-mem-nodm.c index 77ddb19a9f3..6d9ab61769a 100644 --- a/drivers/spi/spi-mem-nodm.c +++ b/drivers/spi/spi-mem-nodm.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ */ +#include <errno.h> #include <log.h> #include <malloc.h> #include <spi.h> diff --git a/drivers/spi/zynqmp_gqspi.c b/drivers/spi/zynqmp_gqspi.c index ec59ef58044..a323994fb2d 100644 --- a/drivers/spi/zynqmp_gqspi.c +++ b/drivers/spi/zynqmp_gqspi.c @@ -5,6 +5,8 @@ * Xilinx ZynqMP Generic Quad-SPI(QSPI) controller driver(master mode only) */ +#define LOG_CATEGORY UCLASS_SPI + #include <common.h> #include <cpu_func.h> #include <log.h> @@ -192,8 +194,6 @@ static int zynqmp_qspi_of_to_plat(struct udevice *bus) { struct zynqmp_qspi_plat *plat = dev_get_plat(bus); - debug("%s\n", __func__); - plat->regs = (struct zynqmp_qspi_regs *)(dev_read_addr(bus) + GQSPI_REG_OFFSET); plat->dma_regs = (struct zynqmp_qspi_dma_regs *) @@ -250,7 +250,7 @@ static u32 zynqmp_qspi_genfifo_mode(u8 buswidth) case 4: return GQSPI_SPI_MODE_QSPI; default: - debug("Unsupported bus width %u\n", buswidth); + log_warning("Unsupported bus width %u\n", buswidth); return GQSPI_SPI_MODE_SPI; } } @@ -262,6 +262,8 @@ static void zynqmp_qspi_fill_gen_fifo(struct zynqmp_qspi_priv *priv, u32 config_reg, ier; int ret = 0; + log_content("%s, GFIFO_CMD: 0x%X\n", __func__, gqspi_fifo_reg); + writel(gqspi_fifo_reg, ®s->genfifo); config_reg = readl(®s->confr); @@ -278,7 +280,7 @@ static void zynqmp_qspi_fill_gen_fifo(struct zynqmp_qspi_priv *priv, ret = wait_for_bit_le32(®s->isr, GQSPI_IXR_GFEMTY_MASK, 1, GQSPI_TIMEOUT, 1); if (ret) - printf("%s Timeout\n", __func__); + log_warning("%s, Timeout\n", __func__); } @@ -286,6 +288,8 @@ static void zynqmp_qspi_chipselect(struct zynqmp_qspi_priv *priv, int is_on) { u32 gqspi_fifo_reg = 0; + log_debug("%s, assert: %d\r\n", __func__, is_on); + if (is_on) { gqspi_fifo_reg = zynqmp_qspi_bus_select(priv); gqspi_fifo_reg |= GQSPI_SPI_MODE_SPI | @@ -295,8 +299,6 @@ static void zynqmp_qspi_chipselect(struct zynqmp_qspi_priv *priv, int is_on) gqspi_fifo_reg |= GQSPI_IMD_DATA_CS_DEASSERT; } - debug("GFIFO_CMD_CS: 0x%x\n", gqspi_fifo_reg); - zynqmp_qspi_fill_gen_fifo(priv, gqspi_fifo_reg); } @@ -311,8 +313,8 @@ static void zynqmp_qspi_set_tapdelay(struct udevice *bus, u32 baudrateval) clk_rate = plat->frequency; reqhz = (clk_rate / (GQSPI_BAUD_DIV_SHIFT << baudrateval)); - debug("%s, req_hz:%d, clk_rate:%d, baudrateval:%d\n", - __func__, reqhz, clk_rate, baudrateval); + log_debug("%s, clk_rate:%d, baudrateval:%d, bus_clk: %d\n", + __func__, clk_rate, baudrateval, reqhz); if (!(IS_ENABLED(CONFIG_ARCH_VERSAL) || IS_ENABLED(CONFIG_ARCH_VERSAL_NET))) { @@ -362,7 +364,8 @@ static int zynqmp_qspi_set_speed(struct udevice *bus, uint speed) u32 confr; u8 baud_rate_val = 0; - debug("%s\n", __func__); + log_debug("%s, Speed: %d, Max: %d\n", __func__, speed, plat->frequency); + if (speed > plat->frequency) speed = plat->frequency; @@ -383,9 +386,8 @@ static int zynqmp_qspi_set_speed(struct udevice *bus, uint speed) confr &= ~GQSPI_BAUD_DIV_MASK; confr |= (baud_rate_val << 3); writel(confr, ®s->confr); - zynqmp_qspi_set_tapdelay(bus, baud_rate_val); - debug("regs=%p, speed=%d\n", priv->regs, plat->speed_hz); + zynqmp_qspi_set_tapdelay(bus, baud_rate_val); } return 0; @@ -399,8 +401,6 @@ static int zynqmp_qspi_probe(struct udevice *bus) unsigned long clock; int ret; - debug("%s: bus:%p, priv:%p\n", __func__, bus, priv); - priv->regs = plat->regs; priv->dma_regs = plat->dma_regs; priv->io_mode = plat->io_mode; @@ -416,7 +416,6 @@ static int zynqmp_qspi_probe(struct udevice *bus) dev_err(bus, "failed to get rate\n"); return clock; } - debug("%s: CLK %ld\n", __func__, clock); ret = clk_enable(&clk); if (ret) { @@ -429,6 +428,8 @@ static int zynqmp_qspi_probe(struct udevice *bus) /* init the zynq spi hw */ zynqmp_qspi_init_hw(priv); + log_debug("%s, Rerence clock frequency: %ld\n", __func__, clock); + return 0; } @@ -438,7 +439,8 @@ static int zynqmp_qspi_set_mode(struct udevice *bus, uint mode) struct zynqmp_qspi_regs *regs = priv->regs; u32 confr; - debug("%s\n", __func__); + log_debug("%s, 0x%X\n", __func__, mode); + /* Set the SPI Clock phase and polarities */ confr = readl(®s->confr); confr &= ~(GQSPI_CONFIG_CPHA_MASK | GQSPI_CONFIG_CPOL_MASK); @@ -461,16 +463,11 @@ static int zynqmp_qspi_fill_tx_fifo(struct zynqmp_qspi_priv *priv, u32 size) u32 *buf = (u32 *)priv->tx_buf; u32 len = size; - debug("TxFIFO: 0x%x, size: 0x%x\n", readl(®s->isr), - size); - while (size) { ret = wait_for_bit_le32(®s->isr, GQSPI_IXR_TXNFULL_MASK, 1, GQSPI_TIMEOUT, 1); - if (ret) { - printf("%s: Timeout\n", __func__); - return ret; - } + if (ret) + return log_msg_ret("Timeout\n", ret); if (size >= 4) { writel(*buf, ®s->txd0r); @@ -501,10 +498,8 @@ static int zynqmp_qspi_fill_tx_fifo(struct zynqmp_qspi_priv *priv, u32 size) ret = wait_for_bit_le32(®s->isr, GQSPI_IXR_TXFIFOEMPTY_MASK, 1, GQSPI_TIMEOUT, 1); - if (ret) { - printf("%s: Timeout\n", __func__); - return ret; - } + if (ret) + return log_msg_ret("Timeout\n", ret); priv->tx_buf += len; return 0; @@ -516,6 +511,9 @@ static void zynqmp_qspi_genfifo_cmd(struct zynqmp_qspi_priv *priv) u32 gen_fifo_cmd; u8 i, dummy_cycles, addr; + log_debug("%s, opcode: 0x%0X, addr.nbytes: %d, dummy.mbytes: %d\r\n", + __func__, op->cmd.opcode, op->addr.nbytes, op->dummy.nbytes); + /* Send opcode */ gen_fifo_cmd = zynqmp_qspi_bus_select(priv); gen_fifo_cmd |= zynqmp_qspi_genfifo_mode(op->cmd.buswidth); @@ -532,8 +530,6 @@ static void zynqmp_qspi_genfifo_cmd(struct zynqmp_qspi_priv *priv) gen_fifo_cmd |= GQSPI_GFIFO_TX; gen_fifo_cmd |= addr; - debug("GFIFO_CMD_Cmd = 0x%x\n", gen_fifo_cmd); - zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); } @@ -583,6 +579,8 @@ static int zynqmp_qspi_genfifo_fill_tx(struct zynqmp_qspi_priv *priv) u32 len; int ret = 0; + log_debug("%s, length: %d\r\n", __func__, priv->len); + gen_fifo_cmd = zynqmp_qspi_bus_select(priv); gen_fifo_cmd |= zynqmp_qspi_genfifo_mode(priv->op->data.buswidth); gen_fifo_cmd |= GQSPI_GFIFO_TX | GQSPI_GFIFO_DATA_XFR_MASK; @@ -591,8 +589,6 @@ static int zynqmp_qspi_genfifo_fill_tx(struct zynqmp_qspi_priv *priv) len = zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd); zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); - debug("GFIFO_CMD_TX:0x%x\n", gen_fifo_cmd); - if (gen_fifo_cmd & GQSPI_GFIFO_EXP_MASK) ret = zynqmp_qspi_fill_tx_fifo(priv, 1 << len); else @@ -608,7 +604,6 @@ static int zynqmp_qspi_start_io(struct zynqmp_qspi_priv *priv, u32 gen_fifo_cmd, u32 *buf) { u32 len; - u32 actuallen = priv->len; u32 config_reg, ier, isr; u32 timeout = GQSPI_TIMEOUT; struct zynqmp_qspi_regs *regs = priv->regs; @@ -623,7 +618,7 @@ static int zynqmp_qspi_start_io(struct zynqmp_qspi_priv *priv, else priv->bytes_to_receive = len; zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); - debug("GFIFO_CMD_RX:0x%x\n", gen_fifo_cmd); + /* Manual start */ config_reg = readl(®s->confr); config_reg |= GQSPI_STRT_GEN_FIFO; @@ -652,13 +647,8 @@ static int zynqmp_qspi_start_io(struct zynqmp_qspi_priv *priv, } } - debug("buf:0x%lx, rxbuf:0x%lx, *buf:0x%x len: 0x%x\n", - (unsigned long)buf, (unsigned long)priv->rx_buf, - *buf, actuallen); - if (!timeout) { - printf("IO timeout: %d\n", readl(®s->isr)); - return -1; - } + if (!timeout) + return log_msg_retz("Timeout\n", timeout); } return 0; @@ -695,26 +685,18 @@ static int zynqmp_qspi_start_dma(struct zynqmp_qspi_priv *priv, while (priv->len) { zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd); zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); - - debug("GFIFO_CMD_RX:0x%x\n", gen_fifo_cmd); } ret = wait_for_bit_le32(&dma_regs->dmaisr, GQSPI_DMA_DST_I_STS_DONE, 1, GQSPI_TIMEOUT, 1); - if (ret) { - printf("DMA Timeout:0x%x\n", readl(&dma_regs->dmaisr)); - return -ETIMEDOUT; - } + if (ret) + return log_msg_ret("Timeout:\n", ret); invalidate_dcache_range(addr, addr + size); writel(GQSPI_DMA_DST_I_STS_DONE, &dma_regs->dmaisr); - debug("buf:0x%lx, rxbuf:0x%lx, *buf:0x%x len: 0x%x\n", - (unsigned long)buf, (unsigned long)priv->rx_buf, *buf, - actuallen); - if (buf != priv->rx_buf) memcpy(priv->rx_buf, buf, actuallen); @@ -731,6 +713,8 @@ static int zynqmp_qspi_genfifo_fill_rx(struct zynqmp_qspi_priv *priv) u32 *buf; u32 actuallen = priv->len; + log_debug("%s, length: %d\r\n", __func__, priv->len); + gen_fifo_cmd = zynqmp_qspi_bus_select(priv); gen_fifo_cmd |= zynqmp_qspi_genfifo_mode(priv->op->data.buswidth); gen_fifo_cmd |= GQSPI_GFIFO_RX | GQSPI_GFIFO_DATA_XFR_MASK; diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig index e35f7cb1791..2030e4babc9 100644 --- a/drivers/sysinfo/Kconfig +++ b/drivers/sysinfo/Kconfig @@ -8,6 +8,13 @@ menuconfig SYSINFO if SYSINFO +config SYSINFO_EXTRA + bool "Show extra information on startup" + help + Enable this to see extra information on startup. Normally only the + model is shown, but with this option the vendor and any prior-stage + firmware's version and date are shown as well. + config SPL_SYSINFO depends on SPL_DM bool "Enable board driver support in SPL" diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig index bdbe2a95364..0e52f996283 100644 --- a/drivers/sysreset/Kconfig +++ b/drivers/sysreset/Kconfig @@ -74,6 +74,13 @@ config SYSRESET_GPIO example on Microblaze where reset logic can be controlled via GPIO pin which triggers cpu reset. +config SYSRESET_MAX77663 + bool "Enable support for MAX77663 PMIC System Reset" + depends on DM_PMIC_MAX77663 + select SYSRESET_CMD_POWEROFF if CMD_POWEROFF + help + Enable system power management functions found in MAX77663 PMIC. + config SYSRESET_MICROBLAZE bool "Enable support for Microblaze soft reset" depends on MICROBLAZE @@ -102,6 +109,13 @@ config SYSRESET_SPL_AT91 This enables the system reset driver support for Microchip/Atmel SoCs in SPL. +config SYSRESET_PALMAS + bool "Enable support for PALMAS System Reset" + depends on PMIC_PALMAS + select SYSRESET_CMD_POWEROFF if CMD_POWEROFF + help + Enable system power management functions found in PLAMAS PMIC family. + config SYSRESET_PSCI bool "Enable support for PSCI System Reset" depends on ARM_PSCI_FW @@ -137,6 +151,12 @@ config SYSRESET_SOCFPGA_SOC64 This enables the system reset driver support for Intel SOCFPGA SoC64 SoCs. +config SYSRESET_TEGRA + bool "Tegra PMC system reset driver" + depends on ARCH_TEGRA + help + This enables the system reset ability of PMC used in Tegra SoCs. + config SYSRESET_TI_SCI bool "TI System Control Interface (TI SCI) system reset driver" depends on TI_SCI_PROTOCOL @@ -144,6 +164,22 @@ config SYSRESET_TI_SCI This enables the system reset driver support over TI System Control Interface available on some new TI's SoCs. +config SYSRESET_TPS65910 + bool "Enable support for TPS65910/TPS65911 PMIC System Reset" + depends on DM_PMIC_TPS65910 + select SYSRESET_CMD_POWEROFF if CMD_POWEROFF + help + Enable system power management functions found in TPS65910/TPS65911 + PMICs. + +config SYSRESET_TPS80031 + bool "Enable support for TPS80031/TPS80032 PMIC System Reset" + depends on DM_PMIC_TPS80031 + select SYSRESET_CMD_POWEROFF if CMD_POWEROFF + help + Enable system power management functions found in TPS80031/TPS80032 + PMICs. + config SYSRESET_SYSCON bool "Enable support for mfd syscon reboot driver" select REGMAP diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile index 40c876764af..c9f1c625aeb 100644 --- a/drivers/sysreset/Makefile +++ b/drivers/sysreset/Makefile @@ -9,14 +9,19 @@ obj-$(CONFIG_ARCH_STI) += sysreset_sti.o obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o obj-$(CONFIG_POWEROFF_GPIO) += poweroff_gpio.o obj-$(CONFIG_SYSRESET_GPIO) += sysreset_gpio.o +obj-$(CONFIG_$(SPL_TPL_)SYSRESET_MAX77663) += sysreset_max77663.o obj-$(CONFIG_SYSRESET_MPC83XX) += sysreset_mpc83xx.o obj-$(CONFIG_SYSRESET_MICROBLAZE) += sysreset_microblaze.o obj-$(CONFIG_SYSRESET_OCTEON) += sysreset_octeon.o +obj-$(CONFIG_$(SPL_TPL_)SYSRESET_PALMAS) += sysreset_palmas.o obj-$(CONFIG_SYSRESET_PSCI) += sysreset_psci.o obj-$(CONFIG_SYSRESET_SBI) += sysreset_sbi.o obj-$(CONFIG_SYSRESET_SOCFPGA) += sysreset_socfpga.o obj-$(CONFIG_SYSRESET_SOCFPGA_SOC64) += sysreset_socfpga_soc64.o +obj-$(CONFIG_SYSRESET_TEGRA) += sysreset_tegra.o obj-$(CONFIG_SYSRESET_TI_SCI) += sysreset-ti-sci.o +obj-$(CONFIG_$(SPL_TPL_)SYSRESET_TPS65910) += sysreset_tps65910.o +obj-$(CONFIG_$(SPL_TPL_)SYSRESET_TPS80031) += sysreset_tps80031.o obj-$(CONFIG_SYSRESET_SYSCON) += sysreset_syscon.o obj-$(CONFIG_SYSRESET_WATCHDOG) += sysreset_watchdog.o obj-$(CONFIG_SYSRESET_RESETCTL) += sysreset_resetctl.o diff --git a/drivers/sysreset/poweroff_gpio.c b/drivers/sysreset/poweroff_gpio.c index a5c24fd85bc..ad04e4b1a85 100644 --- a/drivers/sysreset/poweroff_gpio.c +++ b/drivers/sysreset/poweroff_gpio.c @@ -33,7 +33,7 @@ static int poweroff_gpio_request(struct udevice *dev, enum sysreset_t type) int r; if (type != SYSRESET_POWER_OFF) - return -ENOSYS; + return -EPROTONOSUPPORT; debug("GPIO poweroff\n"); diff --git a/drivers/sysreset/sysreset-ti-sci.c b/drivers/sysreset/sysreset-ti-sci.c index 81bfd67ad99..5fc05c46cb0 100644 --- a/drivers/sysreset/sysreset-ti-sci.c +++ b/drivers/sysreset/sysreset-ti-sci.c @@ -2,7 +2,7 @@ /* * Texas Instruments System Control Interface (TI SCI) system reset driver * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Andreas Dannenberg <dannenberg@ti.com> */ diff --git a/drivers/sysreset/sysreset_max77663.c b/drivers/sysreset/sysreset_max77663.c new file mode 100644 index 00000000000..8febcf8de6c --- /dev/null +++ b/drivers/sysreset/sysreset_max77663.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <i2c.h> +#include <errno.h> +#include <sysreset.h> +#include <power/pmic.h> +#include <power/max77663.h> + +static int max77663_sysreset_request(struct udevice *dev, + enum sysreset_t type) +{ + int val; + + val = pmic_reg_read(dev->parent, MAX77663_REG_ONOFF_CFG1); + if (val < 0) + return val; + + /* clear both bits */ + val &= ~ONOFF_SFT_RST; + val &= ~ONOFF_PWR_OFF; + + switch (type) { + case SYSRESET_POWER: + /* MAX77663: SFT_RST > ONOFF_CFG1 */ + pmic_reg_write(dev->parent, MAX77663_REG_ONOFF_CFG1, + val | ONOFF_SFT_RST); + break; + case SYSRESET_POWER_OFF: + /* MAX77663: PWR_OFF > ONOFF_CFG1 */ + pmic_reg_write(dev->parent, MAX77663_REG_ONOFF_CFG1, + val | ONOFF_PWR_OFF); + break; + default: + return -EPROTONOSUPPORT; + } + + return -EINPROGRESS; +} + +static struct sysreset_ops max77663_sysreset = { + .request = max77663_sysreset_request, +}; + +U_BOOT_DRIVER(sysreset_max77663) = { + .id = UCLASS_SYSRESET, + .name = MAX77663_RST_DRIVER, + .ops = &max77663_sysreset, +}; diff --git a/drivers/sysreset/sysreset_palmas.c b/drivers/sysreset/sysreset_palmas.c new file mode 100644 index 00000000000..9e3aa403488 --- /dev/null +++ b/drivers/sysreset/sysreset_palmas.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <i2c.h> +#include <errno.h> +#include <sysreset.h> +#include <power/pmic.h> +#include <power/palmas.h> + +static int palmas_sysreset_request(struct udevice *dev, + enum sysreset_t type) +{ + struct palmas_priv *priv = dev_get_priv(dev->parent); + int ret; + + /* + * Mask INT3 on second page which detects vbus + * or device will immediately turn on. + */ + ret = dm_i2c_reg_clrset(priv->chip2, PALMAS_INT3_MASK, + MASK_VBUS, MASK_VBUS); + if (ret < 0) + return ret; + + switch (type) { + case SYSRESET_POWER: + /* PALMAS: SW_RST > DEV_CTRL */ + pmic_reg_write(dev->parent, PALMAS_DEV_CTRL, SW_RST); + break; + case SYSRESET_POWER_OFF: + /* PALMAS: DEV_OFF > DEV_CTRL */ + pmic_reg_write(dev->parent, PALMAS_DEV_CTRL, DEV_OFF); + break; + default: + return -EPROTONOSUPPORT; + } + + return -EINPROGRESS; +} + +static struct sysreset_ops palmas_sysreset = { + .request = palmas_sysreset_request, +}; + +U_BOOT_DRIVER(sysreset_palmas) = { + .id = UCLASS_SYSRESET, + .name = PALMAS_RST_DRIVER, + .ops = &palmas_sysreset, +}; diff --git a/drivers/sysreset/sysreset_psci.c b/drivers/sysreset/sysreset_psci.c index a8a41528a84..aa09d0b8827 100644 --- a/drivers/sysreset/sysreset_psci.c +++ b/drivers/sysreset/sysreset_psci.c @@ -25,7 +25,7 @@ static int psci_sysreset_request(struct udevice *dev, enum sysreset_t type) psci_sys_poweroff(); break; default: - return -ENOSYS; + return -EPROTONOSUPPORT; } return -EINPROGRESS; diff --git a/drivers/sysreset/sysreset_sandbox.c b/drivers/sysreset/sysreset_sandbox.c index 3750c60b9b9..c12eda81d03 100644 --- a/drivers/sysreset/sysreset_sandbox.c +++ b/drivers/sysreset/sysreset_sandbox.c @@ -21,7 +21,7 @@ static int sandbox_warm_sysreset_request(struct udevice *dev, state->last_sysreset = type; break; default: - return -ENOSYS; + return -EPROTONOSUPPORT; } if (!state->sysreset_allowed[type]) return -EACCES; @@ -70,7 +70,7 @@ static int sandbox_sysreset_request(struct udevice *dev, enum sysreset_t type) return -EACCES; sandbox_exit(); default: - return -ENOSYS; + return -EPROTONOSUPPORT; } if (!state->sysreset_allowed[type]) return -EACCES; @@ -132,7 +132,7 @@ U_BOOT_DRIVER(warm_sysreset_sandbox) = { .ops = &sandbox_warm_sysreset_ops, }; -#if CONFIG_IS_ENABLED(OF_REAL) +#if CONFIG_IS_ENABLED(OF_REAL) && !CONFIG_IS_ENABLED(OF_PLATDATA) /* This is here in case we don't have a device tree */ U_BOOT_DRVINFO(sysreset_sandbox_non_fdt) = { .name = "sysreset_sandbox", diff --git a/drivers/sysreset/sysreset_tegra.c b/drivers/sysreset/sysreset_tegra.c new file mode 100644 index 00000000000..10bcd3a1873 --- /dev/null +++ b/drivers/sysreset/sysreset_tegra.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <errno.h> +#include <sysreset.h> +#include <linux/err.h> +#include <asm/arch-tegra/pmc.h> + +static int tegra_sysreset_request(struct udevice *dev, + enum sysreset_t type) +{ + u32 value; + + switch (type) { + case SYSRESET_WARM: + case SYSRESET_COLD: + /* resets everything but scratch 0 and reset status */ + value = tegra_pmc_readl(PMC_CNTRL); + value |= PMC_CNTRL_MAIN_RST; + tegra_pmc_writel(value, PMC_CNTRL); + break; + default: + return -EPROTONOSUPPORT; + } + + return -EINPROGRESS; +} + +static struct sysreset_ops tegra_sysreset = { + .request = tegra_sysreset_request, +}; + +U_BOOT_DRIVER(sysreset_tegra) = { + .id = UCLASS_SYSRESET, + .name = "sysreset_tegra", + .ops = &tegra_sysreset, +}; + +/* Link to Tegra PMC once there is a driver */ +U_BOOT_DRVINFO(sysreset_tegra) = { + .name = "sysreset_tegra" +}; diff --git a/drivers/sysreset/sysreset_tps65910.c b/drivers/sysreset/sysreset_tps65910.c new file mode 100644 index 00000000000..98da56661c0 --- /dev/null +++ b/drivers/sysreset/sysreset_tps65910.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <i2c.h> +#include <errno.h> +#include <sysreset.h> +#include <power/pmic.h> +#include <power/tps65910_pmic.h> + +static int tps65910_sysreset_request(struct udevice *dev, + enum sysreset_t type) +{ + int val; + + val = pmic_reg_read(dev->parent, TPS65910_REG_DEVICE_CTRL); + if (val < 0) + return val; + + /* define power-off to be sequential */ + val |= PWR_OFF_SEQ; + pmic_reg_write(dev->parent, TPS65910_REG_DEVICE_CTRL, val); + + val &= ~DEV_ON; + + switch (type) { + case SYSRESET_POWER: + /* TPS65910: DEV_OFF_RST > DEVICE_CTRL */ + pmic_reg_write(dev->parent, TPS65910_REG_DEVICE_CTRL, + val | DEV_OFF_RST); + break; + case SYSRESET_POWER_OFF: + /* TPS65910: DEV_OFF > DEVICE_CTRL */ + pmic_reg_write(dev->parent, TPS65910_REG_DEVICE_CTRL, + val | DEV_OFF); + break; + default: + return -EPROTONOSUPPORT; + } + + return -EINPROGRESS; +} + +static struct sysreset_ops tps65910_sysreset = { + .request = tps65910_sysreset_request, +}; + +U_BOOT_DRIVER(sysreset_tps65910) = { + .id = UCLASS_SYSRESET, + .name = TPS65910_RST_DRIVER, + .ops = &tps65910_sysreset, +}; diff --git a/drivers/sysreset/sysreset_tps80031.c b/drivers/sysreset/sysreset_tps80031.c new file mode 100644 index 00000000000..50024fe4e79 --- /dev/null +++ b/drivers/sysreset/sysreset_tps80031.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <i2c.h> +#include <errno.h> +#include <sysreset.h> +#include <power/pmic.h> +#include <power/tps80031.h> + +static int tps80031_sysreset_request(struct udevice *dev, + enum sysreset_t type) +{ + switch (type) { + case SYSRESET_POWER: + /* TPS80031: SW_RESET > PHOENIX_DEV_ON */ + pmic_reg_write(dev->parent, TPS80031_PHOENIX_DEV_ON, SW_RESET); + break; + case SYSRESET_POWER_OFF: + /* TPS80031: DEVOFF > PHOENIX_DEV_ON */ + pmic_reg_write(dev->parent, TPS80031_PHOENIX_DEV_ON, DEVOFF); + break; + default: + return -EPROTONOSUPPORT; + } + + return -EINPROGRESS; +} + +static struct sysreset_ops tps80031_sysreset = { + .request = tps80031_sysreset_request, +}; + +U_BOOT_DRIVER(sysreset_tps80031) = { + .id = UCLASS_SYSRESET, + .name = TPS80031_RST_DRIVER, + .ops = &tps80031_sysreset, +}; diff --git a/drivers/sysreset/sysreset_watchdog.c b/drivers/sysreset/sysreset_watchdog.c index ceada2e47b5..6db5aa75b54 100644 --- a/drivers/sysreset/sysreset_watchdog.c +++ b/drivers/sysreset/sysreset_watchdog.c @@ -29,7 +29,7 @@ static int wdt_reboot_request(struct udevice *dev, enum sysreset_t type) return ret; break; default: - return -ENOSYS; + return -EPROTONOSUPPORT; } return -EINPROGRESS; diff --git a/drivers/sysreset/sysreset_x86.c b/drivers/sysreset/sysreset_x86.c index 4936fdb76c7..dc772b5ff9e 100644 --- a/drivers/sysreset/sysreset_x86.c +++ b/drivers/sysreset/sysreset_x86.c @@ -87,7 +87,7 @@ static int x86_sysreset_request(struct udevice *dev, enum sysreset_t type) return ret; return -EINPROGRESS; default: - return -ENOSYS; + return -EPROTONOSUPPORT; } outb(value, IO_PORT_RESET); diff --git a/drivers/thermal/ti-bandgap.c b/drivers/thermal/ti-bandgap.c index 0b533d4c420..0ea17a909dd 100644 --- a/drivers/thermal/ti-bandgap.c +++ b/drivers/thermal/ti-bandgap.c @@ -1,7 +1,7 @@ /* * TI Bandgap temperature sensor driver * - * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/drivers/timer/timer-uclass.c b/drivers/timer/timer-uclass.c index 0c2018bfe3b..60ff65529ab 100644 --- a/drivers/timer/timer-uclass.c +++ b/drivers/timer/timer-uclass.c @@ -66,13 +66,13 @@ static int timer_pre_probe(struct udevice *dev) err = clk_get_by_index(dev, 0, &timer_clk); if (!err) { ret = clk_get_rate(&timer_clk); - if (IS_ERR_VALUE(ret)) - return ret; - uc_priv->clock_rate = ret; - } else { - uc_priv->clock_rate = - dev_read_u32_default(dev, "clock-frequency", 0); + if (!IS_ERR_VALUE(ret)) { + uc_priv->clock_rate = ret; + return 0; + } } + + uc_priv->clock_rate = dev_read_u32_default(dev, "clock-frequency", 0); } return 0; diff --git a/drivers/tpm/tpm2_tis_core.c b/drivers/tpm/tpm2_tis_core.c index 985a8162198..81b9210056d 100644 --- a/drivers/tpm/tpm2_tis_core.c +++ b/drivers/tpm/tpm2_tis_core.c @@ -224,9 +224,6 @@ int tpm_tis_send(struct udevice *dev, const u8 *buf, size_t len) u8 status; int ret; - if (!chip) - return -ENODEV; - ret = tpm_tis_request_locality(dev, 0); if (ret < 0) return -EBUSY; diff --git a/drivers/tpm/tpm2_tis_sandbox.c b/drivers/tpm/tpm2_tis_sandbox.c index e4004cfcca3..d15a28d9fc8 100644 --- a/drivers/tpm/tpm2_tis_sandbox.c +++ b/drivers/tpm/tpm2_tis_sandbox.c @@ -22,11 +22,6 @@ enum tpm2_hierarchy { TPM2_HIERARCHY_NB, }; -/* Subset of supported capabilities */ -enum tpm2_capability { - TPM_CAP_TPM_PROPERTIES = 0x6, -}; - /* Subset of supported properties */ #define TPM2_PROPERTIES_OFFSET 0x0000020E @@ -38,7 +33,8 @@ enum tpm2_cap_tpm_property { TPM2_PROPERTY_NB, }; -#define SANDBOX_TPM_PCR_NB 1 +#define SANDBOX_TPM_PCR_NB TPM2_MAX_PCRS +#define SANDBOX_TPM_PCR_SELECT_MAX ((SANDBOX_TPM_PCR_NB + 7) / 8) /* * Information about our TPM emulation. This is preserved in the sandbox @@ -433,7 +429,7 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf, int i, j; /* TPM2_GetProperty */ - u32 capability, property, property_count; + u32 capability, property, property_count, val; /* TPM2_PCR_Read/Extend variables */ int pcr_index = 0; @@ -542,19 +538,32 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf, case TPM2_CC_GET_CAPABILITY: capability = get_unaligned_be32(sent); sent += sizeof(capability); - if (capability != TPM_CAP_TPM_PROPERTIES) { - printf("Sandbox TPM only support TPM_CAPABILITIES\n"); - return TPM2_RC_HANDLE; - } - property = get_unaligned_be32(sent); sent += sizeof(property); - property -= TPM2_PROPERTIES_OFFSET; - property_count = get_unaligned_be32(sent); sent += sizeof(property_count); - if (!property_count || - property + property_count > TPM2_PROPERTY_NB) { + + switch (capability) { + case TPM2_CAP_PCRS: + break; + case TPM2_CAP_TPM_PROPERTIES: + if (!property_count) { + rc = TPM2_RC_HANDLE; + return sandbox_tpm2_fill_buf(recv, recv_len, + tag, rc); + } + + if (property >= TPM2_PROPERTIES_OFFSET && + ((property - TPM2_PROPERTIES_OFFSET) + + property_count > TPM2_PROPERTY_NB)) { + rc = TPM2_RC_HANDLE; + return sandbox_tpm2_fill_buf(recv, recv_len, + tag, rc); + } + break; + default: + printf("Sandbox TPM2 only supports TPM2_CAP_PCRS or " + "TPM2_CAP_TPM_PROPERTIES\n"); rc = TPM2_RC_HANDLE; return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc); } @@ -578,18 +587,53 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf, put_unaligned_be32(capability, recv); recv += sizeof(capability); - /* Give the number of properties that follow */ - put_unaligned_be32(property_count, recv); - recv += sizeof(property_count); - - /* Fill with the properties */ - for (i = 0; i < property_count; i++) { - put_unaligned_be32(TPM2_PROPERTIES_OFFSET + property + - i, recv); - recv += sizeof(property); - put_unaligned_be32(tpm->properties[property + i], - recv); - recv += sizeof(property); + switch (capability) { + case TPM2_CAP_PCRS: + /* Give the number of algorithms supported - just SHA256 */ + put_unaligned_be32(1, recv); + recv += sizeof(u32); + + /* Give SHA256 algorithm */ + put_unaligned_be16(TPM2_ALG_SHA256, recv); + recv += sizeof(u16); + + /* Select the PCRs supported */ + *recv = SANDBOX_TPM_PCR_SELECT_MAX; + recv++; + + /* Activate all the PCR bits */ + for (i = 0; i < SANDBOX_TPM_PCR_SELECT_MAX; ++i) { + *recv = 0xff; + recv++; + } + break; + case TPM2_CAP_TPM_PROPERTIES: + /* Give the number of properties that follow */ + put_unaligned_be32(property_count, recv); + recv += sizeof(property_count); + + /* Fill with the properties */ + for (i = 0; i < property_count; i++) { + put_unaligned_be32(property + i, recv); + recv += sizeof(property); + if (property >= TPM2_PROPERTIES_OFFSET) { + val = tpm->properties[(property - + TPM2_PROPERTIES_OFFSET) + i]; + } else { + switch (property) { + case TPM2_PT_PCR_COUNT: + val = SANDBOX_TPM_PCR_NB; + break; + default: + val = 0xffffffff; + break; + } + } + + put_unaligned_be32(val, recv); + recv += sizeof(property); + } + break; } /* Add trailing \0 */ diff --git a/drivers/ufs/Kconfig b/drivers/ufs/Kconfig index 0e0cc58e3d6..7da46faed6b 100644 --- a/drivers/ufs/Kconfig +++ b/drivers/ufs/Kconfig @@ -2,7 +2,7 @@ menu "UFS Host Controller Support" config UFS bool "Support UFS controllers" - depends on DM_SCSI + depends on SCSI select CHARSET help This selects support for Universal Flash Subsystem (UFS). @@ -15,6 +15,17 @@ config CADENCE_UFS This selects the platform driver for the Cadence UFS host controller present on present TI's J721e devices. +config UFS_PCI + bool "PCI bus based UFS Controller support" + depends on PCI && UFS + help + This selects the PCI UFS Host Controller Interface. Select this if + you have UFS Host Controller with PCI Interface. + + If you have a controller with this interface, say Y here. + + If unsure, say N. + config TI_J721E_UFS bool "Glue Layer driver for UFS on TI J721E devices" help diff --git a/drivers/ufs/Makefile b/drivers/ufs/Makefile index 4f3344fd4e4..67c42621aba 100644 --- a/drivers/ufs/Makefile +++ b/drivers/ufs/Makefile @@ -1,9 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 # -# Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com +# Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com # obj-$(CONFIG_UFS) += ufs.o ufs-uclass.o obj-$(CONFIG_CADENCE_UFS) += cdns-platform.o obj-$(CONFIG_TI_J721E_UFS) += ti-j721e-ufs.o +obj-$(CONFIG_UFS_PCI) += ufs-pci.o obj-$(CONFIG_UFS_RENESAS) += ufs-renesas.o diff --git a/drivers/ufs/cdns-platform.c b/drivers/ufs/cdns-platform.c index 1e62e252e7a..d1f346937c5 100644 --- a/drivers/ufs/cdns-platform.c +++ b/drivers/ufs/cdns-platform.c @@ -2,7 +2,7 @@ /** * cdns-platform.c - Platform driver for Cadence UFSHCI device * - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com */ #include <clk.h> @@ -13,11 +13,10 @@ #include <dm/device_compat.h> #include <linux/bitops.h> #include <linux/err.h> +#include <linux/time.h> #include "ufs.h" -#define USEC_PER_SEC 1000000L - #define CDNS_UFS_REG_HCLKDIV 0xFC #define CDNS_UFS_REG_PHY_XCFGD1 0x113C diff --git a/drivers/ufs/ti-j721e-ufs.c b/drivers/ufs/ti-j721e-ufs.c index d875269760c..1860e0dca29 100644 --- a/drivers/ufs/ti-j721e-ufs.c +++ b/drivers/ufs/ti-j721e-ufs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ */ #include <asm/io.h> diff --git a/drivers/ufs/ufs-pci.c b/drivers/ufs/ufs-pci.c new file mode 100644 index 00000000000..ad41358727a --- /dev/null +++ b/drivers/ufs/ufs-pci.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 tinylab.org + * Author: Bin Meng <bmeng@tinylab.org> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <pci.h> +#include <ufs.h> +#include <dm/device_compat.h> +#include "ufs.h" + +static int ufs_pci_bind(struct udevice *dev) +{ + struct udevice *scsi_dev; + + return ufs_scsi_bind(dev, &scsi_dev); +} + +static int ufs_pci_probe(struct udevice *dev) +{ + int err; + + err = ufshcd_probe(dev, NULL); + if (err) + dev_err(dev, "%s failed (ret=%d)\n", __func__, err); + + return err; +} + +U_BOOT_DRIVER(ufs_pci) = { + .name = "ufs_pci", + .id = UCLASS_UFS, + .bind = ufs_pci_bind, + .probe = ufs_pci_probe, +}; + +static struct pci_device_id ufs_supported[] = { + { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_UFS) }, + {}, +}; + +U_BOOT_PCI_DEVICE(ufs_pci, ufs_supported); diff --git a/drivers/ufs/ufs-uclass.c b/drivers/ufs/ufs-uclass.c index ceea30c4a95..92fcdf4e6cb 100644 --- a/drivers/ufs/ufs-uclass.c +++ b/drivers/ufs/ufs-uclass.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /** - * ufs-uclass.c - Universal Flash Subsystem (UFS) Uclass driver + * ufs-uclass.c - Universal Flash Storage (UFS) Uclass driver * - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com */ #define LOG_CATEGORY UCLASS_UFS diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index 7c48d57f99d..e4400f319a7 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0+ /** - * ufs.c - Universal Flash Subsystem (UFS) driver + * ufs.c - Universal Flash Storage (UFS) driver * * Taken from Linux Kernel v5.2 (drivers/scsi/ufs/ufshcd.c) and ported * to u-boot. * - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com */ #include <bouncebuf.h> @@ -320,7 +320,7 @@ static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer) UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)), 0); if (err) { - dev_err(hba->dev, "%s: TX LCC Disable failed, peer = %d, lane = %d, err = %d", + dev_err(hba->dev, "%s: TX LCC Disable failed, peer = %d, lane = %d, err = %d\n", __func__, peer, i, err); break; } @@ -441,7 +441,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) ufshcd_enable_run_stop_reg(hba); } else { dev_err(hba->dev, - "Host controller not ready to process requests"); + "Host controller not ready to process requests\n"); err = -EIO; goto out; } @@ -930,7 +930,7 @@ static int ufshcd_copy_query_response(struct ufs_hba *hba) memcpy(hba->dev_cmd.query.descriptor, descp, resp_len); } else { dev_warn(hba->dev, - "%s: Response size is bigger than buffer", + "%s: Response size is bigger than buffer\n", __func__); return -EINVAL; } @@ -1179,11 +1179,11 @@ static int ufshcd_read_desc_length(struct ufs_hba *hba, enum desc_idn desc_id, &header_len); if (ret) { - dev_err(hba->dev, "%s: Failed to get descriptor header id %d", + dev_err(hba->dev, "%s: Failed to get descriptor header id %d\n", __func__, desc_id); return ret; } else if (desc_id != header[QUERY_DESC_DESC_TYPE_OFFSET]) { - dev_warn(hba->dev, "%s: descriptor header id %d and desc_id %d mismatch", + dev_warn(hba->dev, "%s: descriptor header id %d and desc_id %d mismatch\n", __func__, header[QUERY_DESC_DESC_TYPE_OFFSET], desc_id); ret = -EINVAL; @@ -1302,7 +1302,7 @@ int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, /* Sanity checks */ if (ret || !buff_len) { - dev_err(hba->dev, "%s: Failed to get full descriptor length", + dev_err(hba->dev, "%s: Failed to get full descriptor length\n", __func__); return ret; } @@ -1323,14 +1323,14 @@ int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, &buff_len); if (ret) { - dev_err(hba->dev, "%s: Failed reading descriptor. desc_id %d, desc_index %d, param_offset %d, ret %d", + dev_err(hba->dev, "%s: Failed reading descriptor. desc_id %d, desc_index %d, param_offset %d, ret %d\n", __func__, desc_id, desc_index, param_offset, ret); goto out; } /* Sanity check */ if (desc_buf[QUERY_DESC_DESC_TYPE_OFFSET] != desc_id) { - dev_err(hba->dev, "%s: invalid desc_id %d in descriptor header", + dev_err(hba->dev, "%s: invalid desc_id %d in descriptor header\n", __func__, desc_buf[QUERY_DESC_DESC_TYPE_OFFSET]); ret = -EINVAL; goto out; @@ -1914,6 +1914,7 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops) struct ufs_hba *hba = dev_get_uclass_priv(ufs_dev); struct scsi_plat *scsi_plat; struct udevice *scsi_dev; + void __iomem *mmio_base; int err; device_find_first_child(ufs_dev, &scsi_dev); @@ -1927,7 +1928,14 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops) hba->dev = ufs_dev; hba->ops = hba_ops; - hba->mmio_base = dev_read_addr_ptr(ufs_dev); + + if (device_is_on_pci_bus(ufs_dev)) { + mmio_base = dm_pci_map_bar(ufs_dev, PCI_BASE_ADDRESS_0, 0, 0, + PCI_REGION_TYPE, PCI_REGION_MEM); + } else { + mmio_base = dev_read_addr_ptr(ufs_dev); + } + hba->mmio_base = mmio_base; /* Set descriptor lengths to specification defaults */ ufshcd_def_desc_sizes(hba); @@ -1945,7 +1953,8 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops) hba->version != UFSHCI_VERSION_11 && hba->version != UFSHCI_VERSION_20 && hba->version != UFSHCI_VERSION_21 && - hba->version != UFSHCI_VERSION_30) + hba->version != UFSHCI_VERSION_30 && + hba->version != UFSHCI_VERSION_31) dev_err(hba->dev, "invalid UFS version 0x%x\n", hba->version); diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h index 9daaf03d222..816a5ce0caf 100644 --- a/drivers/ufs/ufs.h +++ b/drivers/ufs/ufs.h @@ -782,6 +782,7 @@ enum { UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */ UFSHCI_VERSION_21 = 0x00000210, /* 2.1 */ UFSHCI_VERSION_30 = 0x00000300, /* 3.0 */ + UFSHCI_VERSION_31 = 0x00000310, /* 3.1 */ }; /* Interrupt disable masks */ diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c index 8958f0166bd..2e44aadea47 100644 --- a/drivers/usb/cdns3/cdns3-ti.c +++ b/drivers/usb/cdns3/cdns3-ti.c @@ -2,11 +2,10 @@ /** * cdns_ti-ti.c - TI specific Glue layer for Cadence USB Controller * - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com */ #include <common.h> -#include <asm-generic/io.h> #include <clk.h> #include <dm.h> #include <dm/device_compat.h> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 7ca9d09824e..4b4fcd8a22e 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -2,7 +2,7 @@ /** * core.c - DesignWare USB3 DRD Controller Core file * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> @@ -31,6 +31,7 @@ #include <linux/usb/gadget.h> #include <linux/bitfield.h> #include <linux/math64.h> +#include <linux/time.h> #include "core.h" #include "gadget.h" @@ -38,8 +39,6 @@ #include "linux-compat.h" -#define NSEC_PER_SEC 1000000000L - static LIST_HEAD(dwc3_list); /* -------------------------------------------------------------------------- */ diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 532746dd88d..4162a682298 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -2,7 +2,7 @@ /** * core.h - DesignWare USB3 DRD Core Header * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c index 744fde80694..6fb2de8a5ac 100644 --- a/drivers/usb/dwc3/dwc3-generic.c +++ b/drivers/usb/dwc3/dwc3-generic.c @@ -610,6 +610,7 @@ static const struct udevice_id dwc3_glue_ids[] = { { .compatible = "rockchip,rk3328-dwc3", .data = (ulong)&rk_ops }, { .compatible = "rockchip,rk3399-dwc3" }, { .compatible = "rockchip,rk3568-dwc3", .data = (ulong)&rk_ops }, + { .compatible = "rockchip,rk3588-dwc3", .data = (ulong)&rk_ops }, { .compatible = "qcom,dwc3" }, { .compatible = "fsl,imx8mp-dwc3", .data = (ulong)&imx8mp_ops }, { .compatible = "fsl,imx8mq-dwc3" }, diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index dc5a976f71a..196035215a6 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -8,13 +8,13 @@ #include <common.h> #include <log.h> -#include <asm-generic/io.h> #include <dm.h> #include <dm/device-internal.h> #include <dm/lists.h> #include <dwc3-uboot.h> #include <generic-phy.h> #include <linux/delay.h> +#include <linux/io.h> #include <linux/printk.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -29,6 +29,7 @@ #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/compat.h> +#include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h> /* USB2 Ports Control Registers */ @@ -103,10 +104,22 @@ enum { PHY_COUNT, }; -static const char *phy_names[PHY_COUNT] = { +static const char *const dwc3_meson_g12a_phy_names[] = { "usb2-phy0", "usb2-phy1", "usb3-phy0", }; +static const char *const dwc3_meson_a1_phy_names[] = { + "usb2-phy0", "usb2-phy1" +}; + +struct dwc3_meson_g12a; + +struct dwc3_meson_g12a_drvdata { + const char *const *phy_names; + unsigned int phy_cnt; + int (*clk_init)(struct dwc3_meson_g12a *priv); +}; + struct dwc3_meson_g12a { struct udevice *dev; struct regmap *regmap; @@ -120,6 +133,7 @@ struct dwc3_meson_g12a { #if CONFIG_IS_ENABLED(DM_REGULATOR) struct udevice *vbus_supply; #endif + struct dwc3_meson_g12a_drvdata *drvdata; }; #define U2P_REG_SIZE 0x20 @@ -294,10 +308,11 @@ int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode) static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) { + struct dwc3_meson_g12a_drvdata *data = priv->drvdata; int i, ret; - for (i = 0 ; i < PHY_COUNT ; ++i) { - ret = generic_phy_get_by_name(priv->dev, phy_names[i], + for (i = 0 ; i < data->phy_cnt; ++i) { + ret = generic_phy_get_by_name(priv->dev, data->phy_names[i], &priv->phys[i]); if (ret == -ENOENT || ret == -ENODATA) continue; @@ -355,18 +370,36 @@ static int dwc3_meson_g12a_clk_init(struct dwc3_meson_g12a *priv) return 0; } +static int dwc3_meson_a1_clk_init(struct dwc3_meson_g12a *priv) +{ + int ret; + + ret = clk_get_by_name(priv->dev, "usb_bus", &priv->clk); + if (ret) + return ret; + + ret = clk_enable(&priv->clk); + if (ret) + return ret; + + return 0; +} + static int dwc3_meson_g12a_probe(struct udevice *dev) { struct dwc3_meson_g12a *priv = dev_get_plat(dev); + struct dwc3_meson_g12a_drvdata *data = + (struct dwc3_meson_g12a_drvdata *)dev_get_driver_data(dev); int ret, i; + priv->drvdata = data; priv->dev = dev; ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); if (ret) return ret; - ret = dwc3_meson_g12a_clk_init(priv); + ret = data->clk_init(priv); if (ret) return ret; @@ -399,7 +432,7 @@ static int dwc3_meson_g12a_probe(struct udevice *dev) if (ret) return ret; - for (i = 0 ; i < PHY_COUNT ; ++i) { + for (i = 0 ; i < data->phy_cnt; ++i) { if (!priv->phys[i].dev) continue; @@ -408,7 +441,7 @@ static int dwc3_meson_g12a_probe(struct udevice *dev) goto err_phy_init; } - for (i = 0; i < PHY_COUNT; ++i) { + for (i = 0; i < data->phy_cnt; ++i) { if (!priv->phys[i].dev) continue; @@ -420,7 +453,7 @@ static int dwc3_meson_g12a_probe(struct udevice *dev) return 0; err_phy_init: - for (i = 0 ; i < PHY_COUNT ; ++i) { + for (i = 0 ; i < data->phy_cnt ; ++i) { if (!priv->phys[i].dev) continue; @@ -433,20 +466,21 @@ err_phy_init: static int dwc3_meson_g12a_remove(struct udevice *dev) { struct dwc3_meson_g12a *priv = dev_get_plat(dev); + struct dwc3_meson_g12a_drvdata *data = priv->drvdata; int i; reset_release_all(&priv->reset, 1); clk_release_all(&priv->clk, 1); - for (i = 0; i < PHY_COUNT; ++i) { + for (i = 0; i < data->phy_cnt; ++i) { if (!priv->phys[i].dev) continue; generic_phy_power_off(&priv->phys[i]); } - for (i = 0 ; i < PHY_COUNT ; ++i) { + for (i = 0 ; i < data->phy_cnt; ++i) { if (!priv->phys[i].dev) continue; @@ -456,11 +490,26 @@ static int dwc3_meson_g12a_remove(struct udevice *dev) return dm_scan_fdt_dev(dev); } +static const struct dwc3_meson_g12a_drvdata meson_g12a_drvdata = { + .phy_names = dwc3_meson_g12a_phy_names, + .phy_cnt = ARRAY_SIZE(dwc3_meson_g12a_phy_names), + .clk_init = dwc3_meson_g12a_clk_init, +}; + +static const struct dwc3_meson_g12a_drvdata meson_a1_drvdata = { + .phy_names = dwc3_meson_a1_phy_names, + .phy_cnt = ARRAY_SIZE(dwc3_meson_a1_phy_names), + .clk_init = dwc3_meson_a1_clk_init, +}; + static int dwc3_meson_g12a_child_pre_probe(struct udevice *dev) { if (ofnode_device_is_compatible(dev_ofnode(dev), "amlogic,meson-g12a-usb")) return dwc3_meson_g12a_force_mode(dev->parent, USB_DR_MODE_PERIPHERAL); + if (ofnode_device_is_compatible(dev_ofnode(dev), "amlogic,meson-a1-usb")) + return dwc3_meson_g12a_force_mode(dev->parent, USB_DR_MODE_PERIPHERAL); + return 0; } @@ -469,11 +518,21 @@ static int dwc3_meson_g12a_child_post_remove(struct udevice *dev) if (ofnode_device_is_compatible(dev_ofnode(dev), "amlogic,meson-g12a-usb")) return dwc3_meson_g12a_force_mode(dev->parent, USB_DR_MODE_HOST); + if (ofnode_device_is_compatible(dev_ofnode(dev), "amlogic,meson-a1-usb")) + return dwc3_meson_g12a_force_mode(dev->parent, USB_DR_MODE_HOST); + return 0; } static const struct udevice_id dwc3_meson_g12a_ids[] = { - { .compatible = "amlogic,meson-g12a-usb-ctrl" }, + { + .compatible = "amlogic,meson-g12a-usb-ctrl", + .data = (ulong)&meson_g12a_drvdata, + }, + { + .compatible = "amlogic,meson-a1-usb-ctrl", + .data = (ulong)&meson_a1_drvdata, + }, { } }; diff --git a/drivers/usb/dwc3/dwc3-meson-gxl.c b/drivers/usb/dwc3/dwc3-meson-gxl.c index d56f2747b63..cbe8aaa005b 100644 --- a/drivers/usb/dwc3/dwc3-meson-gxl.c +++ b/drivers/usb/dwc3/dwc3-meson-gxl.c @@ -8,12 +8,12 @@ #define DEBUG #include <common.h> -#include <asm-generic/io.h> #include <dm.h> #include <dm/device-internal.h> #include <dm/lists.h> #include <dwc3-uboot.h> #include <generic-phy.h> +#include <linux/io.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <malloc.h> diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index ff4ebfb4447..4fadb4a3e20 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -2,7 +2,7 @@ /** * dwc3-omap.c - OMAP Specific Glue layer * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 75ac993bc64..1133cf82b1a 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -2,7 +2,7 @@ /** * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 68cf32cd189..406d36ceafe 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2,7 +2,7 @@ /** * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 7806ce59a27..f28a9755dcb 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -2,7 +2,7 @@ /** * gadget.h - DesignWare USB3 DRD Gadget Header * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index 2407f826c16..04791d4c9be 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -2,7 +2,7 @@ /** * io.h - DesignWare USB3 DRD IO Header * - * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> diff --git a/drivers/usb/dwc3/linux-compat.h b/drivers/usb/dwc3/linux-compat.h index 3bb0bda5a6b..563f8727cdd 100644 --- a/drivers/usb/dwc3/linux-compat.h +++ b/drivers/usb/dwc3/linux-compat.h @@ -2,7 +2,7 @@ /** * linux-compat.h - DesignWare USB3 Linux Compatibiltiy Adapter Header * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com * * Authors: Kishon Vijay Abraham I <kishon@ti.com> * diff --git a/drivers/usb/dwc3/ti_usb_phy.c b/drivers/usb/dwc3/ti_usb_phy.c index f476810763d..8ae130860f7 100644 --- a/drivers/usb/dwc3/ti_usb_phy.c +++ b/drivers/usb/dwc3/ti_usb_phy.c @@ -2,7 +2,7 @@ /** * ti_usb_phy.c - USB3 and USB3 PHY programming for dwc3 * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com * * Author: Kishon Vijay Abraham I <kishon@ti.com> * diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 4eccc5e3370..c72a8047635 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -17,6 +17,7 @@ menuconfig USB_GADGET bool "USB Gadget Support" depends on DM select DM_USB + imply CMD_BIND help USB is a master/slave protocol, organized with one master host (such as a PC) controlling up to 127 peripheral devices. diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index f16731c8ebd..4c420747b0b 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -58,13 +58,9 @@ static void submit_request(struct usba_ep *ep, struct usba_request *req) req->submitted = 1; next_fifo_transaction(ep, req); - if (req->last_transaction) { - usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); - usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); - } else { + if (ep_is_control(ep)) usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); - usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY); - } + usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY); } static void submit_next_request(struct usba_ep *ep) @@ -890,7 +886,6 @@ restart: if (req) { list_del_init(&req->queue); request_complete(ep, req, 0); - submit_next_request(ep); } usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); ep->state = WAIT_FOR_SETUP; @@ -1037,7 +1032,6 @@ static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep) DBG(DBG_BUS, "%s: TX PK ready\n", ep->ep.name); if (list_empty(&ep->queue)) { - DBG(DBG_INT, "ep_irq: queue empty\n"); usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); return; } @@ -1051,7 +1045,6 @@ static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep) if (req->last_transaction) { list_del_init(&req->queue); - submit_next_request(ep); request_complete(ep, req, 0); } diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index 2bfacfe59f9..750d4714879 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -13,6 +13,7 @@ #include <cpu_func.h> #include <net.h> #include <malloc.h> +#include <wait_bit.h> #include <asm/byteorder.h> #include <asm/cache.h> #include <linux/delay.h> @@ -354,12 +355,49 @@ static int ci_ep_enable(struct usb_ep *ep, return 0; } +static int ep_disable(int num, int in) +{ + struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + unsigned int ep_bit, enable_bit; + int err; + + if (in) { + ep_bit = EPT_TX(num); + enable_bit = CTRL_TXE; + } else { + ep_bit = EPT_RX(num); + enable_bit = CTRL_RXE; + } + + /* clear primed buffers */ + do { + writel(ep_bit, &udc->epflush); + err = wait_for_bit_le32(&udc->epflush, ep_bit, false, 1000, false); + if (err) + return err; + } while (readl(&udc->epstat) & ep_bit); + + /* clear enable bit */ + clrbits_le32(&udc->epctrl[num], enable_bit); + + return 0; +} + static int ci_ep_disable(struct usb_ep *ep) { struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep); + int num, in, err; + + num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0; + + err = ep_disable(num, in); + if (err) + return err; ci_ep->desc = NULL; ep->desc = NULL; + ci_ep->req_primed = false; return 0; } diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 741775a7bcf..9f322c95508 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -520,7 +520,7 @@ static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) cmdbuf[req->actual] = '\0'; cmd = fastboot_handle_command(cmdbuf, response); } else { - pr_err("buffer overflow"); + pr_err("buffer overflow\n"); fastboot_fail("buffer overflow", response); } diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 1d17331cb03..c725aed3f62 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -327,6 +327,7 @@ struct fsg_common { unsigned int short_packet_received:1; unsigned int bad_lun_okay:1; unsigned int running:1; + unsigned int eject:1; int thread_wakeup_needed; struct completion thread_notifier; @@ -669,6 +670,10 @@ static int sleep_thread(struct fsg_common *common) } if (k == 10) { + /* Handle START-STOP UNIT */ + if (common->eject) + return -EPIPE; + /* Handle CTRL+C */ if (ctrlc()) return -EPIPE; @@ -1325,6 +1330,8 @@ static int do_start_stop(struct fsg_common *common) return -EINVAL; } + common->eject = 1; + return 0; } diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c index 2b3a9c5fd4c..ca2760c00d0 100644 --- a/drivers/usb/gadget/f_sdp.c +++ b/drivers/usb/gadget/f_sdp.c @@ -34,6 +34,7 @@ #include <spl.h> #include <image.h> #include <imximage.h> +#include <imx_container.h> #include <watchdog.h> #define HID_REPORT_ID_MASK 0x000000ff @@ -743,7 +744,7 @@ static ulong sdp_load_read(struct spl_load_info *load, ulong sector, { debug("%s: sector %lx, count %lx, buf %lx\n", __func__, sector, count, (ulong)buf); - memcpy(buf, (void *)(load->dev + sector), count); + memcpy(buf, (void *)(load->priv + sector), count); return count; } @@ -843,8 +844,8 @@ static int sdp_handle_in_ep(struct spl_image_info *spl_image, struct spl_load_info load; debug("Found FIT\n"); - load.dev = header; - load.bl_len = 1; + load.priv = header; + spl_set_bl_len(&load, 1); load.read = sdp_load_read; spl_load_simple_fit(spl_image, &load, 0, header); @@ -852,11 +853,12 @@ static int sdp_handle_in_ep(struct spl_image_info *spl_image, return SDP_EXIT; } #endif - if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) { + if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER) && + valid_container_hdr((void *)header)) { struct spl_load_info load; - load.dev = header; - load.bl_len = 1; + load.priv = header; + spl_set_bl_len(&load, 1); load.read = sdp_load_read; spl_load_imx_container(spl_image, &load, 0); return SDP_EXIT; diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index 7f73926cb3e..ba658d92296 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -2,7 +2,7 @@ /** * udc-core.c - Core UDC Framework * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com * * Author: Felipe Balbi <balbi@ti.com> * @@ -323,6 +323,7 @@ err1: int usb_gadget_probe_driver(struct usb_gadget_driver *driver) { struct usb_udc *udc = NULL; + unsigned int udc_count = 0; int ret; if (!driver || !driver->bind || !driver->setup) @@ -330,12 +331,22 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver) mutex_lock(&udc_lock); list_for_each_entry(udc, &udc_list, list) { + udc_count++; + /* For now we take the first one */ if (!udc->driver) goto found; } - printf("couldn't find an available UDC\n"); + if (!udc_count) + printf("No UDC available in the system\n"); + else + /* When this happens, users should 'unbind <class> <index>' + * using the output of 'dm tree' and looking at the line right + * after the USB peripheral/device controller. + */ + printf("All UDCs in use (%d available), use the unbind command\n", + udc_count); mutex_unlock(&udc_lock); return -ENODEV; found: diff --git a/drivers/usb/gadget/udc/udc-uclass.c b/drivers/usb/gadget/udc/udc-uclass.c index 3e433129ace..30ee1cab066 100644 --- a/drivers/usb/gadget/udc/udc-uclass.c +++ b/drivers/usb/gadget/udc/udc-uclass.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com * Written by Jean-Jacques Hiblot <jjhiblot@ti.com> */ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 1a883babf4c..35610ffc2b3 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -90,7 +90,7 @@ config USB_XHCI_OMAP config USB_XHCI_PCI bool "Support for PCI-based xHCI USB controller" - depends on DM_USB + depends on DM_USB && PCI default y if X86 help Enables support for the PCI-based xHCI controller. @@ -231,7 +231,6 @@ config USB_EHCI_MXS config USB_EHCI_NPCM bool "Support for Nuvoton NPCM on-chip EHCI USB controller" depends on ARCH_NPCM - default n ---help--- Enables support for the on-chip EHCI controller on Nuvoton NPCM chips. @@ -368,7 +367,6 @@ config USB_OHCI_DA8XX config USB_OHCI_NPCM bool "Support for Nuvoton NPCM on-chip OHCI USB controller" depends on ARCH_NPCM - default n ---help--- Enables support for the on-chip OHCI controller on Nuvoton NPCM chips. diff --git a/drivers/usb/host/dwc3-of-simple.c b/drivers/usb/host/dwc3-of-simple.c index 66b3e96b007..f9df59d2e5d 100644 --- a/drivers/usb/host/dwc3-of-simple.c +++ b/drivers/usb/host/dwc3-of-simple.c @@ -2,7 +2,7 @@ /* * dwc3-of-simple.c - OF glue layer for simple integrations * - * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (c) 2015 Texas Instruments Incorporated - https://www.ti.com * * Author: Felipe Balbi <balbi@ti.com> * diff --git a/drivers/usb/host/ehci-mxs.c b/drivers/usb/host/ehci-mxs.c index 147b2fa145d..ddf7cc2d00a 100644 --- a/drivers/usb/host/ehci-mxs.c +++ b/drivers/usb/host/ehci-mxs.c @@ -136,11 +136,12 @@ static int ehci_usb_ofdata_to_platdata(struct udevice *dev) struct usb_plat *plat = dev_get_plat(dev); struct ehci_mxs_port *port = &priv->port; u32 phandle, phy_reg, clk_reg, clk_id; + ofnode np = dev_ofnode(dev); ofnode phy_node, clk_node; const char *mode; int ret; - mode = ofnode_read_string(dev->node_, "dr_mode"); + mode = ofnode_read_string(np, "dr_mode"); if (mode) { if (strcmp(mode, "peripheral") == 0) plat->init_type = USB_INIT_DEVICE; @@ -151,12 +152,12 @@ static int ehci_usb_ofdata_to_platdata(struct udevice *dev) } /* Read base address of the USB IP block */ - ret = ofnode_read_u32(dev->node_, "reg", &port->usb_regs); + ret = ofnode_read_u32(np, "reg", &port->usb_regs); if (ret) return ret; /* Read base address of the USB PHY IP block */ - ret = ofnode_read_u32(dev->node_, "fsl,usbphy", &phandle); + ret = ofnode_read_u32(np, "fsl,usbphy", &phandle); if (ret) return ret; @@ -235,9 +236,9 @@ static int ehci_usb_probe(struct udevice *dev) debug("%s: No vbus supply\n", dev->name); if (!ret && priv->vbus_supply) { - ret = regulator_set_enable(priv->vbus_supply, - (type == USB_INIT_DEVICE) ? - false : true); + ret = regulator_set_enable_if_allowed(priv->vbus_supply, + (type == USB_INIT_DEVICE) ? + false : true); if (ret) { puts("Error enabling VBUS supply\n"); return ret; @@ -264,7 +265,7 @@ static int ehci_usb_remove(struct udevice *dev) #if CONFIG_IS_ENABLED(DM_REGULATOR) if (priv->vbus_supply) { - ret = regulator_set_enable(priv->vbus_supply, false); + ret = regulator_set_enable_if_allowed(priv->vbus_supply, false); if (ret) { puts("Error disabling VBUS supply\n"); return ret; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index c8260cbdf94..b60661fe05e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -202,6 +202,7 @@ static dma_addr_t queue_trb(struct xhci_ctrl *ctrl, struct xhci_ring *ring, bool more_trbs_coming, unsigned int *trb_fields) { struct xhci_generic_trb *trb; + dma_addr_t addr; int i; trb = &ring->enqueue->generic; @@ -211,9 +212,11 @@ static dma_addr_t queue_trb(struct xhci_ctrl *ctrl, struct xhci_ring *ring, xhci_flush_cache((uintptr_t)trb, sizeof(struct xhci_generic_trb)); + addr = xhci_trb_virt_to_dma(ring->enq_seg, (union xhci_trb *)trb); + inc_enq(ctrl, ring, more_trbs_coming); - return xhci_trb_virt_to_dma(ring->enq_seg, (union xhci_trb *)trb); + return addr; } /** @@ -243,7 +246,8 @@ static int prepare_ring(struct xhci_ctrl *ctrl, struct xhci_ring *ep_ring, puts("WARN waiting for error on ep to be cleared\n"); return -EINVAL; case EP_STATE_HALTED: - puts("WARN halted endpoint, queueing URB anyway.\n"); + puts("WARN endpoint is halted\n"); + return -EINVAL; case EP_STATE_STOPPED: case EP_STATE_RUNNING: debug("EP STATE RUNNING.\n"); @@ -466,7 +470,8 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected) continue; type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags)); - if (type == expected) + if (type == expected || + (expected == TRB_NONE && type != TRB_PORT_STATUS)) return event; if (type == TRB_PORT_STATUS) @@ -492,8 +497,9 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected) if (expected == TRB_TRANSFER) return NULL; - printf("XHCI timeout on event type %d... cannot recover.\n", expected); - BUG(); + printf("XHCI timeout on event type %d...\n", expected); + + return NULL; } /* @@ -511,6 +517,9 @@ static void reset_ep(struct usb_device *udev, int ep_index) printf("Resetting EP %d...\n", ep_index); xhci_queue_command(ctrl, 0, udev->slot_id, ep_index, TRB_RESET_EP); event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + if (!event) + return; + field = le32_to_cpu(event->trans_event.flags); BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id); xhci_acknowledge_event(ctrl); @@ -519,6 +528,9 @@ static void reset_ep(struct usb_device *udev, int ep_index) (void *)((uintptr_t)ring->enqueue | ring->cycle_state)); xhci_queue_command(ctrl, addr, udev->slot_id, ep_index, TRB_SET_DEQ); event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + if (!event) + return; + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) != udev->slot_id || GET_COMP_CODE(le32_to_cpu( event->event_cmd.status)) != COMP_SUCCESS); @@ -538,29 +550,49 @@ static void abort_td(struct usb_device *udev, int ep_index) struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring; union xhci_trb *event; + xhci_comp_code comp; + trb_type type; u64 addr; u32 field; xhci_queue_command(ctrl, 0, udev->slot_id, ep_index, TRB_STOP_RING); - event = xhci_wait_for_event(ctrl, TRB_TRANSFER); - field = le32_to_cpu(event->trans_event.flags); - BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id); - BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); - BUG_ON(GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len - != COMP_STOP))); - xhci_acknowledge_event(ctrl); + event = xhci_wait_for_event(ctrl, TRB_NONE); + if (!event) + return; - event = xhci_wait_for_event(ctrl, TRB_COMPLETION); - BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) - != udev->slot_id || GET_COMP_CODE(le32_to_cpu( - event->event_cmd.status)) != COMP_SUCCESS); + type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags)); + if (type == TRB_TRANSFER) { + field = le32_to_cpu(event->trans_event.flags); + BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id); + BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); + BUG_ON(GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len + != COMP_STOP))); + xhci_acknowledge_event(ctrl); + + event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + if (!event) + return; + type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags)); + + } else { + printf("abort_td: Expected a TRB_TRANSFER TRB first\n"); + } + + comp = GET_COMP_CODE(le32_to_cpu(event->event_cmd.status)); + BUG_ON(type != TRB_COMPLETION || + TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) + != udev->slot_id || (comp != COMP_SUCCESS && comp + != COMP_CTX_STATE)); xhci_acknowledge_event(ctrl); addr = xhci_trb_virt_to_dma(ring->enq_seg, (void *)((uintptr_t)ring->enqueue | ring->cycle_state)); xhci_queue_command(ctrl, addr, udev->slot_id, ep_index, TRB_SET_DEQ); event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + if (!event) + return; + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) != udev->slot_id || GET_COMP_CODE(le32_to_cpu( event->event_cmd.status)) != COMP_SUCCESS); @@ -644,6 +676,14 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index); + /* + * If the endpoint was halted due to a prior error, resume it before + * the next transfer. It is the responsibility of the upper layer to + * have dealt with whatever caused the error. + */ + if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == EP_STATE_HALTED) + reset_ep(udev, ep_index); + ring = virt_dev->eps[ep_index].ring; /* * How much data is (potentially) left before the 64KB boundary? diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 5cacf0769ec..d13cbff9b37 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -451,6 +451,9 @@ static int xhci_configure_endpoints(struct usb_device *udev, bool ctx_change) xhci_queue_command(ctrl, in_ctx->dma, udev->slot_id, 0, ctx_change ? TRB_EVAL_CONTEXT : TRB_CONFIG_EP); event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + if (!event) + return -ETIMEDOUT; + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) != udev->slot_id); @@ -647,6 +650,9 @@ static int xhci_address_device(struct usb_device *udev, int root_portnr) xhci_queue_command(ctrl, virt_dev->in_ctx->dma, slot_id, 0, TRB_ADDR_DEV); event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + if (!event) + return -ETIMEDOUT; + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) != slot_id); switch (GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))) { @@ -722,6 +728,9 @@ static int _xhci_alloc_device(struct usb_device *udev) xhci_queue_command(ctrl, 0, 0, 0, TRB_ENABLE_SLOT); event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + if (!event) + return -ETIMEDOUT; + BUG_ON(GET_COMP_CODE(le32_to_cpu(event->event_cmd.status)) != COMP_SUCCESS); diff --git a/drivers/usb/musb-new/musb_io.h b/drivers/usb/musb-new/musb_io.h index 72a53656321..19b12f36a5c 100644 --- a/drivers/usb/musb-new/musb_io.h +++ b/drivers/usb/musb-new/musb_io.h @@ -14,31 +14,7 @@ #ifndef __MUSB_LINUX_PLATFORM_ARCH_H__ #define __MUSB_LINUX_PLATFORM_ARCH_H__ -#ifndef __UBOOT__ #include <linux/io.h> -#else -#include <asm/io.h> -#endif - -#if !defined(CONFIG_ARM) && !defined(CONFIG_SUPERH) \ - && !defined(CONFIG_PPC32) \ - && !defined(CONFIG_PPC64) && !defined(CONFIG_MIPS) \ - && !defined(CONFIG_M68K) -static inline void readsl(const void __iomem *addr, void *buf, int len) - { insl((unsigned long)addr, buf, len); } -static inline void readsw(const void __iomem *addr, void *buf, int len) - { insw((unsigned long)addr, buf, len); } -static inline void readsb(const void __iomem *addr, void *buf, int len) - { insb((unsigned long)addr, buf, len); } - -static inline void writesl(const void __iomem *addr, const void *buf, int len) - { outsl((unsigned long)addr, buf, len); } -static inline void writesw(const void __iomem *addr, const void *buf, int len) - { outsw((unsigned long)addr, buf, len); } -static inline void writesb(const void __iomem *addr, const void *buf, int len) - { outsb((unsigned long)addr, buf, len); } - -#endif /* NOTE: these offsets are all in bytes */ diff --git a/drivers/usb/ulpi/omap-ulpi-viewport.c b/drivers/usb/ulpi/omap-ulpi-viewport.c index 8d71db04a2d..1b01cd4c559 100644 --- a/drivers/usb/ulpi/omap-ulpi-viewport.c +++ b/drivers/usb/ulpi/omap-ulpi-viewport.c @@ -3,7 +3,7 @@ * OMAP ulpi viewport support * Based on drivers/usb/ulpi/ulpi-viewport.c * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com * Author: Govindraj R <govindraj.raja@ti.com> */ diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index ab927641bb7..6f319ba0d54 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -180,7 +180,6 @@ config CONSOLE_ROTATION config CONSOLE_TRUETYPE bool "Support a console that uses TrueType fonts" - select CMD_SELECT_FONT help TrueTrype fonts can provide outline-drawing capability rather than needing to provide a bitmap for each font and size that is needed. diff --git a/drivers/video/console_core.c b/drivers/video/console_core.c index b5d0e3dceca..d17764d0b08 100644 --- a/drivers/video/console_core.c +++ b/drivers/video/console_core.c @@ -176,6 +176,37 @@ int fill_char_horizontally(uchar *pfont, void **line, struct video_priv *vid_pri return ret; } +int draw_cursor_vertically(void **line, struct video_priv *vid_priv, + uint height, bool direction) +{ + int step, line_step, pbytes, ret; + uint value; + void *dst; + + ret = check_bpix_support(vid_priv->bpix); + if (ret) + return ret; + + pbytes = VNBYTES(vid_priv->bpix); + if (direction) { + step = -pbytes; + line_step = -vid_priv->line_length; + } else { + step = pbytes; + line_step = vid_priv->line_length; + } + + value = vid_priv->colour_fg; + + for (int row = 0; row < height; row++) { + dst = *line; + for (int col = 0; col < VIDCONSOLE_CURSOR_WIDTH; col++) + fill_pixel_and_goto_next(&dst, value, pbytes, step); + *line += line_step; + } + return ret; +} + int console_probe(struct udevice *dev) { return console_set_font(dev, fonts); diff --git a/drivers/video/console_normal.c b/drivers/video/console_normal.c index 413c7abee9e..a0231293f31 100644 --- a/drivers/video/console_normal.c +++ b/drivers/video/console_normal.c @@ -97,6 +97,34 @@ static int console_putc_xy(struct udevice *dev, uint x_frac, uint y, char ch) return VID_TO_POS(fontdata->width); } +static int console_set_cursor_visible(struct udevice *dev, bool visible, + uint x, uint y, uint index) +{ + struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev); + struct udevice *vid = dev->parent; + struct video_priv *vid_priv = dev_get_uclass_priv(vid); + struct console_simple_priv *priv = dev_get_priv(dev); + struct video_fontdata *fontdata = priv->fontdata; + int pbytes = VNBYTES(vid_priv->bpix); + void *start, *line; + + /* for now, this is not used outside expo */ + if (!IS_ENABLED(CONFIG_EXPO)) + return -ENOSYS; + + x += index * fontdata->width; + start = vid_priv->fb + y * vid_priv->line_length + x * pbytes; + + /* place the cursor 1 pixel before the start of the next char */ + x -= 1; + + line = start; + draw_cursor_vertically(&line, vid_priv, vc_priv->y_charsize, + NORMAL_DIRECTION); + + return 0; +} + struct vidconsole_ops console_ops = { .putc_xy = console_putc_xy, .move_rows = console_move_rows, @@ -104,6 +132,7 @@ struct vidconsole_ops console_ops = { .get_font_size = console_simple_get_font_size, .get_font = console_simple_get_font, .select_font = console_simple_select_font, + .set_cursor_visible = console_set_cursor_visible, }; U_BOOT_DRIVER(vidconsole_normal) = { diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 0f9bb49e44f..14fb81e9563 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -4,6 +4,7 @@ */ #include <common.h> +#include <abuf.h> #include <dm.h> #include <log.h> #include <malloc.h> @@ -175,6 +176,17 @@ struct console_tt_priv { int pos_ptr; }; +/** + * struct console_tt_store - Format used for save/restore of entry information + * + * @priv: Private data + * @cur: Current cursor position + */ +struct console_tt_store { + struct console_tt_priv priv; + struct pos_info cur; +}; + static int console_truetype_set_row(struct udevice *dev, uint row, int clr) { struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); @@ -706,8 +718,8 @@ static int truetype_select_font(struct udevice *dev, const char *name, return 0; } -int truetype_measure(struct udevice *dev, const char *name, uint size, - const char *text, struct vidconsole_bbox *bbox) +static int truetype_measure(struct udevice *dev, const char *name, uint size, + const char *text, struct vidconsole_bbox *bbox) { struct console_tt_metrics *met; stbtt_fontinfo *font; @@ -750,6 +762,177 @@ int truetype_measure(struct udevice *dev, const char *name, uint size, return 0; } +static int truetype_nominal(struct udevice *dev, const char *name, uint size, + uint num_chars, struct vidconsole_bbox *bbox) +{ + struct console_tt_metrics *met; + stbtt_fontinfo *font; + int lsb, advance; + int width; + int ret; + + ret = get_metrics(dev, name, size, &met); + if (ret) + return log_msg_ret("sel", ret); + + font = &met->font; + width = 0; + + /* First get some basic metrics about this character */ + stbtt_GetCodepointHMetrics(font, 'W', &advance, &lsb); + + width = advance; + + bbox->valid = true; + bbox->x0 = 0; + bbox->y0 = 0; + bbox->x1 = tt_ceil((double)width * num_chars * met->scale); + bbox->y1 = met->font_size; + + return 0; +} + +static int truetype_entry_save(struct udevice *dev, struct abuf *buf) +{ + struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev); + struct console_tt_priv *priv = dev_get_priv(dev); + struct console_tt_store store; + const uint size = sizeof(store); + + /* + * store the whole priv structure as it is simpler that picking out + * what we need + */ + if (!abuf_realloc(buf, size)) + return log_msg_ret("sav", -ENOMEM); + + store.priv = *priv; + store.cur.xpos_frac = vc_priv->xcur_frac; + store.cur.ypos = vc_priv->ycur; + memcpy(abuf_data(buf), &store, size); + + return 0; +} + +static int truetype_entry_restore(struct udevice *dev, struct abuf *buf) +{ + struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev); + struct console_tt_priv *priv = dev_get_priv(dev); + struct console_tt_store store; + + memcpy(&store, abuf_data(buf), sizeof(store)); + + vc_priv->xcur_frac = store.cur.xpos_frac; + vc_priv->ycur = store.cur.ypos; + priv->pos_ptr = store.priv.pos_ptr; + memcpy(priv->pos, store.priv.pos, + store.priv.pos_ptr * sizeof(struct pos_info)); + + return 0; +} + +static int truetype_set_cursor_visible(struct udevice *dev, bool visible, + uint x, uint y, uint index) +{ + struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev); + struct udevice *vid = dev->parent; + struct video_priv *vid_priv = dev_get_uclass_priv(vid); + struct console_tt_priv *priv = dev_get_priv(dev); + struct console_tt_metrics *met = priv->cur_met; + uint row, width, height, xoff; + void *start, *line; + uint out, val; + int ret; + + if (!visible) + return 0; + + /* + * figure out where to place the cursor. This driver ignores the + * passed-in values, since an entry_restore() must have been done before + * calling this function. + */ + if (index < priv->pos_ptr) + x = VID_TO_PIXEL(priv->pos[index].xpos_frac); + else + x = VID_TO_PIXEL(vc_priv->xcur_frac); + + y = vc_priv->ycur; + height = met->font_size; + xoff = 0; + + val = vid_priv->colour_bg ? 0 : 255; + width = VIDCONSOLE_CURSOR_WIDTH; + + /* Figure out where to write the cursor in the frame buffer */ + start = vid_priv->fb + y * vid_priv->line_length + + x * VNBYTES(vid_priv->bpix); + line = start; + + /* draw a vertical bar in the correct position */ + for (row = 0; row < height; row++) { + switch (vid_priv->bpix) { + case VIDEO_BPP8: + if (IS_ENABLED(CONFIG_VIDEO_BPP8)) { + u8 *dst = line + xoff; + int i; + + out = val; + for (i = 0; i < width; i++) { + if (vid_priv->colour_fg) + *dst++ |= out; + else + *dst++ &= out; + } + } + break; + case VIDEO_BPP16: { + u16 *dst = (u16 *)line + xoff; + int i; + + if (IS_ENABLED(CONFIG_VIDEO_BPP16)) { + for (i = 0; i < width; i++) { + out = val >> 3 | + (val >> 2) << 5 | + (val >> 3) << 11; + if (vid_priv->colour_fg) + *dst++ |= out; + else + *dst++ &= out; + } + } + break; + } + case VIDEO_BPP32: { + u32 *dst = (u32 *)line + xoff; + int i; + + if (IS_ENABLED(CONFIG_VIDEO_BPP32)) { + for (i = 0; i < width; i++) { + int out; + + out = val | val << 8 | val << 16; + if (vid_priv->colour_fg) + *dst++ |= out; + else + *dst++ &= out; + } + } + break; + } + default: + return -ENOSYS; + } + + line += vid_priv->line_length; + } + ret = vidconsole_sync_copy(dev, start, line); + if (ret) + return ret; + + return video_sync(vid, true); +} + const char *console_truetype_get_font_size(struct udevice *dev, uint *sizep) { struct console_tt_priv *priv = dev_get_priv(dev); @@ -802,6 +985,10 @@ struct vidconsole_ops console_truetype_ops = { .get_font_size = console_truetype_get_font_size, .select_font = truetype_select_font, .measure = truetype_measure, + .nominal = truetype_nominal, + .entry_save = truetype_entry_save, + .entry_restore = truetype_entry_restore, + .set_cursor_visible = truetype_set_cursor_visible }; U_BOOT_DRIVER(vidconsole_truetype) = { diff --git a/drivers/video/dw_mipi_dsi.c b/drivers/video/dw_mipi_dsi.c index 22fef7e8825..a7e0784596a 100644 --- a/drivers/video/dw_mipi_dsi.c +++ b/drivers/video/dw_mipi_dsi.c @@ -22,6 +22,7 @@ #include <linux/bitops.h> #include <linux/delay.h> #include <linux/iopoll.h> +#include <linux/time.h> #include <video_bridge.h> #define HWVER_131 0x31333100 /* IP version 1.31 */ @@ -214,8 +215,6 @@ #define PHY_STATUS_TIMEOUT_US 10000 #define CMD_PKT_STATUS_TIMEOUT_US 20000 -#define MSEC_PER_SEC 1000 - struct dw_mipi_dsi { struct mipi_dsi_host dsi_host; struct mipi_dsi_device *device; diff --git a/drivers/video/hitachi_tx18d42vm_lcd.c b/drivers/video/hitachi_tx18d42vm_lcd.c index 87c4d27438a..95984fe3d3d 100644 --- a/drivers/video/hitachi_tx18d42vm_lcd.c +++ b/drivers/video/hitachi_tx18d42vm_lcd.c @@ -10,6 +10,7 @@ #include <linux/delay.h> #include <asm/gpio.h> +#include <sunxi_gpio.h> #include <errno.h> /* diff --git a/drivers/video/pwm_backlight.c b/drivers/video/pwm_backlight.c index aa0e2928666..1c747d98d7a 100644 --- a/drivers/video/pwm_backlight.c +++ b/drivers/video/pwm_backlight.c @@ -100,8 +100,8 @@ static int enable_sequence(struct udevice *dev, int seq) plat = dev_get_uclass_plat(priv->reg); log_debug("Enable '%s', regulator '%s'/'%s'\n", dev->name, priv->reg->name, plat->name); - ret = regulator_set_enable(priv->reg, true); - if (ret) { + ret = regulator_set_enable_if_allowed(priv->reg, true); + if (ret && ret != -ENOSYS) { log_debug("Cannot enable regulator for PWM '%s'\n", dev->name); return log_ret(ret); @@ -181,11 +181,10 @@ static int pwm_backlight_set_brightness(struct udevice *dev, int percent) } if (disable) { dm_gpio_set_value(&priv->enable, 0); - if (priv->reg) { - ret = regulator_set_enable(priv->reg, false); - if (ret) - return log_ret(ret); - } + ret = regulator_set_enable_if_allowed(priv->reg, false); + if (ret && ret != -ENOSYS) + return log_ret(ret); + priv->enabled = false; } diff --git a/drivers/video/rockchip/dw_mipi_dsi_rockchip.c b/drivers/video/rockchip/dw_mipi_dsi_rockchip.c index 1a5ab781e3f..5e75b6ec68c 100644 --- a/drivers/video/rockchip/dw_mipi_dsi_rockchip.c +++ b/drivers/video/rockchip/dw_mipi_dsi_rockchip.c @@ -30,12 +30,11 @@ #include <asm/io.h> #include <dm/device-internal.h> #include <linux/bitops.h> +#include <linux/time.h> #include <asm/arch-rockchip/clock.h> #include <asm/arch-rockchip/hardware.h> -#define USEC_PER_SEC 1000000L - /* * DSI wrapper registers & bit definitions * Note: registers are named as in the Reference Manual diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c index 6a6473eb0e5..efb122b534a 100644 --- a/drivers/video/simple_panel.c +++ b/drivers/video/simple_panel.c @@ -114,11 +114,11 @@ static int simple_panel_probe(struct udevice *dev) const u32 dsi_data = dev_get_driver_data(dev); int ret; - if (CONFIG_IS_ENABLED(DM_REGULATOR) && priv->reg) { - debug("%s: Enable regulator '%s'\n", __func__, priv->reg->name); - ret = regulator_set_enable(priv->reg, true); - if (ret) - return ret; + ret = regulator_set_enable_if_allowed(priv->reg, true); + if (ret && ret != -ENOSYS) { + debug("%s: failed to enable regulator '%s' %d\n", + __func__, priv->reg->name, ret); + return ret; } switch (dsi_data) { diff --git a/drivers/video/ssd2828.c b/drivers/video/ssd2828.c index 4cdcbe7755a..948f5e74d0f 100644 --- a/drivers/video/ssd2828.c +++ b/drivers/video/ssd2828.c @@ -12,7 +12,6 @@ #include <common.h> #include <malloc.h> #include <mipi_display.h> -#include <asm/arch/gpio.h> #include <asm/gpio.h> #include <linux/delay.h> diff --git a/drivers/video/sunxi/sunxi_display.c b/drivers/video/sunxi/sunxi_display.c index 9110a484821..8da44a1bb6d 100644 --- a/drivers/video/sunxi/sunxi_display.c +++ b/drivers/video/sunxi/sunxi_display.c @@ -31,6 +31,7 @@ #include <malloc.h> #include <video.h> #include <dm/uclass-internal.h> +#include <sunxi_gpio.h> #include "../videomodes.h" #include "../anx9804.h" #include "../hitachi_tx18d42vm_lcd.h" diff --git a/drivers/video/sunxi/sunxi_lcd.c b/drivers/video/sunxi/sunxi_lcd.c index 8b9c3b2bfa9..7a01cc343ca 100644 --- a/drivers/video/sunxi/sunxi_lcd.c +++ b/drivers/video/sunxi/sunxi_lcd.c @@ -17,6 +17,7 @@ #include <asm/arch/lcdc.h> #include <asm/global_data.h> #include <asm/gpio.h> +#include <sunxi_gpio.h> struct sunxi_lcd_priv { struct display_timing timing; diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index 8c3404e085d..a48f9c85d0f 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -14,6 +14,7 @@ #include <panel.h> #include <linux/delay.h> #include <linux/err.h> +#include <linux/time.h> #include <power/regulator.h> #include <asm/gpio.h> @@ -24,9 +25,6 @@ #include "mipi-phy.h" -#define USEC_PER_SEC 1000000L -#define NSEC_PER_SEC 1000000000L - struct tegra_dsi_priv { struct mipi_dsi_host host; struct mipi_dsi_device device; @@ -831,11 +829,9 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) tegra_dsi_get_format(device->format, &priv->format); - if (priv->avdd) { - ret = regulator_set_enable(priv->avdd, true); - if (ret) - return ret; - } + ret = regulator_set_enable_if_allowed(priv->avdd, true); + if (ret && ret != -ENOSYS) + return ret; tegra_dsi_init_clocks(dev); diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index b5b3b662590..22d55df71f6 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -10,6 +10,7 @@ #define LOG_CATEGORY UCLASS_VIDEO_CONSOLE #include <common.h> +#include <abuf.h> #include <command.h> #include <console.h> #include <log.h> @@ -47,7 +48,7 @@ int vidconsole_set_row(struct udevice *dev, uint row, int clr) return ops->set_row(dev, row, clr); } -static int vidconsole_entry_start(struct udevice *dev) +int vidconsole_entry_start(struct udevice *dev) { struct vidconsole_ops *ops = vidconsole_get_ops(dev); @@ -618,6 +619,74 @@ int vidconsole_measure(struct udevice *dev, const char *name, uint size, return 0; } +int vidconsole_nominal(struct udevice *dev, const char *name, uint size, + uint num_chars, struct vidconsole_bbox *bbox) +{ + struct vidconsole_priv *priv = dev_get_uclass_priv(dev); + struct vidconsole_ops *ops = vidconsole_get_ops(dev); + int ret; + + if (ops->measure) { + ret = ops->nominal(dev, name, size, num_chars, bbox); + if (ret != -ENOSYS) + return ret; + } + + bbox->valid = true; + bbox->x0 = 0; + bbox->y0 = 0; + bbox->x1 = priv->x_charsize * num_chars; + bbox->y1 = priv->y_charsize; + + return 0; +} + +int vidconsole_entry_save(struct udevice *dev, struct abuf *buf) +{ + struct vidconsole_ops *ops = vidconsole_get_ops(dev); + int ret; + + if (ops->measure) { + ret = ops->entry_save(dev, buf); + if (ret != -ENOSYS) + return ret; + } + + /* no data so make sure the buffer is empty */ + abuf_realloc(buf, 0); + + return 0; +} + +int vidconsole_entry_restore(struct udevice *dev, struct abuf *buf) +{ + struct vidconsole_ops *ops = vidconsole_get_ops(dev); + int ret; + + if (ops->measure) { + ret = ops->entry_restore(dev, buf); + if (ret != -ENOSYS) + return ret; + } + + return 0; +} + +int vidconsole_set_cursor_visible(struct udevice *dev, bool visible, + uint x, uint y, uint index) +{ + struct vidconsole_ops *ops = vidconsole_get_ops(dev); + int ret; + + if (ops->set_cursor_visible) { + ret = ops->set_cursor_visible(dev, visible, x, y, index); + if (ret != -ENOSYS) + return ret; + } + + return 0; +} + void vidconsole_push_colour(struct udevice *dev, enum colour_idx fg, enum colour_idx bg, struct vidconsole_colour *old) { diff --git a/drivers/video/vidconsole_internal.h b/drivers/video/vidconsole_internal.h index c41edd45249..0ec581b2663 100644 --- a/drivers/video/vidconsole_internal.h +++ b/drivers/video/vidconsole_internal.h @@ -93,6 +93,30 @@ int fill_char_horizontally(uchar *pfont, void **line, struct video_priv *vid_pri struct video_fontdata *fontdata, bool direction); /** + * draw_cursor_vertically() - Draw a simple vertical cursor + * + * @line: pointer to framebuffer buffer: upper left cursor corner + * @vid_priv: driver private data + * @height: height of the cursor in pixels + * @param direction controls cursor orientation. Can be normal or flipped. + * When normal: When flipped: + *|-----------------------------------------------| + *| * | line stepping | + *| ^ * * * * * | | | + *| | * * | v * * | + *| | | * * * * * | + *| line stepping | * | + *| | | + *| stepping -> | <<- stepping | + *|---!!we're starting from upper left char corner| + *|-----------------------------------------------| + * + * Return: 0, if success, or else error code. + */ +int draw_cursor_vertically(void **line, struct video_priv *vid_priv, + uint height, bool direction); + +/** * console probe function. * * @param dev a pointer to device. diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 852f6735b60..1de68867d52 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -56,7 +56,7 @@ config VIRTIO_SANDBOX config VIRTIO_NET bool "virtio net driver" - depends on VIRTIO + depends on VIRTIO && NETDEVICES help This is the virtual net driver for virtio. It can be used with QEMU based targets. diff --git a/drivers/virtio/virtio_rng.c b/drivers/virtio/virtio_rng.c index b85545c2ee5..786359a6e36 100644 --- a/drivers/virtio/virtio_rng.c +++ b/drivers/virtio/virtio_rng.c @@ -20,7 +20,7 @@ struct virtio_rng_priv { static int virtio_rng_read(struct udevice *dev, void *data, size_t len) { int ret; - unsigned int rsize; + unsigned int rsize = 1; unsigned char buf[BUFFER_SIZE] __aligned(4); unsigned char *ptr = data; struct virtio_sg sg; @@ -29,7 +29,12 @@ static int virtio_rng_read(struct udevice *dev, void *data, size_t len) while (len) { sg.addr = buf; - sg.length = min(len, sizeof(buf)); + /* + * Work around implementations which always return 8 bytes + * less than requested, down to 0 bytes, which would + * cause an endless loop otherwise. + */ + sg.length = min(rsize ? len : len + 8, sizeof(buf)); sgs[0] = &sg; ret = virtqueue_add(priv->rng_vq, sgs, 0, 1); diff --git a/drivers/watchdog/npcm_wdt.c b/drivers/watchdog/npcm_wdt.c index e56aa0ebe1d..57b61215a2a 100644 --- a/drivers/watchdog/npcm_wdt.c +++ b/drivers/watchdog/npcm_wdt.c @@ -69,15 +69,21 @@ static int npcm_wdt_stop(struct udevice *dev) static int npcm_wdt_reset(struct udevice *dev) { struct npcm_wdt_priv *priv = dev_get_priv(dev); + u32 val; - writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, priv->regs); + val = readl(priv->regs); + writel(val | NPCM_WTR, priv->regs); return 0; } static int npcm_wdt_expire_now(struct udevice *dev, ulong flags) { - return npcm_wdt_reset(dev); + struct npcm_wdt_priv *priv = dev_get_priv(dev); + + writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, priv->regs); + + return 0; } static int npcm_wdt_of_to_plat(struct udevice *dev) diff --git a/drivers/watchdog/s5p_wdt.c b/drivers/watchdog/s5p_wdt.c index 5ad7d2609f0..80524a00101 100644 --- a/drivers/watchdog/s5p_wdt.c +++ b/drivers/watchdog/s5p_wdt.c @@ -6,6 +6,7 @@ #include <common.h> #include <asm/io.h> +#include <asm/arch/cpu.h> #include <asm/arch/watchdog.h> #define PRESCALER_VAL 255 diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index b40a1d29caa..8eeac935760 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -9,8 +9,7 @@ #include <wdt.h> #include <asm/io.h> #include <linux/delay.h> - -#define MSEC_PER_SEC 1000 +#include <linux/time.h> #define WDT_MAX_TIMEOUT 16 #define WDT_TIMEOUT_MASK 0xf diff --git a/drivers/watchdog/wdt-uclass.c b/drivers/watchdog/wdt-uclass.c index ed329284dec..417e8d7eef9 100644 --- a/drivers/watchdog/wdt-uclass.c +++ b/drivers/watchdog/wdt-uclass.c @@ -7,6 +7,7 @@ #include <common.h> #include <cyclic.h> +#include <div64.h> #include <dm.h> #include <errno.h> #include <hang.h> @@ -141,7 +142,7 @@ int wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) printf("WDT: Started %s with%s servicing %s (%ds timeout)\n", dev->name, IS_ENABLED(CONFIG_WATCHDOG) ? "" : "out", - str, priv->timeout); + str, (u32)lldiv(timeout_ms, 1000)); } return ret; diff --git a/drivers/xen/pvblock.c b/drivers/xen/pvblock.c index 4ad548d599d..1df04e239ad 100644 --- a/drivers/xen/pvblock.c +++ b/drivers/xen/pvblock.c @@ -632,7 +632,8 @@ static ulong pvblock_iop(struct udevice *udev, lbaint_t blknr, memcpy(blk_dev->bounce_buffer, buffer, desc->blksz); aiocb.aio_nbytes = unaligned ? desc->blksz : - min((size_t)(BLKIF_MAX_SEGMENTS_PER_REQUEST * PAGE_SIZE), + min((size_t)((BLKIF_MAX_SEGMENTS_PER_REQUEST - 1) + * PAGE_SIZE), (size_t)(blocks_todo * desc->blksz)); blkfront_io(&aiocb, write); |