diff options
Diffstat (limited to 'drivers')
382 files changed, 30648 insertions, 5556 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 0e1f58c515b..3c0ad17138f 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -34,15 +34,15 @@ obj-$(CONFIG_$(PHASE_)SERIAL) += serial/ obj-$(CONFIG_$(PHASE_)SPI) += spi/ obj-$(CONFIG_$(PHASE_)TIMER) += timer/ obj-$(CONFIG_$(PHASE_)VIRTIO) += virtio/ -obj-$(CONFIG_$(XPL_)DM_MAILBOX) += mailbox/ -obj-$(CONFIG_$(XPL_)REMOTEPROC) += remoteproc/ -obj-$(CONFIG_$(XPL_)SYSINFO) += sysinfo/ +obj-$(CONFIG_$(PHASE_)DM_MAILBOX) += mailbox/ +obj-$(CONFIG_$(PHASE_)REMOTEPROC) += remoteproc/ +obj-$(CONFIG_$(PHASE_)SYSINFO) += sysinfo/ obj-$(CONFIG_$(PHASE_)SM) += sm/ obj-$(CONFIG_$(PHASE_)TPM) += tpm/ -obj-$(CONFIG_$(XPL_)NVME) += nvme/ +obj-$(CONFIG_$(PHASE_)NVME) += nvme/ obj-$(CONFIG_XEN) += xen/ -obj-$(CONFIG_$(XPL_)FPGA) += fpga/ -obj-$(CONFIG_$(XPL_)VIDEO) += video/ +obj-$(CONFIG_$(PHASE_)FPGA) += fpga/ +obj-$(CONFIG_$(PHASE_)VIDEO) += video/ obj-y += bus/ @@ -55,7 +55,7 @@ obj-$(CONFIG_SPL_CRYPTO) += crypto/ obj-$(CONFIG_SPL_MPC8XXX_INIT_DDR) += ddr/fsl/ obj-$(CONFIG_ARMADA_38X) += ddr/marvell/a38x/ obj-$(CONFIG_ARMADA_XP) += ddr/marvell/axp/ -obj-$(CONFIG_$(XPL_)ALTERA_SDRAM) += ddr/altera/ +obj-$(CONFIG_$(PHASE_)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/ diff --git a/drivers/adc/rockchip-saradc.c b/drivers/adc/rockchip-saradc.c index 7cf9735f60d..1515951403c 100644 --- a/drivers/adc/rockchip-saradc.c +++ b/drivers/adc/rockchip-saradc.c @@ -339,6 +339,14 @@ static const struct rockchip_saradc_data rk3399_saradc_data = { .stop = rockchip_saradc_stop_v1, }; +static const struct rockchip_saradc_data rk3528_saradc_data = { + .num_bits = 10, + .num_channels = 4, + .clk_rate = 1000000, + .channel_data = rockchip_saradc_channel_data_v2, + .start_channel = rockchip_saradc_start_channel_v2, +}; + static const struct rockchip_saradc_data rk3588_saradc_data = { .num_bits = 12, .num_channels = 8, @@ -354,6 +362,8 @@ static const struct udevice_id rockchip_saradc_ids[] = { .data = (ulong)&rk3066_tsadc_data }, { .compatible = "rockchip,rk3399-saradc", .data = (ulong)&rk3399_saradc_data }, + { .compatible = "rockchip,rk3528-saradc", + .data = (ulong)&rk3528_saradc_data }, { .compatible = "rockchip,rk3588-saradc", .data = (ulong)&rk3588_saradc_data }, { } diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 69fa9b707e0..20e33f2f27a 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_DWC_AHCI) += dwc_ahci.o obj-$(CONFIG_AHCI) += ahci-uclass.o -obj-$(CONFIG_$(XPL_)AHCI_PCI) += ahci-pci.o +obj-$(CONFIG_$(PHASE_)AHCI_PCI) += ahci-pci.o obj-$(CONFIG_SCSI_AHCI) += ahci.o obj-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o obj-$(CONFIG_FSL_SATA) += fsl_sata.o diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 8058d5ff1c3..38e953ee79c 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -420,7 +420,7 @@ static int ahci_fill_sg(struct ahci_uc_priv *uc_priv, u8 port, static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 opts) { - phys_addr_t pa = virt_to_phys((void *)pp->cmd_tbl); + phys_addr_t pa = virt_to_phys(pp->cmd_tbl); pp->cmd_slot->opts = cpu_to_le32(opts); pp->cmd_slot->status = 0; @@ -449,7 +449,7 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port) { struct ahci_ioports *pp = &(uc_priv->port[port]); void __iomem *port_mmio = pp->port_mmio; - u64 dma_addr; + phys_addr_t dma_addr; u32 port_status; void __iomem *mem; @@ -463,7 +463,6 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port) mem = memalign(2048, AHCI_PORT_PRIV_DMA_SZ); if (!mem) { - free(pp); printf("%s: No mem for table!\n", __func__); return -ENOMEM; } @@ -473,34 +472,32 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port) * First item in chunk of DMA memory: 32-slot command table, * 32 bytes each in size */ - pp->cmd_slot = - (struct ahci_cmd_hdr *)(uintptr_t)virt_to_phys((void *)mem); - debug("cmd_slot = %p\n", pp->cmd_slot); - mem += (AHCI_CMD_SLOT_SZ + 224); + pp->cmd_slot = (struct ahci_cmd_hdr *)mem; + mem += AHCI_CMD_SLOT_SZ * AHCI_MAX_CMD_SLOT; /* * Second item: Received-FIS area */ - pp->rx_fis = virt_to_phys((void *)mem); + pp->rx_fis = mem; mem += AHCI_RX_FIS_SZ; /* * Third item: data area for storing a single command * and its scatter-gather table */ - pp->cmd_tbl = virt_to_phys((void *)mem); - debug("cmd_tbl_dma = %lx\n", pp->cmd_tbl); + pp->cmd_tbl = mem; mem += AHCI_CMD_TBL_HDR; - pp->cmd_tbl_sg = - (struct ahci_sg *)(uintptr_t)virt_to_phys((void *)mem); + pp->cmd_tbl_sg = (struct ahci_sg *)(mem); - dma_addr = (ulong)pp->cmd_slot; - writel_with_flush(dma_addr, port_mmio + PORT_LST_ADDR); - writel_with_flush(dma_addr >> 32, port_mmio + PORT_LST_ADDR_HI); - dma_addr = (ulong)pp->rx_fis; - writel_with_flush(dma_addr, port_mmio + PORT_FIS_ADDR); - writel_with_flush(dma_addr >> 32, port_mmio + PORT_FIS_ADDR_HI); + dma_addr = virt_to_phys(pp->cmd_slot); + debug("cmd_slot_dma = 0x%08llx\n", (u64)dma_addr); + writel_with_flush(lower_32_bits(dma_addr), port_mmio + PORT_LST_ADDR); + writel_with_flush(upper_32_bits(dma_addr), port_mmio + PORT_LST_ADDR_HI); + dma_addr = virt_to_phys(pp->rx_fis); + debug("rx_fis_dma = 0x%08llx\n", (u64)dma_addr); + writel_with_flush(lower_32_bits(dma_addr), port_mmio + PORT_FIS_ADDR); + writel_with_flush(upper_32_bits(dma_addr), port_mmio + PORT_FIS_ADDR_HI); #ifdef CONFIG_SUNXI_AHCI sunxi_dma_init(port_mmio); @@ -736,17 +733,6 @@ static int ata_scsiop_read_write(struct ahci_uc_priv *uc_priv, is_write ? "WRITE" : "READ"); return -EIO; } - - /* If this transaction is a write, do a following flush. - * Writes in u-boot are so rare, and the logic to know when is - * the last write and do a flush only there is sufficiently - * difficult. Just do a flush after every write. This incurs, - * usually, one extra flush when the rare writes do happen. - */ - if (is_write) { - if (-EIO == ata_io_flush(uc_priv, pccb->target)) - return -EIO; - } user_buffer += transfer_size; user_buffer_size -= transfer_size; blocks -= now_blocks; @@ -846,6 +832,9 @@ static int ahci_scsi_exec(struct udevice *dev, struct scsi_cmd *pccb) case SCSI_INQUIRY: ret = ata_scsiop_inquiry(uc_priv, pccb); break; + case SCSI_SYNC_CACHE: + ret = ata_io_flush(uc_priv, pccb->target); + break; default: printf("Unsupport SCSI command 0x%02x\n", pccb->cmd[0]); return -ENOTSUPP; diff --git a/drivers/ata/dwc_ahsata.c b/drivers/ata/dwc_ahsata.c index 203f98edffc..d225289fe6e 100644 --- a/drivers/ata/dwc_ahsata.c +++ b/drivers/ata/dwc_ahsata.c @@ -7,6 +7,7 @@ #include <ahci.h> #include <blk.h> #include <bootdev.h> +#include <clk.h> #include <cpu_func.h> #include <dm.h> #include <dwc_ahsata.h> @@ -19,9 +20,11 @@ #include <sata.h> #include <asm/cache.h> #include <asm/io.h> +#if IS_ENABLED(CONFIG_ARCH_MX5) || IS_ENABLED(CONFIG_ARCH_MX6) #include <asm/arch/clock.h> #include <asm/arch/sys_proto.h> #include <asm/mach-imx/sata.h> +#endif #include <linux/bitops.h> #include <linux/ctype.h> #include <linux/delay.h> @@ -116,13 +119,12 @@ static int ahci_setup_oobr(struct ahci_uc_priv *uc_priv, int clk) return 0; } -static int ahci_host_init(struct ahci_uc_priv *uc_priv) +static int ahci_host_init(struct ahci_uc_priv *uc_priv, int clk) { u32 tmp, cap_save, num_ports; int i, j, timeout = 1000; struct sata_port_regs *port_mmio = NULL; struct sata_host_regs *host_mmio = uc_priv->mmio_base; - int clk = mxc_get_clock(MXC_SATA_CLK); cap_save = readl(&host_mmio->cap); cap_save |= SATA_HOST_CAP_SSS; @@ -330,6 +332,7 @@ static int ahci_fill_sg(struct ahci_uc_priv *uc_priv, u8 port, { struct ahci_ioports *pp = &uc_priv->port[port]; struct ahci_sg *ahci_sg = pp->cmd_tbl_sg; + phys_addr_t pa = virt_to_phys(buf); u32 sg_count, max_bytes; int i; @@ -341,9 +344,8 @@ static int ahci_fill_sg(struct ahci_uc_priv *uc_priv, u8 port, } for (i = 0; i < sg_count; i++) { - ahci_sg->addr = - cpu_to_le32((u32)buf + i * max_bytes); - ahci_sg->addr_hi = 0; + ahci_sg->addr = cpu_to_le32(lower_32_bits(pa)); + ahci_sg->addr_hi = cpu_to_le32(upper_32_bits(pa)); ahci_sg->flags_size = cpu_to_le32(0x3fffff & (buf_len < max_bytes ? (buf_len - 1) @@ -359,14 +361,14 @@ static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 cmd_slot, u32 opts) { struct ahci_cmd_hdr *cmd_hdr = (struct ahci_cmd_hdr *)(pp->cmd_slot + AHCI_CMD_SLOT_SZ * cmd_slot); + phys_addr_t pa = virt_to_phys(pp->cmd_tbl); memset(cmd_hdr, 0, AHCI_CMD_SLOT_SZ); cmd_hdr->opts = cpu_to_le32(opts); cmd_hdr->status = 0; - pp->cmd_slot->tbl_addr = cpu_to_le32((u32)pp->cmd_tbl & 0xffffffff); + pp->cmd_slot->tbl_addr = cpu_to_le32(lower_32_bits(pa)); #ifdef CONFIG_PHYS_64BIT - pp->cmd_slot->tbl_addr_hi = - cpu_to_le32((u32)(((pp->cmd_tbl) >> 16) >> 16)); + pp->cmd_slot->tbl_addr_hi = cpu_to_le32(upper_32_bits(pa)); #endif } @@ -404,7 +406,7 @@ static int ahci_exec_ata_cmd(struct ahci_uc_priv *uc_priv, u8 port, } ahci_fill_cmd_slot(pp, cmd_slot, opts); - flush_cache((int)(pp->cmd_slot), AHCI_PORT_PRIV_DMA_SZ); + flush_cache((ulong)(pp->cmd_slot), AHCI_PORT_PRIV_DMA_SZ); writel_with_flush(1 << cmd_slot, &port_mmio->ci); if (waiting_for_cmd_completed((u8 *)&port_mmio->ci, 10000, @@ -412,8 +414,8 @@ static int ahci_exec_ata_cmd(struct ahci_uc_priv *uc_priv, u8 port, printf("timeout exit!\n"); return -1; } - invalidate_dcache_range((int)(pp->cmd_slot), - (int)(pp->cmd_slot)+AHCI_PORT_PRIV_DMA_SZ); + invalidate_dcache_range((ulong)(pp->cmd_slot), + (ulong)(pp->cmd_slot) + AHCI_PORT_PRIV_DMA_SZ); debug("ahci_exec_ata_cmd: %d byte transferred.\n", pp->cmd_slot->status); if (!is_write) @@ -441,8 +443,9 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port) { struct ahci_ioports *pp = &uc_priv->port[port]; struct sata_port_regs *port_mmio = pp->port_mmio; + phys_addr_t dma_addr; u32 port_status; - u32 mem; + void *mem; int timeout = 10000000; debug("Enter start port: %d\n", port); @@ -453,22 +456,20 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port) return -1; } - mem = (u32)malloc(AHCI_PORT_PRIV_DMA_SZ + 1024); + mem = memalign(2048, AHCI_PORT_PRIV_DMA_SZ); if (!mem) { printf("No mem for table!\n"); return -ENOMEM; } - mem = (mem + 0x400) & (~0x3ff); /* Aligned to 1024-bytes */ - memset((u8 *)mem, 0, AHCI_PORT_PRIV_DMA_SZ); + memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ); /* * First item in chunk of DMA memory: 32-slot command table, * 32 bytes each in size */ pp->cmd_slot = (struct ahci_cmd_hdr *)mem; - debug("cmd_slot = 0x%x\n", (unsigned int) pp->cmd_slot); - mem += (AHCI_CMD_SLOT_SZ * DWC_AHSATA_MAX_CMD_SLOTS); + mem += AHCI_CMD_SLOT_SZ * AHCI_MAX_CMD_SLOT; /* * Second item: Received-FIS area, 256-Byte aligned @@ -481,14 +482,19 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port) * and its scatter-gather table */ pp->cmd_tbl = mem; - debug("cmd_tbl_dma = 0x%lx\n", pp->cmd_tbl); - mem += AHCI_CMD_TBL_HDR; + pp->cmd_tbl_sg = (struct ahci_sg *)mem; writel_with_flush(0x00004444, &port_mmio->dmacr); - pp->cmd_tbl_sg = (struct ahci_sg *)mem; - writel_with_flush((u32)pp->cmd_slot, &port_mmio->clb); - writel_with_flush(pp->rx_fis, &port_mmio->fb); + dma_addr = virt_to_phys(pp->cmd_slot); + debug("cmd_slot_dma = 0x%08llx\n", (u64)dma_addr); + writel_with_flush(lower_32_bits(dma_addr), &port_mmio->clb); + writel_with_flush(upper_32_bits(dma_addr), &port_mmio->clbu); + dma_addr = virt_to_phys(pp->cmd_slot); + debug("rx_fis_slot_dma = 0x%08llx\n", (u64)dma_addr); + writel_with_flush(lower_32_bits(dma_addr), &port_mmio->fb); + writel_with_flush(upper_32_bits(dma_addr), &port_mmio->fbu); + /* Enable FRE */ writel_with_flush((SATA_PORT_CMD_FRE | readl(&port_mmio->cmd)), @@ -910,17 +916,41 @@ int dwc_ahsata_scan(struct udevice *dev) int dwc_ahsata_probe(struct udevice *dev) { struct ahci_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct clk_bulk clk_bulk __maybe_unused; + struct clk clk __maybe_unused; + int sataclk; int ret; -#if defined(CONFIG_MX6) +#if IS_ENABLED(CONFIG_MX6) setup_sata(); #endif +#if IS_ENABLED(CONFIG_MX5) || IS_ENABLED(CONFIG_MX6) + sataclk = mxc_get_clock(MXC_SATA_CLK); +#else + ret = clk_get_bulk(dev, &clk_bulk); + if (ret) + return ret; + + ret = clk_enable_bulk(&clk_bulk); + if (ret) + return ret; + + ret = clk_get_by_name(dev, "sata", &clk); + if (ret) + return ret; + + sataclk = clk_get_rate(&clk); +#endif + if (IS_ERR_VALUE(sataclk)) { + log_err("Unable to get SATA clock rate\n"); + return -EINVAL; + } uc_priv->host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_NO_ATAPI; uc_priv->mmio_base = dev_read_addr_ptr(dev); /* initialize adapter */ - ret = ahci_host_init(uc_priv); + ret = ahci_host_init(uc_priv, sataclk); if (ret) return ret; @@ -962,7 +992,6 @@ U_BOOT_DRIVER(dwc_ahsata_blk) = { .ops = &dwc_ahsata_blk_ops, }; -#if CONFIG_IS_ENABLED(DWC_AHSATA_AHCI) struct ahci_ops dwc_ahsata_ahci_ops = { .port_status = dwc_ahsata_port_status, .reset = dwc_ahsata_bus_reset, @@ -970,7 +999,9 @@ struct ahci_ops dwc_ahsata_ahci_ops = { }; static const struct udevice_id dwc_ahsata_ahci_ids[] = { + { .compatible = "fsl,imx53-ahci" }, { .compatible = "fsl,imx6q-ahci" }, + { .compatible = "fsl,imx6qp-ahci" }, { } }; @@ -981,4 +1012,3 @@ U_BOOT_DRIVER(dwc_ahsata_ahci) = { .ops = &dwc_ahsata_ahci_ops, .probe = dwc_ahsata_probe, }; -#endif diff --git a/drivers/ata/dwc_ahsata_priv.h b/drivers/ata/dwc_ahsata_priv.h index 5b0579ae115..0c2cd5446b5 100644 --- a/drivers/ata/dwc_ahsata_priv.h +++ b/drivers/ata/dwc_ahsata_priv.h @@ -7,8 +7,6 @@ #ifndef __DWC_AHSATA_PRIV_H__ #define __DWC_AHSATA_PRIV_H__ -#define DWC_AHSATA_MAX_CMD_SLOTS 32 - /* Max host controller numbers */ #define SATA_HC_MAX_NUM 4 /* Max command queue depth per host controller */ diff --git a/drivers/block/Makefile b/drivers/block/Makefile index ee290620545..f5a9d8637a3 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_$(PHASE_)BLK) += blk-uclass.o -ifndef CONFIG_$(XPL_)BLK +ifndef CONFIG_$(PHASE_)BLK obj-$(CONFIG_SPL_LEGACY_BLOCK) += blk_legacy.o endif diff --git a/drivers/block/blkmap.c b/drivers/block/blkmap.c index 34eed1380dc..473c65b5911 100644 --- a/drivers/block/blkmap.c +++ b/drivers/block/blkmap.c @@ -17,6 +17,30 @@ struct blkmap; /** + * define BLKMAP_SLICE_LINEAR - Linear mapping to another block device + * + * This blkmap slice type is used for mapping to other existing block + * devices. + */ +#define BLKMAP_SLICE_LINEAR BIT(0) + +/** + * define BLKMAP_SLICE_MEM - Linear mapping to memory based block device + * + * This blkmap slice type is used for mapping to memory based block + * devices, like ramdisks. + */ +#define BLKMAP_SLICE_MEM BIT(1) + +/** + * define BLKMAP_SLICE_PRESERVE - Preserved blkmap slice + * + * This blkmap slice is intended to be preserved, and it's + * information passed on to a later stage, like OS. + */ +#define BLKMAP_SLICE_PRESERVE BIT(2) + +/** * struct blkmap_slice - Region mapped to a blkmap * * Common data for a region mapped to a blkmap, specialized by each @@ -25,12 +49,14 @@ struct blkmap; * @node: List node used to associate this slice with a blkmap * @blknr: Start block number of the mapping * @blkcnt: Number of blocks covered by this mapping + * @attr: Attributes of blkmap slice */ struct blkmap_slice { struct list_head node; lbaint_t blknr; lbaint_t blkcnt; + uint attr; /** * @read: - Read from slice @@ -169,6 +195,7 @@ int blkmap_map_linear(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, .slice = { .blknr = blknr, .blkcnt = blkcnt, + .attr = BLKMAP_SLICE_LINEAR, .read = blkmap_linear_read, .write = blkmap_linear_write, @@ -234,7 +261,7 @@ static void blkmap_mem_destroy(struct blkmap *bm, struct blkmap_slice *bms) } int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, - void *addr, bool remapped) + void *addr, bool remapped, bool preserve) { struct blkmap *bm = dev_get_plat(dev); struct blkmap_mem *bmm; @@ -248,6 +275,7 @@ int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, .slice = { .blknr = blknr, .blkcnt = blkcnt, + .attr = BLKMAP_SLICE_MEM, .read = blkmap_mem_read, .write = blkmap_mem_write, @@ -258,6 +286,9 @@ int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, .remapped = remapped, }; + if (preserve) + bmm->slice.attr |= BLKMAP_SLICE_PRESERVE; + err = blkmap_slice_add(bm, &bmm->slice); if (err) free(bmm); @@ -268,11 +299,11 @@ int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, int blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, void *addr) { - return __blkmap_map_mem(dev, blknr, blkcnt, addr, false); + return __blkmap_map_mem(dev, blknr, blkcnt, addr, false, false); } int blkmap_map_pmem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, - phys_addr_t paddr) + phys_addr_t paddr, bool preserve) { struct blkmap *bm = dev_get_plat(dev); struct blk_desc *bd = dev_get_uclass_plat(bm->blk); @@ -283,7 +314,7 @@ int blkmap_map_pmem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, if (!addr) return -ENOMEM; - err = __blkmap_map_mem(dev, blknr, blkcnt, addr, true); + err = __blkmap_map_mem(dev, blknr, blkcnt, addr, true, preserve); if (err) unmap_sysmem(addr); @@ -486,6 +517,49 @@ err: return err; } +static bool blkmap_mem_preserve_slice(struct blkmap_slice *bms) +{ + return (bms->attr & (BLKMAP_SLICE_MEM | BLKMAP_SLICE_PRESERVE)) == + (BLKMAP_SLICE_MEM | BLKMAP_SLICE_PRESERVE); +} + +int blkmap_get_preserved_pmem_slices(int (*cb)(void *ctx, u64 addr, + u64 size), void *ctx) +{ + int ret; + u64 addr, size; + struct udevice *dev; + struct uclass *uc; + struct blkmap *bm; + struct blkmap_mem *bmm; + struct blkmap_slice *bms; + struct blk_desc *bd; + + if (!cb) { + log_debug("%s: No callback passed to the function\n", __func__); + return 0; + } + + uclass_id_foreach_dev(UCLASS_BLKMAP, dev, uc) { + bm = dev_get_plat(dev); + bd = dev_get_uclass_plat(bm->blk); + + list_for_each_entry(bms, &bm->slices, node) { + if (!blkmap_mem_preserve_slice(bms)) + continue; + + bmm = container_of(bms, struct blkmap_mem, slice); + addr = (u64)(uintptr_t)bmm->addr; + size = (u64)bms->blkcnt << bd->log2blksz; + ret = cb(ctx, addr, size); + if (ret) + return ret; + } + } + + return 0; +} + int blkmap_destroy(struct udevice *dev) { int err; diff --git a/drivers/block/blkmap_helper.c b/drivers/block/blkmap_helper.c index bfba14110d2..2f1bc28ee5d 100644 --- a/drivers/block/blkmap_helper.c +++ b/drivers/block/blkmap_helper.c @@ -28,7 +28,7 @@ int blkmap_create_ramdisk(const char *label, ulong image_addr, ulong image_size, 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); + ret = blkmap_map_pmem(bm_dev, 0, blknum, image_addr, true); if (ret) { log_err("Unable to map %#llx at block %d : %d\n", (unsigned long long)image_addr, 0, ret); diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig index 0080d2a165c..99b6c7534fd 100644 --- a/drivers/bootcount/Kconfig +++ b/drivers/bootcount/Kconfig @@ -183,6 +183,9 @@ config BOOTCOUNT_BOOTLIMIT counter being cleared. If set to 0, do not set a boot limit in the environment. +config BOOTCOUNT_ALTBOOTCMD + string "Alternative boot command when BOOTLIMIT is reached" + config SYS_BOOTCOUNT_SINGLEWORD bool "Use single word to pack boot count and magic value" depends on BOOTCOUNT_GENERIC diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 7daf8247247..2d242bc2886 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -8,4 +8,4 @@ obj-$(CONFIG_TI_PWMSS) += ti-pwmss.o obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o endif -obj-$(CONFIG_$(XPL_)TI_SYSC) += ti-sysc.o +obj-$(CONFIG_$(PHASE_)TI_SYSC) += ti-sysc.o diff --git a/drivers/button/button-qcom-pmic.c b/drivers/button/button-qcom-pmic.c index f9f0948ae09..e3bb9bd758a 100644 --- a/drivers/button/button-qcom-pmic.c +++ b/drivers/button/button-qcom-pmic.c @@ -73,25 +73,25 @@ static const struct qcom_pmic_btn_data qcom_pmic_btn_data_table[] = { .compatible = "qcom,pm8941-pwrkey", .status_bit = PON_KPDPWR_N_SET, .code = KEY_ENTER, - .label = "pwrkey", + .label = "Power Button", }, { .compatible = "qcom,pm8941-resin", .status_bit = PON_RESIN_N_SET, .code = KEY_DOWN, - .label = "vol_down", + .label = "Volume Down", }, { .compatible = "qcom,pmk8350-pwrkey", .status_bit = PON_GEN3_KPDPWR_N_SET, .code = KEY_ENTER, - .label = "pwrkey", + .label = "Power Button", }, { .compatible = "qcom,pmk8350-resin", .status_bit = PON_GEN3_RESIN_N_SET, .code = KEY_DOWN, - .label = "vol_down", + .label = "Volume Down", }, }; diff --git a/drivers/button/button-uclass.c b/drivers/button/button-uclass.c index 729983d5870..025917887e8 100644 --- a/drivers/button/button-uclass.c +++ b/drivers/button/button-uclass.c @@ -21,7 +21,7 @@ int button_get_by_label(const char *label, struct udevice **devp) struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); /* Ignore the top-level button node */ - if (uc_plat->label && !strcmp(label, uc_plat->label)) + if (uc_plat->label && !strcasecmp(label, uc_plat->label)) return uclass_get_device_tail(dev, 0, devp); } diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index fe0e49f6112..8411205ee04 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_$(PHASE_)CLK_GPIO) += clk-gpio.o obj-$(CONFIG_$(PHASE_)CLK_STUB) += clk-stub.o obj-y += adi/ +obj-y += airoha/ obj-y += analogbits/ obj-y += imx/ obj-$(CONFIG_CLK_JH7110) += starfive/ diff --git a/drivers/clk/airoha/Makefile b/drivers/clk/airoha/Makefile new file mode 100644 index 00000000000..1744c5f7236 --- /dev/null +++ b/drivers/clk/airoha/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +# Core +obj-$(CONFIG_ARCH_AIROHA) += clk-airoha.o diff --git a/drivers/clk/airoha/clk-airoha.c b/drivers/clk/airoha/clk-airoha.c new file mode 100644 index 00000000000..1b2c4c98de5 --- /dev/null +++ b/drivers/clk/airoha/clk-airoha.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Based on the Linux clk-en7523.c but majorly reworked + * for U-Boot that doesn't require CCF subsystem. + * + * Major modification, support for set_rate, realtime + * get_rate and split for reset part to a different driver. + * + * Author: Lorenzo Bianconi <lorenzo@kernel.org> (original driver) + * Christian Marangi <ansuelsmth@gmail.com> + */ + +#include <clk-uclass.h> +#include <dm.h> +#include <dm/devres.h> +#include <dm/device_compat.h> +#include <dm/lists.h> +#include <regmap.h> +#include <syscon.h> + +#include <dt-bindings/clock/en7523-clk.h> + +#define REG_GSW_CLK_DIV_SEL 0x1b4 +#define REG_EMI_CLK_DIV_SEL 0x1b8 +#define REG_BUS_CLK_DIV_SEL 0x1bc +#define REG_SPI_CLK_DIV_SEL 0x1c4 +#define REG_SPI_CLK_FREQ_SEL 0x1c8 +#define REG_NPU_CLK_DIV_SEL 0x1fc + +#define REG_NP_SCU_PCIC 0x88 +#define REG_NP_SCU_SSTR 0x9c +#define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13) +#define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11) +#define REG_CRYPTO_CLKSRC2 0x20c + +#define EN7581_MAX_CLKS 9 + +struct airoha_clk_desc { + int id; + const char *name; + u32 base_reg; + u8 base_bits; + u8 base_shift; + union { + const unsigned int *base_values; + unsigned int base_value; + }; + size_t n_base_values; + + u16 div_reg; + u8 div_bits; + u8 div_shift; + u16 div_val0; + u8 div_step; + u8 div_offset; +}; + +struct airoha_clk_priv { + struct regmap *chip_scu_map; + struct airoha_clk_soc_data *data; +}; + +struct airoha_clk_soc_data { + u32 num_clocks; + const struct airoha_clk_desc *descs; +}; + +static const u32 gsw_base[] = { 400000000, 500000000 }; +static const u32 slic_base[] = { 100000000, 3125000 }; + +static const u32 emi7581_base[] = { 540000000, 480000000, 400000000, 300000000 }; +static const u32 bus7581_base[] = { 600000000, 540000000 }; +static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 }; +static const u32 crypto_base[] = { 540000000, 480000000 }; +static const u32 emmc7581_base[] = { 200000000, 150000000 }; + +static const struct airoha_clk_desc en7581_base_clks[EN7581_MAX_CLKS] = { + [EN7523_CLK_GSW] = { + .id = EN7523_CLK_GSW, + .name = "gsw", + + .base_reg = REG_GSW_CLK_DIV_SEL, + .base_bits = 1, + .base_shift = 8, + .base_values = gsw_base, + .n_base_values = ARRAY_SIZE(gsw_base), + + .div_bits = 3, + .div_shift = 0, + .div_step = 1, + .div_offset = 1, + }, + [EN7523_CLK_EMI] = { + .id = EN7523_CLK_EMI, + .name = "emi", + + .base_reg = REG_EMI_CLK_DIV_SEL, + .base_bits = 2, + .base_shift = 8, + .base_values = emi7581_base, + .n_base_values = ARRAY_SIZE(emi7581_base), + + .div_bits = 3, + .div_shift = 0, + .div_step = 1, + .div_offset = 1, + }, + [EN7523_CLK_BUS] = { + .id = EN7523_CLK_BUS, + .name = "bus", + + .base_reg = REG_BUS_CLK_DIV_SEL, + .base_bits = 1, + .base_shift = 8, + .base_values = bus7581_base, + .n_base_values = ARRAY_SIZE(bus7581_base), + + .div_bits = 3, + .div_shift = 0, + .div_step = 1, + .div_offset = 1, + }, + [EN7523_CLK_SLIC] = { + .id = EN7523_CLK_SLIC, + .name = "slic", + + .base_reg = REG_SPI_CLK_FREQ_SEL, + .base_bits = 1, + .base_shift = 0, + .base_values = slic_base, + .n_base_values = ARRAY_SIZE(slic_base), + + .div_reg = REG_SPI_CLK_DIV_SEL, + .div_bits = 5, + .div_shift = 24, + .div_val0 = 20, + .div_step = 2, + }, + [EN7523_CLK_SPI] = { + .id = EN7523_CLK_SPI, + .name = "spi", + + .base_reg = REG_SPI_CLK_DIV_SEL, + + .base_value = 400000000, + + .div_bits = 5, + .div_shift = 8, + .div_val0 = 40, + .div_step = 2, + }, + [EN7523_CLK_NPU] = { + .id = EN7523_CLK_NPU, + .name = "npu", + + .base_reg = REG_NPU_CLK_DIV_SEL, + .base_bits = 2, + .base_shift = 8, + .base_values = npu7581_base, + .n_base_values = ARRAY_SIZE(npu7581_base), + + .div_bits = 3, + .div_shift = 0, + .div_step = 1, + .div_offset = 1, + }, + [EN7523_CLK_CRYPTO] = { + .id = EN7523_CLK_CRYPTO, + .name = "crypto", + + .base_reg = REG_CRYPTO_CLKSRC2, + .base_bits = 1, + .base_shift = 0, + .base_values = crypto_base, + .n_base_values = ARRAY_SIZE(crypto_base), + }, + [EN7581_CLK_EMMC] = { + .id = EN7581_CLK_EMMC, + .name = "emmc", + + .base_reg = REG_CRYPTO_CLKSRC2, + .base_bits = 1, + .base_shift = 12, + .base_values = emmc7581_base, + .n_base_values = ARRAY_SIZE(emmc7581_base), + } +}; + +static u32 airoha_clk_get_base_rate(const struct airoha_clk_desc *desc, u32 val) +{ + if (!desc->base_bits) + return desc->base_value; + + val >>= desc->base_shift; + val &= (1 << desc->base_bits) - 1; + + if (val >= desc->n_base_values) + return 0; + + return desc->base_values[val]; +} + +static u32 airoha_clk_get_div(const struct airoha_clk_desc *desc, u32 val) +{ + if (!desc->div_bits) + return 1; + + val >>= desc->div_shift; + val &= (1 << desc->div_bits) - 1; + + if (!val && desc->div_val0) + return desc->div_val0; + + return (val + desc->div_offset) * desc->div_step; +} + +static int airoha_clk_enable(struct clk *clk) +{ + struct airoha_clk_priv *priv = dev_get_priv(clk->dev); + struct airoha_clk_soc_data *data = priv->data; + int id = clk->id; + + if (id > data->num_clocks) + return -EINVAL; + + return 0; +} + +static int airoha_clk_disable(struct clk *clk) +{ + return 0; +} + +static ulong airoha_clk_get_rate(struct clk *clk) +{ + struct airoha_clk_priv *priv = dev_get_priv(clk->dev); + struct airoha_clk_soc_data *data = priv->data; + const struct airoha_clk_desc *desc; + struct regmap *map = priv->chip_scu_map; + int id = clk->id; + u32 reg, val; + ulong rate; + int ret; + + if (id > data->num_clocks) { + dev_err(clk->dev, "Invalid clk ID %d\n", id); + return 0; + } + + desc = &data->descs[id]; + + ret = regmap_read(map, desc->base_reg, &val); + if (ret) { + dev_err(clk->dev, "Failed to read reg for clock %s\n", + desc->name); + return 0; + } + + rate = airoha_clk_get_base_rate(desc, val); + + reg = desc->div_reg ? desc->div_reg : desc->base_reg; + ret = regmap_read(map, reg, &val); + if (ret) { + dev_err(clk->dev, "Failed to read reg for clock %s\n", + desc->name); + return 0; + } + + rate /= airoha_clk_get_div(desc, val); + + return rate; +} + +static int airoha_clk_search_rate(const struct airoha_clk_desc *desc, int div, + ulong rate) +{ + int i; + + /* Single base rate */ + if (!desc->base_bits) { + if (rate != desc->base_value / div) + goto err; + + return 0; + } + + /* Check every base rate with provided divisor */ + for (i = 0; i < desc->n_base_values; i++) + if (rate == desc->base_values[i] / div) + return i; + +err: + return -EINVAL; +} + +static ulong airoha_clk_set_rate(struct clk *clk, ulong rate) +{ + struct airoha_clk_priv *priv = dev_get_priv(clk->dev); + struct airoha_clk_soc_data *data = priv->data; + const struct airoha_clk_desc *desc; + struct regmap *map = priv->chip_scu_map; + int div_val, base_val; + u32 reg, val, mask; + int id = clk->id; + int div; + int ret; + + if (id > data->num_clocks) { + dev_err(clk->dev, "Invalid clk ID %d\n", id); + return 0; + } + + desc = &data->descs[id]; + + if (!desc->base_bits && !desc->div_bits) { + dev_err(clk->dev, "Can't set rate for fixed clock %s\n", + desc->name); + return 0; + } + + if (!desc->div_bits) { + /* Divisor not supported, just search in base rate */ + div_val = 0; + base_val = airoha_clk_search_rate(desc, 1, rate); + if (base_val < 0) { + dev_err(clk->dev, "Invalid rate for clock %s\n", + desc->name); + return 0; + } + } else { + div_val = 0; + + /* Check if div0 satisfy the request */ + if (desc->div_val0) { + base_val = airoha_clk_search_rate(desc, desc->div_val0, + rate); + if (base_val >= 0) { + div_val = 0; + goto apply; + } + + /* Skip checking first divisor val */ + div_val = 1; + } + + /* Simulate rate with every divisor supported */ + for (div_val = div_val + desc->div_offset; + div_val < BIT(desc->div_bits) - 1; div_val++) { + div = div_val * desc->div_step; + + base_val = airoha_clk_search_rate(desc, div, rate); + if (base_val >= 0) + break; + } + + if (div_val == BIT(desc->div_bits) - 1) { + dev_err(clk->dev, "Invalid rate for clock %s\n", + desc->name); + return 0; + } + } + +apply: + if (desc->div_bits) { + reg = desc->div_reg ? desc->div_reg : desc->base_reg; + + mask = (BIT(desc->div_bits) - 1) << desc->div_shift; + val = div_val << desc->div_shift; + + ret = regmap_update_bits(map, reg, mask, val); + if (ret) { + dev_err(clk->dev, "Failed to update div reg for clock %s\n", + desc->name); + return 0; + } + } + + if (desc->base_bits) { + mask = (BIT(desc->base_bits) - 1) << desc->base_shift; + val = base_val << desc->base_shift; + + ret = regmap_update_bits(map, desc->base_reg, mask, val); + if (ret) { + dev_err(clk->dev, "Failed to update reg for clock %s\n", + desc->name); + return 0; + } + } + + return rate; +} + +const struct clk_ops airoha_clk_ops = { + .enable = airoha_clk_enable, + .disable = airoha_clk_disable, + .get_rate = airoha_clk_get_rate, + .set_rate = airoha_clk_set_rate, +}; + +static int airoha_clk_probe(struct udevice *dev) +{ + struct airoha_clk_priv *priv = dev_get_priv(dev); + ofnode chip_scu_node; + + chip_scu_node = ofnode_by_compatible(ofnode_null(), + "airoha,en7581-chip-scu"); + if (!ofnode_valid(chip_scu_node)) + return -EINVAL; + + priv->chip_scu_map = syscon_node_to_regmap(chip_scu_node); + if (IS_ERR(priv->chip_scu_map)) + return PTR_ERR(priv->chip_scu_map); + + priv->data = (void *)dev_get_driver_data(dev); + + return 0; +} + +static int airoha_clk_bind(struct udevice *dev) +{ + struct udevice *rst_dev; + int ret = 0; + + if (CONFIG_IS_ENABLED(RESET_AIROHA)) { + ret = device_bind_driver_to_node(dev, "airoha-reset", "reset", + dev_ofnode(dev), &rst_dev); + if (ret) + debug("Warning: failed to bind reset controller\n"); + } + + return ret; +} + +static const struct airoha_clk_soc_data en7581_data = { + .num_clocks = ARRAY_SIZE(en7581_base_clks), + .descs = en7581_base_clks, +}; + +static const struct udevice_id airoha_clk_ids[] = { + { .compatible = "airoha,en7581-scu", + .data = (ulong)&en7581_data, + }, + { } +}; + +U_BOOT_DRIVER(airoha_clk) = { + .name = "clk-airoha", + .id = UCLASS_CLK, + .of_match = airoha_clk_ids, + .probe = airoha_clk_probe, + .bind = airoha_clk_bind, + .priv_auto = sizeof(struct airoha_clk_priv), + .ops = &airoha_clk_ops, +}; diff --git a/drivers/clk/altera/clk-agilex5.c b/drivers/clk/altera/clk-agilex5.c index 716c71598bc..fb1e72ffc5c 100644 --- a/drivers/clk/altera/clk-agilex5.c +++ b/drivers/clk/altera/clk-agilex5.c @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2024 Intel Corporation <www.intel.com> + * Copyright (C) 2025 Altera Corporation <www.altera.com> */ -#include <clk-uclass.h> #include <config.h> -#include <errno.h> -#include <dm.h> #include <log.h> +#include <dm.h> +#include <errno.h> #include <stdarg.h> #include <stdio.h> #include <time.h> @@ -23,9 +23,14 @@ #include <linux/types.h> #include <asm/arch/clock_manager.h> #include <dt-bindings/clock/agilex5-clock.h> +#include <wait_bit.h> +#include <clk-uclass.h> DECLARE_GLOBAL_DATA_PTR; +#define CLKMGR_CTRL_SWCTRLBTCLKEN_MASK BIT(8) +#define CLKMGR_CTRL_SWCTRLBTCLKSEL_MASK BIT(9) + struct socfpga_clk_plat { void __iomem *regs; }; @@ -36,21 +41,30 @@ struct socfpga_clk_plat { */ static void clk_write_bypass_mainpll(struct socfpga_clk_plat *plat, u32 val) { + uintptr_t base_addr = (uintptr_t)plat->regs; + CM_REG_WRITEL(plat, val, CLKMGR_MAINPLL_BYPASS); - cm_wait_for_fsm(); + wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), CLKMGR_STAT_BUSY, + false, 20000, false); } static void clk_write_bypass_perpll(struct socfpga_clk_plat *plat, u32 val) { + uintptr_t base_addr = (uintptr_t)plat->regs; + CM_REG_WRITEL(plat, val, CLKMGR_PERPLL_BYPASS); - cm_wait_for_fsm(); + wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), CLKMGR_STAT_BUSY, + false, 20000, false); } /* function to write the ctrl register which requires a poll of the busy bit */ static void clk_write_ctrl(struct socfpga_clk_plat *plat, u32 val) { + uintptr_t base_addr = (uintptr_t)plat->regs; + CM_REG_WRITEL(plat, val, CLKMGR_CTRL); - cm_wait_for_fsm(); + wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), CLKMGR_STAT_BUSY, + false, 20000, false); } static const struct { @@ -59,15 +73,6 @@ static const struct { u32 mask; } membus_pll[] = { { - MEMBUS_CLKSLICE_REG, - /* - * BIT[7:7] - * Enable source synchronous mode - */ - BIT(7), - BIT(7) - }, - { MEMBUS_SYNTHCALFOSC_INIT_CENTERFREQ_REG, /* * BIT[0:0] @@ -238,6 +243,7 @@ static void clk_basic_init(struct udevice *dev, { struct socfpga_clk_plat *plat = dev_get_plat(dev); u32 vcocalib; + uintptr_t base_addr = (uintptr_t)plat->regs; if (!cfg) return; @@ -249,7 +255,8 @@ static void clk_basic_init(struct udevice *dev, CM_REG_SETBITS(plat, CLKMGR_PERPLL_PLLGLOB, CLKMGR_PLLGLOB_PD_MASK | CLKMGR_PLLGLOB_RST_MASK); - cm_wait_for_lock(CLKMGR_STAT_ALLPLL_LOCKED_MASK); + wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), + CLKMGR_STAT_ALLPLL_LOCKED_MASK, true, 20000, false); /* Put both PLLs in bypass */ clk_write_bypass_mainpll(plat, CLKMGR_BYPASS_MAINPLL_ALL); @@ -264,9 +271,14 @@ static void clk_basic_init(struct udevice *dev, CM_REG_READL(plat, CLKMGR_CTRL) & ~CLKMGR_CTRL_BOOTMODE); } else { #ifdef CONFIG_XPL_BUILD - /* Always force clock manager into boot mode before any configuration */ - clk_write_ctrl(plat, - CM_REG_READL(plat, CLKMGR_CTRL) | CLKMGR_CTRL_BOOTMODE); + /* + * Configure HPS Internal Oscillator as default boot_clk source, + * always force clock manager into boot mode before any configuration + */ + clk_write_ctrl(plat, CM_REG_READL(plat, CLKMGR_CTRL) | + CLKMGR_CTRL_BOOTMODE | + CLKMGR_CTRL_SWCTRLBTCLKEN_MASK | + CLKMGR_CTRL_SWCTRLBTCLKSEL_MASK); #else /* Skip clock configuration in SSBL if it's not in boot mode */ if (!(CM_REG_READL(plat, CLKMGR_CTRL) & CLKMGR_CTRL_BOOTMODE)) @@ -365,7 +377,8 @@ static void clk_basic_init(struct udevice *dev, CLKMGR_PLLCX_EN_SET_MSK, CLKMGR_PERPLL_PLLC3); - cm_wait_for_lock(CLKMGR_STAT_ALLPLL_LOCKED_MASK); + wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), + CLKMGR_STAT_ALLPLL_LOCKED_MASK, true, 20000, false); CM_REG_WRITEL(plat, CLKMGR_LOSTLOCK_SET_MASK, CLKMGR_MAINPLL_LOSTLOCK); CM_REG_WRITEL(plat, CLKMGR_LOSTLOCK_SET_MASK, CLKMGR_PERPLL_LOSTLOCK); diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 199ca6eaa37..9e3b5191767 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -97,7 +97,7 @@ static int clk_composite_disable(struct clk *clk) return 0; } -struct clk *clk_register_composite(struct device *dev, const char *name, +struct clk *clk_register_composite(struct udevice *dev, const char *name, const char * const *parent_names, int num_parents, struct clk *mux, const struct clk_ops *mux_ops, @@ -149,7 +149,7 @@ struct clk *clk_register_composite(struct device *dev, const char *name, clk = &composite->clk; clk->flags = flags; ret = clk_register(clk, UBOOT_DM_CLK_COMPOSITE, name, - parent_names[clk_composite_get_parent(clk)]); + clk_resolve_parent_clk(dev, parent_names[clk_composite_get_parent(clk)])); if (ret) { clk = ERR_PTR(ret); goto err; diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index aa210e3d15f..e692b9c2167 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -183,7 +183,7 @@ const struct clk_ops clk_divider_ops = { .set_rate = clk_divider_set_rate, }; -static struct clk *_register_divider(struct device *dev, const char *name, +static struct clk *_register_divider(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, const struct clk_div_table *table) @@ -218,7 +218,8 @@ static struct clk *_register_divider(struct device *dev, const char *name, clk = &div->clk; clk->flags = flags; - ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name); + ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, + clk_resolve_parent_clk(dev, parent_name)); if (ret) { kfree(div); return ERR_PTR(ret); @@ -227,7 +228,7 @@ static struct clk *_register_divider(struct device *dev, const char *name, return clk; } -struct clk *clk_register_divider(struct device *dev, const char *name, +struct clk *clk_register_divider(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags) diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index 068798cf9b0..4eb8be728e6 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -37,7 +37,7 @@ const struct clk_ops ccf_clk_fixed_factor_ops = { .get_rate = clk_factor_recalc_rate, }; -struct clk *clk_hw_register_fixed_factor(struct device *dev, +struct clk *clk_hw_register_fixed_factor(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div) { @@ -56,7 +56,7 @@ struct clk *clk_hw_register_fixed_factor(struct device *dev, clk->flags = flags; ret = clk_register(clk, UBOOT_DM_CLK_IMX_FIXED_FACTOR, name, - parent_name); + clk_resolve_parent_clk(dev, parent_name)); if (ret) { kfree(fix); return ERR_PTR(ret); @@ -65,7 +65,7 @@ struct clk *clk_hw_register_fixed_factor(struct device *dev, return clk; } -struct clk *clk_register_fixed_factor(struct device *dev, const char *name, +struct clk *clk_register_fixed_factor(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div) { diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index bf1c6a93b46..256ff108991 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -117,7 +117,7 @@ const struct clk_ops clk_gate_ops = { .get_rate = clk_generic_get_rate, }; -struct clk *clk_register_gate(struct device *dev, const char *name, +struct clk *clk_register_gate(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock) @@ -149,7 +149,8 @@ struct clk *clk_register_gate(struct device *dev, const char *name, clk = &gate->clk; clk->flags = flags; - ret = clk_register(clk, UBOOT_DM_CLK_GATE, name, parent_name); + ret = clk_register(clk, UBOOT_DM_CLK_GATE, name, + clk_resolve_parent_clk(dev, parent_name)); if (ret) { kfree(gate); return ERR_PTR(ret); diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 62477e15d27..d7411f8f282 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -113,6 +113,11 @@ int clk_mux_fetch_parent_index(struct clk *clk, struct clk *parent) for (i = 0; i < mux->num_parents; i++) { if (!strcmp(parent->dev->name, mux->parent_names[i])) return i; + if (!strcmp(parent->dev->name, + clk_resolve_parent_clk(clk->dev, + mux->parent_names[i]))) + return i; + } return -EINVAL; @@ -159,15 +164,15 @@ const struct clk_ops clk_mux_ops = { .set_parent = clk_mux_set_parent, }; -struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, +struct clk *clk_register_mux(struct udevice *dev, const char *name, const char * const *parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u32 mask, - u8 clk_mux_flags, u32 *table) + void __iomem *reg, u8 shift, u8 width, + u8 clk_mux_flags) { + u32 mask = BIT(width) - 1; struct clk_mux *mux; struct clk *clk; - u8 width = 0; int ret; if (clk_mux_flags & CLK_MUX_HIWORD_MASK) { @@ -192,7 +197,7 @@ struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, mux->shift = shift; mux->mask = mask; mux->flags = clk_mux_flags; - mux->table = table; + mux->table = NULL; #if IS_ENABLED(CONFIG_SANDBOX_CLK_CCF) mux->io_mux_val = *(u32 *)reg; #endif @@ -207,7 +212,8 @@ struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, * for the corresponding clock (to do that define .set_parent() method). */ ret = clk_register(clk, UBOOT_DM_CLK_CCF_MUX, name, - parent_names[clk_mux_get_parent(clk)]); + clk_resolve_parent_clk(dev, + parent_names[clk_mux_get_parent(clk)])); if (ret) { kfree(mux); return ERR_PTR(ret); @@ -216,35 +222,6 @@ struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, return clk; } -struct clk *clk_register_mux_table(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, - unsigned long flags, - void __iomem *reg, u8 shift, u32 mask, - u8 clk_mux_flags, u32 *table) -{ - struct clk *clk; - - clk = clk_hw_register_mux_table(dev, name, parent_names, num_parents, - flags, reg, shift, mask, clk_mux_flags, - table); - if (IS_ERR(clk)) - return ERR_CAST(clk); - return clk; -} - -struct clk *clk_register_mux(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, - unsigned long flags, - void __iomem *reg, u8 shift, u8 width, - u8 clk_mux_flags) -{ - u32 mask = BIT(width) - 1; - - return clk_register_mux_table(dev, name, parent_names, num_parents, - flags, reg, shift, mask, clk_mux_flags, - NULL); -} - U_BOOT_DRIVER(ccf_clk_mux) = { .name = UBOOT_DM_CLK_CCF_MUX, .id = UCLASS_CLK, diff --git a/drivers/clk/clk-stub.c b/drivers/clk/clk-stub.c index 5fbbb07b7f7..5f5aca41d5b 100644 --- a/drivers/clk/clk-stub.c +++ b/drivers/clk/clk-stub.c @@ -14,7 +14,7 @@ static const struct udevice_id nop_parent_ids[] = { { .compatible = "qcom,rpm-proc" }, { .compatible = "qcom,glink-rpm" }, - { .compatible = "qcom,rpm-sm6115" }, + { .compatible = "qcom,glink-smd-rpm" }, { } }; @@ -50,6 +50,8 @@ static struct clk_ops stub_clk_ops = { static const struct udevice_id stub_clk_ids[] = { { .compatible = "qcom,rpmcc" }, + { .compatible = "qcom,sdm845-rpmh-clk" }, + { .compatible = "qcom,sc7280-rpmh-clk" }, { .compatible = "qcom,sm8150-rpmh-clk" }, { .compatible = "qcom,sm8250-rpmh-clk" }, { .compatible = "qcom,sm8550-rpmh-clk" }, diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 353ae476068..4b3d812f9c6 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -420,6 +420,24 @@ int clk_get_by_name_nodev(ofnode node, const char *name, struct clk *clk) return clk_get_by_index_nodev(node, index, clk); } +const char * +clk_resolve_parent_clk(struct udevice *dev, const char *name) +{ + struct udevice *parent; + struct clk clk; + int ret; + + ret = uclass_get_device_by_name(UCLASS_CLK, name, &parent); + if (!ret) + return name; + + ret = clk_get_by_name(dev, name, &clk); + if (!clk.dev) + return name; + + return clk.dev->name; +} + int clk_release_all(struct clk *clk, unsigned int count) { unsigned int i; @@ -605,14 +623,27 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!ops->set_parent) return -ENOSYS; + ret = clk_enable(parent); + if (ret) { + printf("Cannot enable parent %s\n", parent->dev->name); + return ret; + } + ret = ops->set_parent(clk, parent); - if (ret) + if (ret) { + clk_disable(parent); return ret; + } - if (CONFIG_IS_ENABLED(CLK_CCF)) + if (CONFIG_IS_ENABLED(CLK_CCF)) { ret = device_reparent(clk->dev, parent->dev); + if (ret) { + clk_disable(parent); + return ret; + } + } - return ret; + return 0; } int clk_enable(struct clk *clk) diff --git a/drivers/clk/clk_boston.c b/drivers/clk/clk_boston.c index 030ff7cc58e..71e030f463e 100644 --- a/drivers/clk/clk_boston.c +++ b/drivers/clk/clk_boston.c @@ -58,17 +58,21 @@ const struct clk_ops clk_boston_ops = { .get_rate = clk_boston_get_rate, }; -static int clk_boston_of_to_plat(struct udevice *dev) +static int clk_boston_probe(struct udevice *dev) { struct clk_boston *state = dev_get_plat(dev); struct udevice *syscon; int err; - err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, - "regmap", &syscon); - if (err) { - pr_err("unable to find syscon device\n"); - return err; + if (dev->parent && device_get_uclass_id(dev->parent) == UCLASS_SYSCON) { + syscon = dev->parent; + } else { + err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, + "regmap", &syscon); + if (err) { + pr_err("unable to find syscon device\n"); + return err; + } } state->regmap = syscon_get_regmap(syscon); @@ -91,7 +95,8 @@ U_BOOT_DRIVER(clk_boston) = { .name = "boston_clock", .id = UCLASS_CLK, .of_match = clk_boston_match, - .of_to_plat = clk_boston_of_to_plat, + .probe = clk_boston_probe, .plat_auto = sizeof(struct clk_boston), .ops = &clk_boston_ops, + .flags = DM_FLAG_PRE_RELOC, }; diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig index 56d893e0579..d17a54fb9b3 100644 --- a/drivers/clk/imx/Kconfig +++ b/drivers/clk/imx/Kconfig @@ -60,6 +60,7 @@ config SPL_CLK_IMX8MP depends on ARCH_IMX8M && 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.MX8MP diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c index 64bffa3b181..14c5b92939c 100644 --- a/drivers/clk/imx/clk-composite-8m.c +++ b/drivers/clk/imx/clk-composite-8m.c @@ -151,7 +151,7 @@ const struct clk_ops imx8m_clk_mux_ops = { .set_parent = imx8m_clk_mux_set_parent, }; -struct clk *imx8m_clk_composite_flags(const char *name, +struct clk *imx8m_clk_composite_flags(struct udevice *dev, const char *name, const char * const *parent_names, int num_parents, void __iomem *reg, unsigned long flags) @@ -187,7 +187,7 @@ struct clk *imx8m_clk_composite_flags(const char *name, gate->reg = reg; gate->bit_idx = PCG_CGC_SHIFT; - clk = clk_register_composite(NULL, name, + clk = clk_register_composite(dev, name, parent_names, num_parents, &mux->clk, &imx8m_clk_mux_ops, &div->clk, &imx8m_clk_composite_divider_ops, diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c index 65fa6b5b139..fa07b13249b 100644 --- a/drivers/clk/imx/clk-gate2.c +++ b/drivers/clk/imx/clk-gate2.c @@ -90,7 +90,7 @@ static const struct clk_ops clk_gate2_ops = { .get_rate = clk_generic_get_rate, }; -struct clk *clk_register_gate2(struct device *dev, const char *name, +struct clk *clk_register_gate2(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 clk_gate2_flags, unsigned int *share_count) @@ -111,7 +111,8 @@ struct clk *clk_register_gate2(struct device *dev, const char *name, clk = &gate->clk; - ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, name, parent_name); + ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, name, + clk_resolve_parent_clk(dev, parent_name)); if (ret) { kfree(gate); return ERR_PTR(ret); diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index df9f0285e1e..13239f2f64d 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -35,6 +35,8 @@ static const char *const usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; static const char *const periph_sels[] = { "periph_pre", "periph_clk2", }; static const char *const periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll2_198m", }; +static const char *const uart_sels[] = { "pll3_80m", "osc", }; +static const char *const ecspi_sels[] = { "pll3_60m", "osc", }; static int imx6q_clk_probe(struct udevice *dev) { @@ -44,21 +46,21 @@ static int imx6q_clk_probe(struct udevice *dev) base = (void *)ANATOP_BASE_ADDR; clk_dm(IMX6QDL_CLK_PLL2, - imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1)); clk_dm(IMX6QDL_CLK_PLL3_USB_OTG, - imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3)); clk_dm(IMX6QDL_CLK_PLL3_60M, - imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8)); + imx_clk_fixed_factor(dev, "pll3_60m", "pll3_usb_otg", 1, 8)); clk_dm(IMX6QDL_CLK_PLL2_PFD0_352M, imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0)); clk_dm(IMX6QDL_CLK_PLL2_PFD2_396M, imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2)); clk_dm(IMX6QDL_CLK_PLL6, - imx_clk_pllv3(IMX_PLLV3_ENET, "pll6", "osc", base + 0xe0, 0x3)); + imx_clk_pllv3(dev, IMX_PLLV3_ENET, "pll6", "osc", base + 0xe0, 0x3)); clk_dm(IMX6QDL_CLK_PLL6_ENET, - imx_clk_gate("pll6_enet", "pll6", base + 0xe0, 13)); + imx_clk_gate(dev, "pll6_enet", "pll6", base + 0xe0, 13)); /* CCM clocks */ base = dev_read_addr_ptr(dev); @@ -66,76 +68,98 @@ static int imx6q_clk_probe(struct udevice *dev) return -EINVAL; clk_dm(IMX6QDL_CLK_USDHC1_SEL, - imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1, + imx_clk_mux(dev, "usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMX6QDL_CLK_USDHC2_SEL, - imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1, + imx_clk_mux(dev, "usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMX6QDL_CLK_USDHC3_SEL, - imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1, + imx_clk_mux(dev, "usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMX6QDL_CLK_USDHC4_SEL, - imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1, + imx_clk_mux(dev, "usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); + if (of_machine_is_compatible("fsl,imx6qp")) { + clk_dm(IMX6QDL_CLK_UART_SEL, + imx_clk_mux(dev, "uart_sel", base + 0x24, 6, 1, uart_sels, + ARRAY_SIZE(uart_sels))); + clk_dm(IMX6QDL_CLK_ECSPI_SEL, + imx_clk_mux(dev, "ecspi_sel", base + 0x38, 18, 1, ecspi_sels, + ARRAY_SIZE(ecspi_sels))); + } + clk_dm(IMX6QDL_CLK_USDHC1_PODF, - imx_clk_divider("usdhc1_podf", "usdhc1_sel", + imx_clk_divider(dev, "usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3)); clk_dm(IMX6QDL_CLK_USDHC2_PODF, - imx_clk_divider("usdhc2_podf", "usdhc2_sel", + imx_clk_divider(dev, "usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3)); clk_dm(IMX6QDL_CLK_USDHC3_PODF, - imx_clk_divider("usdhc3_podf", "usdhc3_sel", + imx_clk_divider(dev, "usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3)); clk_dm(IMX6QDL_CLK_USDHC4_PODF, - imx_clk_divider("usdhc4_podf", "usdhc4_sel", + imx_clk_divider(dev, "usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3)); - clk_dm(IMX6QDL_CLK_ECSPI_ROOT, - imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6)); + if (of_machine_is_compatible("fsl,imx6qp")) { + clk_dm(IMX6QDL_CLK_UART_SERIAL_PODF, + imx_clk_divider(dev, "uart_serial_podf", "uart_sel", base + 0x24, 0, 6)); + clk_dm(IMX6QDL_CLK_ECSPI_ROOT, + imx_clk_divider(dev, "ecspi_root", "ecspi_sel", base + 0x38, 19, 6)); + } else { + clk_dm(IMX6QDL_CLK_UART_SERIAL_PODF, + imx_clk_divider(dev, "uart_serial_podf", "pll3_80m", base + 0x24, 0, 6)); + clk_dm(IMX6QDL_CLK_ECSPI_ROOT, + imx_clk_divider(dev, "ecspi_root", "pll3_60m", base + 0x38, 19, 6)); + } clk_dm(IMX6QDL_CLK_ECSPI1, - imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0)); + imx_clk_gate2(dev, "ecspi1", "ecspi_root", base + 0x6c, 0)); clk_dm(IMX6QDL_CLK_ECSPI2, - imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2)); + imx_clk_gate2(dev, "ecspi2", "ecspi_root", base + 0x6c, 2)); clk_dm(IMX6QDL_CLK_ECSPI3, - imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4)); + imx_clk_gate2(dev, "ecspi3", "ecspi_root", base + 0x6c, 4)); clk_dm(IMX6QDL_CLK_ECSPI4, - imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6)); + imx_clk_gate2(dev, "ecspi4", "ecspi_root", base + 0x6c, 6)); + clk_dm(IMX6QDL_CLK_UART_IPG, + imx_clk_gate2(dev, "uart_ipg", "ipg", base + 0x7c, 24)); + clk_dm(IMX6QDL_CLK_UART_SERIAL, + imx_clk_gate2(dev, "uart_serial", "uart_serial_podf", base + 0x7c, 26)); clk_dm(IMX6QDL_CLK_USDHC1, - imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2)); + imx_clk_gate2(dev, "usdhc1", "usdhc1_podf", base + 0x80, 2)); clk_dm(IMX6QDL_CLK_USDHC2, - imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4)); + imx_clk_gate2(dev, "usdhc2", "usdhc2_podf", base + 0x80, 4)); clk_dm(IMX6QDL_CLK_USDHC3, - imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6)); + imx_clk_gate2(dev, "usdhc3", "usdhc3_podf", base + 0x80, 6)); clk_dm(IMX6QDL_CLK_USDHC4, - imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8)); + imx_clk_gate2(dev, "usdhc4", "usdhc4_podf", base + 0x80, 8)); clk_dm(IMX6QDL_CLK_PERIPH_PRE, - imx_clk_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, + imx_clk_mux(dev, "periph_pre", base + 0x18, 18, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels))); clk_dm(IMX6QDL_CLK_PERIPH, - imx_clk_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, + imx_clk_busy_mux(dev, "periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels))); clk_dm(IMX6QDL_CLK_AHB, - imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, + imx_clk_busy_divider(dev, "ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1)); clk_dm(IMX6QDL_CLK_IPG, - imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2)); + imx_clk_divider(dev, "ipg", "ahb", base + 0x14, 8, 2)); clk_dm(IMX6QDL_CLK_IPG_PER, - imx_clk_divider("ipg_per", "ipg", base + 0x1c, 0, 6)); + imx_clk_divider(dev, "ipg_per", "ipg", base + 0x1c, 0, 6)); clk_dm(IMX6QDL_CLK_I2C1, - imx_clk_gate2("i2c1", "ipg_per", base + 0x70, 6)); + imx_clk_gate2(dev, "i2c1", "ipg_per", base + 0x70, 6)); clk_dm(IMX6QDL_CLK_I2C2, - imx_clk_gate2("i2c2", "ipg_per", base + 0x70, 8)); + imx_clk_gate2(dev, "i2c2", "ipg_per", base + 0x70, 8)); clk_dm(IMX6QDL_CLK_I2C3, - imx_clk_gate2("i2c3", "ipg_per", base + 0x70, 10)); + imx_clk_gate2(dev, "i2c3", "ipg_per", base + 0x70, 10)); clk_dm(IMX6QDL_CLK_PWM1, - imx_clk_gate2("pwm1", "ipg_per", base + 0x78, 16)); + imx_clk_gate2(dev, "pwm1", "ipg_per", base + 0x78, 16)); - clk_dm(IMX6QDL_CLK_ENET, imx_clk_gate2("enet", "ipg", base + 0x6c, 10)); + clk_dm(IMX6QDL_CLK_ENET, imx_clk_gate2(dev, "enet", "ipg", base + 0x6c, 10)); clk_dm(IMX6QDL_CLK_ENET_REF, - imx_clk_fixed_factor("enet_ref", "pll6_enet", 1, 1)); + imx_clk_fixed_factor(dev, "enet_ref", "pll6_enet", 1, 1)); return 0; } diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index bb6958f0ec2..b81db516a69 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -14,7 +14,7 @@ #include "clk.h" -static const char * const pll_ref_sels[] = { "clock-osc-24m", "dummy", "dummy", "dummy", }; +static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; static const char * const sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; @@ -23,128 +23,144 @@ static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_se static const char * const imx8mm_arm_core_sels[] = {"arm_a53_src", "arm_pll_out", }; -static const char * const imx8mm_a53_sels[] = {"clock-osc-24m", "arm_pll_out", "sys_pll2_500m", +static const char * const imx8mm_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; -static const char * const imx8mm_ahb_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_800m", +static const char * const imx8mm_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", }; #ifndef CONFIG_XPL_BUILD -static const char * const imx8mm_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", +static const char * const imx8mm_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; -static const char * const imx8mm_enet_ref_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m", +static const char * const imx8mm_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", }; -static const char * const imx8mm_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out", +static const char * const imx8mm_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", "video_pll1_out", }; -static const char * const imx8mm_enet_phy_sels[] = {"clock-osc-24m", "sys_pll2_50m", "sys_pll2_125m", +static const char * const imx8mm_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", "sys_pll2_200m", "sys_pll2_500m", "video_pll1_out", "audio_pll2_out", }; #endif -static const char * const imx8mm_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", +static const char * const imx8mm_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out", "sys_pll2_250m", "audio_pll1_out", }; -static const char * const imx8mm_usb_bus_sels[] = {"clock-osc-24m", "sys_pll2_500m", "sys_pll1_800m", +static const char * const imx8mm_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char * const imx8mm_usdhc1_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mm_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; -static const char * const imx8mm_usdhc2_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mm_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; -static const char * const imx8mm_i2c1_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mm_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mm_i2c2_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mm_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mm_i2c3_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mm_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mm_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mm_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; +static const char * const imx8mm_uart1_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext4", + "audio_pll2_out", }; + +static const char * const imx8mm_uart2_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext3", + "audio_pll2_out", }; + +static const char * const imx8mm_uart3_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext4", + "audio_pll2_out", }; + +static const char * const imx8mm_uart4_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext3", + "audio_pll2_out", }; + #if CONFIG_IS_ENABLED(PCIE_DW_IMX) -static const char * const imx8mm_pcie1_ctrl_sels[] = {"clock-osc-24m", "sys_pll2_250m", "sys_pll2_200m", +static const char * const imx8mm_pcie1_ctrl_sels[] = {"osc_24m", "sys_pll2_250m", "sys_pll2_200m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll2_333m", "sys_pll3_out", }; -static const char * const imx8mm_pcie1_phy_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll2_500m", +static const char * const imx8mm_pcie1_phy_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll2_500m", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", "sys_pll1_400m", }; -static const char * const imx8mm_pcie1_aux_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll2_50m", +static const char * const imx8mm_pcie1_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out", "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", }; #endif #ifndef CONFIG_XPL_BUILD -static const char * const imx8mm_pwm1_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mm_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mm_pwm2_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mm_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mm_pwm3_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mm_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mm_pwm4_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mm_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; #endif -static const char * const imx8mm_wdog_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_160m", +static const char * const imx8mm_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", "vpu_pll_out", "sys_pll2_125m", "sys_pll3_out", "sys_pll1_80m", "sys_pll2_166m", }; -static const char * const imx8mm_usdhc3_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mm_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_clk", "sys_pll1_100m", }; #if CONFIG_IS_ENABLED(NXP_FSPI) -static const char * const imx8mm_qspi_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll2_333m", +static const char * const imx8mm_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m", "sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m", "sys_pll3_out", "sys_pll1_100m", }; #endif -static const char * const imx8mm_usb_core_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", +static const char * const imx8mm_usb_core_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char * const imx8mm_usb_phy_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", +static const char * const imx8mm_usb_phy_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; #if CONFIG_IS_ENABLED(DM_SPI) -static const char * const imx8mm_ecspi1_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mm_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mm_ecspi2_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mm_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mm_ecspi3_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mm_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; #endif @@ -156,19 +172,19 @@ static int imx8mm_clk_probe(struct udevice *dev) base = (void *)ANATOP_BASE_ADDR; clk_dm(IMX8MM_DRAM_PLL_REF_SEL, - imx_clk_mux("dram_pll_ref_sel", base + 0x50, 0, 2, + imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MM_ARM_PLL_REF_SEL, - imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2, + imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MM_SYS_PLL1_REF_SEL, - imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2, + imx_clk_mux(dev, "sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MM_SYS_PLL2_REF_SEL, - imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2, + imx_clk_mux(dev, "sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MM_SYS_PLL3_REF_SEL, - imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, + imx_clk_mux(dev, "sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MM_DRAM_PLL, @@ -189,238 +205,254 @@ static int imx8mm_clk_probe(struct udevice *dev) /* PLL bypass out */ clk_dm(IMX8MM_DRAM_PLL_BYPASS, - imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 4, 1, + imx_clk_mux_flags(dev, "dram_pll_bypass", base + 0x50, 4, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MM_ARM_PLL_BYPASS, - imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 4, 1, + imx_clk_mux_flags(dev, "arm_pll_bypass", base + 0x84, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MM_SYS_PLL1_BYPASS, - imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 4, 1, + imx_clk_mux_flags(dev, "sys_pll1_bypass", base + 0x94, 4, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MM_SYS_PLL2_BYPASS, - imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 4, 1, + imx_clk_mux_flags(dev, "sys_pll2_bypass", base + 0x104, 4, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MM_SYS_PLL3_BYPASS, - imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 4, 1, + imx_clk_mux_flags(dev, "sys_pll3_bypass", base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT)); /* PLL out gate */ clk_dm(IMX8MM_DRAM_PLL_OUT, - imx_clk_gate("dram_pll_out", "dram_pll_bypass", + imx_clk_gate(dev, "dram_pll_out", "dram_pll_bypass", base + 0x50, 13)); clk_dm(IMX8MM_ARM_PLL_OUT, - imx_clk_gate("arm_pll_out", "arm_pll_bypass", + imx_clk_gate(dev, "arm_pll_out", "arm_pll_bypass", base + 0x84, 11)); clk_dm(IMX8MM_SYS_PLL1_OUT, - imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", + imx_clk_gate(dev, "sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11)); clk_dm(IMX8MM_SYS_PLL2_OUT, - imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", + imx_clk_gate(dev, "sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11)); clk_dm(IMX8MM_SYS_PLL3_OUT, - imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", + imx_clk_gate(dev, "sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11)); /* SYS PLL fixed output */ clk_dm(IMX8MM_SYS_PLL1_40M, - imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20)); + imx_clk_fixed_factor(dev, "sys_pll1_40m", "sys_pll1_out", 1, 20)); clk_dm(IMX8MM_SYS_PLL1_80M, - imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10)); + imx_clk_fixed_factor(dev, "sys_pll1_80m", "sys_pll1_out", 1, 10)); clk_dm(IMX8MM_SYS_PLL1_100M, - imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8)); + imx_clk_fixed_factor(dev, "sys_pll1_100m", "sys_pll1_out", 1, 8)); clk_dm(IMX8MM_SYS_PLL1_133M, - imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6)); + imx_clk_fixed_factor(dev, "sys_pll1_133m", "sys_pll1_out", 1, 6)); clk_dm(IMX8MM_SYS_PLL1_160M, - imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5)); + imx_clk_fixed_factor(dev, "sys_pll1_160m", "sys_pll1_out", 1, 5)); clk_dm(IMX8MM_SYS_PLL1_200M, - imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4)); + imx_clk_fixed_factor(dev, "sys_pll1_200m", "sys_pll1_out", 1, 4)); clk_dm(IMX8MM_SYS_PLL1_266M, - imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3)); + imx_clk_fixed_factor(dev, "sys_pll1_266m", "sys_pll1_out", 1, 3)); clk_dm(IMX8MM_SYS_PLL1_400M, - imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll1_400m", "sys_pll1_out", 1, 2)); clk_dm(IMX8MM_SYS_PLL1_800M, - imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1)); + imx_clk_fixed_factor(dev, "sys_pll1_800m", "sys_pll1_out", 1, 1)); clk_dm(IMX8MM_SYS_PLL2_50M, - imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20)); + imx_clk_fixed_factor(dev, "sys_pll2_50m", "sys_pll2_out", 1, 20)); clk_dm(IMX8MM_SYS_PLL2_100M, - imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10)); + imx_clk_fixed_factor(dev, "sys_pll2_100m", "sys_pll2_out", 1, 10)); clk_dm(IMX8MM_SYS_PLL2_125M, - imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8)); + imx_clk_fixed_factor(dev, "sys_pll2_125m", "sys_pll2_out", 1, 8)); clk_dm(IMX8MM_SYS_PLL2_166M, - imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6)); + imx_clk_fixed_factor(dev, "sys_pll2_166m", "sys_pll2_out", 1, 6)); clk_dm(IMX8MM_SYS_PLL2_200M, - imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5)); + imx_clk_fixed_factor(dev, "sys_pll2_200m", "sys_pll2_out", 1, 5)); clk_dm(IMX8MM_SYS_PLL2_250M, - imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4)); + imx_clk_fixed_factor(dev, "sys_pll2_250m", "sys_pll2_out", 1, 4)); clk_dm(IMX8MM_SYS_PLL2_333M, - imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3)); + imx_clk_fixed_factor(dev, "sys_pll2_333m", "sys_pll2_out", 1, 3)); clk_dm(IMX8MM_SYS_PLL2_500M, - imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll2_500m", "sys_pll2_out", 1, 2)); clk_dm(IMX8MM_SYS_PLL2_1000M, - imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1)); + imx_clk_fixed_factor(dev, "sys_pll2_1000m", "sys_pll2_out", 1, 1)); base = dev_read_addr_ptr(dev); if (!base) return -EINVAL; clk_dm(IMX8MM_CLK_A53_SRC, - imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, + imx_clk_mux2(dev, "arm_a53_src", base + 0x8000, 24, 3, imx8mm_a53_sels, ARRAY_SIZE(imx8mm_a53_sels))); clk_dm(IMX8MM_CLK_A53_CG, - imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); + imx_clk_gate3(dev, "arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); clk_dm(IMX8MM_CLK_A53_DIV, - imx_clk_divider2("arm_a53_div", "arm_a53_cg", + imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3)); clk_dm(IMX8MM_CLK_AHB, - imx8m_clk_composite_critical("ahb", imx8mm_ahb_sels, + imx8m_clk_composite_critical(dev, "ahb", imx8mm_ahb_sels, base + 0x9000)); clk_dm(IMX8MM_CLK_IPG_ROOT, - imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1)); + imx_clk_divider2(dev, "ipg_root", "ahb", base + 0x9080, 0, 1)); clk_dm(IMX8MM_CLK_NAND_USDHC_BUS, - imx8m_clk_composite_critical("nand_usdhc_bus", + imx8m_clk_composite_critical(dev, "nand_usdhc_bus", imx8mm_nand_usdhc_sels, base + 0x8900)); clk_dm(IMX8MM_CLK_USB_BUS, - imx8m_clk_composite("usb_bus", imx8mm_usb_bus_sels, base + 0x8b80)); + imx8m_clk_composite(dev, "usb_bus", imx8mm_usb_bus_sels, base + 0x8b80)); /* IP */ #if CONFIG_IS_ENABLED(PCIE_DW_IMX) clk_dm(IMX8MM_CLK_PCIE1_CTRL, - imx8m_clk_composite("pcie1_ctrl", imx8mm_pcie1_ctrl_sels, + imx8m_clk_composite(dev, "pcie1_ctrl", imx8mm_pcie1_ctrl_sels, base + 0xa300)); clk_dm(IMX8MM_CLK_PCIE1_PHY, - imx8m_clk_composite("pcie1_phy", imx8mm_pcie1_phy_sels, + imx8m_clk_composite(dev, "pcie1_phy", imx8mm_pcie1_phy_sels, base + 0xa380)); clk_dm(IMX8MM_CLK_PCIE1_AUX, - imx8m_clk_composite("pcie1_aux", imx8mm_pcie1_aux_sels, + imx8m_clk_composite(dev, "pcie1_aux", imx8mm_pcie1_aux_sels, base + 0xa400)); #endif clk_dm(IMX8MM_CLK_USDHC1, - imx8m_clk_composite("usdhc1", imx8mm_usdhc1_sels, + imx8m_clk_composite(dev, "usdhc1", imx8mm_usdhc1_sels, base + 0xac00)); clk_dm(IMX8MM_CLK_USDHC2, - imx8m_clk_composite("usdhc2", imx8mm_usdhc2_sels, + imx8m_clk_composite(dev, "usdhc2", imx8mm_usdhc2_sels, base + 0xac80)); clk_dm(IMX8MM_CLK_I2C1, - imx8m_clk_composite("i2c1", imx8mm_i2c1_sels, base + 0xad00)); + imx8m_clk_composite(dev, "i2c1", imx8mm_i2c1_sels, base + 0xad00)); clk_dm(IMX8MM_CLK_I2C2, - imx8m_clk_composite("i2c2", imx8mm_i2c2_sels, base + 0xad80)); + imx8m_clk_composite(dev, "i2c2", imx8mm_i2c2_sels, base + 0xad80)); clk_dm(IMX8MM_CLK_I2C3, - imx8m_clk_composite("i2c3", imx8mm_i2c3_sels, base + 0xae00)); + imx8m_clk_composite(dev, "i2c3", imx8mm_i2c3_sels, base + 0xae00)); clk_dm(IMX8MM_CLK_I2C4, - imx8m_clk_composite("i2c4", imx8mm_i2c4_sels, base + 0xae80)); + imx8m_clk_composite(dev, "i2c4", imx8mm_i2c4_sels, base + 0xae80)); + clk_dm(IMX8MM_CLK_UART1, + imx8m_clk_composite(dev, "uart1", imx8mm_uart1_sels, base + 0xaf00)); + clk_dm(IMX8MM_CLK_UART2, + imx8m_clk_composite(dev, "uart2", imx8mm_uart2_sels, base + 0xaf80)); + clk_dm(IMX8MM_CLK_UART3, + imx8m_clk_composite(dev, "uart3", imx8mm_uart3_sels, base + 0xb000)); + clk_dm(IMX8MM_CLK_UART4, + imx8m_clk_composite(dev, "uart4", imx8mm_uart4_sels, base + 0xb080)); + clk_dm(IMX8MM_CLK_UART1_ROOT, + imx_clk_gate4(dev, "uart1_root_clk", "uart1", base + 0x4490, 0)); + clk_dm(IMX8MM_CLK_UART2_ROOT, + imx_clk_gate4(dev, "uart2_root_clk", "uart2", base + 0x44a0, 0)); + clk_dm(IMX8MM_CLK_UART3_ROOT, + imx_clk_gate4(dev, "uart3_root_clk", "uart3", base + 0x44b0, 0)); + clk_dm(IMX8MM_CLK_UART4_ROOT, + imx_clk_gate4(dev, "uart4_root_clk", "uart4", base + 0x44c0, 0)); clk_dm(IMX8MM_CLK_WDOG, - imx8m_clk_composite("wdog", imx8mm_wdog_sels, base + 0xb900)); + imx8m_clk_composite(dev, "wdog", imx8mm_wdog_sels, base + 0xb900)); clk_dm(IMX8MM_CLK_USDHC3, - imx8m_clk_composite("usdhc3", imx8mm_usdhc3_sels, + imx8m_clk_composite(dev, "usdhc3", imx8mm_usdhc3_sels, base + 0xbc80)); clk_dm(IMX8MM_CLK_USB_CORE_REF, - imx8m_clk_composite("usb_core_ref", imx8mm_usb_core_sels, base + 0xb100)); + imx8m_clk_composite(dev, "usb_core_ref", imx8mm_usb_core_sels, base + 0xb100)); clk_dm(IMX8MM_CLK_USB_PHY_REF, - imx8m_clk_composite("usb_phy_ref", imx8mm_usb_phy_sels, base + 0xb180)); + imx8m_clk_composite(dev, "usb_phy_ref", imx8mm_usb_phy_sels, base + 0xb180)); clk_dm(IMX8MM_CLK_I2C1_ROOT, - imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0)); + imx_clk_gate4(dev, "i2c1_root_clk", "i2c1", base + 0x4170, 0)); clk_dm(IMX8MM_CLK_I2C2_ROOT, - imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0)); + imx_clk_gate4(dev, "i2c2_root_clk", "i2c2", base + 0x4180, 0)); clk_dm(IMX8MM_CLK_I2C3_ROOT, - imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0)); + imx_clk_gate4(dev, "i2c3_root_clk", "i2c3", base + 0x4190, 0)); clk_dm(IMX8MM_CLK_I2C4_ROOT, - imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0)); + imx_clk_gate4(dev, "i2c4_root_clk", "i2c4", base + 0x41a0, 0)); clk_dm(IMX8MM_CLK_OCOTP_ROOT, - imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0)); + imx_clk_gate4(dev, "ocotp_root_clk", "ipg_root", base + 0x4220, 0)); clk_dm(IMX8MM_CLK_USDHC1_ROOT, - imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); + imx_clk_gate4(dev, "usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); clk_dm(IMX8MM_CLK_USDHC2_ROOT, - imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); + imx_clk_gate4(dev, "usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); clk_dm(IMX8MM_CLK_WDOG1_ROOT, - imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0)); + imx_clk_gate4(dev, "wdog1_root_clk", "wdog", base + 0x4530, 0)); clk_dm(IMX8MM_CLK_WDOG2_ROOT, - imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0)); + imx_clk_gate4(dev, "wdog2_root_clk", "wdog", base + 0x4540, 0)); clk_dm(IMX8MM_CLK_WDOG3_ROOT, - imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0)); + imx_clk_gate4(dev, "wdog3_root_clk", "wdog", base + 0x4550, 0)); clk_dm(IMX8MM_CLK_USDHC3_ROOT, - imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); + imx_clk_gate4(dev, "usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); clk_dm(IMX8MM_CLK_USB1_CTRL_ROOT, - imx_clk_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0)); + imx_clk_gate4(dev, "usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0)); /* clks not needed in SPL stage */ #ifndef CONFIG_XPL_BUILD clk_dm(IMX8MM_CLK_ENET_AXI, - imx8m_clk_composite("enet_axi", imx8mm_enet_axi_sels, + imx8m_clk_composite(dev, "enet_axi", imx8mm_enet_axi_sels, base + 0x8880)); clk_dm(IMX8MM_CLK_ENET_REF, - imx8m_clk_composite("enet_ref", imx8mm_enet_ref_sels, + imx8m_clk_composite(dev, "enet_ref", imx8mm_enet_ref_sels, base + 0xa980)); clk_dm(IMX8MM_CLK_ENET_TIMER, - imx8m_clk_composite("enet_timer", imx8mm_enet_timer_sels, + imx8m_clk_composite(dev, "enet_timer", imx8mm_enet_timer_sels, base + 0xaa00)); clk_dm(IMX8MM_CLK_ENET_PHY_REF, - imx8m_clk_composite("enet_phy", imx8mm_enet_phy_sels, + imx8m_clk_composite(dev, "enet_phy", imx8mm_enet_phy_sels, base + 0xaa80)); clk_dm(IMX8MM_CLK_ENET1_ROOT, - imx_clk_gate4("enet1_root_clk", "enet_axi", + imx_clk_gate4(dev, "enet1_root_clk", "enet_axi", base + 0x40a0, 0)); clk_dm(IMX8MM_CLK_PWM1, - imx8m_clk_composite("pwm1", imx8mm_pwm1_sels, base + 0xb380)); + imx8m_clk_composite(dev, "pwm1", imx8mm_pwm1_sels, base + 0xb380)); clk_dm(IMX8MM_CLK_PWM2, - imx8m_clk_composite("pwm2", imx8mm_pwm2_sels, base + 0xb400)); + imx8m_clk_composite(dev, "pwm2", imx8mm_pwm2_sels, base + 0xb400)); clk_dm(IMX8MM_CLK_PWM3, - imx8m_clk_composite("pwm3", imx8mm_pwm3_sels, base + 0xb480)); + imx8m_clk_composite(dev, "pwm3", imx8mm_pwm3_sels, base + 0xb480)); clk_dm(IMX8MM_CLK_PWM4, - imx8m_clk_composite("pwm4", imx8mm_pwm4_sels, base + 0xb500)); + imx8m_clk_composite(dev, "pwm4", imx8mm_pwm4_sels, base + 0xb500)); clk_dm(IMX8MM_CLK_PWM1_ROOT, - imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0)); + imx_clk_gate4(dev, "pwm1_root_clk", "pwm1", base + 0x4280, 0)); clk_dm(IMX8MM_CLK_PWM2_ROOT, - imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0)); + imx_clk_gate4(dev, "pwm2_root_clk", "pwm2", base + 0x4290, 0)); clk_dm(IMX8MM_CLK_PWM3_ROOT, - imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0)); + imx_clk_gate4(dev, "pwm3_root_clk", "pwm3", base + 0x42a0, 0)); clk_dm(IMX8MM_CLK_PWM4_ROOT, - imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0)); + imx_clk_gate4(dev, "pwm4_root_clk", "pwm4", base + 0x42b0, 0)); #endif #if CONFIG_IS_ENABLED(PCIE_DW_IMX) clk_dm(IMX8MM_CLK_PCIE1_ROOT, - imx_clk_gate4("pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0)); + imx_clk_gate4(dev, "pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0)); #endif #if CONFIG_IS_ENABLED(DM_SPI) clk_dm(IMX8MM_CLK_ECSPI1, - imx8m_clk_composite("ecspi1", imx8mm_ecspi1_sels, base + 0xb280)); + imx8m_clk_composite(dev, "ecspi1", imx8mm_ecspi1_sels, base + 0xb280)); clk_dm(IMX8MM_CLK_ECSPI2, - imx8m_clk_composite("ecspi2", imx8mm_ecspi2_sels, base + 0xb300)); + imx8m_clk_composite(dev, "ecspi2", imx8mm_ecspi2_sels, base + 0xb300)); clk_dm(IMX8MM_CLK_ECSPI3, - imx8m_clk_composite("ecspi3", imx8mm_ecspi3_sels, base + 0xc180)); + imx8m_clk_composite(dev, "ecspi3", imx8mm_ecspi3_sels, base + 0xc180)); clk_dm(IMX8MM_CLK_ECSPI1_ROOT, - imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); + imx_clk_gate4(dev, "ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); clk_dm(IMX8MM_CLK_ECSPI2_ROOT, - imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); + imx_clk_gate4(dev, "ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); clk_dm(IMX8MM_CLK_ECSPI3_ROOT, - imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); + imx_clk_gate4(dev, "ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); #endif #if CONFIG_IS_ENABLED(NXP_FSPI) clk_dm(IMX8MM_CLK_QSPI, - imx8m_clk_composite("qspi", imx8mm_qspi_sels, base + 0xab80)); + imx8m_clk_composite(dev, "qspi", imx8mm_qspi_sels, base + 0xab80)); clk_dm(IMX8MM_CLK_QSPI_ROOT, - imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0)); + imx_clk_gate4(dev, "qspi_root_clk", "qspi", base + 0x42f0, 0)); #endif clk_dm(IMX8MM_CLK_ARM, - imx_clk_mux2_flags("arm_core", base + 0x9880, 24, 1, + imx_clk_mux2_flags(dev, "arm_core", base + 0x9880, 24, 1, imx8mm_arm_core_sels, ARRAY_SIZE(imx8mm_arm_core_sels), CLK_IS_CRITICAL)); diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index be15ebd0e25..be5b7933a8d 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -16,7 +16,7 @@ static u32 share_count_nand; -static const char * const pll_ref_sels[] = { "clock-osc-24m", "dummy", "dummy", "dummy", }; +static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; static const char * const sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; @@ -25,117 +25,133 @@ static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_se static const char * const imx8mn_arm_core_sels[] = {"arm_a53_src", "arm_pll_out", }; -static const char * const imx8mn_a53_sels[] = {"clock-osc-24m", "arm_pll_out", "sys_pll2_500m", +static const char * const imx8mn_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; -static const char * const imx8mn_ahb_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_800m", +static const char * const imx8mn_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll_out", }; -static const char * const imx8mn_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", +static const char * const imx8mn_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out", "video_pll_out", "sys_pll3_out", }; #ifndef CONFIG_XPL_BUILD -static const char * const imx8mn_enet_ref_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m", +static const char * const imx8mn_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", "video_pll_out", "clk_ext4", }; -static const char * const imx8mn_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out", +static const char * const imx8mn_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", "video_pll_out", }; -static const char * const imx8mn_enet_phy_sels[] = {"clock-osc-24m", "sys_pll2_50m", "sys_pll2_125m", +static const char * const imx8mn_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", "sys_pll2_200m", "sys_pll2_500m", "audio_pll1_out", "video_pll_out", "audio_pll2_out", }; #endif -static const char * const imx8mn_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", +static const char * const imx8mn_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out", "sys_pll2_250m", "audio_pll1_out", }; -static const char * const imx8mn_usb_bus_sels[] = {"clock-osc-24m", "sys_pll2_500m", "sys_pll1_800m", +static const char * const imx8mn_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char * const imx8mn_usdhc1_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mn_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; -static const char * const imx8mn_usdhc2_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mn_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; #if CONFIG_IS_ENABLED(DM_SPI) -static const char * const imx8mn_ecspi1_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mn_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mn_ecspi2_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mn_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mn_ecspi3_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mn_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; #endif -static const char * const imx8mn_i2c1_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mn_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mn_i2c2_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mn_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mn_i2c3_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mn_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mn_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mn_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll_out", "audio_pll2_out", "sys_pll1_133m", }; +static const char * const imx8mn_uart1_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mn_uart2_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_uart3_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mn_uart4_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + #ifndef CONFIG_XPL_BUILD -static const char * const imx8mn_pwm1_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mn_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll_out", }; -static const char * const imx8mn_pwm2_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mn_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll_out", }; -static const char * const imx8mn_pwm3_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mn_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll_out", }; -static const char * const imx8mn_pwm4_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mn_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll_out", }; #endif -static const char * const imx8mn_wdog_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_160m", +static const char * const imx8mn_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", "m7_alt_pll", "sys_pll2_125m", "sys_pll3_out", "sys_pll1_80m", "sys_pll2_166m", }; -static const char * const imx8mn_usdhc3_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mn_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_clk", "sys_pll1_100m", }; -static const char * const imx8mn_qspi_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll2_333m", +static const char * const imx8mn_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m", "sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m", "sys_pll3_out", "sys_pll1_100m", }; -static const char * const imx8mn_nand_sels[] = {"clock-osc-24m", "sys_pll2_500m", "audio_pll1_out", +static const char * const imx8mn_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out", "sys_pll1_400m", "audio_pll2_out", "sys_pll3_out", "sys_pll2_250m", "video_pll_out", }; -static const char * const imx8mn_usb_core_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", +static const char * const imx8mn_usb_core_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char * const imx8mn_usb_phy_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", +static const char * const imx8mn_usb_phy_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; @@ -148,19 +164,19 @@ static int imx8mn_clk_probe(struct udevice *dev) base = (void *)ANATOP_BASE_ADDR; clk_dm(IMX8MN_DRAM_PLL_REF_SEL, - imx_clk_mux("dram_pll_ref_sel", base + 0x50, 0, 2, + imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MN_ARM_PLL_REF_SEL, - imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2, + imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MN_SYS_PLL1_REF_SEL, - imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2, + imx_clk_mux(dev, "sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MN_SYS_PLL2_REF_SEL, - imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2, + imx_clk_mux(dev, "sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MN_SYS_PLL3_REF_SEL, - imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, + imx_clk_mux(dev, "sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MN_DRAM_PLL, @@ -181,86 +197,86 @@ static int imx8mn_clk_probe(struct udevice *dev) /* PLL bypass out */ clk_dm(IMX8MN_DRAM_PLL_BYPASS, - imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 4, 1, + imx_clk_mux_flags(dev, "dram_pll_bypass", base + 0x50, 4, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MN_ARM_PLL_BYPASS, - imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 4, 1, + imx_clk_mux_flags(dev, "arm_pll_bypass", base + 0x84, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MN_SYS_PLL1_BYPASS, - imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 4, 1, + imx_clk_mux_flags(dev, "sys_pll1_bypass", base + 0x94, 4, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MN_SYS_PLL2_BYPASS, - imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 4, 1, + imx_clk_mux_flags(dev, "sys_pll2_bypass", base + 0x104, 4, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MN_SYS_PLL3_BYPASS, - imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 4, 1, + imx_clk_mux_flags(dev, "sys_pll3_bypass", base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT)); /* PLL out gate */ clk_dm(IMX8MN_DRAM_PLL_OUT, - imx_clk_gate("dram_pll_out", "dram_pll_bypass", + imx_clk_gate(dev, "dram_pll_out", "dram_pll_bypass", base + 0x50, 13)); clk_dm(IMX8MN_ARM_PLL_OUT, - imx_clk_gate("arm_pll_out", "arm_pll_bypass", + imx_clk_gate(dev, "arm_pll_out", "arm_pll_bypass", base + 0x84, 11)); clk_dm(IMX8MN_SYS_PLL1_OUT, - imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", + imx_clk_gate(dev, "sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11)); clk_dm(IMX8MN_SYS_PLL2_OUT, - imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", + imx_clk_gate(dev, "sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11)); clk_dm(IMX8MN_SYS_PLL3_OUT, - imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", + imx_clk_gate(dev, "sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11)); /* SYS PLL fixed output */ clk_dm(IMX8MN_SYS_PLL1_40M, - imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20)); + imx_clk_fixed_factor(dev, "sys_pll1_40m", "sys_pll1_out", 1, 20)); clk_dm(IMX8MN_SYS_PLL1_80M, - imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10)); + imx_clk_fixed_factor(dev, "sys_pll1_80m", "sys_pll1_out", 1, 10)); clk_dm(IMX8MN_SYS_PLL1_100M, - imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8)); + imx_clk_fixed_factor(dev, "sys_pll1_100m", "sys_pll1_out", 1, 8)); clk_dm(IMX8MN_SYS_PLL1_133M, - imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6)); + imx_clk_fixed_factor(dev, "sys_pll1_133m", "sys_pll1_out", 1, 6)); clk_dm(IMX8MN_SYS_PLL1_160M, - imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5)); + imx_clk_fixed_factor(dev, "sys_pll1_160m", "sys_pll1_out", 1, 5)); clk_dm(IMX8MN_SYS_PLL1_200M, - imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4)); + imx_clk_fixed_factor(dev, "sys_pll1_200m", "sys_pll1_out", 1, 4)); clk_dm(IMX8MN_SYS_PLL1_266M, - imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3)); + imx_clk_fixed_factor(dev, "sys_pll1_266m", "sys_pll1_out", 1, 3)); clk_dm(IMX8MN_SYS_PLL1_400M, - imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll1_400m", "sys_pll1_out", 1, 2)); clk_dm(IMX8MN_SYS_PLL1_800M, - imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1)); + imx_clk_fixed_factor(dev, "sys_pll1_800m", "sys_pll1_out", 1, 1)); clk_dm(IMX8MN_SYS_PLL2_50M, - imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20)); + imx_clk_fixed_factor(dev, "sys_pll2_50m", "sys_pll2_out", 1, 20)); clk_dm(IMX8MN_SYS_PLL2_100M, - imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10)); + imx_clk_fixed_factor(dev, "sys_pll2_100m", "sys_pll2_out", 1, 10)); clk_dm(IMX8MN_SYS_PLL2_125M, - imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8)); + imx_clk_fixed_factor(dev, "sys_pll2_125m", "sys_pll2_out", 1, 8)); clk_dm(IMX8MN_SYS_PLL2_166M, - imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6)); + imx_clk_fixed_factor(dev, "sys_pll2_166m", "sys_pll2_out", 1, 6)); clk_dm(IMX8MN_SYS_PLL2_200M, - imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5)); + imx_clk_fixed_factor(dev, "sys_pll2_200m", "sys_pll2_out", 1, 5)); clk_dm(IMX8MN_SYS_PLL2_250M, - imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4)); + imx_clk_fixed_factor(dev, "sys_pll2_250m", "sys_pll2_out", 1, 4)); clk_dm(IMX8MN_SYS_PLL2_333M, - imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3)); + imx_clk_fixed_factor(dev, "sys_pll2_333m", "sys_pll2_out", 1, 3)); clk_dm(IMX8MN_SYS_PLL2_500M, - imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll2_500m", "sys_pll2_out", 1, 2)); clk_dm(IMX8MN_SYS_PLL2_1000M, - imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1)); + imx_clk_fixed_factor(dev, "sys_pll2_1000m", "sys_pll2_out", 1, 1)); ret = clk_get_by_name(dev, "osc_24m", &osc_24m_clk); if (ret) @@ -272,141 +288,157 @@ static int imx8mn_clk_probe(struct udevice *dev) return -EINVAL; clk_dm(IMX8MN_CLK_A53_SRC, - imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, + imx_clk_mux2(dev, "arm_a53_src", base + 0x8000, 24, 3, imx8mn_a53_sels, ARRAY_SIZE(imx8mn_a53_sels))); clk_dm(IMX8MN_CLK_A53_CG, - imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); + imx_clk_gate3(dev, "arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); clk_dm(IMX8MN_CLK_A53_DIV, - imx_clk_divider2("arm_a53_div", "arm_a53_cg", + imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3)); clk_dm(IMX8MN_CLK_AHB, - imx8m_clk_composite_critical("ahb", imx8mn_ahb_sels, + imx8m_clk_composite_critical(dev, "ahb", imx8mn_ahb_sels, base + 0x9000)); clk_dm(IMX8MN_CLK_IPG_ROOT, - imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1)); + imx_clk_divider2(dev, "ipg_root", "ahb", base + 0x9080, 0, 1)); clk_dm(IMX8MN_CLK_ENET_AXI, - imx8m_clk_composite("enet_axi", imx8mn_enet_axi_sels, + imx8m_clk_composite(dev, "enet_axi", imx8mn_enet_axi_sels, base + 0x8880)); clk_dm(IMX8MN_CLK_NAND_USDHC_BUS, - imx8m_clk_composite_critical("nand_usdhc_bus", + imx8m_clk_composite_critical(dev, "nand_usdhc_bus", imx8mn_nand_usdhc_sels, base + 0x8900)); clk_dm(IMX8MN_CLK_USB_BUS, - imx8m_clk_composite("usb_bus", imx8mn_usb_bus_sels, base + 0x8b80)); + imx8m_clk_composite(dev, "usb_bus", imx8mn_usb_bus_sels, base + 0x8b80)); /* IP */ clk_dm(IMX8MN_CLK_USDHC1, - imx8m_clk_composite("usdhc1", imx8mn_usdhc1_sels, + imx8m_clk_composite(dev, "usdhc1", imx8mn_usdhc1_sels, base + 0xac00)); clk_dm(IMX8MN_CLK_USDHC2, - imx8m_clk_composite("usdhc2", imx8mn_usdhc2_sels, + imx8m_clk_composite(dev, "usdhc2", imx8mn_usdhc2_sels, base + 0xac80)); clk_dm(IMX8MN_CLK_I2C1, - imx8m_clk_composite("i2c1", imx8mn_i2c1_sels, base + 0xad00)); + imx8m_clk_composite(dev, "i2c1", imx8mn_i2c1_sels, base + 0xad00)); clk_dm(IMX8MN_CLK_I2C2, - imx8m_clk_composite("i2c2", imx8mn_i2c2_sels, base + 0xad80)); + imx8m_clk_composite(dev, "i2c2", imx8mn_i2c2_sels, base + 0xad80)); clk_dm(IMX8MN_CLK_I2C3, - imx8m_clk_composite("i2c3", imx8mn_i2c3_sels, base + 0xae00)); + imx8m_clk_composite(dev, "i2c3", imx8mn_i2c3_sels, base + 0xae00)); clk_dm(IMX8MN_CLK_I2C4, - imx8m_clk_composite("i2c4", imx8mn_i2c4_sels, base + 0xae80)); + imx8m_clk_composite(dev, "i2c4", imx8mn_i2c4_sels, base + 0xae80)); + clk_dm(IMX8MN_CLK_UART1, + imx8m_clk_composite(dev, "uart1", imx8mn_uart1_sels, base + 0xaf00)); + clk_dm(IMX8MN_CLK_UART2, + imx8m_clk_composite(dev, "uart2", imx8mn_uart2_sels, base + 0xaf80)); + clk_dm(IMX8MN_CLK_UART3, + imx8m_clk_composite(dev, "uart3", imx8mn_uart3_sels, base + 0xb000)); + clk_dm(IMX8MN_CLK_UART4, + imx8m_clk_composite(dev, "uart4", imx8mn_uart4_sels, base + 0xb080)); clk_dm(IMX8MN_CLK_WDOG, - imx8m_clk_composite("wdog", imx8mn_wdog_sels, base + 0xb900)); + imx8m_clk_composite(dev, "wdog", imx8mn_wdog_sels, base + 0xb900)); clk_dm(IMX8MN_CLK_USDHC3, - imx8m_clk_composite("usdhc3", imx8mn_usdhc3_sels, + imx8m_clk_composite(dev, "usdhc3", imx8mn_usdhc3_sels, base + 0xbc80)); clk_dm(IMX8MN_CLK_NAND, - imx8m_clk_composite("nand", imx8mn_nand_sels, base + 0xab00)); + imx8m_clk_composite(dev, "nand", imx8mn_nand_sels, base + 0xab00)); clk_dm(IMX8MN_CLK_QSPI, - imx8m_clk_composite("qspi", imx8mn_qspi_sels, base + 0xab80)); + imx8m_clk_composite(dev, "qspi", imx8mn_qspi_sels, base + 0xab80)); clk_dm(IMX8MN_CLK_USB_CORE_REF, - imx8m_clk_composite("usb_core_ref", imx8mn_usb_core_sels, base + 0xb100)); + imx8m_clk_composite(dev, "usb_core_ref", imx8mn_usb_core_sels, base + 0xb100)); clk_dm(IMX8MN_CLK_USB_PHY_REF, - imx8m_clk_composite("usb_phy_ref", imx8mn_usb_phy_sels, base + 0xb180)); + imx8m_clk_composite(dev, "usb_phy_ref", imx8mn_usb_phy_sels, base + 0xb180)); clk_dm(IMX8MN_CLK_I2C1_ROOT, - imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0)); + imx_clk_gate4(dev, "i2c1_root_clk", "i2c1", base + 0x4170, 0)); clk_dm(IMX8MN_CLK_I2C2_ROOT, - imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0)); + imx_clk_gate4(dev, "i2c2_root_clk", "i2c2", base + 0x4180, 0)); clk_dm(IMX8MN_CLK_I2C3_ROOT, - imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0)); + imx_clk_gate4(dev, "i2c3_root_clk", "i2c3", base + 0x4190, 0)); clk_dm(IMX8MN_CLK_I2C4_ROOT, - imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0)); + imx_clk_gate4(dev, "i2c4_root_clk", "i2c4", base + 0x41a0, 0)); clk_dm(IMX8MN_CLK_OCOTP_ROOT, - imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0)); + imx_clk_gate4(dev, "ocotp_root_clk", "ipg_root", base + 0x4220, 0)); clk_dm(IMX8MN_CLK_USDHC1_ROOT, - imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); + imx_clk_gate4(dev, "usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); clk_dm(IMX8MN_CLK_USDHC2_ROOT, - imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); + imx_clk_gate4(dev, "usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); clk_dm(IMX8MN_CLK_WDOG1_ROOT, - imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0)); + imx_clk_gate4(dev, "wdog1_root_clk", "wdog", base + 0x4530, 0)); clk_dm(IMX8MN_CLK_WDOG2_ROOT, - imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0)); + imx_clk_gate4(dev, "wdog2_root_clk", "wdog", base + 0x4540, 0)); clk_dm(IMX8MN_CLK_WDOG3_ROOT, - imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0)); + imx_clk_gate4(dev, "wdog3_root_clk", "wdog", base + 0x4550, 0)); clk_dm(IMX8MN_CLK_USDHC3_ROOT, - imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); + imx_clk_gate4(dev, "usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); clk_dm(IMX8MN_CLK_QSPI_ROOT, - imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0)); + imx_clk_gate4(dev, "qspi_root_clk", "qspi", base + 0x42f0, 0)); clk_dm(IMX8MN_CLK_NAND_ROOT, - imx_clk_gate2_shared2("nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand)); + imx_clk_gate2_shared2(dev, "nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand)); clk_dm(IMX8MN_CLK_NAND_USDHC_BUS_RAWNAND_CLK, - imx_clk_gate2_shared2("nand_usdhc_rawnand_clk", + imx_clk_gate2_shared2(dev, "nand_usdhc_rawnand_clk", "nand_usdhc_bus", base + 0x4300, 0, &share_count_nand)); + clk_dm(IMX8MN_CLK_UART1_ROOT, + imx_clk_gate4(dev, "uart1_root_clk", "uart1", base + 0x4490, 0)); + clk_dm(IMX8MN_CLK_UART2_ROOT, + imx_clk_gate4(dev, "uart2_root_clk", "uart2", base + 0x44a0, 0)); + clk_dm(IMX8MN_CLK_UART3_ROOT, + imx_clk_gate4(dev, "uart3_root_clk", "uart3", base + 0x44b0, 0)); + clk_dm(IMX8MN_CLK_UART4_ROOT, + imx_clk_gate4(dev, "uart4_root_clk", "uart4", base + 0x44c0, 0)); clk_dm(IMX8MN_CLK_USB1_CTRL_ROOT, - imx_clk_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0)); + imx_clk_gate4(dev, "usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0)); /* clks not needed in SPL stage */ #ifndef CONFIG_XPL_BUILD clk_dm(IMX8MN_CLK_ENET_REF, - imx8m_clk_composite("enet_ref", imx8mn_enet_ref_sels, + imx8m_clk_composite(dev, "enet_ref", imx8mn_enet_ref_sels, base + 0xa980)); clk_dm(IMX8MN_CLK_ENET_TIMER, - imx8m_clk_composite("enet_timer", imx8mn_enet_timer_sels, + imx8m_clk_composite(dev, "enet_timer", imx8mn_enet_timer_sels, base + 0xaa00)); clk_dm(IMX8MN_CLK_ENET_PHY_REF, - imx8m_clk_composite("enet_phy", imx8mn_enet_phy_sels, + imx8m_clk_composite(dev, "enet_phy", imx8mn_enet_phy_sels, base + 0xaa80)); clk_dm(IMX8MN_CLK_ENET1_ROOT, - imx_clk_gate4("enet1_root_clk", "enet_axi", + imx_clk_gate4(dev, "enet1_root_clk", "enet_axi", base + 0x40a0, 0)); clk_dm(IMX8MN_CLK_PWM1, - imx8m_clk_composite("pwm1", imx8mn_pwm1_sels, base + 0xb380)); + imx8m_clk_composite(dev, "pwm1", imx8mn_pwm1_sels, base + 0xb380)); clk_dm(IMX8MN_CLK_PWM2, - imx8m_clk_composite("pwm2", imx8mn_pwm2_sels, base + 0xb400)); + imx8m_clk_composite(dev, "pwm2", imx8mn_pwm2_sels, base + 0xb400)); clk_dm(IMX8MN_CLK_PWM3, - imx8m_clk_composite("pwm3", imx8mn_pwm3_sels, base + 0xb480)); + imx8m_clk_composite(dev, "pwm3", imx8mn_pwm3_sels, base + 0xb480)); clk_dm(IMX8MN_CLK_PWM4, - imx8m_clk_composite("pwm4", imx8mn_pwm4_sels, base + 0xb500)); + imx8m_clk_composite(dev, "pwm4", imx8mn_pwm4_sels, base + 0xb500)); clk_dm(IMX8MN_CLK_PWM1_ROOT, - imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0)); + imx_clk_gate4(dev, "pwm1_root_clk", "pwm1", base + 0x4280, 0)); clk_dm(IMX8MN_CLK_PWM2_ROOT, - imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0)); + imx_clk_gate4(dev, "pwm2_root_clk", "pwm2", base + 0x4290, 0)); clk_dm(IMX8MN_CLK_PWM3_ROOT, - imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0)); + imx_clk_gate4(dev, "pwm3_root_clk", "pwm3", base + 0x42a0, 0)); clk_dm(IMX8MN_CLK_PWM4_ROOT, - imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0)); + imx_clk_gate4(dev, "pwm4_root_clk", "pwm4", base + 0x42b0, 0)); #endif #if CONFIG_IS_ENABLED(DM_SPI) clk_dm(IMX8MN_CLK_ECSPI1, - imx8m_clk_composite("ecspi1", imx8mn_ecspi1_sels, base + 0xb280)); + imx8m_clk_composite(dev, "ecspi1", imx8mn_ecspi1_sels, base + 0xb280)); clk_dm(IMX8MN_CLK_ECSPI2, - imx8m_clk_composite("ecspi2", imx8mn_ecspi2_sels, base + 0xb300)); + imx8m_clk_composite(dev, "ecspi2", imx8mn_ecspi2_sels, base + 0xb300)); clk_dm(IMX8MN_CLK_ECSPI3, - imx8m_clk_composite("ecspi3", imx8mn_ecspi3_sels, base + 0xc180)); + imx8m_clk_composite(dev, "ecspi3", imx8mn_ecspi3_sels, base + 0xc180)); clk_dm(IMX8MN_CLK_ECSPI1_ROOT, - imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); + imx_clk_gate4(dev, "ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); clk_dm(IMX8MN_CLK_ECSPI2_ROOT, - imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); + imx_clk_gate4(dev, "ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); clk_dm(IMX8MN_CLK_ECSPI3_ROOT, - imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); + imx_clk_gate4(dev, "ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); #endif clk_dm(IMX8MN_CLK_ARM, - imx_clk_mux2_flags("arm_core", base + 0x9880, 24, 1, + imx_clk_mux2_flags(dev, "arm_core", base + 0x9880, 24, 1, imx8mn_arm_core_sels, ARRAY_SIZE(imx8mn_arm_core_sels), CLK_IS_CRITICAL)); diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c index 1d04090ca00..3d8ed21d3b9 100644 --- a/drivers/clk/imx/clk-imx8mp.c +++ b/drivers/clk/imx/clk-imx8mp.c @@ -14,7 +14,14 @@ #include "clk.h" -static const char * const pll_ref_sels[] = { "clock-osc-24m", "dummy", "dummy", "dummy", }; +#if CONFIG_IS_ENABLED(VIDEO) +static u32 share_count_media; +#endif + +static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; +#if CONFIG_IS_ENABLED(VIDEO) +static const char * const video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", }; +#endif static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; static const char * const sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; @@ -23,167 +30,194 @@ static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_se static const char * const imx8mp_arm_core_sels[] = {"arm_a53_src", "arm_pll_out", }; -static const char * const imx8mp_a53_sels[] = {"clock-osc-24m", "arm_pll_out", "sys_pll2_500m", +static const char * const imx8mp_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; -static const char * const imx8mp_hsio_axi_sels[] = {"clock-osc-24m", "sys_pll2_500m", "sys_pll1_800m", +static const char * const imx8mp_hsio_axi_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char * const imx8mp_main_axi_sels[] = {"clock-osc-24m", "sys_pll2_333m", "sys_pll1_800m", +#if CONFIG_IS_ENABLED(VIDEO) +static const char * const imx8mp_media_isp_sels[] = {"osc_24m", "sys_pll2_1000m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_400m", "audio_pll2_out", + "clk_ext1", "sys_pll2_500m", }; +#endif + +static const char * const imx8mp_main_axi_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll1_800m", "sys_pll2_250m", "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "sys_pll1_100m",}; -static const char * const imx8mp_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", +static const char * const imx8mp_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; -static const char * const imx8mp_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", +static const char * const imx8mp_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out", "sys_pll2_250m", "audio_pll1_out", }; -static const char * const imx8mp_noc_sels[] = {"clock-osc-24m", "sys_pll1_800m", "sys_pll3_out", +#if CONFIG_IS_ENABLED(VIDEO) +static const char * const imx8mp_media_axi_sels[] = {"osc_24m", "sys_pll2_1000m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_40m", "audio_pll2_out", + "clk_ext1", "sys_pll2_500m", }; + +static const char * const imx8mp_media_apb_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_40m", "audio_pll2_out", + "clk_ext1", "sys_pll1_133m", }; +#endif + +static const char * const imx8mp_noc_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; -static const char * const imx8mp_noc_io_sels[] = {"clock-osc-24m", "sys_pll1_800m", "sys_pll3_out", +static const char * const imx8mp_noc_io_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; -static const char * const imx8mp_ahb_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_800m", +static const char * const imx8mp_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", }; -static const char * const imx8mp_dram_alt_sels[] = {"clock-osc-24m", "sys_pll1_800m", "sys_pll1_100m", +static const char * const imx8mp_dram_alt_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll1_100m", "sys_pll2_500m", "sys_pll2_1000m", "sys_pll3_out", "audio_pll1_out", "sys_pll1_266m", }; -static const char * const imx8mp_dram_apb_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mp_dram_apb_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mp_pcie_aux_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll2_50m", +static const char * const imx8mp_pcie_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out", "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", }; -static const char * const imx8mp_i2c5_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_i2c5_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mp_i2c6_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_i2c6_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mp_enet_qos_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m", +static const char * const imx8mp_enet_qos_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", }; -static const char * const imx8mp_enet_qos_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out", +static const char * const imx8mp_enet_qos_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", "video_pll1_out", }; -static const char * const imx8mp_usdhc1_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mp_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; -static const char * const imx8mp_usdhc2_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mp_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; -static const char * const imx8mp_i2c1_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mp_i2c2_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mp_i2c3_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mp_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mp_uart1_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", +static const char * const imx8mp_uart1_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char * const imx8mp_uart2_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", +static const char * const imx8mp_uart2_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char * const imx8mp_uart3_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", +static const char * const imx8mp_uart3_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char * const imx8mp_uart4_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", +static const char * const imx8mp_uart4_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char * const imx8mp_usb_core_ref_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", +static const char * const imx8mp_usb_core_ref_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char * const imx8mp_usb_phy_ref_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", +static const char * const imx8mp_usb_phy_ref_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char * const imx8mp_gic_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mp_gic_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll1_800m", "sys_pll2_500m", "clk_ext4", "audio_pll2_out" }; -static const char * const imx8mp_pwm1_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mp_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mp_pwm2_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mp_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mp_pwm3_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mp_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mp_pwm4_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mp_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mp_ecspi1_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mp_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mp_ecspi2_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mp_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mp_ecspi3_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mp_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mp_wdog_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_160m", +static const char * const imx8mp_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", "vpu_pll_out", "sys_pll2_125m", "sys_pll3_out", "sys_pll1_80m", "sys_pll2_166m" }; -static const char * const imx8mp_qspi_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll2_333m", +static const char * const imx8mp_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m", "sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m", "sys_pll3_out", "sys_pll1_100m", }; -static const char * const imx8mp_usdhc3_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mp_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; -static const char * const imx8mp_enet_ref_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m", +#if CONFIG_IS_ENABLED(VIDEO) +static const char * const imx8mp_media_disp_pix_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out", + "audio_pll1_out", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "clk_ext4", }; + +static const char * const imx8mp_media_ldb_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", + "sys_pll1_800m", "sys_pll2_1000m", + "clk_ext2", "audio_pll2_out", + "video_pll1_out", }; +#endif + +static const char * const imx8mp_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", }; -static const char * const imx8mp_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out", +static const char * const imx8mp_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", "video_pll1_out", }; -static const char * const imx8mp_enet_phy_ref_sels[] = {"clock-osc-24m", "sys_pll2_50m", "sys_pll2_125m", +static const char * const imx8mp_enet_phy_ref_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", "sys_pll2_200m", "sys_pll2_500m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; @@ -199,12 +233,19 @@ static int imx8mp_clk_probe(struct udevice *dev) clk_dm(IMX8MP_CLK_DUMMY, clk_register_fixed_rate(NULL, "dummy", 0)); - clk_dm(IMX8MP_DRAM_PLL_REF_SEL, imx_clk_mux("dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); - clk_dm(IMX8MP_ARM_PLL_REF_SEL, imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); - clk_dm(IMX8MP_SYS_PLL1_REF_SEL, imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); - clk_dm(IMX8MP_SYS_PLL2_REF_SEL, imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); - clk_dm(IMX8MP_SYS_PLL3_REF_SEL, imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); - +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_VIDEO_PLL1_REF_SEL, imx_clk_mux(dev, "video_pll1_ref_sel", base + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); +#endif + clk_dm(IMX8MP_DRAM_PLL_REF_SEL, imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); + clk_dm(IMX8MP_ARM_PLL_REF_SEL, imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); + clk_dm(IMX8MP_SYS_PLL1_REF_SEL, imx_clk_mux(dev, "sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); + clk_dm(IMX8MP_SYS_PLL2_REF_SEL, imx_clk_mux(dev, "sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); + clk_dm(IMX8MP_SYS_PLL3_REF_SEL, imx_clk_mux(dev, "sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); + +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_VIDEO_PLL1, imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, + &imx_1443x_pll)); +#endif clk_dm(IMX8MP_DRAM_PLL, imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_dram_pll)); clk_dm(IMX8MP_ARM_PLL, imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, @@ -216,37 +257,43 @@ static int imx8mp_clk_probe(struct udevice *dev) clk_dm(IMX8MP_SYS_PLL3, imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx_1416x_pll)); - clk_dm(IMX8MP_DRAM_PLL_BYPASS, imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 4, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT)); - clk_dm(IMX8MP_ARM_PLL_BYPASS, imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT)); - clk_dm(IMX8MP_SYS_PLL1_BYPASS, imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 4, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT)); - clk_dm(IMX8MP_SYS_PLL2_BYPASS, imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 4, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT)); - clk_dm(IMX8MP_SYS_PLL3_BYPASS, imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT)); - - clk_dm(IMX8MP_DRAM_PLL_OUT, imx_clk_gate("dram_pll_out", "dram_pll_bypass", base + 0x50, 13)); - clk_dm(IMX8MP_ARM_PLL_OUT, imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x84, 11)); - clk_dm(IMX8MP_SYS_PLL1_OUT, imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11)); - clk_dm(IMX8MP_SYS_PLL2_OUT, imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11)); - clk_dm(IMX8MP_SYS_PLL3_OUT, imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11)); - - clk_dm(IMX8MP_SYS_PLL1_40M, imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20)); - clk_dm(IMX8MP_SYS_PLL1_80M, imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10)); - clk_dm(IMX8MP_SYS_PLL1_100M, imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8)); - clk_dm(IMX8MP_SYS_PLL1_133M, imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6)); - clk_dm(IMX8MP_SYS_PLL1_160M, imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5)); - clk_dm(IMX8MP_SYS_PLL1_200M, imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4)); - clk_dm(IMX8MP_SYS_PLL1_266M, imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3)); - clk_dm(IMX8MP_SYS_PLL1_400M, imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2)); - clk_dm(IMX8MP_SYS_PLL1_800M, imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1)); - - clk_dm(IMX8MP_SYS_PLL2_50M, imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20)); - clk_dm(IMX8MP_SYS_PLL2_100M, imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10)); - clk_dm(IMX8MP_SYS_PLL2_125M, imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8)); - clk_dm(IMX8MP_SYS_PLL2_166M, imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6)); - clk_dm(IMX8MP_SYS_PLL2_200M, imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5)); - clk_dm(IMX8MP_SYS_PLL2_250M, imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4)); - clk_dm(IMX8MP_SYS_PLL2_333M, imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3)); - clk_dm(IMX8MP_SYS_PLL2_500M, imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2)); - clk_dm(IMX8MP_SYS_PLL2_1000M, imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_VIDEO_PLL1_BYPASS, imx_clk_mux_flags(dev, "video_pll1_bypass", base + 0x28, 16, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT)); +#endif + clk_dm(IMX8MP_DRAM_PLL_BYPASS, imx_clk_mux_flags(dev, "dram_pll_bypass", base + 0x50, 4, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT)); + clk_dm(IMX8MP_ARM_PLL_BYPASS, imx_clk_mux_flags(dev, "arm_pll_bypass", base + 0x84, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT)); + clk_dm(IMX8MP_SYS_PLL1_BYPASS, imx_clk_mux_flags(dev, "sys_pll1_bypass", base + 0x94, 4, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT)); + clk_dm(IMX8MP_SYS_PLL2_BYPASS, imx_clk_mux_flags(dev, "sys_pll2_bypass", base + 0x104, 4, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT)); + clk_dm(IMX8MP_SYS_PLL3_BYPASS, imx_clk_mux_flags(dev, "sys_pll3_bypass", base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT)); + +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_VIDEO_PLL1_OUT, imx_clk_gate(dev, "video_pll1_out", "video_pll1_bypass", base + 0x28, 13)); +#endif + clk_dm(IMX8MP_DRAM_PLL_OUT, imx_clk_gate(dev, "dram_pll_out", "dram_pll_bypass", base + 0x50, 13)); + clk_dm(IMX8MP_ARM_PLL_OUT, imx_clk_gate(dev, "arm_pll_out", "arm_pll_bypass", base + 0x84, 11)); + clk_dm(IMX8MP_SYS_PLL1_OUT, imx_clk_gate(dev, "sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11)); + clk_dm(IMX8MP_SYS_PLL2_OUT, imx_clk_gate(dev, "sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11)); + clk_dm(IMX8MP_SYS_PLL3_OUT, imx_clk_gate(dev, "sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11)); + + clk_dm(IMX8MP_SYS_PLL1_40M, imx_clk_fixed_factor(dev, "sys_pll1_40m", "sys_pll1_out", 1, 20)); + clk_dm(IMX8MP_SYS_PLL1_80M, imx_clk_fixed_factor(dev, "sys_pll1_80m", "sys_pll1_out", 1, 10)); + clk_dm(IMX8MP_SYS_PLL1_100M, imx_clk_fixed_factor(dev, "sys_pll1_100m", "sys_pll1_out", 1, 8)); + clk_dm(IMX8MP_SYS_PLL1_133M, imx_clk_fixed_factor(dev, "sys_pll1_133m", "sys_pll1_out", 1, 6)); + clk_dm(IMX8MP_SYS_PLL1_160M, imx_clk_fixed_factor(dev, "sys_pll1_160m", "sys_pll1_out", 1, 5)); + clk_dm(IMX8MP_SYS_PLL1_200M, imx_clk_fixed_factor(dev, "sys_pll1_200m", "sys_pll1_out", 1, 4)); + clk_dm(IMX8MP_SYS_PLL1_266M, imx_clk_fixed_factor(dev, "sys_pll1_266m", "sys_pll1_out", 1, 3)); + clk_dm(IMX8MP_SYS_PLL1_400M, imx_clk_fixed_factor(dev, "sys_pll1_400m", "sys_pll1_out", 1, 2)); + clk_dm(IMX8MP_SYS_PLL1_800M, imx_clk_fixed_factor(dev, "sys_pll1_800m", "sys_pll1_out", 1, 1)); + + clk_dm(IMX8MP_SYS_PLL2_50M, imx_clk_fixed_factor(dev, "sys_pll2_50m", "sys_pll2_out", 1, 20)); + clk_dm(IMX8MP_SYS_PLL2_100M, imx_clk_fixed_factor(dev, "sys_pll2_100m", "sys_pll2_out", 1, 10)); + clk_dm(IMX8MP_SYS_PLL2_125M, imx_clk_fixed_factor(dev, "sys_pll2_125m", "sys_pll2_out", 1, 8)); + clk_dm(IMX8MP_SYS_PLL2_166M, imx_clk_fixed_factor(dev, "sys_pll2_166m", "sys_pll2_out", 1, 6)); + clk_dm(IMX8MP_SYS_PLL2_200M, imx_clk_fixed_factor(dev, "sys_pll2_200m", "sys_pll2_out", 1, 5)); + clk_dm(IMX8MP_SYS_PLL2_250M, imx_clk_fixed_factor(dev, "sys_pll2_250m", "sys_pll2_out", 1, 4)); + clk_dm(IMX8MP_SYS_PLL2_333M, imx_clk_fixed_factor(dev, "sys_pll2_333m", "sys_pll2_out", 1, 3)); + clk_dm(IMX8MP_SYS_PLL2_500M, imx_clk_fixed_factor(dev, "sys_pll2_500m", "sys_pll2_out", 1, 2)); + clk_dm(IMX8MP_SYS_PLL2_1000M, imx_clk_fixed_factor(dev, "sys_pll2_1000m", "sys_pll2_out", 1, 1)); ret = clk_get_by_name(dev, "osc_24m", &osc_24m_clk); if (ret) @@ -262,104 +309,126 @@ static int imx8mp_clk_probe(struct udevice *dev) if (!base) return -EINVAL; - clk_dm(IMX8MP_CLK_A53_SRC, imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mp_a53_sels, ARRAY_SIZE(imx8mp_a53_sels))); - clk_dm(IMX8MP_CLK_A53_CG, imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); - clk_dm(IMX8MP_CLK_A53_DIV, imx_clk_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3)); - - clk_dm(IMX8MP_CLK_HSIO_AXI, imx8m_clk_composite("hsio_axi", imx8mp_hsio_axi_sels, base + 0x8380)); - clk_dm(IMX8MP_CLK_MAIN_AXI, imx8m_clk_composite_critical("main_axi", imx8mp_main_axi_sels, base + 0x8800)); - clk_dm(IMX8MP_CLK_ENET_AXI, imx8m_clk_composite_critical("enet_axi", imx8mp_enet_axi_sels, base + 0x8880)); - clk_dm(IMX8MP_CLK_NAND_USDHC_BUS, imx8m_clk_composite_critical("nand_usdhc_bus", imx8mp_nand_usdhc_sels, base + 0x8900)); - clk_dm(IMX8MP_CLK_NOC, imx8m_clk_composite_critical("noc", imx8mp_noc_sels, base + 0x8d00)); - clk_dm(IMX8MP_CLK_NOC_IO, imx8m_clk_composite_critical("noc_io", imx8mp_noc_io_sels, base + 0x8d80)); - - clk_dm(IMX8MP_CLK_AHB, imx8m_clk_composite_critical("ahb_root", imx8mp_ahb_sels, base + 0x9000)); - - clk_dm(IMX8MP_CLK_IPG_ROOT, imx_clk_divider2("ipg_root", "ahb_root", base + 0x9080, 0, 1)); - - clk_dm(IMX8MP_CLK_DRAM_ALT, imx8m_clk_composite("dram_alt", imx8mp_dram_alt_sels, base + 0xa000)); - clk_dm(IMX8MP_CLK_DRAM_APB, imx8m_clk_composite_critical("dram_apb", imx8mp_dram_apb_sels, base + 0xa080)); - clk_dm(IMX8MP_CLK_PCIE_AUX, imx8m_clk_composite("pcie_aux", imx8mp_pcie_aux_sels, base + 0xa400)); - clk_dm(IMX8MP_CLK_I2C5, imx8m_clk_composite("i2c5", imx8mp_i2c5_sels, base + 0xa480)); - clk_dm(IMX8MP_CLK_I2C6, imx8m_clk_composite("i2c6", imx8mp_i2c6_sels, base + 0xa500)); - clk_dm(IMX8MP_CLK_ENET_QOS, imx8m_clk_composite("enet_qos", imx8mp_enet_qos_sels, base + 0xa880)); - clk_dm(IMX8MP_CLK_ENET_QOS_TIMER, imx8m_clk_composite("enet_qos_timer", imx8mp_enet_qos_timer_sels, base + 0xa900)); - clk_dm(IMX8MP_CLK_ENET_REF, imx8m_clk_composite("enet_ref", imx8mp_enet_ref_sels, base + 0xa980)); - clk_dm(IMX8MP_CLK_ENET_TIMER, imx8m_clk_composite("enet_timer", imx8mp_enet_timer_sels, base + 0xaa00)); - clk_dm(IMX8MP_CLK_ENET_PHY_REF, imx8m_clk_composite("enet_phy_ref", imx8mp_enet_phy_ref_sels, base + 0xaa80)); - clk_dm(IMX8MP_CLK_QSPI, imx8m_clk_composite("qspi", imx8mp_qspi_sels, base + 0xab80)); - clk_dm(IMX8MP_CLK_USDHC1, imx8m_clk_composite("usdhc1", imx8mp_usdhc1_sels, base + 0xac00)); - clk_dm(IMX8MP_CLK_USDHC2, imx8m_clk_composite("usdhc2", imx8mp_usdhc2_sels, base + 0xac80)); - clk_dm(IMX8MP_CLK_I2C1, imx8m_clk_composite("i2c1", imx8mp_i2c1_sels, base + 0xad00)); - clk_dm(IMX8MP_CLK_I2C2, imx8m_clk_composite("i2c2", imx8mp_i2c2_sels, base + 0xad80)); - clk_dm(IMX8MP_CLK_I2C3, imx8m_clk_composite("i2c3", imx8mp_i2c3_sels, base + 0xae00)); - clk_dm(IMX8MP_CLK_I2C4, imx8m_clk_composite("i2c4", imx8mp_i2c4_sels, base + 0xae80)); - - clk_dm(IMX8MP_CLK_UART1, imx8m_clk_composite("uart1", imx8mp_uart1_sels, base + 0xaf00)); - clk_dm(IMX8MP_CLK_UART2, imx8m_clk_composite("uart2", imx8mp_uart2_sels, base + 0xaf80)); - clk_dm(IMX8MP_CLK_UART3, imx8m_clk_composite("uart3", imx8mp_uart3_sels, base + 0xb000)); - clk_dm(IMX8MP_CLK_UART4, imx8m_clk_composite("uart4", imx8mp_uart4_sels, base + 0xb080)); - clk_dm(IMX8MP_CLK_USB_CORE_REF, imx8m_clk_composite("usb_core_ref", imx8mp_usb_core_ref_sels, base + 0xb100)); - clk_dm(IMX8MP_CLK_USB_PHY_REF, imx8m_clk_composite("usb_phy_ref", imx8mp_usb_phy_ref_sels, base + 0xb180)); - clk_dm(IMX8MP_CLK_GIC, imx8m_clk_composite_critical("gic", imx8mp_gic_sels, base + 0xb200)); - clk_dm(IMX8MP_CLK_ECSPI1, imx8m_clk_composite("ecspi1", imx8mp_ecspi1_sels, base + 0xb280)); - clk_dm(IMX8MP_CLK_ECSPI2, imx8m_clk_composite("ecspi2", imx8mp_ecspi2_sels, base + 0xb300)); - clk_dm(IMX8MP_CLK_PWM1, imx8m_clk_composite_critical("pwm1", imx8mp_pwm1_sels, base + 0xb380)); - clk_dm(IMX8MP_CLK_PWM2, imx8m_clk_composite_critical("pwm2", imx8mp_pwm2_sels, base + 0xb400)); - clk_dm(IMX8MP_CLK_PWM3, imx8m_clk_composite_critical("pwm3", imx8mp_pwm3_sels, base + 0xb480)); - clk_dm(IMX8MP_CLK_PWM4, imx8m_clk_composite_critical("pwm4", imx8mp_pwm4_sels, base + 0xb500)); - clk_dm(IMX8MP_CLK_ECSPI3, imx8m_clk_composite("ecspi3", imx8mp_ecspi3_sels, base + 0xc180)); - - clk_dm(IMX8MP_CLK_WDOG, imx8m_clk_composite("wdog", imx8mp_wdog_sels, base + 0xb900)); - clk_dm(IMX8MP_CLK_USDHC3, imx8m_clk_composite("usdhc3", imx8mp_usdhc3_sels, base + 0xbc80)); - - clk_dm(IMX8MP_CLK_DRAM_ALT_ROOT, imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4)); - clk_dm(IMX8MP_CLK_DRAM_CORE, imx_clk_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mp_dram_core_sels, ARRAY_SIZE(imx8mp_dram_core_sels), CLK_IS_CRITICAL)); - - clk_dm(IMX8MP_CLK_DRAM1_ROOT, imx_clk_gate4_flags("dram1_root_clk", "dram_core_clk", base + 0x4050, 0, CLK_IS_CRITICAL)); - clk_dm(IMX8MP_CLK_ECSPI1_ROOT, imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); - clk_dm(IMX8MP_CLK_ECSPI2_ROOT, imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); - clk_dm(IMX8MP_CLK_ECSPI3_ROOT, imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); - clk_dm(IMX8MP_CLK_ENET1_ROOT, imx_clk_gate4("enet1_root_clk", "enet_axi", base + 0x40a0, 0)); - clk_dm(IMX8MP_CLK_GPIO1_ROOT, imx_clk_gate4("gpio1_root_clk", "ipg_root", base + 0x40b0, 0)); - clk_dm(IMX8MP_CLK_GPIO2_ROOT, imx_clk_gate4("gpio2_root_clk", "ipg_root", base + 0x40c0, 0)); - clk_dm(IMX8MP_CLK_GPIO3_ROOT, imx_clk_gate4("gpio3_root_clk", "ipg_root", base + 0x40d0, 0)); - clk_dm(IMX8MP_CLK_GPIO4_ROOT, imx_clk_gate4("gpio4_root_clk", "ipg_root", base + 0x40e0, 0)); - clk_dm(IMX8MP_CLK_GPIO5_ROOT, imx_clk_gate4("gpio5_root_clk", "ipg_root", base + 0x40f0, 0)); - clk_dm(IMX8MP_CLK_I2C1_ROOT, imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0)); - clk_dm(IMX8MP_CLK_I2C2_ROOT, imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0)); - clk_dm(IMX8MP_CLK_I2C3_ROOT, imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0)); - clk_dm(IMX8MP_CLK_I2C4_ROOT, imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0)); - clk_dm(IMX8MP_CLK_PCIE_ROOT, imx_clk_gate4("pcie_root_clk", "pcie_aux", base + 0x4250, 0)); - clk_dm(IMX8MP_CLK_PWM1_ROOT, imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0)); - clk_dm(IMX8MP_CLK_PWM2_ROOT, imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0)); - clk_dm(IMX8MP_CLK_PWM3_ROOT, imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0)); - clk_dm(IMX8MP_CLK_PWM4_ROOT, imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0)); - clk_dm(IMX8MP_CLK_QOS_ROOT, imx_clk_gate4("qos_root_clk", "ipg_root", base + 0x42c0, 0)); - clk_dm(IMX8MP_CLK_QOS_ENET_ROOT, imx_clk_gate4("qos_enet_root_clk", "ipg_root", base + 0x42e0, 0)); - clk_dm(IMX8MP_CLK_QSPI_ROOT, imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0)); - clk_dm(IMX8MP_CLK_I2C5_ROOT, imx_clk_gate2("i2c5_root_clk", "i2c5", base + 0x4330, 0)); - clk_dm(IMX8MP_CLK_I2C6_ROOT, imx_clk_gate2("i2c6_root_clk", "i2c6", base + 0x4340, 0)); - clk_dm(IMX8MP_CLK_SIM_ENET_ROOT, imx_clk_gate4("sim_enet_root_clk", "enet_axi", base + 0x4400, 0)); - clk_dm(IMX8MP_CLK_ENET_QOS_ROOT, imx_clk_gate4("enet_qos_root_clk", "sim_enet_root_clk", base + 0x43b0, 0)); - clk_dm(IMX8MP_CLK_UART1_ROOT, imx_clk_gate4("uart1_root_clk", "uart1", base + 0x4490, 0)); - clk_dm(IMX8MP_CLK_UART2_ROOT, imx_clk_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0)); - clk_dm(IMX8MP_CLK_UART3_ROOT, imx_clk_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0)); - clk_dm(IMX8MP_CLK_UART4_ROOT, imx_clk_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0)); - clk_dm(IMX8MP_CLK_USB_ROOT, imx_clk_gate2("usb_root_clk", "hsio_axi", base + 0x44d0, 0)); - clk_dm(IMX8MP_CLK_USB_SUSP, imx_clk_gate2("usb_suspend_clk", "clock-osc-24m", base + 0x44d0, 0)); - clk_dm(IMX8MP_CLK_USB_PHY_ROOT, imx_clk_gate4("usb_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0)); - clk_dm(IMX8MP_CLK_USDHC1_ROOT, imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); - clk_dm(IMX8MP_CLK_USDHC2_ROOT, imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); - clk_dm(IMX8MP_CLK_WDOG1_ROOT, imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0)); - clk_dm(IMX8MP_CLK_WDOG2_ROOT, imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0)); - clk_dm(IMX8MP_CLK_WDOG3_ROOT, imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0)); - clk_dm(IMX8MP_CLK_HSIO_ROOT, imx_clk_gate4("hsio_root_clk", "ipg_root", base + 0x45c0, 0)); - - clk_dm(IMX8MP_CLK_USDHC3_ROOT, imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); + clk_dm(IMX8MP_CLK_A53_SRC, imx_clk_mux2(dev, "arm_a53_src", base + 0x8000, 24, 3, imx8mp_a53_sels, ARRAY_SIZE(imx8mp_a53_sels))); + clk_dm(IMX8MP_CLK_A53_CG, imx_clk_gate3(dev, "arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); + clk_dm(IMX8MP_CLK_A53_DIV, imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3)); + + clk_dm(IMX8MP_CLK_HSIO_AXI, imx8m_clk_composite(dev, "hsio_axi", imx8mp_hsio_axi_sels, base + 0x8380)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_CLK_MEDIA_ISP, imx8m_clk_composite(dev, "media_isp", imx8mp_media_isp_sels, base + 0x8400)); +#endif + clk_dm(IMX8MP_CLK_MAIN_AXI, imx8m_clk_composite_critical(dev, "main_axi", imx8mp_main_axi_sels, base + 0x8800)); + clk_dm(IMX8MP_CLK_ENET_AXI, imx8m_clk_composite_critical(dev, "enet_axi", imx8mp_enet_axi_sels, base + 0x8880)); + clk_dm(IMX8MP_CLK_NAND_USDHC_BUS, imx8m_clk_composite_critical(dev, "nand_usdhc_bus", imx8mp_nand_usdhc_sels, base + 0x8900)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_CLK_MEDIA_AXI, imx8m_clk_composite(dev, "media_axi", imx8mp_media_axi_sels, base + 0x8a00)); + clk_dm(IMX8MP_CLK_MEDIA_APB, imx8m_clk_composite(dev, "media_apb", imx8mp_media_apb_sels, base + 0x8a80)); +#endif + clk_dm(IMX8MP_CLK_NOC, imx8m_clk_composite_critical(dev, "noc", imx8mp_noc_sels, base + 0x8d00)); + clk_dm(IMX8MP_CLK_NOC_IO, imx8m_clk_composite_critical(dev, "noc_io", imx8mp_noc_io_sels, base + 0x8d80)); + + clk_dm(IMX8MP_CLK_AHB, imx8m_clk_composite_critical(dev, "ahb_root", imx8mp_ahb_sels, base + 0x9000)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_CLK_MEDIA_DISP2_PIX, imx8m_clk_composite(dev, "media_disp2_pix", imx8mp_media_disp_pix_sels, base + 0x9300)); +#endif + + clk_dm(IMX8MP_CLK_IPG_ROOT, imx_clk_divider2(dev, "ipg_root", "ahb_root", base + 0x9080, 0, 1)); + + clk_dm(IMX8MP_CLK_DRAM_ALT, imx8m_clk_composite(dev, "dram_alt", imx8mp_dram_alt_sels, base + 0xa000)); + clk_dm(IMX8MP_CLK_DRAM_APB, imx8m_clk_composite_critical(dev, "dram_apb", imx8mp_dram_apb_sels, base + 0xa080)); + clk_dm(IMX8MP_CLK_PCIE_AUX, imx8m_clk_composite(dev, "pcie_aux", imx8mp_pcie_aux_sels, base + 0xa400)); + clk_dm(IMX8MP_CLK_I2C5, imx8m_clk_composite(dev, "i2c5", imx8mp_i2c5_sels, base + 0xa480)); + clk_dm(IMX8MP_CLK_I2C6, imx8m_clk_composite(dev, "i2c6", imx8mp_i2c6_sels, base + 0xa500)); + clk_dm(IMX8MP_CLK_ENET_QOS, imx8m_clk_composite(dev, "enet_qos", imx8mp_enet_qos_sels, base + 0xa880)); + clk_dm(IMX8MP_CLK_ENET_QOS_TIMER, imx8m_clk_composite(dev, "enet_qos_timer", imx8mp_enet_qos_timer_sels, base + 0xa900)); + clk_dm(IMX8MP_CLK_ENET_REF, imx8m_clk_composite(dev, "enet_ref", imx8mp_enet_ref_sels, base + 0xa980)); + clk_dm(IMX8MP_CLK_ENET_TIMER, imx8m_clk_composite(dev, "enet_timer", imx8mp_enet_timer_sels, base + 0xaa00)); + clk_dm(IMX8MP_CLK_ENET_PHY_REF, imx8m_clk_composite(dev, "enet_phy_ref", imx8mp_enet_phy_ref_sels, base + 0xaa80)); + clk_dm(IMX8MP_CLK_QSPI, imx8m_clk_composite(dev, "qspi", imx8mp_qspi_sels, base + 0xab80)); + clk_dm(IMX8MP_CLK_USDHC1, imx8m_clk_composite(dev, "usdhc1", imx8mp_usdhc1_sels, base + 0xac00)); + clk_dm(IMX8MP_CLK_USDHC2, imx8m_clk_composite(dev, "usdhc2", imx8mp_usdhc2_sels, base + 0xac80)); + clk_dm(IMX8MP_CLK_I2C1, imx8m_clk_composite(dev, "i2c1", imx8mp_i2c1_sels, base + 0xad00)); + clk_dm(IMX8MP_CLK_I2C2, imx8m_clk_composite(dev, "i2c2", imx8mp_i2c2_sels, base + 0xad80)); + clk_dm(IMX8MP_CLK_I2C3, imx8m_clk_composite(dev, "i2c3", imx8mp_i2c3_sels, base + 0xae00)); + clk_dm(IMX8MP_CLK_I2C4, imx8m_clk_composite(dev, "i2c4", imx8mp_i2c4_sels, base + 0xae80)); + + clk_dm(IMX8MP_CLK_UART1, imx8m_clk_composite(dev, "uart1", imx8mp_uart1_sels, base + 0xaf00)); + clk_dm(IMX8MP_CLK_UART2, imx8m_clk_composite(dev, "uart2", imx8mp_uart2_sels, base + 0xaf80)); + clk_dm(IMX8MP_CLK_UART3, imx8m_clk_composite(dev, "uart3", imx8mp_uart3_sels, base + 0xb000)); + clk_dm(IMX8MP_CLK_UART4, imx8m_clk_composite(dev, "uart4", imx8mp_uart4_sels, base + 0xb080)); + clk_dm(IMX8MP_CLK_USB_CORE_REF, imx8m_clk_composite(dev, "usb_core_ref", imx8mp_usb_core_ref_sels, base + 0xb100)); + clk_dm(IMX8MP_CLK_USB_PHY_REF, imx8m_clk_composite(dev, "usb_phy_ref", imx8mp_usb_phy_ref_sels, base + 0xb180)); + clk_dm(IMX8MP_CLK_GIC, imx8m_clk_composite_critical(dev, "gic", imx8mp_gic_sels, base + 0xb200)); + clk_dm(IMX8MP_CLK_ECSPI1, imx8m_clk_composite(dev, "ecspi1", imx8mp_ecspi1_sels, base + 0xb280)); + clk_dm(IMX8MP_CLK_ECSPI2, imx8m_clk_composite(dev, "ecspi2", imx8mp_ecspi2_sels, base + 0xb300)); + clk_dm(IMX8MP_CLK_PWM1, imx8m_clk_composite_critical(dev, "pwm1", imx8mp_pwm1_sels, base + 0xb380)); + clk_dm(IMX8MP_CLK_PWM2, imx8m_clk_composite_critical(dev, "pwm2", imx8mp_pwm2_sels, base + 0xb400)); + clk_dm(IMX8MP_CLK_PWM3, imx8m_clk_composite_critical(dev, "pwm3", imx8mp_pwm3_sels, base + 0xb480)); + clk_dm(IMX8MP_CLK_PWM4, imx8m_clk_composite_critical(dev, "pwm4", imx8mp_pwm4_sels, base + 0xb500)); + clk_dm(IMX8MP_CLK_ECSPI3, imx8m_clk_composite(dev, "ecspi3", imx8mp_ecspi3_sels, base + 0xc180)); + + clk_dm(IMX8MP_CLK_WDOG, imx8m_clk_composite(dev, "wdog", imx8mp_wdog_sels, base + 0xb900)); + clk_dm(IMX8MP_CLK_USDHC3, imx8m_clk_composite(dev, "usdhc3", imx8mp_usdhc3_sels, base + 0xbc80)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_CLK_MEDIA_DISP1_PIX, imx8m_clk_composite(dev, "media_disp1_pix", imx8mp_media_disp_pix_sels, base + 0xbe00)); + clk_dm(IMX8MP_CLK_MEDIA_LDB, imx8m_clk_composite(dev, "media_ldb", imx8mp_media_ldb_sels, base + 0xbf00)); +#endif + + clk_dm(IMX8MP_CLK_DRAM_ALT_ROOT, imx_clk_fixed_factor(dev, "dram_alt_root", "dram_alt", 1, 4)); + clk_dm(IMX8MP_CLK_DRAM_CORE, imx_clk_mux2_flags(dev, "dram_core_clk", base + 0x9800, 24, 1, imx8mp_dram_core_sels, ARRAY_SIZE(imx8mp_dram_core_sels), CLK_IS_CRITICAL)); + + clk_dm(IMX8MP_CLK_DRAM1_ROOT, imx_clk_gate4_flags(dev, "dram1_root_clk", "dram_core_clk", base + 0x4050, 0, CLK_IS_CRITICAL)); + clk_dm(IMX8MP_CLK_ECSPI1_ROOT, imx_clk_gate4(dev, "ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); + clk_dm(IMX8MP_CLK_ECSPI2_ROOT, imx_clk_gate4(dev, "ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); + clk_dm(IMX8MP_CLK_ECSPI3_ROOT, imx_clk_gate4(dev, "ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); + clk_dm(IMX8MP_CLK_ENET1_ROOT, imx_clk_gate4(dev, "enet1_root_clk", "enet_axi", base + 0x40a0, 0)); + clk_dm(IMX8MP_CLK_GPIO1_ROOT, imx_clk_gate4(dev, "gpio1_root_clk", "ipg_root", base + 0x40b0, 0)); + clk_dm(IMX8MP_CLK_GPIO2_ROOT, imx_clk_gate4(dev, "gpio2_root_clk", "ipg_root", base + 0x40c0, 0)); + clk_dm(IMX8MP_CLK_GPIO3_ROOT, imx_clk_gate4(dev, "gpio3_root_clk", "ipg_root", base + 0x40d0, 0)); + clk_dm(IMX8MP_CLK_GPIO4_ROOT, imx_clk_gate4(dev, "gpio4_root_clk", "ipg_root", base + 0x40e0, 0)); + clk_dm(IMX8MP_CLK_GPIO5_ROOT, imx_clk_gate4(dev, "gpio5_root_clk", "ipg_root", base + 0x40f0, 0)); + clk_dm(IMX8MP_CLK_I2C1_ROOT, imx_clk_gate4(dev, "i2c1_root_clk", "i2c1", base + 0x4170, 0)); + clk_dm(IMX8MP_CLK_I2C2_ROOT, imx_clk_gate4(dev, "i2c2_root_clk", "i2c2", base + 0x4180, 0)); + clk_dm(IMX8MP_CLK_I2C3_ROOT, imx_clk_gate4(dev, "i2c3_root_clk", "i2c3", base + 0x4190, 0)); + clk_dm(IMX8MP_CLK_I2C4_ROOT, imx_clk_gate4(dev, "i2c4_root_clk", "i2c4", base + 0x41a0, 0)); + clk_dm(IMX8MP_CLK_PCIE_ROOT, imx_clk_gate4(dev, "pcie_root_clk", "pcie_aux", base + 0x4250, 0)); + clk_dm(IMX8MP_CLK_PWM1_ROOT, imx_clk_gate4(dev, "pwm1_root_clk", "pwm1", base + 0x4280, 0)); + clk_dm(IMX8MP_CLK_PWM2_ROOT, imx_clk_gate4(dev, "pwm2_root_clk", "pwm2", base + 0x4290, 0)); + clk_dm(IMX8MP_CLK_PWM3_ROOT, imx_clk_gate4(dev, "pwm3_root_clk", "pwm3", base + 0x42a0, 0)); + clk_dm(IMX8MP_CLK_PWM4_ROOT, imx_clk_gate4(dev, "pwm4_root_clk", "pwm4", base + 0x42b0, 0)); + clk_dm(IMX8MP_CLK_QOS_ROOT, imx_clk_gate4(dev, "qos_root_clk", "ipg_root", base + 0x42c0, 0)); + clk_dm(IMX8MP_CLK_QOS_ENET_ROOT, imx_clk_gate4(dev, "qos_enet_root_clk", "ipg_root", base + 0x42e0, 0)); + clk_dm(IMX8MP_CLK_QSPI_ROOT, imx_clk_gate4(dev, "qspi_root_clk", "qspi", base + 0x42f0, 0)); + clk_dm(IMX8MP_CLK_I2C5_ROOT, imx_clk_gate2(dev, "i2c5_root_clk", "i2c5", base + 0x4330, 0)); + clk_dm(IMX8MP_CLK_I2C6_ROOT, imx_clk_gate2(dev, "i2c6_root_clk", "i2c6", base + 0x4340, 0)); + clk_dm(IMX8MP_CLK_SIM_ENET_ROOT, imx_clk_gate4(dev, "sim_enet_root_clk", "enet_axi", base + 0x4400, 0)); + clk_dm(IMX8MP_CLK_ENET_QOS_ROOT, imx_clk_gate4(dev, "enet_qos_root_clk", "sim_enet_root_clk", base + 0x43b0, 0)); + clk_dm(IMX8MP_CLK_UART1_ROOT, imx_clk_gate4(dev, "uart1_root_clk", "uart1", base + 0x4490, 0)); + clk_dm(IMX8MP_CLK_UART2_ROOT, imx_clk_gate4(dev, "uart2_root_clk", "uart2", base + 0x44a0, 0)); + clk_dm(IMX8MP_CLK_UART3_ROOT, imx_clk_gate4(dev, "uart3_root_clk", "uart3", base + 0x44b0, 0)); + clk_dm(IMX8MP_CLK_UART4_ROOT, imx_clk_gate4(dev, "uart4_root_clk", "uart4", base + 0x44c0, 0)); + clk_dm(IMX8MP_CLK_USB_ROOT, imx_clk_gate2(dev, "usb_root_clk", "hsio_axi", base + 0x44d0, 0)); + clk_dm(IMX8MP_CLK_USB_SUSP, imx_clk_gate2(dev, "usb_suspend_clk", "osc_24m", base + 0x44d0, 0)); + clk_dm(IMX8MP_CLK_USB_PHY_ROOT, imx_clk_gate4(dev, "usb_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0)); + clk_dm(IMX8MP_CLK_USDHC1_ROOT, imx_clk_gate4(dev, "usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); + clk_dm(IMX8MP_CLK_USDHC2_ROOT, imx_clk_gate4(dev, "usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); + clk_dm(IMX8MP_CLK_WDOG1_ROOT, imx_clk_gate4(dev, "wdog1_root_clk", "wdog", base + 0x4530, 0)); + clk_dm(IMX8MP_CLK_WDOG2_ROOT, imx_clk_gate4(dev, "wdog2_root_clk", "wdog", base + 0x4540, 0)); + clk_dm(IMX8MP_CLK_WDOG3_ROOT, imx_clk_gate4(dev, "wdog3_root_clk", "wdog", base + 0x4550, 0)); + clk_dm(IMX8MP_CLK_HSIO_ROOT, imx_clk_gate4(dev, "hsio_root_clk", "ipg_root", base + 0x45c0, 0)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_CLK_MEDIA_APB_ROOT, imx_clk_gate2_shared2(dev, "media_apb_root_clk", "media_apb", base + 0x45d0, 0, &share_count_media)); + clk_dm(IMX8MP_CLK_MEDIA_AXI_ROOT, imx_clk_gate2_shared2(dev, "media_axi_root_clk", "media_axi", base + 0x45d0, 0, &share_count_media)); + clk_dm(IMX8MP_CLK_MEDIA_DISP1_PIX_ROOT, imx_clk_gate2_shared2(dev, "media_disp1_pix_root_clk", "media_disp1_pix", base + 0x45d0, 0, &share_count_media)); + clk_dm(IMX8MP_CLK_MEDIA_DISP2_PIX_ROOT, imx_clk_gate2_shared2(dev, "media_disp2_pix_root_clk", "media_disp2_pix", base + 0x45d0, 0, &share_count_media)); + clk_dm(IMX8MP_CLK_MEDIA_LDB_ROOT, imx_clk_gate2_shared2(dev, "media_ldb_root_clk", "media_ldb", base + 0x45d0, 0, &share_count_media)); + clk_dm(IMX8MP_CLK_MEDIA_ISP_ROOT, imx_clk_gate2_shared2(dev, "media_isp_root_clk", "media_isp", base + 0x45d0, 0, &share_count_media)); +#endif + + clk_dm(IMX8MP_CLK_USDHC3_ROOT, imx_clk_gate4(dev, "usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); clk_dm(IMX8MP_CLK_ARM, - imx_clk_mux2_flags("arm_core", base + 0x9880, 24, 1, + imx_clk_mux2_flags(dev, "arm_core", base + 0x9880, 24, 1, imx8mp_arm_core_sels, ARRAY_SIZE(imx8mp_arm_core_sels), CLK_IS_CRITICAL)); diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c index ed4acd79ef7..fe6cba19758 100644 --- a/drivers/clk/imx/clk-imx8mq.c +++ b/drivers/clk/imx/clk-imx8mq.c @@ -152,31 +152,31 @@ static int imx8mq_clk_probe(struct udevice *dev) clk_dm(IMX8MQ_CLK_27M, clk_register_fixed_rate(NULL, "clock-osc-27m", 27000000)); clk_dm(IMX8MQ_DRAM_PLL1_REF_SEL, - imx_clk_mux("dram_pll_ref_sel", base + 0x60, 0, 2, + imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x60, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_ARM_PLL_REF_SEL, - imx_clk_mux("arm_pll_ref_sel", base + 0x28, 0, 2, + imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_GPU_PLL_REF_SEL, - imx_clk_mux("gpu_pll_ref_sel", base + 0x18, 0, 2, + imx_clk_mux(dev, "gpu_pll_ref_sel", base + 0x18, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_VPU_PLL_REF_SEL, - imx_clk_mux("vpu_pll_ref_sel", base + 0x20, 0, 2, + imx_clk_mux(dev, "vpu_pll_ref_sel", base + 0x20, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_SYS3_PLL1_REF_SEL, - imx_clk_mux("sys3_pll_ref_sel", base + 0x48, 0, 2, + imx_clk_mux(dev, "sys3_pll_ref_sel", base + 0x48, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_AUDIO_PLL1_REF_SEL, - imx_clk_mux("audio_pll1_ref_sel", base + 0x0, 0, 2, + imx_clk_mux(dev, "audio_pll1_ref_sel", base + 0x0, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_AUDIO_PLL2_REF_SEL, - imx_clk_mux("audio_pll2_ref_sel", base + 0x8, 0, 2, + imx_clk_mux(dev, "audio_pll2_ref_sel", base + 0x8, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_VIDEO_PLL1_REF_SEL, - imx_clk_mux("video_pll1_ref_sel", base + 0x10, 0, 2, + imx_clk_mux(dev, "video_pll1_ref_sel", base + 0x10, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_VIDEO2_PLL1_REF_SEL, - imx_clk_mux("video_pll2_ref_sel", base + 0x54, 0, 2, + imx_clk_mux(dev, "video_pll2_ref_sel", base + 0x54, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_ARM_PLL, @@ -207,140 +207,140 @@ static int imx8mq_clk_probe(struct udevice *dev) /* PLL bypass out */ clk_dm(IMX8MQ_ARM_PLL_BYPASS, - imx_clk_mux_flags("arm_pll_bypass", base + 0x28, 4, 1, + imx_clk_mux_flags(dev, "arm_pll_bypass", base + 0x28, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MQ_GPU_PLL_BYPASS, - imx_clk_mux_flags("gpu_pll_bypass", base + 0x18, 4, 1, + imx_clk_mux_flags(dev, "gpu_pll_bypass", base + 0x18, 4, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MQ_VPU_PLL_BYPASS, - imx_clk_mux_flags("vpu_pll_bypass", base + 0x20, 4, 1, + imx_clk_mux_flags(dev, "vpu_pll_bypass", base + 0x20, 4, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MQ_AUDIO_PLL1_BYPASS, - imx_clk_mux_flags("audio_pll1_bypass", base + 0x0, 4, 1, + imx_clk_mux_flags(dev, "audio_pll1_bypass", base + 0x0, 4, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MQ_AUDIO_PLL2_BYPASS, - imx_clk_mux_flags("audio_pll2_bypass", base + 0x8, 4, 1, + imx_clk_mux_flags(dev, "audio_pll2_bypass", base + 0x8, 4, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MQ_VIDEO_PLL1_BYPASS, - imx_clk_mux_flags("video_pll1_bypass", base + 0x10, 4, 1, + imx_clk_mux_flags(dev, "video_pll1_bypass", base + 0x10, 4, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT)); /* PLL out gate */ clk_dm(IMX8MQ_DRAM_PLL_OUT, - imx_clk_gate("dram_pll_out", "dram_pll_ref_sel", + imx_clk_gate(dev, "dram_pll_out", "dram_pll_ref_sel", base + 0x60, 13)); clk_dm(IMX8MQ_ARM_PLL_OUT, - imx_clk_gate("arm_pll_out", "arm_pll_bypass", + imx_clk_gate(dev, "arm_pll_out", "arm_pll_bypass", base + 0x28, 11)); clk_dm(IMX8MQ_GPU_PLL_OUT, - imx_clk_gate("gpu_pll_out", "gpu_pll_bypass", + imx_clk_gate(dev, "gpu_pll_out", "gpu_pll_bypass", base + 0x18, 11)); clk_dm(IMX8MQ_VPU_PLL_OUT, - imx_clk_gate("vpu_pll_out", "vpu_pll_bypass", + imx_clk_gate(dev, "vpu_pll_out", "vpu_pll_bypass", base + 0x20, 11)); clk_dm(IMX8MQ_AUDIO_PLL1_OUT, - imx_clk_gate("audio_pll1_out", "audio_pll1_bypass", + imx_clk_gate(dev, "audio_pll1_out", "audio_pll1_bypass", base + 0x0, 11)); clk_dm(IMX8MQ_AUDIO_PLL2_OUT, - imx_clk_gate("audio_pll2_out", "audio_pll2_bypass", + imx_clk_gate(dev, "audio_pll2_out", "audio_pll2_bypass", base + 0x8, 11)); clk_dm(IMX8MQ_VIDEO_PLL1_OUT, - imx_clk_gate("video_pll1_out", "video_pll1_bypass", + imx_clk_gate(dev, "video_pll1_out", "video_pll1_bypass", base + 0x10, 11)); clk_dm(IMX8MQ_SYS1_PLL_OUT, - imx_clk_gate("sys_pll1_out", "sys1_pll", + imx_clk_gate(dev, "sys_pll1_out", "sys1_pll", base + 0x30, 11)); clk_dm(IMX8MQ_SYS2_PLL_OUT, - imx_clk_gate("sys_pll2_out", "sys2_pll", + imx_clk_gate(dev, "sys_pll2_out", "sys2_pll", base + 0x3c, 11)); clk_dm(IMX8MQ_SYS3_PLL_OUT, - imx_clk_gate("sys_pll3_out", "sys3_pll", + imx_clk_gate(dev, "sys_pll3_out", "sys3_pll", base + 0x48, 11)); clk_dm(IMX8MQ_VIDEO2_PLL_OUT, - imx_clk_gate("video_pll2_out", "video_pll2_ref_sel", + imx_clk_gate(dev, "video_pll2_out", "video_pll2_ref_sel", base + 0x54, 11)); /* SYS PLL fixed output */ clk_dm(IMX8MQ_SYS1_PLL_40M, - imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20)); + imx_clk_fixed_factor(dev, "sys_pll1_40m", "sys_pll1_out", 1, 20)); clk_dm(IMX8MQ_SYS1_PLL_80M, - imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10)); + imx_clk_fixed_factor(dev, "sys_pll1_80m", "sys_pll1_out", 1, 10)); clk_dm(IMX8MQ_SYS1_PLL_100M, - imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8)); + imx_clk_fixed_factor(dev, "sys_pll1_100m", "sys_pll1_out", 1, 8)); clk_dm(IMX8MQ_SYS1_PLL_133M, - imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6)); + imx_clk_fixed_factor(dev, "sys_pll1_133m", "sys_pll1_out", 1, 6)); clk_dm(IMX8MQ_SYS1_PLL_160M, - imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5)); + imx_clk_fixed_factor(dev, "sys_pll1_160m", "sys_pll1_out", 1, 5)); clk_dm(IMX8MQ_SYS1_PLL_200M, - imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4)); + imx_clk_fixed_factor(dev, "sys_pll1_200m", "sys_pll1_out", 1, 4)); clk_dm(IMX8MQ_SYS1_PLL_266M, - imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3)); + imx_clk_fixed_factor(dev, "sys_pll1_266m", "sys_pll1_out", 1, 3)); clk_dm(IMX8MQ_SYS1_PLL_400M, - imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll1_400m", "sys_pll1_out", 1, 2)); clk_dm(IMX8MQ_SYS1_PLL_800M, - imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1)); + imx_clk_fixed_factor(dev, "sys_pll1_800m", "sys_pll1_out", 1, 1)); clk_dm(IMX8MQ_SYS2_PLL_50M, - imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20)); + imx_clk_fixed_factor(dev, "sys_pll2_50m", "sys_pll2_out", 1, 20)); clk_dm(IMX8MQ_SYS2_PLL_100M, - imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10)); + imx_clk_fixed_factor(dev, "sys_pll2_100m", "sys_pll2_out", 1, 10)); clk_dm(IMX8MQ_SYS2_PLL_125M, - imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8)); + imx_clk_fixed_factor(dev, "sys_pll2_125m", "sys_pll2_out", 1, 8)); clk_dm(IMX8MQ_SYS2_PLL_166M, - imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6)); + imx_clk_fixed_factor(dev, "sys_pll2_166m", "sys_pll2_out", 1, 6)); clk_dm(IMX8MQ_SYS2_PLL_200M, - imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5)); + imx_clk_fixed_factor(dev, "sys_pll2_200m", "sys_pll2_out", 1, 5)); clk_dm(IMX8MQ_SYS2_PLL_250M, - imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4)); + imx_clk_fixed_factor(dev, "sys_pll2_250m", "sys_pll2_out", 1, 4)); clk_dm(IMX8MQ_SYS2_PLL_333M, - imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3)); + imx_clk_fixed_factor(dev, "sys_pll2_333m", "sys_pll2_out", 1, 3)); clk_dm(IMX8MQ_SYS2_PLL_500M, - imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll2_500m", "sys_pll2_out", 1, 2)); clk_dm(IMX8MQ_SYS2_PLL_1000M, - imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1)); + imx_clk_fixed_factor(dev, "sys_pll2_1000m", "sys_pll2_out", 1, 1)); clk_dm(IMX8MQ_CLK_MON_AUDIO_PLL1_DIV, - imx_clk_divider("audio_pll1_out_monitor", "audio_pll1_bypass", base + 0x78, 0, 3)); + imx_clk_divider(dev, "audio_pll1_out_monitor", "audio_pll1_bypass", base + 0x78, 0, 3)); clk_dm(IMX8MQ_CLK_MON_AUDIO_PLL2_DIV, - imx_clk_divider("audio_pll2_out_monitor", "audio_pll2_bypass", base + 0x78, 4, 3)); + imx_clk_divider(dev, "audio_pll2_out_monitor", "audio_pll2_bypass", base + 0x78, 4, 3)); clk_dm(IMX8MQ_CLK_MON_VIDEO_PLL1_DIV, - imx_clk_divider("video_pll1_out_monitor", "video_pll1_bypass", base + 0x78, 8, 3)); + imx_clk_divider(dev, "video_pll1_out_monitor", "video_pll1_bypass", base + 0x78, 8, 3)); clk_dm(IMX8MQ_CLK_MON_GPU_PLL_DIV, - imx_clk_divider("gpu_pll_out_monitor", "gpu_pll_bypass", base + 0x78, 12, 3)); + imx_clk_divider(dev, "gpu_pll_out_monitor", "gpu_pll_bypass", base + 0x78, 12, 3)); clk_dm(IMX8MQ_CLK_MON_VPU_PLL_DIV, - imx_clk_divider("vpu_pll_out_monitor", "vpu_pll_bypass", base + 0x78, 16, 3)); + imx_clk_divider(dev, "vpu_pll_out_monitor", "vpu_pll_bypass", base + 0x78, 16, 3)); clk_dm(IMX8MQ_CLK_MON_ARM_PLL_DIV, - imx_clk_divider("arm_pll_out_monitor", "arm_pll_bypass", base + 0x78, 20, 3)); + imx_clk_divider(dev, "arm_pll_out_monitor", "arm_pll_bypass", base + 0x78, 20, 3)); clk_dm(IMX8MQ_CLK_MON_SYS_PLL1_DIV, - imx_clk_divider("sys_pll1_out_monitor", "sys_pll1_out", base + 0x7c, 0, 3)); + imx_clk_divider(dev, "sys_pll1_out_monitor", "sys_pll1_out", base + 0x7c, 0, 3)); clk_dm(IMX8MQ_CLK_MON_SYS_PLL2_DIV, - imx_clk_divider("sys_pll2_out_monitor", "sys_pll2_out", base + 0x7c, 4, 3)); + imx_clk_divider(dev, "sys_pll2_out_monitor", "sys_pll2_out", base + 0x7c, 4, 3)); clk_dm(IMX8MQ_CLK_MON_SYS_PLL3_DIV, - imx_clk_divider("sys_pll3_out_monitor", "sys_pll3_out", base + 0x7c, 8, 3)); + imx_clk_divider(dev, "sys_pll3_out_monitor", "sys_pll3_out", base + 0x7c, 8, 3)); clk_dm(IMX8MQ_CLK_MON_DRAM_PLL_DIV, - imx_clk_divider("dram_pll_out_monitor", "dram_pll_out", base + 0x7c, 12, 3)); + imx_clk_divider(dev, "dram_pll_out_monitor", "dram_pll_out", base + 0x7c, 12, 3)); clk_dm(IMX8MQ_CLK_MON_VIDEO_PLL2_DIV, - imx_clk_divider("video_pll2_out_monitor", "video_pll2_out", base + 0x7c, 16, 3)); + imx_clk_divider(dev, "video_pll2_out_monitor", "video_pll2_out", base + 0x7c, 16, 3)); clk_dm(IMX8MQ_CLK_MON_SEL, - imx_clk_mux_flags("pllout_monitor_sel", base + 0x74, 0, 4, + imx_clk_mux_flags(dev, "pllout_monitor_sel", base + 0x74, 0, 4, pllout_monitor_sels, ARRAY_SIZE(pllout_monitor_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MQ_CLK_MON_CLK2_OUT, - imx_clk_gate4("pllout_monitor_clk2", "pllout_monitor_sel", base + 0x74, 4)); + imx_clk_gate4(dev, "pllout_monitor_clk2", "pllout_monitor_sel", base + 0x74, 4)); base = dev_read_addr_ptr(dev); if (!base) { @@ -349,140 +349,140 @@ static int imx8mq_clk_probe(struct udevice *dev) } clk_dm(IMX8MQ_CLK_A53_SRC, - imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, + imx_clk_mux2(dev, "arm_a53_src", base + 0x8000, 24, 3, imx8mq_a53_sels, ARRAY_SIZE(imx8mq_a53_sels))); clk_dm(IMX8MQ_CLK_A53_CG, - imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); + imx_clk_gate3(dev, "arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); clk_dm(IMX8MQ_CLK_A53_DIV, - imx_clk_divider2("arm_a53_div", "arm_a53_cg", + imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3)); clk_dm(IMX8MQ_CLK_A53_CORE, - imx_clk_mux2("arm_a53_src", base + 0x9880, 24, 1, + imx_clk_mux2(dev, "arm_a53_src", base + 0x9880, 24, 1, imx8mq_a53_core_sels, ARRAY_SIZE(imx8mq_a53_core_sels))); clk_dm(IMX8MQ_CLK_AHB, - imx8m_clk_composite_critical("ahb", imx8mq_ahb_sels, + imx8m_clk_composite_critical(dev, "ahb", imx8mq_ahb_sels, base + 0x9000)); clk_dm(IMX8MQ_CLK_IPG_ROOT, - imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1)); + imx_clk_divider2(dev, "ipg_root", "ahb", base + 0x9080, 0, 1)); clk_dm(IMX8MQ_CLK_ENET_AXI, - imx8m_clk_composite("enet_axi", imx8mq_enet_axi_sels, + imx8m_clk_composite(dev, "enet_axi", imx8mq_enet_axi_sels, base + 0x8880)); clk_dm(IMX8MQ_CLK_NAND_USDHC_BUS, - imx8m_clk_composite_critical("nand_usdhc_bus", + imx8m_clk_composite_critical(dev, "nand_usdhc_bus", imx8mq_nand_usdhc_sels, base + 0x8900)); clk_dm(IMX8MQ_CLK_USB_BUS, - imx8m_clk_composite("usb_bus", imx8mq_usb_bus_sels, base + 0x8b80)); + imx8m_clk_composite(dev, "usb_bus", imx8mq_usb_bus_sels, base + 0x8b80)); /* DRAM */ clk_dm(IMX8MQ_CLK_DRAM_CORE, - imx_clk_mux2("dram_core_clk", base + 0x9800, 24, 1, + imx_clk_mux2(dev, "dram_core_clk", base + 0x9800, 24, 1, imx8mq_dram_core_sels, ARRAY_SIZE(imx8mq_dram_core_sels))); clk_dm(IMX8MQ_CLK_DRAM_ALT, - imx8m_clk_composite("dram_alt", imx8mq_dram_alt_sels, base + 0xa000)); + imx8m_clk_composite(dev, "dram_alt", imx8mq_dram_alt_sels, base + 0xa000)); clk_dm(IMX8MQ_CLK_DRAM_APB, - imx8m_clk_composite_critical("dram_apb", imx8mq_dram_apb_sels, base + 0xa080)); + imx8m_clk_composite_critical(dev, "dram_apb", imx8mq_dram_apb_sels, base + 0xa080)); /* IP */ clk_dm(IMX8MQ_CLK_USDHC1, - imx8m_clk_composite("usdhc1", imx8mq_usdhc1_sels, + imx8m_clk_composite(dev, "usdhc1", imx8mq_usdhc1_sels, base + 0xac00)); clk_dm(IMX8MQ_CLK_USDHC2, - imx8m_clk_composite("usdhc2", imx8mq_usdhc2_sels, + imx8m_clk_composite(dev, "usdhc2", imx8mq_usdhc2_sels, base + 0xac80)); clk_dm(IMX8MQ_CLK_I2C1, - imx8m_clk_composite("i2c1", imx8mq_i2c1_sels, base + 0xad00)); + imx8m_clk_composite(dev, "i2c1", imx8mq_i2c1_sels, base + 0xad00)); clk_dm(IMX8MQ_CLK_I2C2, - imx8m_clk_composite("i2c2", imx8mq_i2c2_sels, base + 0xad80)); + imx8m_clk_composite(dev, "i2c2", imx8mq_i2c2_sels, base + 0xad80)); clk_dm(IMX8MQ_CLK_I2C3, - imx8m_clk_composite("i2c3", imx8mq_i2c3_sels, base + 0xae00)); + imx8m_clk_composite(dev, "i2c3", imx8mq_i2c3_sels, base + 0xae00)); clk_dm(IMX8MQ_CLK_I2C4, - imx8m_clk_composite("i2c4", imx8mq_i2c4_sels, base + 0xae80)); + imx8m_clk_composite(dev, "i2c4", imx8mq_i2c4_sels, base + 0xae80)); clk_dm(IMX8MQ_CLK_WDOG, - imx8m_clk_composite("wdog", imx8mq_wdog_sels, base + 0xb900)); + imx8m_clk_composite(dev, "wdog", imx8mq_wdog_sels, base + 0xb900)); clk_dm(IMX8MQ_CLK_UART1, - imx8m_clk_composite("uart1", imx8mq_uart1_sels, base + 0xaf00)); + imx8m_clk_composite(dev, "uart1", imx8mq_uart1_sels, base + 0xaf00)); clk_dm(IMX8MQ_CLK_UART2, - imx8m_clk_composite("uart2", imx8mq_uart2_sels, base + 0xaf80)); + imx8m_clk_composite(dev, "uart2", imx8mq_uart2_sels, base + 0xaf80)); clk_dm(IMX8MQ_CLK_UART3, - imx8m_clk_composite("uart3", imx8mq_uart3_sels, base + 0xb000)); + imx8m_clk_composite(dev, "uart3", imx8mq_uart3_sels, base + 0xb000)); clk_dm(IMX8MQ_CLK_UART4, - imx8m_clk_composite("uart4", imx8mq_uart4_sels, base + 0xb080)); + imx8m_clk_composite(dev, "uart4", imx8mq_uart4_sels, base + 0xb080)); clk_dm(IMX8MQ_CLK_QSPI, - imx8m_clk_composite("qspi", imx8mq_qspi_sels, base + 0xab80)); + imx8m_clk_composite(dev, "qspi", imx8mq_qspi_sels, base + 0xab80)); clk_dm(IMX8MQ_CLK_USB_CORE_REF, - imx8m_clk_composite("usb_core_ref", imx8mq_usb_core_sels, base + 0xb100)); + imx8m_clk_composite(dev, "usb_core_ref", imx8mq_usb_core_sels, base + 0xb100)); clk_dm(IMX8MQ_CLK_USB_PHY_REF, - imx8m_clk_composite("usb_phy_ref", imx8mq_usb_phy_sels, base + 0xb180)); + imx8m_clk_composite(dev, "usb_phy_ref", imx8mq_usb_phy_sels, base + 0xb180)); clk_dm(IMX8MQ_CLK_ECSPI1, - imx8m_clk_composite("ecspi1", imx8mq_ecspi1_sels, base + 0xb280)); + imx8m_clk_composite(dev, "ecspi1", imx8mq_ecspi1_sels, base + 0xb280)); clk_dm(IMX8MQ_CLK_ECSPI2, - imx8m_clk_composite("ecspi2", imx8mq_ecspi2_sels, base + 0xb300)); + imx8m_clk_composite(dev, "ecspi2", imx8mq_ecspi2_sels, base + 0xb300)); clk_dm(IMX8MQ_CLK_ECSPI3, - imx8m_clk_composite("ecspi3", imx8mq_ecspi3_sels, base + 0xc180)); + imx8m_clk_composite(dev, "ecspi3", imx8mq_ecspi3_sels, base + 0xc180)); clk_dm(IMX8MQ_CLK_ECSPI1_ROOT, - imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); + imx_clk_gate4(dev, "ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); clk_dm(IMX8MQ_CLK_ECSPI2_ROOT, - imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); + imx_clk_gate4(dev, "ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); clk_dm(IMX8MQ_CLK_ECSPI3_ROOT, - imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); + imx_clk_gate4(dev, "ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); clk_dm(IMX8MQ_CLK_I2C1_ROOT, - imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0)); + imx_clk_gate4(dev, "i2c1_root_clk", "i2c1", base + 0x4170, 0)); clk_dm(IMX8MQ_CLK_I2C2_ROOT, - imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0)); + imx_clk_gate4(dev, "i2c2_root_clk", "i2c2", base + 0x4180, 0)); clk_dm(IMX8MQ_CLK_I2C3_ROOT, - imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0)); + imx_clk_gate4(dev, "i2c3_root_clk", "i2c3", base + 0x4190, 0)); clk_dm(IMX8MQ_CLK_I2C4_ROOT, - imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0)); + imx_clk_gate4(dev, "i2c4_root_clk", "i2c4", base + 0x41a0, 0)); clk_dm(IMX8MQ_CLK_UART1_ROOT, - imx_clk_gate4("uart1_root_clk", "uart1", base + 0x4490, 0)); + imx_clk_gate4(dev, "uart1_root_clk", "uart1", base + 0x4490, 0)); clk_dm(IMX8MQ_CLK_UART2_ROOT, - imx_clk_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0)); + imx_clk_gate4(dev, "uart2_root_clk", "uart2", base + 0x44a0, 0)); clk_dm(IMX8MQ_CLK_UART3_ROOT, - imx_clk_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0)); + imx_clk_gate4(dev, "uart3_root_clk", "uart3", base + 0x44b0, 0)); clk_dm(IMX8MQ_CLK_UART4_ROOT, - imx_clk_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0)); + imx_clk_gate4(dev, "uart4_root_clk", "uart4", base + 0x44c0, 0)); clk_dm(IMX8MQ_CLK_OCOTP_ROOT, - imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0)); + imx_clk_gate4(dev, "ocotp_root_clk", "ipg_root", base + 0x4220, 0)); clk_dm(IMX8MQ_CLK_USDHC1_ROOT, - imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); + imx_clk_gate4(dev, "usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); clk_dm(IMX8MQ_CLK_USDHC2_ROOT, - imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); + imx_clk_gate4(dev, "usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); clk_dm(IMX8MQ_CLK_WDOG1_ROOT, - imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0)); + imx_clk_gate4(dev, "wdog1_root_clk", "wdog", base + 0x4530, 0)); clk_dm(IMX8MQ_CLK_WDOG2_ROOT, - imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0)); + imx_clk_gate4(dev, "wdog2_root_clk", "wdog", base + 0x4540, 0)); clk_dm(IMX8MQ_CLK_WDOG3_ROOT, - imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0)); + imx_clk_gate4(dev, "wdog3_root_clk", "wdog", base + 0x4550, 0)); clk_dm(IMX8MQ_CLK_QSPI_ROOT, - imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0)); + imx_clk_gate4(dev, "qspi_root_clk", "qspi", base + 0x42f0, 0)); clk_dm(IMX8MQ_CLK_USB1_CTRL_ROOT, - imx_clk_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0)); + imx_clk_gate4(dev, "usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0)); clk_dm(IMX8MQ_CLK_USB2_CTRL_ROOT, - imx_clk_gate4("usb2_ctrl_root_clk", "usb_bus", base + 0x44e0, 0)); + imx_clk_gate4(dev, "usb2_ctrl_root_clk", "usb_bus", base + 0x44e0, 0)); clk_dm(IMX8MQ_CLK_USB1_PHY_ROOT, - imx_clk_gate4("usb1_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0)); + imx_clk_gate4(dev, "usb1_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0)); clk_dm(IMX8MQ_CLK_USB2_PHY_ROOT, - imx_clk_gate4("usb2_phy_root_clk", "usb_phy_ref", base + 0x4500, 0)); + imx_clk_gate4(dev, "usb2_phy_root_clk", "usb_phy_ref", base + 0x4500, 0)); clk_dm(IMX8MQ_CLK_ENET_REF, - imx8m_clk_composite("enet_ref", imx8mq_enet_ref_sels, + imx8m_clk_composite(dev, "enet_ref", imx8mq_enet_ref_sels, base + 0xa980)); clk_dm(IMX8MQ_CLK_ENET_TIMER, - imx8m_clk_composite("enet_timer", imx8mq_enet_timer_sels, + imx8m_clk_composite(dev, "enet_timer", imx8mq_enet_timer_sels, base + 0xaa00)); clk_dm(IMX8MQ_CLK_ENET_PHY_REF, - imx8m_clk_composite("enet_phy", imx8mq_enet_phy_sels, + imx8m_clk_composite(dev, "enet_phy", imx8mq_enet_phy_sels, base + 0xaa80)); clk_dm(IMX8MQ_CLK_ENET1_ROOT, - imx_clk_gate4("enet1_root_clk", "enet_axi", + imx_clk_gate4(dev, "enet1_root_clk", "enet_axi", base + 0x40a0, 0)); clk_dm(IMX8MQ_CLK_DRAM_ALT_ROOT, - imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4)); + imx_clk_fixed_factor(dev, "dram_alt_root", "dram_alt", 1, 4)); return 0; } diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c index b31e57a4a01..c3112968c17 100644 --- a/drivers/clk/imx/clk-imx93.c +++ b/drivers/clk/imx/clk-imx93.c @@ -291,15 +291,15 @@ static int imx93_clk_probe(struct udevice *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)); + imx_clk_fixed_factor(dev, "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)); + imx_clk_fixed_factor(dev, "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)); + imx_clk_fixed_factor(dev, "sys_pll_pfd2_div2", "sys_pll_pfd2", 1, 2)); anatop_base = (void *)ANATOP_BASE_ADDR; @@ -338,7 +338,7 @@ static int imx93_clk_probe(struct udevice *dev) } clk_dm(IMX93_CLK_A55_SEL, - imx_clk_mux2("a55_sel", base + 0x4820, 0, 1, + imx_clk_mux2(dev, "a55_sel", base + 0x4820, 0, 1, a55_core_sels, ARRAY_SIZE(a55_core_sels))); return 0; diff --git a/drivers/clk/imx/clk-imxrt1020.c b/drivers/clk/imx/clk-imxrt1020.c index 752434cb0ad..c14afdaf236 100644 --- a/drivers/clk/imx/clk-imxrt1020.c +++ b/drivers/clk/imx/clk-imxrt1020.c @@ -38,26 +38,26 @@ static int imxrt1020_clk_probe(struct udevice *dev) base = (void *)ofnode_get_addr(ofnode_by_compatible(ofnode_null(), "fsl,imxrt-anatop")); clk_dm(IMXRT1020_CLK_PLL2_SYS, - imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_sys", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_GENERIC, "pll2_sys", "osc", base + 0x30, 0x1)); clk_dm(IMXRT1020_CLK_PLL3_USB_OTG, - imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x1)); /* PLL bypass out */ clk_dm(IMXRT1020_CLK_PLL2_BYPASS, - imx_clk_mux_flags("pll2_bypass", base + 0x30, 16, 1, + imx_clk_mux_flags(dev, "pll2_bypass", base + 0x30, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMXRT1020_CLK_PLL3_BYPASS, - imx_clk_mux_flags("pll3_bypass", base + 0x10, 16, 1, + imx_clk_mux_flags(dev, "pll3_bypass", base + 0x10, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMXRT1020_CLK_PLL3_80M, - imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6)); + imx_clk_fixed_factor(dev, "pll3_80m", "pll3_usb_otg", 1, 6)); clk_dm(IMXRT1020_CLK_PLL2_PFD0_352M, imx_clk_pfd("pll2_pfd0_352m", "pll2_sys", base + 0x100, 0)); @@ -78,51 +78,51 @@ static int imxrt1020_clk_probe(struct udevice *dev) return -EINVAL; clk_dm(IMXRT1020_CLK_PRE_PERIPH_SEL, - imx_clk_mux("pre_periph_sel", base + 0x18, 18, 2, + imx_clk_mux(dev, "pre_periph_sel", base + 0x18, 18, 2, pre_periph_sels, ARRAY_SIZE(pre_periph_sels))); clk_dm(IMXRT1020_CLK_PERIPH_SEL, - imx_clk_mux("periph_sel", base + 0x14, 25, 1, + imx_clk_mux(dev, "periph_sel", base + 0x14, 25, 1, periph_sels, ARRAY_SIZE(periph_sels))); clk_dm(IMXRT1020_CLK_USDHC1_SEL, - imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1, + imx_clk_mux(dev, "usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMXRT1020_CLK_USDHC2_SEL, - imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1, + imx_clk_mux(dev, "usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMXRT1020_CLK_LPUART_SEL, - imx_clk_mux("lpuart_sel", base + 0x24, 6, 1, + imx_clk_mux(dev, "lpuart_sel", base + 0x24, 6, 1, lpuart_sels, ARRAY_SIZE(lpuart_sels))); clk_dm(IMXRT1020_CLK_SEMC_ALT_SEL, - imx_clk_mux("semc_alt_sel", base + 0x14, 7, 1, + imx_clk_mux(dev, "semc_alt_sel", base + 0x14, 7, 1, semc_alt_sels, ARRAY_SIZE(semc_alt_sels))); clk_dm(IMXRT1020_CLK_SEMC_SEL, - imx_clk_mux("semc_sel", base + 0x14, 6, 1, + imx_clk_mux(dev, "semc_sel", base + 0x14, 6, 1, semc_sels, ARRAY_SIZE(semc_sels))); clk_dm(IMXRT1020_CLK_AHB_PODF, - imx_clk_divider("ahb_podf", "periph_sel", + imx_clk_divider(dev, "ahb_podf", "periph_sel", base + 0x14, 10, 3)); clk_dm(IMXRT1020_CLK_USDHC1_PODF, - imx_clk_divider("usdhc1_podf", "usdhc1_sel", + imx_clk_divider(dev, "usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3)); clk_dm(IMXRT1020_CLK_USDHC2_PODF, - imx_clk_divider("usdhc2_podf", "usdhc2_sel", + imx_clk_divider(dev, "usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3)); clk_dm(IMXRT1020_CLK_LPUART_PODF, - imx_clk_divider("lpuart_podf", "lpuart_sel", + imx_clk_divider(dev, "lpuart_podf", "lpuart_sel", base + 0x24, 0, 6)); clk_dm(IMXRT1020_CLK_SEMC_PODF, - imx_clk_divider("semc_podf", "semc_sel", + imx_clk_divider(dev, "semc_podf", "semc_sel", base + 0x14, 16, 3)); clk_dm(IMXRT1020_CLK_USDHC1, - imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2)); + imx_clk_gate2(dev, "usdhc1", "usdhc1_podf", base + 0x80, 2)); clk_dm(IMXRT1020_CLK_USDHC2, - imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4)); + imx_clk_gate2(dev, "usdhc2", "usdhc2_podf", base + 0x80, 4)); clk_dm(IMXRT1020_CLK_LPUART1, - imx_clk_gate2("lpuart1", "lpuart_podf", base + 0x7c, 24)); + imx_clk_gate2(dev, "lpuart1", "lpuart_podf", base + 0x7c, 24)); clk_dm(IMXRT1020_CLK_SEMC, - imx_clk_gate2("semc", "semc_podf", base + 0x74, 4)); + imx_clk_gate2(dev, "semc", "semc_podf", base + 0x74, 4)); #ifdef CONFIG_XPL_BUILD struct clk *clk, *clk1; diff --git a/drivers/clk/imx/clk-imxrt1050.c b/drivers/clk/imx/clk-imxrt1050.c index 2c029ec5a6e..ba5b48748ef 100644 --- a/drivers/clk/imx/clk-imxrt1050.c +++ b/drivers/clk/imx/clk-imxrt1050.c @@ -36,63 +36,63 @@ static int imxrt1050_clk_probe(struct udevice *dev) base = (void *)ofnode_get_addr(ofnode_by_compatible(ofnode_null(), "fsl,imxrt-anatop")); clk_dm(IMXRT1050_CLK_PLL1_REF_SEL, - imx_clk_mux("pll1_arm_ref_sel", base + 0x0, 14, 2, + imx_clk_mux(dev, "pll1_arm_ref_sel", base + 0x0, 14, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMXRT1050_CLK_PLL2_REF_SEL, - imx_clk_mux("pll2_sys_ref_sel", base + 0x30, 14, 2, + imx_clk_mux(dev, "pll2_sys_ref_sel", base + 0x30, 14, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMXRT1050_CLK_PLL3_REF_SEL, - imx_clk_mux("pll3_usb_otg_ref_sel", base + 0x10, 14, 2, + imx_clk_mux(dev, "pll3_usb_otg_ref_sel", base + 0x10, 14, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMXRT1050_CLK_PLL5_REF_SEL, - imx_clk_mux("pll5_video_ref_sel", base + 0xa0, 14, 2, + imx_clk_mux(dev, "pll5_video_ref_sel", base + 0xa0, 14, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMXRT1050_CLK_PLL1_ARM, - imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_arm", "pll1_arm_ref_sel", + imx_clk_pllv3(dev, IMX_PLLV3_SYS, "pll1_arm", "pll1_arm_ref_sel", base + 0x0, 0x7f)); clk_dm(IMXRT1050_CLK_PLL2_SYS, - imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_sys", "pll2_sys_ref_sel", + imx_clk_pllv3(dev, IMX_PLLV3_GENERIC, "pll2_sys", "pll2_sys_ref_sel", base + 0x30, 0x1)); clk_dm(IMXRT1050_CLK_PLL3_USB_OTG, - imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", + imx_clk_pllv3(dev, IMX_PLLV3_USB, "pll3_usb_otg", "pll3_usb_otg_ref_sel", base + 0x10, 0x1)); clk_dm(IMXRT1050_CLK_PLL5_VIDEO, - imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "pll5_video_ref_sel", + imx_clk_pllv3(dev, IMX_PLLV3_AV, "pll5_video", "pll5_video_ref_sel", base + 0xa0, 0x7f)); /* PLL bypass out */ clk_dm(IMXRT1050_CLK_PLL1_BYPASS, - imx_clk_mux_flags("pll1_bypass", base + 0x0, 16, 1, + imx_clk_mux_flags(dev, "pll1_bypass", base + 0x0, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMXRT1050_CLK_PLL2_BYPASS, - imx_clk_mux_flags("pll2_bypass", base + 0x30, 16, 1, + imx_clk_mux_flags(dev, "pll2_bypass", base + 0x30, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMXRT1050_CLK_PLL3_BYPASS, - imx_clk_mux_flags("pll3_bypass", base + 0x10, 16, 1, + imx_clk_mux_flags(dev, "pll3_bypass", base + 0x10, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMXRT1050_CLK_PLL5_BYPASS, - imx_clk_mux_flags("pll5_bypass", base + 0xa0, 16, 1, + imx_clk_mux_flags(dev, "pll5_bypass", base + 0xa0, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMXRT1050_CLK_VIDEO_POST_DIV_SEL, - imx_clk_divider("video_post_div_sel", "pll5_video", + imx_clk_divider(dev, "video_post_div_sel", "pll5_video", base + 0xa0, 19, 2)); clk_dm(IMXRT1050_CLK_VIDEO_DIV, - imx_clk_divider("video_div", "video_post_div_sel", + imx_clk_divider(dev, "video_div", "video_post_div_sel", base + 0x170, 30, 2)); clk_dm(IMXRT1050_CLK_PLL3_80M, - imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6)); + imx_clk_fixed_factor(dev, "pll3_80m", "pll3_usb_otg", 1, 6)); clk_dm(IMXRT1050_CLK_PLL2_PFD0_352M, imx_clk_pfd("pll2_pfd0_352m", "pll2_sys", base + 0x100, 0)); @@ -113,73 +113,73 @@ static int imxrt1050_clk_probe(struct udevice *dev) return -EINVAL; clk_dm(IMXRT1050_CLK_ARM_PODF, - imx_clk_divider("arm_podf", "pll1_arm", + imx_clk_divider(dev, "arm_podf", "pll1_arm", base + 0x10, 0, 3)); clk_dm(IMXRT1050_CLK_PRE_PERIPH_SEL, - imx_clk_mux("pre_periph_sel", base + 0x18, 18, 2, + imx_clk_mux(dev, "pre_periph_sel", base + 0x18, 18, 2, pre_periph_sels, ARRAY_SIZE(pre_periph_sels))); clk_dm(IMXRT1050_CLK_PERIPH_SEL, - imx_clk_mux("periph_sel", base + 0x14, 25, 1, + imx_clk_mux(dev, "periph_sel", base + 0x14, 25, 1, periph_sels, ARRAY_SIZE(periph_sels))); clk_dm(IMXRT1050_CLK_USDHC1_SEL, - imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1, + imx_clk_mux(dev, "usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMXRT1050_CLK_USDHC2_SEL, - imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1, + imx_clk_mux(dev, "usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMXRT1050_CLK_LPUART_SEL, - imx_clk_mux("lpuart_sel", base + 0x24, 6, 1, + imx_clk_mux(dev, "lpuart_sel", base + 0x24, 6, 1, lpuart_sels, ARRAY_SIZE(lpuart_sels))); clk_dm(IMXRT1050_CLK_SEMC_ALT_SEL, - imx_clk_mux("semc_alt_sel", base + 0x14, 7, 1, + imx_clk_mux(dev, "semc_alt_sel", base + 0x14, 7, 1, semc_alt_sels, ARRAY_SIZE(semc_alt_sels))); clk_dm(IMXRT1050_CLK_SEMC_SEL, - imx_clk_mux("semc_sel", base + 0x14, 6, 1, + imx_clk_mux(dev, "semc_sel", base + 0x14, 6, 1, semc_sels, ARRAY_SIZE(semc_sels))); clk_dm(IMXRT1050_CLK_LCDIF_SEL, - imx_clk_mux("lcdif_sel", base + 0x38, 15, 3, + imx_clk_mux(dev, "lcdif_sel", base + 0x38, 15, 3, lcdif_sels, ARRAY_SIZE(lcdif_sels))); clk_dm(IMXRT1050_CLK_AHB_PODF, - imx_clk_divider("ahb_podf", "periph_sel", + imx_clk_divider(dev, "ahb_podf", "periph_sel", base + 0x14, 10, 3)); clk_dm(IMXRT1050_CLK_IPG_PDOF, - imx_clk_divider("ipg_podf", "ahb_podf", + imx_clk_divider(dev, "ipg_podf", "ahb_podf", base + 0x14, 8, 2)); clk_dm(IMXRT1050_CLK_USDHC1_PODF, - imx_clk_divider("usdhc1_podf", "usdhc1_sel", + imx_clk_divider(dev, "usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3)); clk_dm(IMXRT1050_CLK_USDHC2_PODF, - imx_clk_divider("usdhc2_podf", "usdhc2_sel", + imx_clk_divider(dev, "usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3)); clk_dm(IMXRT1050_CLK_LPUART_PODF, - imx_clk_divider("lpuart_podf", "lpuart_sel", + imx_clk_divider(dev, "lpuart_podf", "lpuart_sel", base + 0x24, 0, 6)); clk_dm(IMXRT1050_CLK_SEMC_PODF, - imx_clk_divider("semc_podf", "semc_sel", + imx_clk_divider(dev, "semc_podf", "semc_sel", base + 0x14, 16, 3)); clk_dm(IMXRT1050_CLK_LCDIF_PRED, - imx_clk_divider("lcdif_pred", "lcdif_sel", + imx_clk_divider(dev, "lcdif_pred", "lcdif_sel", base + 0x38, 12, 3)); clk_dm(IMXRT1050_CLK_LCDIF_PODF, - imx_clk_divider("lcdif_podf", "lcdif_pred", + imx_clk_divider(dev, "lcdif_podf", "lcdif_pred", base + 0x18, 23, 3)); clk_dm(IMXRT1050_CLK_USDHC1, - imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2)); + imx_clk_gate2(dev, "usdhc1", "usdhc1_podf", base + 0x80, 2)); clk_dm(IMXRT1050_CLK_USDHC2, - imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4)); + imx_clk_gate2(dev, "usdhc2", "usdhc2_podf", base + 0x80, 4)); clk_dm(IMXRT1050_CLK_LPUART1, - imx_clk_gate2("lpuart1", "lpuart_podf", base + 0x7c, 24)); + imx_clk_gate2(dev, "lpuart1", "lpuart_podf", base + 0x7c, 24)); clk_dm(IMXRT1050_CLK_SEMC, - imx_clk_gate2("semc", "semc_podf", base + 0x74, 4)); + imx_clk_gate2(dev, "semc", "semc_podf", base + 0x74, 4)); clk_dm(IMXRT1050_CLK_LCDIF_APB, - imx_clk_gate2("lcdif", "lcdif_podf", base + 0x70, 28)); + imx_clk_gate2(dev, "lcdif", "lcdif_podf", base + 0x70, 28)); clk_dm(IMXRT1050_CLK_LCDIF_PIX, - imx_clk_gate2("lcdif_pix", "lcdif", base + 0x74, 10)); + imx_clk_gate2(dev, "lcdif_pix", "lcdif", base + 0x74, 10)); clk_dm(IMXRT1050_CLK_USBOH3, - imx_clk_gate2("usboh3", "pll3_usb_otg", base + 0x80, 0)); + imx_clk_gate2(dev, "usboh3", "pll3_usb_otg", base + 0x80, 0)); struct clk *clk, *clk1; diff --git a/drivers/clk/imx/clk-imxrt1170.c b/drivers/clk/imx/clk-imxrt1170.c index 88a294f4165..3f55d0d0127 100644 --- a/drivers/clk/imx/clk-imxrt1170.c +++ b/drivers/clk/imx/clk-imxrt1170.c @@ -114,20 +114,20 @@ static int imxrt1170_clk_probe(struct udevice *dev) base = (void *)ofnode_get_addr(ofnode_by_compatible(ofnode_null(), "fsl,imxrt-anatop")); clk_dm(IMXRT1170_CLK_RCOSC_48M, - imx_clk_fixed_factor("rcosc48M", "rcosc16M", 3, 1)); + imx_clk_fixed_factor(dev, "rcosc48M", "rcosc16M", 3, 1)); clk_dm(IMXRT1170_CLK_RCOSC_400M, - imx_clk_fixed_factor("rcosc400M", "rcosc16M", 25, 1)); + imx_clk_fixed_factor(dev, "rcosc400M", "rcosc16M", 25, 1)); clk_dm(IMXRT1170_CLK_RCOSC_48M_DIV2, - imx_clk_fixed_factor("rcosc48M_div2", "rcosc48M", 1, 2)); + imx_clk_fixed_factor(dev, "rcosc48M_div2", "rcosc48M", 1, 2)); clk_dm(IMXRT1170_CLK_PLL_ARM, - imx_clk_pllv3(IMX_PLLV3_SYS, "pll_arm", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_SYS, "pll_arm", "osc", base + 0x200, 0xff)); clk_dm(IMXRT1170_CLK_PLL3, - imx_clk_pllv3(IMX_PLLV3_GENERICV2, "pll3_sys", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_GENERICV2, "pll3_sys", "osc", base + 0x210, 1)); clk_dm(IMXRT1170_CLK_PLL2, - imx_clk_pllv3(IMX_PLLV3_GENERICV2, "pll2_sys", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_GENERICV2, "pll2_sys", "osc", base + 0x240, 1)); clk_dm(IMXRT1170_CLK_PLL3_PFD0, @@ -149,7 +149,7 @@ static int imxrt1170_clk_probe(struct udevice *dev) imx_clk_pfd("pll2_pfd3", "pll2_sys", base + 0x270, 3)); clk_dm(IMXRT1170_CLK_PLL3_DIV2, - imx_clk_fixed_factor("pll3_div2", "pll3_sys", 1, 2)); + imx_clk_fixed_factor(dev, "pll3_div2", "pll3_sys", 1, 2)); /* CCM clocks */ base = dev_read_addr_ptr(dev); @@ -157,31 +157,31 @@ static int imxrt1170_clk_probe(struct udevice *dev) return -EINVAL; clk_dm(IMXRT1170_CLK_LPUART1_SEL, - imx_clk_mux("lpuart1_sel", base + (25 * 0x80), 8, 3, + imx_clk_mux(dev, "lpuart1_sel", base + (25 * 0x80), 8, 3, lpuart1_sels, ARRAY_SIZE(lpuart1_sels))); clk_dm(IMXRT1170_CLK_LPUART1, - imx_clk_divider("lpuart1", "lpuart1_sel", + imx_clk_divider(dev, "lpuart1", "lpuart1_sel", base + (25 * 0x80), 0, 8)); clk_dm(IMXRT1170_CLK_USDHC1_SEL, - imx_clk_mux("usdhc1_sel", base + (58 * 0x80), 8, 3, + imx_clk_mux(dev, "usdhc1_sel", base + (58 * 0x80), 8, 3, usdhc1_sels, ARRAY_SIZE(usdhc1_sels))); clk_dm(IMXRT1170_CLK_USDHC1, - imx_clk_divider("usdhc1", "usdhc1_sel", + imx_clk_divider(dev, "usdhc1", "usdhc1_sel", base + (58 * 0x80), 0, 8)); clk_dm(IMXRT1170_CLK_GPT1_SEL, - imx_clk_mux("gpt1_sel", base + (14 * 0x80), 8, 3, + imx_clk_mux(dev, "gpt1_sel", base + (14 * 0x80), 8, 3, gpt1_sels, ARRAY_SIZE(gpt1_sels))); clk_dm(IMXRT1170_CLK_GPT1, - imx_clk_divider("gpt1", "gpt1_sel", + imx_clk_divider(dev, "gpt1", "gpt1_sel", base + (14 * 0x80), 0, 8)); clk_dm(IMXRT1170_CLK_SEMC_SEL, - imx_clk_mux("semc_sel", base + (4 * 0x80), 8, 3, + imx_clk_mux(dev, "semc_sel", base + (4 * 0x80), 8, 3, semc_sels, ARRAY_SIZE(semc_sels))); clk_dm(IMXRT1170_CLK_SEMC, - imx_clk_divider("semc", "semc_sel", + imx_clk_divider(dev, "semc", "semc_sel", base + (4 * 0x80), 0, 8)); struct clk *clk, *clk1; diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c index c6692f2f9f5..85b6a9809e8 100644 --- a/drivers/clk/imx/clk-pllv3.c +++ b/drivers/clk/imx/clk-pllv3.c @@ -281,9 +281,9 @@ static const struct clk_ops clk_pllv3_enet_ops = { .get_rate = clk_pllv3_enet_get_rate, }; -struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, - const char *parent_name, void __iomem *base, - u32 div_mask) +struct clk *imx_clk_pllv3(struct udevice *dev, enum imx_pllv3_type type, + const char *name, const char *parent_name, + void __iomem *base, u32 div_mask) { struct clk_pllv3 *pll; struct clk *clk; @@ -339,7 +339,8 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, pll->div_mask = div_mask; clk = &pll->clk; - ret = clk_register(clk, drv_name, name, parent_name); + ret = clk_register(clk, drv_name, name, + clk_resolve_parent_clk(dev, parent_name)); if (ret) { kfree(pll); return ERR_PTR(ret); diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 27a53ae5583..7d14dbc395f 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -78,84 +78,89 @@ struct clk *imx_clk_pll14xx(const char *name, const char *parent_name, void __iomem *base, const struct imx_pll14xx_clk *pll_clk); -struct clk *clk_register_gate2(struct device *dev, const char *name, +struct clk *clk_register_gate2(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 clk_gate_flags, unsigned int *share_count); -struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, - const char *parent_name, void __iomem *base, - u32 div_mask); +struct clk *imx_clk_pllv3(struct udevice *dev, enum imx_pllv3_type type, + const char *name, const char *parent_name, + void __iomem *base, u32 div_mask); -static inline struct clk *imx_clk_gate2(const char *name, const char *parent, - void __iomem *reg, u8 shift) +static inline struct clk *imx_clk_gate2(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift) { - return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + return clk_register_gate2(dev, name, parent, CLK_SET_RATE_PARENT, reg, shift, 0x3, 0, NULL); } -static inline struct clk *imx_clk_gate2_shared(const char *name, +static inline struct clk *imx_clk_gate2_shared(struct udevice *dev, const char *name, const char *parent, void __iomem *reg, u8 shift, unsigned int *share_count) { - return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + return clk_register_gate2(dev, name, parent, CLK_SET_RATE_PARENT, reg, shift, 0x3, 0, share_count); } -static inline struct clk *imx_clk_gate2_shared2(const char *name, +static inline struct clk *imx_clk_gate2_shared2(struct udevice *dev, const char *name, const char *parent, void __iomem *reg, u8 shift, unsigned int *share_count) { - return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT | + return clk_register_gate2(dev, name, parent, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0, share_count); } -static inline struct clk *imx_clk_gate4(const char *name, const char *parent, +static inline struct clk *imx_clk_gate4(struct udevice *dev, const char *name, const char *parent, void __iomem *reg, u8 shift) { - return clk_register_gate2(NULL, name, parent, + return clk_register_gate2(dev, name, parent, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0, NULL); } -static inline struct clk *imx_clk_gate4_flags(const char *name, +static inline struct clk *imx_clk_gate4_flags(struct udevice *dev, const char *name, const char *parent, void __iomem *reg, u8 shift, unsigned long flags) { - return clk_register_gate2(NULL, name, parent, + return clk_register_gate2(dev, name, parent, flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0, NULL); } -static inline struct clk *imx_clk_fixed_factor(const char *name, - const char *parent, unsigned int mult, unsigned int div) +static inline struct clk * +imx_clk_fixed_factor(struct udevice *dev, const char *name, const char *parent, + unsigned int mult, unsigned int div) { - return clk_register_fixed_factor(NULL, name, parent, + return clk_register_fixed_factor(dev, name, parent, CLK_SET_RATE_PARENT, mult, div); } -static inline struct clk *imx_clk_divider(const char *name, const char *parent, - void __iomem *reg, u8 shift, u8 width) +static inline struct clk *imx_clk_divider(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift, u8 width) { - return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT, + return clk_register_divider(dev, name, parent, CLK_SET_RATE_PARENT, reg, shift, width, 0); } static inline struct clk * -imx_clk_busy_divider(const char *name, const char *parent, void __iomem *reg, - u8 shift, u8 width, void __iomem *busy_reg, u8 busy_shift) +imx_clk_busy_divider(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, u8 shift, u8 width, + void __iomem *busy_reg, u8 busy_shift) { - return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT, + return clk_register_divider(dev, name, parent, CLK_SET_RATE_PARENT, reg, shift, width, 0); } -static inline struct clk *imx_clk_divider2(const char *name, const char *parent, - void __iomem *reg, u8 shift, u8 width) +static inline struct clk *imx_clk_divider2(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift, u8 width) { - return clk_register_divider(NULL, name, parent, + return clk_register_divider(dev, name, parent, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, reg, shift, width, 0); } @@ -167,90 +172,93 @@ struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents, void (*fixup)(u32 *val)); -static inline struct clk *imx_clk_mux_flags(const char *name, +static inline struct clk *imx_clk_mux_flags(struct udevice *dev, const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents, unsigned long flags) { - return clk_register_mux(NULL, name, parents, num_parents, + return clk_register_mux(dev, name, parents, num_parents, flags | CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0); } -static inline struct clk *imx_clk_mux2_flags(const char *name, +static inline struct clk *imx_clk_mux2_flags(struct udevice *dev, const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents, unsigned long flags) { - return clk_register_mux(NULL, name, parents, num_parents, + return clk_register_mux(dev, name, parents, num_parents, flags | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE, reg, shift, width, 0); } -static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, - u8 shift, u8 width, const char * const *parents, +static inline struct clk *imx_clk_mux(struct udevice *dev, const char *name, + void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents) { - return clk_register_mux(NULL, name, parents, num_parents, + return clk_register_mux(dev, name, parents, num_parents, CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0); } static inline struct clk * -imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift, u8 width, +imx_clk_busy_mux(struct udevice *dev, const char *name, void __iomem *reg, u8 shift, u8 width, void __iomem *busy_reg, u8 busy_shift, const char * const *parents, int num_parents) { - return clk_register_mux(NULL, name, parents, num_parents, + return clk_register_mux(dev, name, parents, num_parents, CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0); } -static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg, +static inline struct clk *imx_clk_mux2(struct udevice *dev, const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents) { - return clk_register_mux(NULL, name, parents, num_parents, + return clk_register_mux(dev, name, parents, num_parents, CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE, reg, shift, width, 0); } -static inline struct clk *imx_clk_gate(const char *name, const char *parent, - void __iomem *reg, u8 shift) +static inline struct clk *imx_clk_gate(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift) { - return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + return clk_register_gate(dev, name, parent, CLK_SET_RATE_PARENT, reg, shift, 0, NULL); } -static inline struct clk *imx_clk_gate_flags(const char *name, const char *parent, - void __iomem *reg, u8 shift, unsigned long flags) +static inline struct clk *imx_clk_gate_flags(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift, unsigned long flags) { - return clk_register_gate(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg, + return clk_register_gate(dev, name, parent, flags | CLK_SET_RATE_PARENT, reg, shift, 0, NULL); } -static inline struct clk *imx_clk_gate3(const char *name, const char *parent, - void __iomem *reg, u8 shift) +static inline struct clk *imx_clk_gate3(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift) { - return clk_register_gate(NULL, name, parent, + return clk_register_gate(dev, name, parent, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, reg, shift, 0, NULL); } -struct clk *imx8m_clk_composite_flags(const char *name, +struct clk *imx8m_clk_composite_flags(struct udevice *dev, const char *name, const char * const *parent_names, int num_parents, void __iomem *reg, unsigned long flags); -#define __imx8m_clk_composite(name, parent_names, reg, flags) \ - imx8m_clk_composite_flags(name, parent_names, \ +#define __imx8m_clk_composite(dev, name, parent_names, reg, flags) \ + imx8m_clk_composite_flags(dev, name, parent_names, \ ARRAY_SIZE(parent_names), reg, \ flags | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) -#define imx8m_clk_composite(name, parent_names, reg) \ - __imx8m_clk_composite(name, parent_names, reg, 0) +#define imx8m_clk_composite(dev, name, parent_names, reg) \ + __imx8m_clk_composite(dev, name, parent_names, reg, 0) -#define imx8m_clk_composite_critical(name, parent_names, reg) \ - __imx8m_clk_composite(name, parent_names, reg, CLK_IS_CRITICAL) +#define imx8m_clk_composite_critical(dev, name, parent_names, reg) \ + __imx8m_clk_composite(dev, name, parent_names, reg, CLK_IS_CRITICAL) struct clk *imx93_clk_composite_flags(const char *name, const char * const *parent_names, diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index e412c92cafc..12893687b68 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -3,7 +3,6 @@ obj-$(CONFIG_ARCH_MEDIATEK) += clk-mtk.o # SoC Drivers -obj-$(CONFIG_MT8512) += clk-mt8512.o obj-$(CONFIG_TARGET_MT7623) += clk-mt7623.o obj-$(CONFIG_TARGET_MT7622) += clk-mt7622.o obj-$(CONFIG_TARGET_MT7629) += clk-mt7629.o @@ -13,5 +12,6 @@ obj-$(CONFIG_TARGET_MT7988) += clk-mt7988.o obj-$(CONFIG_TARGET_MT7987) += clk-mt7987.o obj-$(CONFIG_TARGET_MT8183) += clk-mt8183.o obj-$(CONFIG_TARGET_MT8365) += clk-mt8365.o +obj-$(CONFIG_TARGET_MT8512) += clk-mt8512.o obj-$(CONFIG_TARGET_MT8516) += clk-mt8516.o obj-$(CONFIG_TARGET_MT8518) += clk-mt8518.o diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index cb867acc48c..3ea01f3c969 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -31,6 +31,14 @@ config CLK_QCOM_IPQ4019 on the Snapdragon IPQ4019 SoC. This driver supports the clocks and resets exposed by the GCC hardware block. +config CLK_QCOM_IPQ9574 + bool "Qualcomm IPQ9574 GCC" + select CLK_QCOM + help + Say Y here to enable support for the Global Clock Controller + on the Snapdragon IPQ9574 SoC. This driver supports the clocks + and resets exposed by the GCC hardware block. + config CLK_QCOM_QCM2290 bool "Qualcomm QCM2290 GCC" select CLK_QCOM diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 1bc0f15005b..e13fc8c1071 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_CLK_QCOM_SDM845) += clock-sdm845.o obj-$(CONFIG_CLK_QCOM_APQ8016) += clock-apq8016.o obj-$(CONFIG_CLK_QCOM_APQ8096) += clock-apq8096.o obj-$(CONFIG_CLK_QCOM_IPQ4019) += clock-ipq4019.o +obj-$(CONFIG_CLK_QCOM_IPQ9574) += clock-ipq9574.o obj-$(CONFIG_CLK_QCOM_QCM2290) += clock-qcm2290.o obj-$(CONFIG_CLK_QCOM_QCS404) += clock-qcs404.o obj-$(CONFIG_CLK_QCOM_SA8775P) += clock-sa8775p.o diff --git a/drivers/clk/qcom/clock-apq8016.c b/drivers/clk/qcom/clock-apq8016.c index b5def55dbc2..6a53f900a9e 100644 --- a/drivers/clk/qcom/clock-apq8016.c +++ b/drivers/clk/qcom/clock-apq8016.c @@ -54,8 +54,9 @@ static struct vote_clk gcc_blsp1_ahb_clk = { }; static const struct gate_clk apq8016_clks[] = { - GATE_CLK(GCC_USB_HS_AHB_CLK, 0x41008, 0x00000001), - GATE_CLK(GCC_USB_HS_SYSTEM_CLK, 0x41004, 0x00000001), + GATE_CLK(GCC_PRNG_AHB_CLK, 0x45004, BIT(8)), + GATE_CLK(GCC_USB_HS_AHB_CLK, 0x41008, BIT(0)), + GATE_CLK(GCC_USB_HS_SYSTEM_CLK, 0x41004, BIT(0)), }; /* SDHCI */ @@ -139,15 +140,14 @@ static int apq8016_clk_enable(struct clk *clk) { struct msm_clk_priv *priv = dev_get_priv(clk->dev); - if (priv->data->num_clks < clk->id) { + if (priv->data->num_clks < clk->id || !apq8016_clks[clk->id].reg) { log_warning("%s: unknown clk id %lu\n", __func__, clk->id); return 0; } - debug("%s: clk %s\n", __func__, apq8016_clks[clk->id].name); - qcom_gate_clk_en(priv, clk->id); + debug("%s: enabling clock %s\n", __func__, apq8016_clks[clk->id].name); - return 0; + return qcom_gate_clk_en(priv, clk->id); } static struct msm_clk_data apq8016_clk_data = { diff --git a/drivers/clk/qcom/clock-apq8096.c b/drivers/clk/qcom/clock-apq8096.c index c77d69128b0..551f52d5197 100644 --- a/drivers/clk/qcom/clock-apq8096.c +++ b/drivers/clk/qcom/clock-apq8096.c @@ -83,11 +83,12 @@ static ulong apq8096_clk_set_rate(struct clk *clk, ulong rate) struct msm_clk_priv *priv = dev_get_priv(clk->dev); switch (clk->id) { - case GCC_SDCC1_APPS_CLK: /* SDC1 */ + case GCC_SDCC2_APPS_CLK: /* SDC2 */ return clk_init_sdc(priv, rate); break; case GCC_BLSP2_UART2_APPS_CLK: /*UART2*/ - return clk_init_uart(priv); + clk_init_uart(priv); + return 7372800; default: return 0; } diff --git a/drivers/clk/qcom/clock-ipq9574.c b/drivers/clk/qcom/clock-ipq9574.c new file mode 100644 index 00000000000..b0af4036059 --- /dev/null +++ b/drivers/clk/qcom/clock-ipq9574.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Clock drivers for Qualcomm ipq9574 + * + * (C) Copyright 2025 Linaro Ltd. + */ + +#include <linux/types.h> +#include <clk-uclass.h> +#include <dm.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <linux/bug.h> +#include <linux/bitops.h> +#include <dt-bindings/clock/qcom,ipq9574-gcc.h> +#include <dt-bindings/reset/qcom,ipq9574-gcc.h> + +#include "clock-qcom.h" + +#define GCC_BLSP1_AHB_CBCR 0x1004 +#define GCC_BLSP1_UART3_APPS_CMD_RCGR 0x402C +#define GCC_BLSP1_UART3_APPS_CBCR 0x4054 + +#define GCC_SDCC1_APPS_CBCR 0x3302C +#define GCC_SDCC1_AHB_CBCR 0x33034 +#define GCC_SDCC1_APPS_CMD_RCGR 0x33004 +#define GCC_SDCC1_ICE_CORE_CBCR 0x33030 + +static ulong ipq9574_set_rate(struct clk *clk, ulong rate) +{ + struct msm_clk_priv *priv = dev_get_priv(clk->dev); + + switch (clk->id) { + case GCC_BLSP1_UART3_APPS_CLK: + clk_rcg_set_rate_mnd(priv->base, GCC_BLSP1_UART3_APPS_CMD_RCGR, + 0, 144, 15625, CFG_CLK_SRC_GPLL0, 16); + return rate; + case GCC_SDCC1_APPS_CLK: + clk_rcg_set_rate_mnd(priv->base, GCC_SDCC1_APPS_CMD_RCGR, + 11, 0, 0, CFG_CLK_SRC_GPLL2, 16); + return rate; + default: + return -EINVAL; + } +} + +static const struct gate_clk ipq9574_clks[] = { + GATE_CLK(GCC_BLSP1_UART3_APPS_CLK, 0x4054, 0x00000001), + GATE_CLK(GCC_BLSP1_AHB_CLK, 0x1004, 0x00000001), + GATE_CLK(GCC_SDCC1_AHB_CLK, 0x33034, 0x00000001), + GATE_CLK(GCC_SDCC1_APPS_CLK, 0x3302C, 0x00000001), + GATE_CLK(GCC_SDCC1_ICE_CORE_CLK, 0x33030, 0x00000001), +}; + +static int ipq9574_enable(struct clk *clk) +{ + struct msm_clk_priv *priv = dev_get_priv(clk->dev); + + debug("%s: clk %s\n", __func__, ipq9574_clks[clk->id].name); + + if (!ipq9574_clks[clk->id].reg) + return -EINVAL; + + qcom_gate_clk_en(priv, clk->id); + + return 0; +} + +static const struct qcom_reset_map ipq9574_gcc_resets[] = { + [GCC_SDCC_BCR] = { 0x33000 }, +}; + +static struct msm_clk_data ipq9574_gcc_data = { + .resets = ipq9574_gcc_resets, + .num_resets = ARRAY_SIZE(ipq9574_gcc_resets), + .enable = ipq9574_enable, + .set_rate = ipq9574_set_rate, +}; + +static const struct udevice_id gcc_ipq9574_of_match[] = { + { + .compatible = "qcom,ipq9574-gcc", + .data = (ulong)&ipq9574_gcc_data, + }, + { } +}; + +U_BOOT_DRIVER(gcc_ipq9574) = { + .name = "gcc_ipq9574", + .id = UCLASS_NOP, + .of_match = gcc_ipq9574_of_match, + .bind = qcom_cc_bind, + .flags = DM_FLAG_PRE_RELOC | DM_FLAG_DEFAULT_PD_CTRL_OFF, +}; diff --git a/drivers/clk/qcom/clock-qcm2290.c b/drivers/clk/qcom/clock-qcm2290.c index c78705cb8cf..1326b770c3e 100644 --- a/drivers/clk/qcom/clock-qcm2290.c +++ b/drivers/clk/qcom/clock-qcm2290.c @@ -134,9 +134,7 @@ static int qcm2290_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map qcm2290_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-qcom.h b/drivers/clk/qcom/clock-qcom.h index ff336dea39c..f43edea2525 100644 --- a/drivers/clk/qcom/clock-qcom.h +++ b/drivers/clk/qcom/clock-qcom.h @@ -7,10 +7,12 @@ #include <asm/io.h> #include <linux/bitfield.h> +#include <errno.h> #define CFG_CLK_SRC_CXO (0 << 8) #define CFG_CLK_SRC_GPLL0 (1 << 8) #define CFG_CLK_SRC_GPLL0_AUX2 (2 << 8) +#define CFG_CLK_SRC_GPLL2 (2 << 8) #define CFG_CLK_SRC_GPLL9 (2 << 8) #define CFG_CLK_SRC_GPLL0_ODD (3 << 8) #define CFG_CLK_SRC_GPLL6 (4 << 8) @@ -105,14 +107,19 @@ void clk_rcg_set_rate(phys_addr_t base, uint32_t cmd_rcgr, int div, int source); void clk_phy_mux_enable(phys_addr_t base, uint32_t cmd_rcgr, bool enabled); -static inline void qcom_gate_clk_en(const struct msm_clk_priv *priv, unsigned long id) +static inline int qcom_gate_clk_en(const struct msm_clk_priv *priv, unsigned long id) { u32 val; - if (id >= priv->data->num_clks || priv->data->clks[id].reg == 0) - return; + if (id >= priv->data->num_clks || priv->data->clks[id].reg == 0) { + log_err("gcc@%#08llx: unknown clock ID %lu!\n", + priv->base, id); + return -ENOENT; + } val = readl(priv->base + priv->data->clks[id].reg); writel(val | priv->data->clks[id].en_val, priv->base + priv->data->clks[id].reg); + + return 0; } #endif diff --git a/drivers/clk/qcom/clock-sa8775p.c b/drivers/clk/qcom/clock-sa8775p.c index e31f24ed4f0..527cecf5c82 100644 --- a/drivers/clk/qcom/clock-sa8775p.c +++ b/drivers/clk/qcom/clock-sa8775p.c @@ -73,9 +73,7 @@ static int sa8775p_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sa8775p_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-sc7280.c b/drivers/clk/qcom/clock-sc7280.c index 5d343f12051..9aff8a847ad 100644 --- a/drivers/clk/qcom/clock-sc7280.c +++ b/drivers/clk/qcom/clock-sc7280.c @@ -16,29 +16,64 @@ #include "clock-qcom.h" -#define USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR 0xf038 #define USB30_PRIM_MASTER_CLK_CMD_RCGR 0xf020 +#define USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR 0xf038 +#define USB30_SEC_MASTER_CLK_CMD_RCGR 0x9e020 +#define USB30_SEC_MOCK_UTMI_CLK_CMD_RCGR 0x9e038 +#define PCIE_1_AUX_CLK_CMD_RCGR 0x8d058 +#define PCIE1_PHY_RCHNG_CMD_RCGR 0x8d03c +#define PCIE_1_PIPE_CLK_PHY_MUX 0x8d054 + +static const struct freq_tbl ftbl_gcc_usb30_prim_master_clk_src[] = { + F(66666667, CFG_CLK_SRC_GPLL0_EVEN, 4.5, 0, 0), + F(133333333, CFG_CLK_SRC_GPLL0, 4.5, 0, 0), + F(200000000, CFG_CLK_SRC_GPLL0_ODD, 1, 0, 0), + F(240000000, CFG_CLK_SRC_GPLL0, 2.5, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gcc_usb30_sec_master_clk_src[] = { + F(60000000, CFG_CLK_SRC_GPLL0_EVEN, 5, 0, 0), + F(120000000, CFG_CLK_SRC_GPLL0_EVEN, 2.5, 0, 0), + { } +}; static ulong sc7280_set_rate(struct clk *clk, ulong rate) { struct msm_clk_priv *priv = dev_get_priv(clk->dev); + const struct freq_tbl *freq; if (clk->id < priv->data->num_clks) debug("%s: %s, requested rate=%ld\n", __func__, priv->data->clks[clk->id].name, rate); switch (clk->id) { - case GCC_USB30_PRIM_MOCK_UTMI_CLK: - WARN(rate != 19200000, "Unexpected rate for USB30_PRIM_MOCK_UTMI_CLK: %lu\n", rate); - clk_rcg_set_rate(priv->base, USB30_PRIM_MASTER_CLK_CMD_RCGR, 0, CFG_CLK_SRC_CXO); - return rate; case GCC_USB30_PRIM_MASTER_CLK: - WARN(rate != 200000000, "Unexpected rate for USB30_PRIM_MASTER_CLK: %lu\n", rate); + freq = qcom_find_freq(ftbl_gcc_usb30_prim_master_clk_src, rate); clk_rcg_set_rate_mnd(priv->base, USB30_PRIM_MASTER_CLK_CMD_RCGR, - 1, 0, 0, CFG_CLK_SRC_GPLL0_ODD, 8); - clk_rcg_set_rate(priv->base, 0xf064, 0, 0); - return rate; + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_USB30_PRIM_MOCK_UTMI_CLK: + clk_rcg_set_rate(priv->base, USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR, 1, 0); + return 19200000; + case GCC_USB3_PRIM_PHY_AUX_CLK_SRC: + clk_rcg_set_rate(priv->base, USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR, 1, 0); + return 19200000; + case GCC_USB30_SEC_MASTER_CLK: + freq = qcom_find_freq(ftbl_gcc_usb30_sec_master_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, USB30_SEC_MASTER_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_USB30_SEC_MOCK_UTMI_CLK: + clk_rcg_set_rate(priv->base, USB30_SEC_MOCK_UTMI_CLK_CMD_RCGR, 1, 0); + return 19200000; + case GCC_USB3_SEC_PHY_AUX_CLK_SRC: + clk_rcg_set_rate(priv->base, USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR, 1, 0); + return 19200000; + case GCC_PCIE1_PHY_RCHNG_CLK: + clk_rcg_set_rate(priv->base, PCIE1_PHY_RCHNG_CMD_RCGR, 5, CFG_CLK_SRC_GPLL0_EVEN); + return 100000000; default: - return 0; + return rate; } } @@ -50,13 +85,46 @@ static const struct gate_clk sc7280_clks[] = { GATE_CLK(GCC_USB30_PRIM_MOCK_UTMI_CLK, 0xf01c, 1), GATE_CLK(GCC_USB3_PRIM_PHY_AUX_CLK, 0xf054, 1), GATE_CLK(GCC_USB3_PRIM_PHY_COM_AUX_CLK, 0xf058, 1), + GATE_CLK(GCC_CFG_NOC_USB3_SEC_AXI_CLK, 0x9e07c, 1), + GATE_CLK(GCC_USB30_SEC_MASTER_CLK, 0x9e010, 1), + GATE_CLK(GCC_AGGRE_USB3_SEC_AXI_CLK, 0x9e080, 1), + GATE_CLK(GCC_USB30_SEC_SLEEP_CLK, 0x9e018, 1), + GATE_CLK(GCC_USB30_SEC_MOCK_UTMI_CLK, 0x9e01c, 1), + GATE_CLK(GCC_USB3_SEC_PHY_AUX_CLK, 0x9e054, 1), + GATE_CLK(GCC_USB3_SEC_PHY_COM_AUX_CLK, 0x9e058, 1), + GATE_CLK(GCC_PCIE_CLKREF_EN, 0x8c004, 1), + GATE_CLK(GCC_PCIE_1_PIPE_CLK, 0x52000, BIT(30)), + GATE_CLK(GCC_PCIE_1_AUX_CLK, 0x52000, BIT(29)), + GATE_CLK(GCC_PCIE_1_CFG_AHB_CLK, 0x52000, BIT(28)), + GATE_CLK(GCC_PCIE_1_MSTR_AXI_CLK, 0x52000, BIT(27)), + GATE_CLK(GCC_PCIE_1_SLV_AXI_CLK, 0x52000, BIT(26)), + GATE_CLK(GCC_PCIE_1_SLV_Q2A_AXI_CLK, 0x52000, BIT(25)), + GATE_CLK(GCC_PCIE1_PHY_RCHNG_CLK, 0x52000, BIT(23)), + GATE_CLK(GCC_DDRSS_PCIE_SF_CLK, 0x52000, BIT(19)), + GATE_CLK(GCC_AGGRE_NOC_PCIE_TBU_CLK, 0x52000, BIT(18)), + GATE_CLK(GCC_AGGRE_NOC_PCIE_1_AXI_CLK, 0x52000, BIT(11)), + GATE_CLK(GCC_AGGRE_NOC_PCIE_CENTER_SF_AXI_CLK, 0x52008, BIT(28)), + GATE_CLK(GCC_QUPV3_WRAP0_S0_CLK, 0x52008, BIT(10)), + GATE_CLK(GCC_QUPV3_WRAP0_S1_CLK, 0x52008, BIT(11)), + GATE_CLK(GCC_QUPV3_WRAP0_S3_CLK, 0x52008, BIT(13)), + GATE_CLK(GCC_UFS_PHY_AXI_CLK, 0x77010, BIT(0)), + GATE_CLK(GCC_AGGRE_UFS_PHY_AXI_CLK, 0x770cc, BIT(0)), + GATE_CLK(GCC_UFS_PHY_AHB_CLK, 0x77018, BIT(0)), + GATE_CLK(GCC_UFS_PHY_UNIPRO_CORE_CLK, 0x7705c, BIT(0)), + GATE_CLK(GCC_UFS_PHY_PHY_AUX_CLK, 0x7709c, BIT(0)), + GATE_CLK(GCC_UFS_PHY_TX_SYMBOL_0_CLK, 0x7701c, BIT(0)), + GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_0_CLK, 0x77020, BIT(0)), + GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_1_CLK, 0x770b8, BIT(0)), + GATE_CLK(GCC_UFS_1_CLKREF_EN, 0x8c000, BIT(0)), + GATE_CLK(GCC_SDCC2_AHB_CLK, 0x14008, BIT(0)), + GATE_CLK(GCC_SDCC2_APPS_CLK, 0x14004, BIT(0)), }; static int sc7280_enable(struct clk *clk) { struct msm_clk_priv *priv = dev_get_priv(clk->dev); - if (priv->data->num_clks < clk->id) { + if (priv->data->num_clks <= clk->id) { debug("%s: unknown clk id %lu\n", __func__, clk->id); return 0; } @@ -71,11 +139,32 @@ static int sc7280_enable(struct clk *clk) qcom_gate_clk_en(priv, GCC_USB3_PRIM_PHY_AUX_CLK); qcom_gate_clk_en(priv, GCC_USB3_PRIM_PHY_COM_AUX_CLK); break; + case GCC_AGGRE_USB3_SEC_AXI_CLK: + qcom_gate_clk_en(priv, GCC_USB30_SEC_MASTER_CLK); + fallthrough; + case GCC_USB30_SEC_MASTER_CLK: + qcom_gate_clk_en(priv, GCC_USB3_SEC_PHY_AUX_CLK); + qcom_gate_clk_en(priv, GCC_USB3_SEC_PHY_COM_AUX_CLK); + break; + case GCC_PCIE_1_PIPE_CLK: + clk_phy_mux_enable(priv->base, PCIE_1_PIPE_CLK_PHY_MUX, true); + break; + case GCC_PCIE_1_AUX_CLK: + clk_rcg_set_rate_mnd(priv->base, PCIE_1_AUX_CLK_CMD_RCGR, 1, 0, 0, + CFG_CLK_SRC_CXO, 16); + break; + case GCC_QUPV3_WRAP0_S0_CLK: + clk_rcg_set_rate_mnd(priv->base, 0x17010, 1, 0, 0, CFG_CLK_SRC_CXO, 16); + break; + case GCC_QUPV3_WRAP0_S1_CLK: + clk_rcg_set_rate_mnd(priv->base, 0x17140, 1, 0, 0, CFG_CLK_SRC_CXO, 16); + break; + case GCC_QUPV3_WRAP0_S3_CLK: + clk_rcg_set_rate_mnd(priv->base, 0x173a0, 1, 0, 0, CFG_CLK_SRC_CXO, 16); + break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sc7280_gcc_resets[] = { @@ -100,6 +189,20 @@ static const struct qcom_reset_map sc7280_gcc_resets[] = { static const struct qcom_power_map sc7280_gdscs[] = { [GCC_UFS_PHY_GDSC] = { 0x77004 }, [GCC_USB30_PRIM_GDSC] = { 0xf004 }, + [GCC_USB30_SEC_GDSC] = { 0x9e004 }, + [GCC_PCIE_1_GDSC] = { 0x8d004 }, +}; + +static const phys_addr_t sc7280_rcg_addrs[] = { + 0x10f020, // USB30_PRIM_MASTER_CLK_CMD_RCGR + 0x10f038, // USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR + 0x18d058, // PCIE_1_AUX_CLK_CMD_RCGR +}; + +static const char *const sc7280_rcg_names[] = { + "USB30_PRIM_MASTER_CLK_SRC", + "USB30_PRIM_MOCK_UTMI_CLK_SRC", + "GCC_PCIE_1_AUX_CLK_SRC", }; static struct msm_clk_data qcs404_gcc_data = { @@ -113,6 +216,10 @@ static struct msm_clk_data qcs404_gcc_data = { .enable = sc7280_enable, .set_rate = sc7280_set_rate, + + .dbg_rcg_addrs = sc7280_rcg_addrs, + .num_rcgs = ARRAY_SIZE(sc7280_rcg_addrs), + .dbg_rcg_names = sc7280_rcg_names, }; static const struct udevice_id gcc_sc7280_of_match[] = { diff --git a/drivers/clk/qcom/clock-sdm845.c b/drivers/clk/qcom/clock-sdm845.c index adffb0cb240..5c8702ef2fe 100644 --- a/drivers/clk/qcom/clock-sdm845.c +++ b/drivers/clk/qcom/clock-sdm845.c @@ -77,7 +77,9 @@ static ulong sdm845_clk_set_rate(struct clk *clk, ulong rate) } static const struct gate_clk sdm845_clks[] = { + GATE_CLK(GCC_AGGRE_USB3_PRIM_AXI_CLK, 0x8201c, 0x00000001), GATE_CLK(GCC_AGGRE_USB3_SEC_AXI_CLK, 0x82020, 0x00000001), + GATE_CLK(GCC_CFG_NOC_USB3_PRIM_AXI_CLK, 0x0502c, 0x00000001), GATE_CLK(GCC_CFG_NOC_USB3_SEC_AXI_CLK, 0x05030, 0x00000001), GATE_CLK(GCC_QUPV3_WRAP0_S0_CLK, 0x5200c, 0x00000400), GATE_CLK(GCC_QUPV3_WRAP0_S1_CLK, 0x5200c, 0x00000800), @@ -112,6 +114,7 @@ static const struct gate_clk sdm845_clks[] = { GATE_CLK(GCC_UFS_CARD_TX_SYMBOL_0_CLK, 0x75014, 0x00000001), GATE_CLK(GCC_UFS_CARD_UNIPRO_CORE_CLK, 0x75054, 0x00000001), GATE_CLK(GCC_UFS_MEM_CLKREF_CLK, 0x8c000, 0x00000001), + GATE_CLK(GCC_AGGRE_UFS_PHY_AXI_CLK, 0x82024, 0x00000001), GATE_CLK(GCC_UFS_PHY_AHB_CLK, 0x77010, 0x00000001), GATE_CLK(GCC_UFS_PHY_AXI_CLK, 0x7700c, 0x00000001), GATE_CLK(GCC_UFS_PHY_ICE_CORE_CLK, 0x77058, 0x00000001), @@ -162,9 +165,7 @@ static int sdm845_clk_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sdm845_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-sm6115.c b/drivers/clk/qcom/clock-sm6115.c index 9057dfe0bb1..17c2e561758 100644 --- a/drivers/clk/qcom/clock-sm6115.c +++ b/drivers/clk/qcom/clock-sm6115.c @@ -146,9 +146,7 @@ static int sm6115_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sm6115_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-sm8150.c b/drivers/clk/qcom/clock-sm8150.c index 88f2e678f43..7dd0d56eb43 100644 --- a/drivers/clk/qcom/clock-sm8150.c +++ b/drivers/clk/qcom/clock-sm8150.c @@ -243,9 +243,7 @@ static int sm8150_clk_enable(struct clk *clk) break; }; - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sm8150_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-sm8250.c b/drivers/clk/qcom/clock-sm8250.c index e322a923a5c..26396847d85 100644 --- a/drivers/clk/qcom/clock-sm8250.c +++ b/drivers/clk/qcom/clock-sm8250.c @@ -195,9 +195,7 @@ static int sm8250_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sm8250_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-sm8550.c b/drivers/clk/qcom/clock-sm8550.c index 62b5a409e8e..7c06489b9c4 100644 --- a/drivers/clk/qcom/clock-sm8550.c +++ b/drivers/clk/qcom/clock-sm8550.c @@ -220,9 +220,7 @@ static int sm8550_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sm8550_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-sm8650.c b/drivers/clk/qcom/clock-sm8650.c index 9baaecb571f..364454644a6 100644 --- a/drivers/clk/qcom/clock-sm8650.c +++ b/drivers/clk/qcom/clock-sm8650.c @@ -217,9 +217,7 @@ static int sm8650_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sm8650_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-x1e80100.c b/drivers/clk/qcom/clock-x1e80100.c index bd9c6ed1c8a..542d6248d65 100644 --- a/drivers/clk/qcom/clock-x1e80100.c +++ b/drivers/clk/qcom/clock-x1e80100.c @@ -174,9 +174,7 @@ static int x1e80100_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map x1e80100_gcc_resets[] = { diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index 3c5340df8ee..7fce1f70d13 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -70,17 +70,12 @@ static int rzg2l_cpg_clk_set(struct clk *clk, bool enable) 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)) { - /* - * Non-module clocks are always on. Ignore attempts to enable - * them and reject attempts to disable them. - */ - if (enable) - return 0; - - dev_err(clk->dev, "ID %lu is not a module clock\n", clk->id); - return -EINVAL; - } + /* + * Non-module clocks are always on. Ignore attempts to enable or disable + * them. + */ + if (!is_mod_clk(clk->id)) + return 0; for (i = 0; i < data->info->num_mod_clks; i++) { if (data->info->mod_clks[i].id == cpg_clk_id) { diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index 9e379cc2e3b..34b63d4df34 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -15,7 +15,9 @@ obj-$(CONFIG_ROCKCHIP_RK3308) += clk_rk3308.o obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o obj-$(CONFIG_ROCKCHIP_RK3368) += clk_rk3368.o obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o +obj-$(CONFIG_ROCKCHIP_RK3528) += clk_rk3528.o obj-$(CONFIG_ROCKCHIP_RK3568) += clk_rk3568.o +obj-$(CONFIG_ROCKCHIP_RK3576) += clk_rk3576.o obj-$(CONFIG_ROCKCHIP_RK3588) += clk_rk3588.o obj-$(CONFIG_ROCKCHIP_RV1108) += clk_rv1108.o obj-$(CONFIG_ROCKCHIP_RV1126) += clk_rv1126.o diff --git a/drivers/clk/rockchip/clk_pll.c b/drivers/clk/rockchip/clk_pll.c index 44c6f14618d..9dec40b1fe8 100644 --- a/drivers/clk/rockchip/clk_pll.c +++ b/drivers/clk/rockchip/clk_pll.c @@ -309,9 +309,11 @@ static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll, * When power on or changing PLL setting, * we must force PLL into slow mode to ensure output stable clock. */ - rk_clrsetreg(base + pll->mode_offset, - pll->mode_mask << pll->mode_shift, - RKCLK_PLL_MODE_SLOW << pll->mode_shift); + if (!(pll->pll_flags & ROCKCHIP_PLL_FIXED_MODE)) { + rk_clrsetreg(base + pll->mode_offset, + pll->mode_mask << pll->mode_shift, + RKCLK_PLL_MODE_SLOW << pll->mode_shift); + } /* Power down */ rk_setreg(base + pll->con_offset + 0x4, @@ -345,8 +347,11 @@ static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll, while (!(readl(base + pll->con_offset + 0x4) & (1 << pll->lock_shift))) udelay(1); - rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift, - RKCLK_PLL_MODE_NORMAL << pll->mode_shift); + if (!(pll->pll_flags & ROCKCHIP_PLL_FIXED_MODE)) { + rk_clrsetreg(base + pll->mode_offset, + pll->mode_mask << pll->mode_shift, + RKCLK_PLL_MODE_NORMAL << pll->mode_shift); + } debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n", pll, readl(base + pll->con_offset), readl(base + pll->con_offset + 0x4), @@ -362,12 +367,18 @@ static ulong rk3036_pll_get_rate(struct rockchip_pll_clock *pll, u32 refdiv, fbdiv, postdiv1, postdiv2, dsmpd, frac; u32 con = 0, shift, mask; ulong rate; + int mode; con = readl(base + pll->mode_offset); shift = pll->mode_shift; mask = pll->mode_mask << shift; - switch ((con & mask) >> shift) { + if (!(pll->pll_flags & ROCKCHIP_PLL_FIXED_MODE)) + mode = (con & mask) >> shift; + else + mode = RKCLK_PLL_MODE_NORMAL; + + switch (mode) { case RKCLK_PLL_MODE_SLOW: return OSC_HZ; case RKCLK_PLL_MODE_NORMAL: diff --git a/drivers/clk/rockchip/clk_rk3528.c b/drivers/clk/rockchip/clk_rk3528.c new file mode 100644 index 00000000000..06f20895acc --- /dev/null +++ b/drivers/clk/rockchip/clk_rk3528.c @@ -0,0 +1,1754 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + * Author: Joseph Chen <chenjh@rock-chips.com> + */ + +#include <bitfield.h> +#include <clk-uclass.h> +#include <dm.h> +#include <errno.h> +#include <syscon.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3528.h> +#include <asm/arch-rockchip/hardware.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dt-bindings/clock/rockchip,rk3528-cru.h> +#include <linux/delay.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) + +/* + * PLL attention. + * + * [FRAC PLL]: GPLL, PPLL, DPLL + * - frac mode: refdiv can be 1 or 2 only + * - int mode: refdiv has no special limit + * - VCO range: [950, 3800] MHZ + * + * [INT PLL]: CPLL, APLL + * - int mode: refdiv can be 1 or 2 only + * - VCO range: [475, 1900] MHZ + * + * [PPLL]: normal mode only. + * + */ +static struct rockchip_pll_rate_table rk3528_pll_rates[] = { + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ + RK3036_PLL_RATE(1896000000, 1, 79, 1, 1, 1, 0), + RK3036_PLL_RATE(1800000000, 1, 75, 1, 1, 1, 0), + RK3036_PLL_RATE(1704000000, 1, 71, 1, 1, 1, 0), + RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0), + RK3036_PLL_RATE(1512000000, 1, 63, 1, 1, 1, 0), + RK3036_PLL_RATE(1416000000, 1, 59, 1, 1, 1, 0), + RK3036_PLL_RATE(1296000000, 1, 54, 1, 1, 1, 0), + RK3036_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0), + RK3036_PLL_RATE(1188000000, 1, 99, 2, 1, 1, 0), /* GPLL */ + RK3036_PLL_RATE(1092000000, 2, 91, 1, 1, 1, 0), + RK3036_PLL_RATE(1008000000, 1, 42, 1, 1, 1, 0), + RK3036_PLL_RATE(1000000000, 1, 125, 3, 1, 1, 0), /* PPLL */ + RK3036_PLL_RATE(996000000, 2, 83, 1, 1, 1, 0), /* CPLL */ + RK3036_PLL_RATE(960000000, 1, 40, 1, 1, 1, 0), + RK3036_PLL_RATE(912000000, 1, 76, 2, 1, 1, 0), + RK3036_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0), + RK3036_PLL_RATE(600000000, 1, 50, 2, 1, 1, 0), + RK3036_PLL_RATE(594000000, 2, 99, 2, 1, 1, 0), + RK3036_PLL_RATE(408000000, 1, 68, 2, 2, 1, 0), + RK3036_PLL_RATE(312000000, 1, 78, 6, 1, 1, 0), + RK3036_PLL_RATE(216000000, 1, 72, 4, 2, 1, 0), + RK3036_PLL_RATE(96000000, 1, 24, 3, 2, 1, 0), + { /* sentinel */ }, +}; + +static struct rockchip_pll_clock rk3528_pll_clks[] = { + [APLL] = PLL(pll_rk3328, PLL_APLL, RK3528_PLL_CON(0), + RK3528_MODE_CON, 0, 10, 0, rk3528_pll_rates), + + [CPLL] = PLL(pll_rk3328, PLL_CPLL, RK3528_PLL_CON(8), + RK3528_MODE_CON, 2, 10, 0, rk3528_pll_rates), + + [GPLL] = PLL(pll_rk3328, PLL_GPLL, RK3528_PLL_CON(24), + RK3528_MODE_CON, 4, 10, 0, rk3528_pll_rates), + + [PPLL] = PLL(pll_rk3328, PLL_PPLL, RK3528_PCIE_PLL_CON(32), + RK3528_MODE_CON, 6, 10, ROCKCHIP_PLL_FIXED_MODE, rk3528_pll_rates), + + [DPLL] = PLL(pll_rk3328, PLL_DPLL, RK3528_DDRPHY_PLL_CON(16), + RK3528_DDRPHY_MODE_CON, 0, 10, 0, rk3528_pll_rates), +}; + +#define RK3528_CPUCLK_RATE(_rate, _aclk_m_core, _pclk_dbg) \ +{ \ + .rate = _rate##U, \ + .aclk_div = (_aclk_m_core), \ + .pclk_div = (_pclk_dbg), \ +} + +/* sign-off: _aclk_m_core: 550M, _pclk_dbg: 137.5M, */ +static struct rockchip_cpu_rate_table rk3528_cpu_rates[] = { + RK3528_CPUCLK_RATE(1896000000, 1, 13), + RK3528_CPUCLK_RATE(1800000000, 1, 12), + RK3528_CPUCLK_RATE(1704000000, 1, 11), + RK3528_CPUCLK_RATE(1608000000, 1, 11), + RK3528_CPUCLK_RATE(1512000000, 1, 11), + RK3528_CPUCLK_RATE(1416000000, 1, 9), + RK3528_CPUCLK_RATE(1296000000, 1, 8), + RK3528_CPUCLK_RATE(1200000000, 1, 8), + RK3528_CPUCLK_RATE(1188000000, 1, 8), + RK3528_CPUCLK_RATE(1092000000, 1, 7), + RK3528_CPUCLK_RATE(1008000000, 1, 6), + RK3528_CPUCLK_RATE(1000000000, 1, 6), + RK3528_CPUCLK_RATE(996000000, 1, 6), + RK3528_CPUCLK_RATE(960000000, 1, 6), + RK3528_CPUCLK_RATE(912000000, 1, 6), + RK3528_CPUCLK_RATE(816000000, 1, 5), + RK3528_CPUCLK_RATE(600000000, 1, 3), + RK3528_CPUCLK_RATE(594000000, 1, 3), + RK3528_CPUCLK_RATE(408000000, 1, 2), + RK3528_CPUCLK_RATE(312000000, 1, 2), + RK3528_CPUCLK_RATE(216000000, 1, 1), + RK3528_CPUCLK_RATE(96000000, 1, 0), +}; + +/* + * + * rational_best_approximation(31415, 10000, + * (1 << 8) - 1, (1 << 5) - 1, &n, &d); + * + * you may look at given_numerator as a fixed point number, + * with the fractional part size described in given_denominator. + * + * for theoretical background, see: + * http://en.wikipedia.org/wiki/Continued_fraction + */ +static void rational_best_approximation(unsigned long given_numerator, + unsigned long given_denominator, + unsigned long max_numerator, + unsigned long max_denominator, + unsigned long *best_numerator, + unsigned long *best_denominator) +{ + unsigned long n, d, n0, d0, n1, d1; + + n = given_numerator; + d = given_denominator; + n0 = 0; + d1 = 0; + n1 = 1; + d0 = 1; + for (;;) { + unsigned long t, a; + + if (n1 > max_numerator || d1 > max_denominator) { + n1 = n0; + d1 = d0; + break; + } + if (d == 0) + break; + t = d; + a = n / d; + d = n % d; + n = t; + t = n0 + a * n1; + n0 = n1; + n1 = t; + t = d0 + a * d1; + d0 = d1; + d1 = t; + } + *best_numerator = n1; + *best_denominator = d1; +} + +static int rk3528_armclk_set_clk(struct rk3528_clk_priv *priv, ulong new_rate) +{ + const struct rockchip_cpu_rate_table *rate; + struct rk3528_cru *cru = priv->cru; + ulong old_rate; + + rate = rockchip_get_cpu_settings(rk3528_cpu_rates, new_rate); + if (!rate) { + printf("%s unsupported rate\n", __func__); + return -EINVAL; + } + + /* + * set up dependent divisors for DBG and ACLK clocks. + */ + old_rate = rockchip_pll_get_rate(&rk3528_pll_clks[APLL], priv->cru, APLL); + if (old_rate > new_rate) { + if (rockchip_pll_set_rate(&rk3528_pll_clks[APLL], + priv->cru, APLL, new_rate)) + return -EINVAL; + + rk_clrsetreg(&cru->clksel_con[40], RK3528_DIV_PCLK_DBG_MASK, + rate->pclk_div << RK3528_DIV_PCLK_DBG_SHIFT); + + rk_clrsetreg(&cru->clksel_con[39], RK3528_DIV_ACLK_M_CORE_MASK, + rate->aclk_div << RK3528_DIV_ACLK_M_CORE_SHIFT); + } else if (old_rate < new_rate) { + rk_clrsetreg(&cru->clksel_con[40], RK3528_DIV_PCLK_DBG_MASK, + rate->pclk_div << RK3528_DIV_PCLK_DBG_SHIFT); + + rk_clrsetreg(&cru->clksel_con[39], RK3528_DIV_ACLK_M_CORE_MASK, + rate->aclk_div << RK3528_DIV_ACLK_M_CORE_SHIFT); + + if (rockchip_pll_set_rate(&rk3528_pll_clks[APLL], + priv->cru, APLL, new_rate)) + return -EINVAL; + } + + return 0; +} + +static ulong rk3528_ppll_matrix_get_rate(struct rk3528_clk_priv *priv, + ulong clk_id) +{ + struct rk3528_cru *cru = priv->cru; + u32 div, mask, shift; + void *reg; + + switch (clk_id) { + case CLK_PPLL_50M_MATRIX: + case CLK_GMAC1_RMII_VPU: + mask = PCIE_CLK_MATRIX_50M_SRC_DIV_MASK; + shift = PCIE_CLK_MATRIX_50M_SRC_DIV_SHIFT; + reg = &cru->pcieclksel_con[1]; + break; + + case CLK_PPLL_100M_MATRIX: + mask = PCIE_CLK_MATRIX_100M_SRC_DIV_MASK; + shift = PCIE_CLK_MATRIX_100M_SRC_DIV_SHIFT; + reg = &cru->pcieclksel_con[1]; + break; + + case CLK_PPLL_125M_MATRIX: + case CLK_GMAC1_SRC_VPU: + mask = CLK_MATRIX_125M_SRC_DIV_MASK; + shift = CLK_MATRIX_125M_SRC_DIV_SHIFT; + reg = &cru->clksel_con[60]; + break; + + case CLK_GMAC1_VPU_25M: + mask = CLK_MATRIX_25M_SRC_DIV_MASK; + shift = CLK_MATRIX_25M_SRC_DIV_SHIFT; + reg = &cru->clksel_con[60]; + break; + default: + return -ENOENT; + } + + div = (readl(reg) & mask) >> shift; + + return DIV_TO_RATE(priv->ppll_hz, div); +} + +static ulong rk3528_ppll_matrix_set_rate(struct rk3528_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3528_cru *cru = priv->cru; + u32 id, div, mask, shift; + u8 is_pciecru = 0; + + switch (clk_id) { + case CLK_PPLL_50M_MATRIX: + id = 1; + mask = PCIE_CLK_MATRIX_50M_SRC_DIV_MASK; + shift = PCIE_CLK_MATRIX_50M_SRC_DIV_SHIFT; + is_pciecru = 1; + break; + + case CLK_PPLL_100M_MATRIX: + id = 1; + mask = PCIE_CLK_MATRIX_100M_SRC_DIV_MASK; + shift = PCIE_CLK_MATRIX_100M_SRC_DIV_SHIFT; + is_pciecru = 1; + break; + + case CLK_PPLL_125M_MATRIX: + id = 60; + mask = CLK_MATRIX_125M_SRC_DIV_MASK; + shift = CLK_MATRIX_125M_SRC_DIV_SHIFT; + break; + case CLK_GMAC1_VPU_25M: + id = 60; + mask = CLK_MATRIX_25M_SRC_DIV_MASK; + shift = CLK_MATRIX_25M_SRC_DIV_SHIFT; + break; + default: + return -ENOENT; + } + + div = DIV_ROUND_UP(priv->ppll_hz, rate); + if (is_pciecru) + rk_clrsetreg(&cru->pcieclksel_con[id], mask, (div - 1) << shift); + else + rk_clrsetreg(&cru->clksel_con[id], mask, (div - 1) << shift); + + return rk3528_ppll_matrix_get_rate(priv, clk_id); +} + +static ulong rk3528_cgpll_matrix_get_rate(struct rk3528_clk_priv *priv, + ulong clk_id) +{ + struct rk3528_cru *cru = priv->cru; + u32 sel, div, mask, shift, con; + u32 sel_mask = 0, sel_shift; + u8 is_gpll_parent = 1; + u8 is_halfdiv = 0; + ulong prate; + + switch (clk_id) { + case CLK_MATRIX_50M_SRC: + con = 0; + mask = CLK_MATRIX_50M_SRC_DIV_MASK; + shift = CLK_MATRIX_50M_SRC_DIV_SHIFT; + is_gpll_parent = 0; + break; + + case CLK_MATRIX_100M_SRC: + con = 0; + mask = CLK_MATRIX_100M_SRC_DIV_MASK; + shift = CLK_MATRIX_100M_SRC_DIV_SHIFT; + is_gpll_parent = 0; + break; + + case CLK_MATRIX_150M_SRC: + con = 1; + mask = CLK_MATRIX_150M_SRC_DIV_MASK; + shift = CLK_MATRIX_150M_SRC_DIV_SHIFT; + break; + + case CLK_MATRIX_200M_SRC: + con = 1; + mask = CLK_MATRIX_200M_SRC_DIV_MASK; + shift = CLK_MATRIX_200M_SRC_DIV_SHIFT; + break; + + case CLK_MATRIX_250M_SRC: + con = 1; + mask = CLK_MATRIX_250M_SRC_DIV_MASK; + shift = CLK_MATRIX_250M_SRC_DIV_SHIFT; + sel_mask = CLK_MATRIX_250M_SRC_SEL_MASK; + sel_shift = CLK_MATRIX_250M_SRC_SEL_SHIFT; + break; + + case CLK_MATRIX_300M_SRC: + con = 2; + mask = CLK_MATRIX_300M_SRC_DIV_MASK; + shift = CLK_MATRIX_300M_SRC_DIV_SHIFT; + break; + + case CLK_MATRIX_339M_SRC: + con = 2; + mask = CLK_MATRIX_339M_SRC_DIV_MASK; + shift = CLK_MATRIX_339M_SRC_DIV_SHIFT; + is_halfdiv = 1; + break; + + case CLK_MATRIX_400M_SRC: + con = 2; + mask = CLK_MATRIX_400M_SRC_DIV_MASK; + shift = CLK_MATRIX_400M_SRC_DIV_SHIFT; + break; + + case CLK_MATRIX_500M_SRC: + con = 3; + mask = CLK_MATRIX_500M_SRC_DIV_MASK; + shift = CLK_MATRIX_500M_SRC_DIV_SHIFT; + sel_mask = CLK_MATRIX_500M_SRC_SEL_MASK; + sel_shift = CLK_MATRIX_500M_SRC_SEL_SHIFT; + break; + + case CLK_MATRIX_600M_SRC: + con = 4; + mask = CLK_MATRIX_600M_SRC_DIV_MASK; + shift = CLK_MATRIX_600M_SRC_DIV_SHIFT; + break; + + case ACLK_BUS_VOPGL_ROOT: + case ACLK_BUS_VOPGL_BIU: + con = 43; + mask = ACLK_BUS_VOPGL_ROOT_DIV_MASK; + shift = ACLK_BUS_VOPGL_ROOT_DIV_SHIFT; + break; + + default: + return -ENOENT; + } + + if (sel_mask) { + sel = (readl(&cru->clksel_con[con]) & sel_mask) >> sel_shift; + if (sel == CLK_MATRIX_250M_SRC_SEL_CLK_GPLL_MUX) // TODO + prate = priv->gpll_hz; + else + prate = priv->cpll_hz; + } else { + if (is_gpll_parent) + prate = priv->gpll_hz; + else + prate = priv->cpll_hz; + } + + div = (readl(&cru->clksel_con[con]) & mask) >> shift; + + /* NOTE: '-1' to balance the DIV_TO_RATE() 'div+1' */ + return is_halfdiv ? DIV_TO_RATE(prate * 2, (3 + 2 * div) - 1) : DIV_TO_RATE(prate, div); +} + +static ulong rk3528_cgpll_matrix_set_rate(struct rk3528_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3528_cru *cru = priv->cru; + u32 sel, div, mask, shift, con; + u32 sel_mask = 0, sel_shift; + u8 is_gpll_parent = 1; + u8 is_halfdiv = 0; + ulong prate = 0; + + switch (clk_id) { + case CLK_MATRIX_50M_SRC: + con = 0; + mask = CLK_MATRIX_50M_SRC_DIV_MASK; + shift = CLK_MATRIX_50M_SRC_DIV_SHIFT; + is_gpll_parent = 0; + break; + + case CLK_MATRIX_100M_SRC: + con = 0; + mask = CLK_MATRIX_100M_SRC_DIV_MASK; + shift = CLK_MATRIX_100M_SRC_DIV_SHIFT; + is_gpll_parent = 0; + break; + + case CLK_MATRIX_150M_SRC: + con = 1; + mask = CLK_MATRIX_150M_SRC_DIV_MASK; + shift = CLK_MATRIX_150M_SRC_DIV_SHIFT; + break; + + case CLK_MATRIX_200M_SRC: + con = 1; + mask = CLK_MATRIX_200M_SRC_DIV_MASK; + shift = CLK_MATRIX_200M_SRC_DIV_SHIFT; + break; + + case CLK_MATRIX_250M_SRC: + con = 1; + mask = CLK_MATRIX_250M_SRC_DIV_MASK; + shift = CLK_MATRIX_250M_SRC_DIV_SHIFT; + sel_mask = CLK_MATRIX_250M_SRC_SEL_MASK; + sel_shift = CLK_MATRIX_250M_SRC_SEL_SHIFT; + break; + + case CLK_MATRIX_300M_SRC: + con = 2; + mask = CLK_MATRIX_300M_SRC_DIV_MASK; + shift = CLK_MATRIX_300M_SRC_DIV_SHIFT; + break; + + case CLK_MATRIX_339M_SRC: + con = 2; + mask = CLK_MATRIX_339M_SRC_DIV_MASK; + shift = CLK_MATRIX_339M_SRC_DIV_SHIFT; + is_halfdiv = 1; + break; + + case CLK_MATRIX_400M_SRC: + con = 2; + mask = CLK_MATRIX_400M_SRC_DIV_MASK; + shift = CLK_MATRIX_400M_SRC_DIV_SHIFT; + break; + + case CLK_MATRIX_500M_SRC: + con = 3; + mask = CLK_MATRIX_500M_SRC_DIV_MASK; + shift = CLK_MATRIX_500M_SRC_DIV_SHIFT; + sel_mask = CLK_MATRIX_500M_SRC_SEL_MASK; + sel_shift = CLK_MATRIX_500M_SRC_SEL_SHIFT; + break; + + case CLK_MATRIX_600M_SRC: + con = 4; + mask = CLK_MATRIX_600M_SRC_DIV_MASK; + shift = CLK_MATRIX_600M_SRC_DIV_SHIFT; + break; + + case ACLK_BUS_VOPGL_ROOT: + case ACLK_BUS_VOPGL_BIU: + con = 43; + mask = ACLK_BUS_VOPGL_ROOT_DIV_MASK; + shift = ACLK_BUS_VOPGL_ROOT_DIV_SHIFT; + break; + + default: + return -ENOENT; + } + + if (sel_mask) { + if (priv->gpll_hz % rate == 0) { + sel = CLK_MATRIX_250M_SRC_SEL_CLK_GPLL_MUX; // TODO + prate = priv->gpll_hz; + } else { + sel = CLK_MATRIX_250M_SRC_SEL_CLK_CPLL_MUX; + prate = priv->cpll_hz; + } + } else { + if (is_gpll_parent) + prate = priv->gpll_hz; + else + prate = priv->cpll_hz; + } + + if (is_halfdiv) + /* NOTE: '+1' to balance the following rk_clrsetreg() 'div-1' */ + div = DIV_ROUND_UP((prate * 2) - (3 * rate), 2 * rate) + 1; + else + div = DIV_ROUND_UP(prate, rate); + + rk_clrsetreg(&cru->clksel_con[con], mask, (div - 1) << shift); + if (sel_mask) + rk_clrsetreg(&cru->clksel_con[con], sel_mask, sel << sel_shift); + + return rk3528_cgpll_matrix_get_rate(priv, clk_id); +} + +static ulong rk3528_i2c_get_clk(struct rk3528_clk_priv *priv, ulong clk_id) +{ + struct rk3528_cru *cru = priv->cru; + u32 id, sel, con, mask, shift; + u8 is_pmucru = 0; + ulong rate; + + switch (clk_id) { + case CLK_I2C0: + id = 79; + mask = CLK_I2C0_SEL_MASK; + shift = CLK_I2C0_SEL_SHIFT; + break; + + case CLK_I2C1: + id = 79; + mask = CLK_I2C1_SEL_MASK; + shift = CLK_I2C1_SEL_SHIFT; + break; + + case CLK_I2C2: + id = 0; + mask = CLK_I2C2_SEL_MASK; + shift = CLK_I2C2_SEL_SHIFT; + is_pmucru = 1; + break; + + case CLK_I2C3: + id = 63; + mask = CLK_I2C3_SEL_MASK; + shift = CLK_I2C3_SEL_SHIFT; + break; + + case CLK_I2C4: + id = 85; + mask = CLK_I2C4_SEL_MASK; + shift = CLK_I2C4_SEL_SHIFT; + break; + + case CLK_I2C5: + id = 63; + mask = CLK_I2C5_SEL_MASK; + shift = CLK_I2C5_SEL_SHIFT; + break; + + case CLK_I2C6: + id = 64; + mask = CLK_I2C6_SEL_MASK; + shift = CLK_I2C6_SEL_SHIFT; + break; + + case CLK_I2C7: + id = 86; + mask = CLK_I2C7_SEL_MASK; + shift = CLK_I2C7_SEL_SHIFT; + break; + + default: + return -ENOENT; + } + + if (is_pmucru) + con = readl(&cru->pmuclksel_con[id]); + else + con = readl(&cru->clksel_con[id]); + sel = (con & mask) >> shift; + if (sel == CLK_I2C3_SEL_CLK_MATRIX_200M_SRC) + rate = 200 * MHz; + else if (sel == CLK_I2C3_SEL_CLK_MATRIX_100M_SRC) + rate = 100 * MHz; + else if (sel == CLK_I2C3_SEL_CLK_MATRIX_50M_SRC) + rate = 50 * MHz; + else + rate = OSC_HZ; + + return rate; +} + +static ulong rk3528_i2c_set_clk(struct rk3528_clk_priv *priv, ulong clk_id, + ulong rate) +{ + struct rk3528_cru *cru = priv->cru; + u32 id, sel, mask, shift; + u8 is_pmucru = 0; + + if (rate >= 198 * MHz) + sel = CLK_I2C3_SEL_CLK_MATRIX_200M_SRC; + else if (rate >= 99 * MHz) + sel = CLK_I2C3_SEL_CLK_MATRIX_100M_SRC; + else if (rate >= 50 * MHz) + sel = CLK_I2C3_SEL_CLK_MATRIX_50M_SRC; + else + sel = CLK_I2C3_SEL_XIN_OSC0_FUNC; + + switch (clk_id) { + case CLK_I2C0: + id = 79; + mask = CLK_I2C0_SEL_MASK; + shift = CLK_I2C0_SEL_SHIFT; + break; + + case CLK_I2C1: + id = 79; + mask = CLK_I2C1_SEL_MASK; + shift = CLK_I2C1_SEL_SHIFT; + break; + + case CLK_I2C2: + id = 0; + mask = CLK_I2C2_SEL_MASK; + shift = CLK_I2C2_SEL_SHIFT; + is_pmucru = 1; + break; + + case CLK_I2C3: + id = 63; + mask = CLK_I2C3_SEL_MASK; + shift = CLK_I2C3_SEL_SHIFT; + break; + + case CLK_I2C4: + id = 85; + mask = CLK_I2C4_SEL_MASK; + shift = CLK_I2C4_SEL_SHIFT; + break; + + case CLK_I2C5: + id = 63; + mask = CLK_I2C5_SEL_MASK; + shift = CLK_I2C5_SEL_SHIFT; + break; + + case CLK_I2C6: + id = 64; + mask = CLK_I2C6_SEL_MASK; + shift = CLK_I2C6_SEL_SHIFT; + break; + + case CLK_I2C7: + id = 86; + mask = CLK_I2C7_SEL_MASK; + shift = CLK_I2C7_SEL_SHIFT; + break; + + default: + return -ENOENT; + } + + if (is_pmucru) + rk_clrsetreg(&cru->pmuclksel_con[id], mask, sel << shift); + else + rk_clrsetreg(&cru->clksel_con[id], mask, sel << shift); + + return rk3528_i2c_get_clk(priv, clk_id); +} + +static ulong rk3528_spi_get_clk(struct rk3528_clk_priv *priv, ulong clk_id) +{ + struct rk3528_cru *cru = priv->cru; + u32 id, sel, con, mask, shift; + ulong rate; + + switch (clk_id) { + case CLK_SPI0: + id = 79; + mask = CLK_SPI0_SEL_MASK; + shift = CLK_SPI0_SEL_SHIFT; + break; + + case CLK_SPI1: + id = 63; + mask = CLK_SPI1_SEL_MASK; + shift = CLK_SPI1_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + con = readl(&cru->clksel_con[id]); + sel = (con & mask) >> shift; + if (sel == CLK_SPI1_SEL_CLK_MATRIX_200M_SRC) + rate = 200 * MHz; + else if (sel == CLK_SPI1_SEL_CLK_MATRIX_100M_SRC) + rate = 100 * MHz; + else if (sel == CLK_SPI1_SEL_CLK_MATRIX_50M_SRC) + rate = 50 * MHz; + else + rate = OSC_HZ; + + return rate; +} + +static ulong rk3528_spi_set_clk(struct rk3528_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3528_cru *cru = priv->cru; + u32 id, sel, mask, shift; + + if (rate >= 198 * MHz) + sel = CLK_SPI1_SEL_CLK_MATRIX_200M_SRC; + else if (rate >= 99 * MHz) + sel = CLK_SPI1_SEL_CLK_MATRIX_100M_SRC; + else if (rate >= 50 * MHz) + sel = CLK_SPI1_SEL_CLK_MATRIX_50M_SRC; + else + sel = CLK_SPI1_SEL_XIN_OSC0_FUNC; + + switch (clk_id) { + case CLK_SPI0: + id = 79; + mask = CLK_SPI0_SEL_MASK; + shift = CLK_SPI0_SEL_SHIFT; + break; + + case CLK_SPI1: + id = 63; + mask = CLK_SPI1_SEL_MASK; + shift = CLK_SPI1_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[id], mask, sel << shift); + + return rk3528_spi_get_clk(priv, clk_id); +} + +static ulong rk3528_pwm_get_clk(struct rk3528_clk_priv *priv, ulong clk_id) +{ + struct rk3528_cru *cru = priv->cru; + u32 id, sel, con, mask, shift; + ulong rate; + + switch (clk_id) { + case CLK_PWM0: + id = 44; + mask = CLK_PWM0_SEL_MASK; + shift = CLK_PWM0_SEL_SHIFT; + break; + + case CLK_PWM1: + id = 44; + mask = CLK_PWM1_SEL_MASK; + shift = CLK_PWM1_SEL_SHIFT; + break; + + default: + return -ENOENT; + } + + con = readl(&cru->clksel_con[id]); + sel = (con & mask) >> shift; + if (sel == CLK_PWM0_SEL_CLK_MATRIX_100M_SRC) + rate = 100 * MHz; + if (sel == CLK_PWM0_SEL_CLK_MATRIX_50M_SRC) + rate = 50 * MHz; + else + rate = OSC_HZ; + + return rate; +} + +static ulong rk3528_pwm_set_clk(struct rk3528_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3528_cru *cru = priv->cru; + u32 id, sel, mask, shift; + + if (rate >= 99 * MHz) + sel = CLK_PWM0_SEL_CLK_MATRIX_100M_SRC; + else if (rate >= 50 * MHz) + sel = CLK_PWM0_SEL_CLK_MATRIX_50M_SRC; + else + sel = CLK_PWM0_SEL_XIN_OSC0_FUNC; + + switch (clk_id) { + case CLK_PWM0: + id = 44; + mask = CLK_PWM0_SEL_MASK; + shift = CLK_PWM0_SEL_SHIFT; + break; + + case CLK_PWM1: + id = 44; + mask = CLK_PWM1_SEL_MASK; + shift = CLK_PWM1_SEL_SHIFT; + break; + + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[id], mask, sel << shift); + + return rk3528_pwm_get_clk(priv, clk_id); +} + +static ulong rk3528_adc_get_clk(struct rk3528_clk_priv *priv, ulong clk_id) +{ + struct rk3528_cru *cru = priv->cru; + u32 div, con; + + con = readl(&cru->clksel_con[74]); + switch (clk_id) { + case CLK_SARADC: + div = (con & CLK_SARADC_DIV_MASK) >> + CLK_SARADC_DIV_SHIFT; + break; + + case CLK_TSADC_TSEN: + div = (con & CLK_TSADC_TSEN_DIV_MASK) >> + CLK_TSADC_TSEN_DIV_SHIFT; + break; + + case CLK_TSADC: + div = (con & CLK_TSADC_DIV_MASK) >> + CLK_TSADC_DIV_SHIFT; + break; + + default: + return -ENOENT; + } + + return DIV_TO_RATE(OSC_HZ, div); +} + +static ulong rk3528_adc_set_clk(struct rk3528_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3528_cru *cru = priv->cru; + u32 div, mask, shift; + + switch (clk_id) { + case CLK_SARADC: + mask = CLK_SARADC_DIV_MASK; + shift = CLK_SARADC_DIV_SHIFT; + break; + + case CLK_TSADC_TSEN: + mask = CLK_TSADC_TSEN_DIV_MASK; + shift = CLK_TSADC_TSEN_DIV_SHIFT; + break; + + case CLK_TSADC: + mask = CLK_TSADC_DIV_MASK; + shift = CLK_TSADC_DIV_SHIFT; + break; + + default: + return -ENOENT; + } + + div = DIV_ROUND_UP(OSC_HZ, rate); + rk_clrsetreg(&cru->clksel_con[74], mask, (div - 1) << shift); + + return rk3528_adc_get_clk(priv, clk_id); +} + +static ulong rk3528_sdmmc_get_clk(struct rk3528_clk_priv *priv, ulong clk_id) +{ + struct rk3528_cru *cru = priv->cru; + u32 div, sel, con; + ulong prate; + + con = readl(&cru->clksel_con[85]); + div = (con & CCLK_SRC_SDMMC0_DIV_MASK) >> + CCLK_SRC_SDMMC0_DIV_SHIFT; + sel = (con & CCLK_SRC_SDMMC0_SEL_MASK) >> + CCLK_SRC_SDMMC0_SEL_SHIFT; + + if (sel == CCLK_SRC_SDMMC0_SEL_CLK_GPLL_MUX) + prate = priv->gpll_hz; + else if (sel == CCLK_SRC_SDMMC0_SEL_CLK_CPLL_MUX) + prate = priv->cpll_hz; + else + prate = OSC_HZ; + + return DIV_TO_RATE(prate, div); +} + +static ulong rk3528_sdmmc_set_clk(struct rk3528_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3528_cru *cru = priv->cru; + u32 div, sel; + + if (OSC_HZ % rate == 0) { + div = DIV_ROUND_UP(OSC_HZ, rate); + sel = CCLK_SRC_SDMMC0_SEL_XIN_OSC0_FUNC; + } else if ((priv->cpll_hz % rate) == 0) { + div = DIV_ROUND_UP(priv->cpll_hz, rate); + sel = CCLK_SRC_SDMMC0_SEL_CLK_CPLL_MUX; + } else { + div = DIV_ROUND_UP(priv->gpll_hz, rate); + sel = CCLK_SRC_SDMMC0_SEL_CLK_GPLL_MUX; + } + + assert(div - 1 <= 63); + rk_clrsetreg(&cru->clksel_con[85], + CCLK_SRC_SDMMC0_SEL_MASK | + CCLK_SRC_SDMMC0_DIV_MASK, + sel << CCLK_SRC_SDMMC0_SEL_SHIFT | + (div - 1) << CCLK_SRC_SDMMC0_DIV_SHIFT); + + return rk3528_sdmmc_get_clk(priv, clk_id); +} + +static ulong rk3528_sfc_get_clk(struct rk3528_clk_priv *priv) +{ + struct rk3528_cru *cru = priv->cru; + u32 div, sel, con, parent; + + con = readl(&cru->clksel_con[61]); + div = (con & SCLK_SFC_DIV_MASK) >> + SCLK_SFC_DIV_SHIFT; + sel = (con & SCLK_SFC_SEL_MASK) >> + SCLK_SFC_SEL_SHIFT; + if (sel == SCLK_SFC_SEL_CLK_GPLL_MUX) + parent = priv->gpll_hz; + else if (sel == SCLK_SFC_SEL_CLK_CPLL_MUX) + parent = priv->cpll_hz; + else + parent = OSC_HZ; + + return DIV_TO_RATE(parent, div); +} + +static ulong rk3528_sfc_set_clk(struct rk3528_clk_priv *priv, ulong rate) +{ + struct rk3528_cru *cru = priv->cru; + int div, sel; + + if (OSC_HZ % rate == 0) { + div = DIV_ROUND_UP(OSC_HZ, rate); + sel = SCLK_SFC_SEL_XIN_OSC0_FUNC; + } else if ((priv->cpll_hz % rate) == 0) { + div = DIV_ROUND_UP(priv->cpll_hz, rate); + sel = SCLK_SFC_SEL_CLK_CPLL_MUX; + } else { + div = DIV_ROUND_UP(priv->gpll_hz, rate); + sel = SCLK_SFC_SEL_CLK_GPLL_MUX; + } + + assert(div - 1 <= 63); + rk_clrsetreg(&cru->clksel_con[61], + SCLK_SFC_SEL_MASK | + SCLK_SFC_DIV_MASK, + sel << SCLK_SFC_SEL_SHIFT | + (div - 1) << SCLK_SFC_DIV_SHIFT); + + return rk3528_sfc_get_clk(priv); +} + +static ulong rk3528_emmc_get_clk(struct rk3528_clk_priv *priv) +{ + struct rk3528_cru *cru = priv->cru; + u32 div, sel, con, parent; + + con = readl(&cru->clksel_con[62]); + div = (con & CCLK_SRC_EMMC_DIV_MASK) >> + CCLK_SRC_EMMC_DIV_SHIFT; + sel = (con & CCLK_SRC_EMMC_SEL_MASK) >> + CCLK_SRC_EMMC_SEL_SHIFT; + + if (sel == CCLK_SRC_EMMC_SEL_CLK_GPLL_MUX) + parent = priv->gpll_hz; + else if (sel == CCLK_SRC_EMMC_SEL_CLK_CPLL_MUX) + parent = priv->cpll_hz; + else + parent = OSC_HZ; + + return DIV_TO_RATE(parent, div); +} + +static ulong rk3528_emmc_set_clk(struct rk3528_clk_priv *priv, ulong rate) +{ + struct rk3528_cru *cru = priv->cru; + u32 div, sel; + + if (OSC_HZ % rate == 0) { + div = DIV_ROUND_UP(OSC_HZ, rate); + sel = CCLK_SRC_EMMC_SEL_XIN_OSC0_FUNC; + } else if ((priv->cpll_hz % rate) == 0) { + div = DIV_ROUND_UP(priv->cpll_hz, rate); + sel = CCLK_SRC_EMMC_SEL_CLK_CPLL_MUX; + } else { + div = DIV_ROUND_UP(priv->gpll_hz, rate); + sel = CCLK_SRC_EMMC_SEL_CLK_GPLL_MUX; + } + + assert(div - 1 <= 63); + rk_clrsetreg(&cru->clksel_con[62], + CCLK_SRC_EMMC_SEL_MASK | + CCLK_SRC_EMMC_DIV_MASK, + sel << CCLK_SRC_EMMC_SEL_SHIFT | + (div - 1) << CCLK_SRC_EMMC_DIV_SHIFT); + + return rk3528_emmc_get_clk(priv); +} + +static ulong rk3528_dclk_vop_get_clk(struct rk3528_clk_priv *priv, ulong clk_id) +{ + struct rk3528_cru *cru = priv->cru; + u32 div_mask, div_shift; + u32 sel_mask, sel_shift; + u32 id, con, sel, div; + ulong prate; + + switch (clk_id) { + case DCLK_VOP0: + id = 32; + sel_mask = DCLK_VOP_SRC0_SEL_MASK; + sel_shift = DCLK_VOP_SRC0_SEL_SHIFT; + /* FIXME if need src: clk_hdmiphy_pixel_io */ + div_mask = DCLK_VOP_SRC0_DIV_MASK; + div_shift = DCLK_VOP_SRC0_DIV_SHIFT; + break; + + case DCLK_VOP1: + id = 33; + sel_mask = DCLK_VOP_SRC1_SEL_MASK; + sel_shift = DCLK_VOP_SRC1_SEL_SHIFT; + div_mask = DCLK_VOP_SRC1_DIV_MASK; + div_shift = DCLK_VOP_SRC1_DIV_SHIFT; + break; + + default: + return -ENOENT; + } + + con = readl(&cru->clksel_con[id]); + div = (con & div_mask) >> div_shift; + sel = (con & sel_mask) >> sel_shift; + if (sel == DCLK_VOP_SRC_SEL_CLK_GPLL_MUX) + prate = priv->gpll_hz; + else + prate = priv->cpll_hz; + + return DIV_TO_RATE(prate, div); +} + +static ulong rk3528_dclk_vop_set_clk(struct rk3528_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3528_cru *cru = priv->cru; + u32 div_mask, div_shift; + u32 sel_mask, sel_shift; + u32 id, sel, div; + ulong prate; + + switch (clk_id) { + case DCLK_VOP0: + id = 32; + sel_mask = DCLK_VOP_SRC0_SEL_MASK; + sel_shift = DCLK_VOP_SRC0_SEL_SHIFT; + /* FIXME if need src: clk_hdmiphy_pixel_io */ + div_mask = DCLK_VOP_SRC0_DIV_MASK; + div_shift = DCLK_VOP_SRC0_DIV_SHIFT; + break; + + case DCLK_VOP1: + id = 33; + sel_mask = DCLK_VOP_SRC1_SEL_MASK; + sel_shift = DCLK_VOP_SRC1_SEL_SHIFT; + div_mask = DCLK_VOP_SRC1_DIV_MASK; + div_shift = DCLK_VOP_SRC1_DIV_SHIFT; + break; + + default: + return -ENOENT; + } + + if ((priv->gpll_hz % rate) == 0) { + prate = priv->gpll_hz; + sel = (DCLK_VOP_SRC_SEL_CLK_GPLL_MUX << sel_shift) & sel_mask; + } else { + prate = priv->cpll_hz; + sel = (DCLK_VOP_SRC_SEL_CLK_CPLL_MUX << sel_shift) & sel_mask; + } + + div = ((DIV_ROUND_UP(prate, rate) - 1) << div_shift) & div_mask; + rk_clrsetreg(&cru->clksel_con[id], sel, div); + + return rk3528_dclk_vop_get_clk(priv, clk_id); +} + +static ulong rk3528_uart_get_rate(struct rk3528_clk_priv *priv, ulong clk_id) +{ + struct rk3528_cru *cru = priv->cru; + u32 sel_shift, sel_mask, div_shift, div_mask; + u32 sel, id, con, frac_div, div; + ulong m, n, rate; + + switch (clk_id) { + case SCLK_UART0: + id = 6; + sel_shift = SCLK_UART0_SRC_SEL_SHIFT; + sel_mask = SCLK_UART0_SRC_SEL_MASK; + div_shift = CLK_UART0_SRC_DIV_SHIFT; + div_mask = CLK_UART0_SRC_DIV_MASK; + break; + + case SCLK_UART1: + id = 8; + sel_shift = SCLK_UART1_SRC_SEL_SHIFT; + sel_mask = SCLK_UART1_SRC_SEL_MASK; + div_shift = CLK_UART1_SRC_DIV_SHIFT; + div_mask = CLK_UART1_SRC_DIV_MASK; + break; + + case SCLK_UART2: + id = 10; + sel_shift = SCLK_UART2_SRC_SEL_SHIFT; + sel_mask = SCLK_UART2_SRC_SEL_MASK; + div_shift = CLK_UART2_SRC_DIV_SHIFT; + div_mask = CLK_UART2_SRC_DIV_MASK; + break; + + case SCLK_UART3: + id = 12; + sel_shift = SCLK_UART3_SRC_SEL_SHIFT; + sel_mask = SCLK_UART3_SRC_SEL_MASK; + div_shift = CLK_UART3_SRC_DIV_SHIFT; + div_mask = CLK_UART3_SRC_DIV_MASK; + break; + + case SCLK_UART4: + id = 14; + sel_shift = SCLK_UART4_SRC_SEL_SHIFT; + sel_mask = SCLK_UART4_SRC_SEL_MASK; + div_shift = CLK_UART4_SRC_DIV_SHIFT; + div_mask = CLK_UART4_SRC_DIV_MASK; + break; + + case SCLK_UART5: + id = 16; + sel_shift = SCLK_UART5_SRC_SEL_SHIFT; + sel_mask = SCLK_UART5_SRC_SEL_MASK; + div_shift = CLK_UART5_SRC_DIV_SHIFT; + div_mask = CLK_UART5_SRC_DIV_MASK; + break; + + case SCLK_UART6: + id = 18; + sel_shift = SCLK_UART6_SRC_SEL_SHIFT; + sel_mask = SCLK_UART6_SRC_SEL_MASK; + div_shift = CLK_UART6_SRC_DIV_SHIFT; + div_mask = CLK_UART6_SRC_DIV_MASK; + break; + + case SCLK_UART7: + id = 20; + sel_shift = SCLK_UART7_SRC_SEL_SHIFT; + sel_mask = SCLK_UART7_SRC_SEL_MASK; + div_shift = CLK_UART7_SRC_DIV_SHIFT; + div_mask = CLK_UART7_SRC_DIV_MASK; + break; + + default: + return -ENOENT; + } + + con = readl(&cru->clksel_con[id - 2]); + div = (con & div_mask) >> div_shift; + + con = readl(&cru->clksel_con[id]); + sel = (con & sel_mask) >> sel_shift; + + if (sel == SCLK_UART0_SRC_SEL_CLK_UART0_SRC) { + rate = DIV_TO_RATE(priv->gpll_hz, div); + } else if (sel == SCLK_UART0_SRC_SEL_CLK_UART0_FRAC) { + frac_div = readl(&cru->clksel_con[id - 1]); + n = (frac_div & 0xffff0000) >> 16; + m = frac_div & 0x0000ffff; + rate = DIV_TO_RATE(priv->gpll_hz, div) * n / m; + } else { + rate = OSC_HZ; + } + + return rate; +} + +static ulong rk3528_uart_set_rate(struct rk3528_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3528_cru *cru = priv->cru; + u32 sel_shift, sel_mask, div_shift, div_mask; + u32 sel, id, div; + ulong m = 0, n = 0, val; + + if (rate == OSC_HZ) { + sel = SCLK_UART0_SRC_SEL_XIN_OSC0_FUNC; + div = DIV_ROUND_UP(OSC_HZ, rate); + } else if (priv->gpll_hz % rate == 0) { + sel = SCLK_UART0_SRC_SEL_CLK_UART0_SRC; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } else { + sel = SCLK_UART0_SRC_SEL_CLK_UART0_FRAC; + div = 2; + rational_best_approximation(rate, priv->gpll_hz / div, + GENMASK(16 - 1, 0), + GENMASK(16 - 1, 0), + &n, &m); + } + + switch (clk_id) { + case SCLK_UART0: + id = 6; + sel_shift = SCLK_UART0_SRC_SEL_SHIFT; + sel_mask = SCLK_UART0_SRC_SEL_MASK; + div_shift = CLK_UART0_SRC_DIV_SHIFT; + div_mask = CLK_UART0_SRC_DIV_MASK; + break; + + case SCLK_UART1: + id = 8; + sel_shift = SCLK_UART1_SRC_SEL_SHIFT; + sel_mask = SCLK_UART1_SRC_SEL_MASK; + div_shift = CLK_UART1_SRC_DIV_SHIFT; + div_mask = CLK_UART1_SRC_DIV_MASK; + break; + + case SCLK_UART2: + id = 10; + sel_shift = SCLK_UART2_SRC_SEL_SHIFT; + sel_mask = SCLK_UART2_SRC_SEL_MASK; + div_shift = CLK_UART2_SRC_DIV_SHIFT; + div_mask = CLK_UART2_SRC_DIV_MASK; + break; + + case SCLK_UART3: + id = 12; + sel_shift = SCLK_UART3_SRC_SEL_SHIFT; + sel_mask = SCLK_UART3_SRC_SEL_MASK; + div_shift = CLK_UART3_SRC_DIV_SHIFT; + div_mask = CLK_UART3_SRC_DIV_MASK; + break; + + case SCLK_UART4: + id = 14; + sel_shift = SCLK_UART4_SRC_SEL_SHIFT; + sel_mask = SCLK_UART4_SRC_SEL_MASK; + div_shift = CLK_UART4_SRC_DIV_SHIFT; + div_mask = CLK_UART4_SRC_DIV_MASK; + break; + + case SCLK_UART5: + id = 16; + sel_shift = SCLK_UART5_SRC_SEL_SHIFT; + sel_mask = SCLK_UART5_SRC_SEL_MASK; + div_shift = CLK_UART5_SRC_DIV_SHIFT; + div_mask = CLK_UART5_SRC_DIV_MASK; + break; + + case SCLK_UART6: + id = 18; + sel_shift = SCLK_UART6_SRC_SEL_SHIFT; + sel_mask = SCLK_UART6_SRC_SEL_MASK; + div_shift = CLK_UART6_SRC_DIV_SHIFT; + div_mask = CLK_UART6_SRC_DIV_MASK; + break; + + case SCLK_UART7: + id = 20; + sel_shift = SCLK_UART7_SRC_SEL_SHIFT; + sel_mask = SCLK_UART7_SRC_SEL_MASK; + div_shift = CLK_UART7_SRC_DIV_SHIFT; + div_mask = CLK_UART7_SRC_DIV_MASK; + break; + + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[id - 2], div_mask, (div - 1) << div_shift); + rk_clrsetreg(&cru->clksel_con[id], sel_mask, sel << sel_shift); + if (m && n) { + val = n << 16 | m; + writel(val, &cru->clksel_con[id - 1]); + } + + return rk3528_uart_get_rate(priv, clk_id); +} + +static ulong rk3528_clk_get_rate(struct clk *clk) +{ + struct rk3528_clk_priv *priv = dev_get_priv(clk->dev); + ulong rate = 0; + + if (!priv->gpll_hz || !priv->cpll_hz) { + printf("%s: gpll=%lu, cpll=%ld\n", + __func__, priv->gpll_hz, priv->cpll_hz); + return -ENOENT; + } + + switch (clk->id) { + case PLL_APLL: + case ARMCLK: + rate = rockchip_pll_get_rate(&rk3528_pll_clks[APLL], priv->cru, + APLL); + break; + case PLL_CPLL: + rate = rockchip_pll_get_rate(&rk3528_pll_clks[CPLL], priv->cru, + CPLL); + break; + case PLL_GPLL: + rate = rockchip_pll_get_rate(&rk3528_pll_clks[GPLL], priv->cru, + GPLL); + break; + + case PLL_PPLL: + rate = rockchip_pll_get_rate(&rk3528_pll_clks[PPLL], priv->cru, + PPLL); + break; + case PLL_DPLL: + rate = rockchip_pll_get_rate(&rk3528_pll_clks[DPLL], priv->cru, + DPLL); + break; + + case TCLK_EMMC: + case TCLK_WDT_NS: + rate = OSC_HZ; + break; + case CLK_I2C0: + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + case CLK_I2C6: + case CLK_I2C7: + rate = rk3528_i2c_get_clk(priv, clk->id); + break; + case CLK_SPI0: + case CLK_SPI1: + rate = rk3528_spi_get_clk(priv, clk->id); + break; + case CLK_PWM0: + case CLK_PWM1: + rate = rk3528_pwm_get_clk(priv, clk->id); + break; + case CLK_SARADC: + case CLK_TSADC: + case CLK_TSADC_TSEN: + rate = rk3528_adc_get_clk(priv, clk->id); + break; + case CCLK_SRC_EMMC: + rate = rk3528_emmc_get_clk(priv); + break; + case HCLK_SDMMC0: + case CCLK_SRC_SDMMC0: + rate = rk3528_sdmmc_get_clk(priv, clk->id); + break; + case SCLK_SFC: + rate = rk3528_sfc_get_clk(priv); + break; + case DCLK_VOP0: + case DCLK_VOP1: + rate = rk3528_dclk_vop_get_clk(priv, clk->id); + break; + case DCLK_CVBS: + rate = rk3528_dclk_vop_get_clk(priv, DCLK_VOP1) / 4; + break; + case DCLK_4X_CVBS: + rate = rk3528_dclk_vop_get_clk(priv, DCLK_VOP1); + break; + case SCLK_UART0: + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + case SCLK_UART5: + case SCLK_UART6: + case SCLK_UART7: + rate = rk3528_uart_get_rate(priv, clk->id); + break; + case CLK_MATRIX_50M_SRC: + case CLK_MATRIX_100M_SRC: + case CLK_MATRIX_150M_SRC: + case CLK_MATRIX_200M_SRC: + case CLK_MATRIX_250M_SRC: + case CLK_MATRIX_300M_SRC: + case CLK_MATRIX_339M_SRC: + case CLK_MATRIX_400M_SRC: + case CLK_MATRIX_500M_SRC: + case CLK_MATRIX_600M_SRC: + case ACLK_BUS_VOPGL_BIU: + rate = rk3528_cgpll_matrix_get_rate(priv, clk->id); + break; + case CLK_PPLL_50M_MATRIX: + case CLK_PPLL_100M_MATRIX: + case CLK_PPLL_125M_MATRIX: + case CLK_GMAC1_VPU_25M: + case CLK_GMAC1_RMII_VPU: + case CLK_GMAC1_SRC_VPU: + rate = rk3528_ppll_matrix_get_rate(priv, clk->id); + break; + default: + return -ENOENT; + } + + return rate; +}; + +static ulong rk3528_clk_set_rate(struct clk *clk, ulong rate) +{ + struct rk3528_clk_priv *priv = dev_get_priv(clk->dev); + ulong ret = 0; + + if (!priv->gpll_hz) { + printf("%s gpll=%lu\n", __func__, priv->gpll_hz); + return -ENOENT; + } + + switch (clk->id) { + case PLL_APLL: + case ARMCLK: + if (priv->armclk_hz) + rk3528_armclk_set_clk(priv, rate); + priv->armclk_hz = rate; + break; + case PLL_CPLL: + ret = rockchip_pll_set_rate(&rk3528_pll_clks[CPLL], priv->cru, + CPLL, rate); + priv->cpll_hz = rockchip_pll_get_rate(&rk3528_pll_clks[CPLL], + priv->cru, CPLL); + break; + case PLL_GPLL: + ret = rockchip_pll_set_rate(&rk3528_pll_clks[GPLL], priv->cru, + GPLL, rate); + priv->gpll_hz = rockchip_pll_get_rate(&rk3528_pll_clks[GPLL], + priv->cru, GPLL); + break; + case PLL_PPLL: + ret = rockchip_pll_set_rate(&rk3528_pll_clks[PPLL], priv->cru, + PPLL, rate); + priv->ppll_hz = rockchip_pll_get_rate(&rk3528_pll_clks[PPLL], + priv->cru, PPLL); + break; + case TCLK_EMMC: + case TCLK_WDT_NS: + return (rate == OSC_HZ) ? 0 : -EINVAL; + case CLK_I2C0: + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + case CLK_I2C6: + case CLK_I2C7: + ret = rk3528_i2c_set_clk(priv, clk->id, rate); + break; + case CLK_SPI0: + case CLK_SPI1: + ret = rk3528_spi_set_clk(priv, clk->id, rate); + break; + case CLK_PWM0: + case CLK_PWM1: + ret = rk3528_pwm_set_clk(priv, clk->id, rate); + break; + case CLK_SARADC: + case CLK_TSADC: + case CLK_TSADC_TSEN: + ret = rk3528_adc_set_clk(priv, clk->id, rate); + break; + case HCLK_SDMMC0: + case CCLK_SRC_SDMMC0: + ret = rk3528_sdmmc_set_clk(priv, clk->id, rate); + break; + case SCLK_SFC: + ret = rk3528_sfc_set_clk(priv, rate); + break; + case CCLK_SRC_EMMC: + ret = rk3528_emmc_set_clk(priv, rate); + break; + case DCLK_VOP0: + case DCLK_VOP1: + ret = rk3528_dclk_vop_set_clk(priv, clk->id, rate); + break; + case SCLK_UART0: + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + case SCLK_UART5: + case SCLK_UART6: + case SCLK_UART7: + ret = rk3528_uart_set_rate(priv, clk->id, rate); + break; + case CLK_MATRIX_50M_SRC: + case CLK_MATRIX_100M_SRC: + case CLK_MATRIX_150M_SRC: + case CLK_MATRIX_200M_SRC: + case CLK_MATRIX_250M_SRC: + case CLK_MATRIX_300M_SRC: + case CLK_MATRIX_339M_SRC: + case CLK_MATRIX_400M_SRC: + case CLK_MATRIX_500M_SRC: + case CLK_MATRIX_600M_SRC: + case ACLK_BUS_VOPGL_BIU: + ret = rk3528_cgpll_matrix_set_rate(priv, clk->id, rate); + break; + case CLK_PPLL_50M_MATRIX: + case CLK_PPLL_100M_MATRIX: + case CLK_PPLL_125M_MATRIX: + case CLK_GMAC1_VPU_25M: + ret = rk3528_ppll_matrix_set_rate(priv, clk->id, rate); + break; + case CLK_GMAC1_RMII_VPU: + case CLK_GMAC1_SRC_VPU: + /* dummy set */ + ret = rk3528_ppll_matrix_get_rate(priv, clk->id); + break; + + /* Might occur in cru assigned-clocks, can be ignored here */ + case ACLK_BUS_VOPGL_ROOT: + case BCLK_EMMC: + case XIN_OSC0_DIV: + ret = 0; + break; + default: + return -ENOENT; + } + + return ret; +}; + +static struct clk_ops rk3528_clk_ops = { + .get_rate = rk3528_clk_get_rate, + .set_rate = rk3528_clk_set_rate, +}; + +#ifdef CONFIG_XPL_BUILD + +#define COREGRF_BASE 0xff300000 +#define PVTPLL_CON0_L 0x0 +#define PVTPLL_CON0_H 0x4 + +static int rk3528_cpu_pvtpll_set_rate(struct rk3528_clk_priv *priv, ulong rate) +{ + struct rk3528_cru *cru = priv->cru; + u32 length; + + if (rate >= 1200000000) + length = 8; + else if (rate >= 1008000000) + length = 11; + else + length = 17; + + /* set pclk dbg div to 9 */ + rk_clrsetreg(&cru->clksel_con[40], RK3528_DIV_PCLK_DBG_MASK, + 9 << RK3528_DIV_PCLK_DBG_SHIFT); + /* set aclk_m_core div to 1 */ + rk_clrsetreg(&cru->clksel_con[39], RK3528_DIV_ACLK_M_CORE_MASK, + 1 << RK3528_DIV_ACLK_M_CORE_SHIFT); + + /* set ring sel = 1 */ + writel(0x07000000 | (1 << 8), COREGRF_BASE + PVTPLL_CON0_L); + /* set length */ + writel(0x007f0000 | length, COREGRF_BASE + PVTPLL_CON0_H); + /* enable pvtpll */ + writel(0x00020002, COREGRF_BASE + PVTPLL_CON0_L); + /* start monitor */ + writel(0x00010001, COREGRF_BASE + PVTPLL_CON0_L); + + /* set core mux pvtpll */ + writel(0x00010001, &cru->clksel_con[40]); + writel(0x00100010, &cru->clksel_con[39]); + + /* set pclk dbg div to 8 */ + rk_clrsetreg(&cru->clksel_con[40], RK3528_DIV_PCLK_DBG_MASK, + 8 << RK3528_DIV_PCLK_DBG_SHIFT); + + return 0; +} +#endif + +static int rk3528_clk_init(struct rk3528_clk_priv *priv) +{ + int ret; + + priv->sync_kernel = false; + +#ifdef CONFIG_XPL_BUILD + /* + * BOOTROM: + * CPU 1902/2(postdiv1)=546M + * CPLL 996/2(postdiv1)=498M + * GPLL 1188/2(postdiv1)=594M + * |-- clk_matrix_200m_src_div=1 => rate: 300M + * |-- clk_matrix_300m_src_div=2 => rate: 200M + * + * Avoid overclocking when change GPLL rate: + * Change clk_matrix_200m_src_div to 5. + * Change clk_matrix_300m_src_div to 3. + */ + writel(0x01200120, &priv->cru->clksel_con[1]); + writel(0x00030003, &priv->cru->clksel_con[2]); + + if (!priv->armclk_enter_hz) { + priv->armclk_enter_hz = + rockchip_pll_get_rate(&rk3528_pll_clks[APLL], + priv->cru, APLL); + priv->armclk_init_hz = priv->armclk_enter_hz; + } + + if (priv->armclk_init_hz != APLL_HZ) { + ret = rk3528_armclk_set_clk(priv, APLL_HZ); + if (!ret) + priv->armclk_init_hz = APLL_HZ; + } + + if (!rk3528_cpu_pvtpll_set_rate(priv, CPU_PVTPLL_HZ)) { + debug("cpu pvtpll %d KHz\n", CPU_PVTPLL_HZ / 1000); + priv->armclk_init_hz = CPU_PVTPLL_HZ; + } +#endif + + if (priv->cpll_hz != CPLL_HZ) { + ret = rockchip_pll_set_rate(&rk3528_pll_clks[CPLL], priv->cru, + CPLL, CPLL_HZ); + if (!ret) + priv->cpll_hz = CPLL_HZ; + } + + if (priv->gpll_hz != GPLL_HZ) { + ret = rockchip_pll_set_rate(&rk3528_pll_clks[GPLL], priv->cru, + GPLL, GPLL_HZ); + if (!ret) + priv->gpll_hz = GPLL_HZ; + } + + if (priv->ppll_hz != PPLL_HZ) { + ret = rockchip_pll_set_rate(&rk3528_pll_clks[PPLL], priv->cru, + PPLL, PPLL_HZ); + if (!ret) + priv->ppll_hz = PPLL_HZ; + } + +#ifdef CONFIG_XPL_BUILD + /* Init to override bootrom config */ + rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_50M_SRC, 50000000); + rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_100M_SRC, 100000000); + rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_150M_SRC, 150000000); + rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_200M_SRC, 200000000); + rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_250M_SRC, 250000000); + rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_300M_SRC, 300000000); + rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_339M_SRC, 340000000); + rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_400M_SRC, 400000000); + rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_500M_SRC, 500000000); + rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_600M_SRC, 600000000); + rk3528_cgpll_matrix_set_rate(priv, ACLK_BUS_VOPGL_BIU, 500000000); + + /* The default rate is 100Mhz, it's not friendly for remote IR module */ + rk3528_pwm_set_clk(priv, CLK_PWM0, 24000000); + rk3528_pwm_set_clk(priv, CLK_PWM1, 24000000); +#endif + return 0; +} + +static int rk3528_clk_probe(struct udevice *dev) +{ + struct rk3528_clk_priv *priv = dev_get_priv(dev); + int ret; + + ret = rk3528_clk_init(priv); + if (ret) + return ret; + + /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */ + ret = clk_set_defaults(dev, 1); + if (ret) + debug("%s clk_set_defaults failed %d\n", __func__, ret); + else + priv->sync_kernel = true; + + return 0; +} + +static int rk3528_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct rk3528_clk_priv *priv = dev_get_priv(dev); + + priv->cru = dev_read_addr_ptr(dev); + + return 0; +} + +static int rk3528_clk_bind(struct udevice *dev) +{ + struct udevice *sys_child; + struct sysreset_reg *priv; + int ret; + + /* The reset driver does not have a device node, so bind it here */ + ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", + &sys_child); + if (ret) { + debug("Warning: No sysreset driver: ret=%d\n", ret); + } else { + priv = malloc(sizeof(struct sysreset_reg)); + priv->glb_srst_fst_value = offsetof(struct rk3528_cru, + glb_srst_fst); + priv->glb_srst_snd_value = offsetof(struct rk3528_cru, + glb_srst_snd); + dev_set_priv(sys_child, priv); + } + +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) + ret = offsetof(struct rk3528_cru, softrst_con[0]); + ret = rk3528_reset_bind_lut(dev, ret, 47); + if (ret) + debug("Warning: software reset driver bind failed\n"); +#endif + + return 0; +} + +static const struct udevice_id rk3528_clk_ids[] = { + { .compatible = "rockchip,rk3528-cru" }, + { } +}; + +U_BOOT_DRIVER(rockchip_rk3528_cru) = { + .name = "rockchip_rk3528_cru", + .id = UCLASS_CLK, + .of_match = rk3528_clk_ids, + .priv_auto = sizeof(struct rk3528_clk_priv), + .of_to_plat = rk3528_clk_ofdata_to_platdata, + .ops = &rk3528_clk_ops, + .bind = rk3528_clk_bind, + .probe = rk3528_clk_probe, +}; diff --git a/drivers/clk/rockchip/clk_rk3576.c b/drivers/clk/rockchip/clk_rk3576.c new file mode 100644 index 00000000000..e84a0943a94 --- /dev/null +++ b/drivers/clk/rockchip/clk_rk3576.c @@ -0,0 +1,2513 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 Fuzhou Rockchip Electronics Co., Ltd + * Author: Elaine Zhang <zhangqing@rock-chips.com> + */ + +#include <bitfield.h> +#include <clk-uclass.h> +#include <dm.h> +#include <errno.h> +#include <syscon.h> +#include <asm/arch-rockchip/cru_rk3576.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/hardware.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dt-bindings/clock/rockchip,rk3576-cru.h> +#include <linux/delay.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) + +static struct rockchip_pll_rate_table rk3576_24m_pll_rates[] = { + /* _mhz, _p, _m, _s, _k */ + RK3588_PLL_RATE(1500000000, 2, 250, 1, 0), + RK3588_PLL_RATE(1200000000, 1, 100, 1, 0), + RK3588_PLL_RATE(1188000000, 2, 198, 1, 0), + RK3588_PLL_RATE(1150000000, 3, 575, 2, 0), + RK3588_PLL_RATE(1100000000, 3, 550, 2, 0), + RK3588_PLL_RATE(1008000000, 2, 336, 2, 0), + RK3588_PLL_RATE(1000000000, 3, 500, 2, 0), + RK3588_PLL_RATE(900000000, 2, 300, 2, 0), + RK3588_PLL_RATE(850000000, 3, 425, 2, 0), + 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), + RK3588_PLL_RATE(200000000, 3, 400, 4, 0), + RK3588_PLL_RATE(100000000, 3, 400, 5, 0), + { /* sentinel */ }, +}; + +static struct rockchip_pll_clock rk3576_pll_clks[] = { + [BPLL] = PLL(pll_rk3588, PLL_BPLL, RK3576_PLL_CON(0), + RK3576_BPLL_MODE_CON0, 0, 15, 0, + rk3576_24m_pll_rates), + [LPLL] = PLL(pll_rk3588, PLL_LPLL, RK3576_LPLL_CON(16), + RK3576_LPLL_MODE_CON0, 0, 15, 0, rk3576_24m_pll_rates), + [VPLL] = PLL(pll_rk3588, PLL_VPLL, RK3576_PLL_CON(88), + RK3576_LPLL_MODE_CON0, 4, 15, 0, rk3576_24m_pll_rates), + [AUPLL] = PLL(pll_rk3588, PLL_AUPLL, RK3576_PLL_CON(96), + RK3576_MODE_CON0, 6, 15, 0, rk3576_24m_pll_rates), + [CPLL] = PLL(pll_rk3588, PLL_CPLL, RK3576_PLL_CON(104), + RK3576_MODE_CON0, 8, 15, 0, rk3576_24m_pll_rates), + [GPLL] = PLL(pll_rk3588, PLL_GPLL, RK3576_PLL_CON(112), + RK3576_MODE_CON0, 2, 15, 0, rk3576_24m_pll_rates), + [PPLL] = PLL(pll_rk3588, PLL_PPLL, RK3576_PMU_PLL_CON(128), + RK3576_MODE_CON0, 10, 15, 0, rk3576_24m_pll_rates), +}; + +#ifdef CONFIG_SPL_BUILD +#ifndef BITS_WITH_WMASK +#define BITS_WITH_WMASK(bits, msk, shift) \ + ((bits) << (shift)) | ((msk) << ((shift) + 16)) +#endif +#endif + +#ifndef CONFIG_SPL_BUILD +/* + * + * rational_best_approximation(31415, 10000, + * (1 << 8) - 1, (1 << 5) - 1, &n, &d); + * + * you may look at given_numerator as a fixed point number, + * with the fractional part size described in given_denominator. + * + * for theoretical background, see: + * http://en.wikipedia.org/wiki/Continued_fraction + */ +static void rational_best_approximation(unsigned long given_numerator, + unsigned long given_denominator, + unsigned long max_numerator, + unsigned long max_denominator, + unsigned long *best_numerator, + unsigned long *best_denominator) +{ + unsigned long n, d, n0, d0, n1, d1; + + n = given_numerator; + d = given_denominator; + n0 = 0; + d1 = 0; + n1 = 1; + d0 = 1; + for (;;) { + unsigned long t, a; + + if (n1 > max_numerator || d1 > max_denominator) { + n1 = n0; + d1 = d0; + break; + } + if (d == 0) + break; + t = d; + a = n / d; + d = n % d; + n = t; + t = n0 + a * n1; + n0 = n1; + n1 = t; + t = d0 + a * d1; + d0 = d1; + d1 = t; + } + *best_numerator = n1; + *best_denominator = d1; +} +#endif + +static ulong rk3576_bus_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 con, sel, div, rate; + + switch (clk_id) { + case ACLK_BUS_ROOT: + con = readl(&cru->clksel_con[55]); + sel = (con & ACLK_BUS_ROOT_SEL_MASK) >> + ACLK_BUS_ROOT_SEL_SHIFT; + div = (con & ACLK_BUS_ROOT_DIV_MASK) >> + ACLK_BUS_ROOT_DIV_SHIFT; + if (sel == ACLK_BUS_ROOT_SEL_CPLL) + rate = DIV_TO_RATE(priv->cpll_hz, div); + else + rate = DIV_TO_RATE(priv->gpll_hz, div); + break; + case HCLK_BUS_ROOT: + con = readl(&cru->clksel_con[55]); + sel = (con & HCLK_BUS_ROOT_SEL_MASK) >> + HCLK_BUS_ROOT_SEL_SHIFT; + if (sel == HCLK_BUS_ROOT_SEL_200M) + rate = 198 * MHz; + else if (sel == HCLK_BUS_ROOT_SEL_100M) + rate = 100 * MHz; + else if (sel == HCLK_BUS_ROOT_SEL_50M) + rate = 50 * MHz; + else + rate = OSC_HZ; + break; + case PCLK_BUS_ROOT: + con = readl(&cru->clksel_con[55]); + sel = (con & PCLK_BUS_ROOT_SEL_MASK) >> + PCLK_BUS_ROOT_SEL_SHIFT; + if (sel == PCLK_BUS_ROOT_SEL_100M) + rate = 100 * MHz; + else if (sel == PCLK_BUS_ROOT_SEL_50M) + rate = 50 * MHz; + else + rate = OSC_HZ; + break; + default: + return -ENOENT; + } + + return rate; +} + +static ulong rk3576_bus_set_clk(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + int src_clk, src_clk_div; + + switch (clk_id) { + case ACLK_BUS_ROOT: + if (!(priv->cpll_hz % rate)) { + src_clk = ACLK_BUS_ROOT_SEL_CPLL; + src_clk_div = DIV_ROUND_UP(priv->cpll_hz, rate); + } else { + src_clk = ACLK_BUS_ROOT_SEL_GPLL; + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate); + } + rk_clrsetreg(&cru->clksel_con[55], + ACLK_BUS_ROOT_SEL_MASK, + src_clk << ACLK_BUS_ROOT_SEL_SHIFT); + assert(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[55], + ACLK_BUS_ROOT_DIV_MASK | + ACLK_BUS_ROOT_SEL_MASK, + (src_clk << + ACLK_BUS_ROOT_SEL_SHIFT) | + (src_clk_div - 1) << ACLK_BUS_ROOT_DIV_SHIFT); + break; + case HCLK_BUS_ROOT: + if (rate >= 198 * MHz) + src_clk = HCLK_BUS_ROOT_SEL_200M; + else if (rate >= 99 * MHz) + src_clk = HCLK_BUS_ROOT_SEL_100M; + else if (rate >= 50 * MHz) + src_clk = HCLK_BUS_ROOT_SEL_50M; + else + src_clk = HCLK_BUS_ROOT_SEL_OSC; + rk_clrsetreg(&cru->clksel_con[55], + HCLK_BUS_ROOT_SEL_MASK, + src_clk << HCLK_BUS_ROOT_SEL_SHIFT); + break; + case PCLK_BUS_ROOT: + if (rate >= 99 * MHz) + src_clk = PCLK_BUS_ROOT_SEL_100M; + else if (rate >= 50 * MHz) + src_clk = PCLK_BUS_ROOT_SEL_50M; + else + src_clk = PCLK_BUS_ROOT_SEL_OSC; + rk_clrsetreg(&cru->clksel_con[55], + PCLK_BUS_ROOT_SEL_MASK, + src_clk << PCLK_BUS_ROOT_SEL_SHIFT); + break; + default: + printf("do not support this center freq\n"); + return -EINVAL; + } + + return rk3576_bus_get_clk(priv, clk_id); +} + +static ulong rk3576_top_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 con, sel, div, rate, prate; + + switch (clk_id) { + case ACLK_TOP: + con = readl(&cru->clksel_con[9]); + div = (con & ACLK_TOP_DIV_MASK) >> + ACLK_TOP_DIV_SHIFT; + sel = (con & ACLK_TOP_SEL_MASK) >> + ACLK_TOP_SEL_SHIFT; + if (sel == ACLK_TOP_SEL_CPLL) + prate = priv->cpll_hz; + else if (sel == ACLK_TOP_SEL_AUPLL) + prate = priv->aupll_hz; + else + prate = priv->gpll_hz; + return DIV_TO_RATE(prate, div); + case ACLK_TOP_MID: + con = readl(&cru->clksel_con[10]); + div = (con & ACLK_TOP_MID_DIV_MASK) >> + ACLK_TOP_MID_DIV_SHIFT; + sel = (con & ACLK_TOP_MID_SEL_MASK) >> + ACLK_TOP_MID_SEL_SHIFT; + if (sel == ACLK_TOP_MID_SEL_CPLL) + prate = priv->cpll_hz; + else + prate = priv->gpll_hz; + return DIV_TO_RATE(prate, div); + case PCLK_TOP_ROOT: + con = readl(&cru->clksel_con[8]); + sel = (con & PCLK_TOP_SEL_MASK) >> PCLK_TOP_SEL_SHIFT; + if (sel == PCLK_TOP_SEL_100M) + rate = 100 * MHz; + else if (sel == PCLK_TOP_SEL_50M) + rate = 50 * MHz; + else + rate = OSC_HZ; + break; + case HCLK_TOP: + con = readl(&cru->clksel_con[19]); + sel = (con & HCLK_TOP_SEL_MASK) >> HCLK_TOP_SEL_SHIFT; + if (sel == HCLK_TOP_SEL_200M) + rate = 200 * MHz; + else if (sel == HCLK_TOP_SEL_100M) + rate = 100 * MHz; + else if (sel == HCLK_TOP_SEL_50M) + rate = 50 * MHz; + else + rate = OSC_HZ; + break; + default: + return -ENOENT; + } + + return rate; +} + +static ulong rk3576_top_set_clk(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + int src_clk, src_clk_div; + + switch (clk_id) { + case ACLK_TOP: + if (!(priv->cpll_hz % rate)) { + src_clk = ACLK_TOP_SEL_CPLL; + src_clk_div = DIV_ROUND_UP(priv->cpll_hz, rate); + } else { + src_clk = ACLK_TOP_SEL_GPLL; + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate); + } + assert(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[9], + ACLK_TOP_DIV_MASK | + ACLK_TOP_SEL_MASK, + (src_clk << + ACLK_TOP_SEL_SHIFT) | + (src_clk_div - 1) << ACLK_TOP_SEL_SHIFT); + break; + case ACLK_TOP_MID: + if (!(priv->cpll_hz % rate)) { + src_clk = ACLK_TOP_MID_SEL_CPLL; + src_clk_div = DIV_ROUND_UP(priv->cpll_hz, rate); + } else { + src_clk = ACLK_TOP_MID_SEL_GPLL; + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate); + } + rk_clrsetreg(&cru->clksel_con[10], + ACLK_TOP_MID_DIV_MASK | + ACLK_TOP_MID_SEL_MASK, + (ACLK_TOP_MID_SEL_GPLL << + ACLK_TOP_MID_SEL_SHIFT) | + (src_clk_div - 1) << ACLK_TOP_MID_DIV_SHIFT); + break; + case PCLK_TOP_ROOT: + if (rate >= 99 * MHz) + src_clk = PCLK_TOP_SEL_100M; + else if (rate >= 50 * MHz) + src_clk = PCLK_TOP_SEL_50M; + else + src_clk = PCLK_TOP_SEL_OSC; + rk_clrsetreg(&cru->clksel_con[8], + PCLK_TOP_SEL_MASK, + src_clk << PCLK_TOP_SEL_SHIFT); + break; + case HCLK_TOP: + if (rate >= 198 * MHz) + src_clk = HCLK_TOP_SEL_200M; + else if (rate >= 99 * MHz) + src_clk = HCLK_TOP_SEL_100M; + else if (rate >= 50 * MHz) + src_clk = HCLK_TOP_SEL_50M; + else + src_clk = HCLK_TOP_SEL_OSC; + rk_clrsetreg(&cru->clksel_con[19], + HCLK_TOP_SEL_MASK, + src_clk << HCLK_TOP_SEL_SHIFT); + break; + default: + printf("do not support this top freq\n"); + return -EINVAL; + } + + return rk3576_top_get_clk(priv, clk_id); +} + +static ulong rk3576_i2c_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 sel, con; + ulong rate; + + switch (clk_id) { + case CLK_I2C0: + con = readl(&cru->pmuclksel_con[6]); + sel = (con & CLK_I2C0_SEL_MASK) >> CLK_I2C0_SEL_SHIFT; + break; + case CLK_I2C1: + con = readl(&cru->clksel_con[57]); + sel = (con & CLK_I2C1_SEL_MASK) >> CLK_I2C1_SEL_SHIFT; + break; + case CLK_I2C2: + con = readl(&cru->clksel_con[57]); + sel = (con & CLK_I2C2_SEL_MASK) >> CLK_I2C2_SEL_SHIFT; + break; + case CLK_I2C3: + con = readl(&cru->clksel_con[57]); + sel = (con & CLK_I2C3_SEL_MASK) >> CLK_I2C3_SEL_SHIFT; + break; + case CLK_I2C4: + con = readl(&cru->clksel_con[57]); + sel = (con & CLK_I2C4_SEL_MASK) >> CLK_I2C4_SEL_SHIFT; + break; + case CLK_I2C5: + con = readl(&cru->clksel_con[57]); + sel = (con & CLK_I2C5_SEL_MASK) >> CLK_I2C5_SEL_SHIFT; + break; + case CLK_I2C6: + con = readl(&cru->clksel_con[57]); + sel = (con & CLK_I2C6_SEL_MASK) >> CLK_I2C6_SEL_SHIFT; + break; + case CLK_I2C7: + con = readl(&cru->clksel_con[57]); + sel = (con & CLK_I2C7_SEL_MASK) >> CLK_I2C7_SEL_SHIFT; + break; + case CLK_I2C8: + con = readl(&cru->clksel_con[57]); + sel = (con & CLK_I2C8_SEL_MASK) >> CLK_I2C8_SEL_SHIFT; + break; + case CLK_I2C9: + con = readl(&cru->clksel_con[58]); + sel = (con & CLK_I2C9_SEL_MASK) >> CLK_I2C9_SEL_SHIFT; + break; + + default: + return -ENOENT; + } + if (sel == CLK_I2C_SEL_200M) + rate = 200 * MHz; + else if (sel == CLK_I2C_SEL_100M) + rate = 100 * MHz; + else if (sel == CLK_I2C_SEL_50M) + rate = 50 * MHz; + else + rate = OSC_HZ; + + return rate; +} + +static ulong rk3576_i2c_set_clk(struct rk3576_clk_priv *priv, ulong clk_id, + ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + int src_clk; + + if (rate >= 198 * MHz) + src_clk = CLK_I2C_SEL_200M; + else if (rate >= 99 * MHz) + src_clk = CLK_I2C_SEL_100M; + if (rate >= 50 * MHz) + src_clk = CLK_I2C_SEL_50M; + else + src_clk = CLK_I2C_SEL_OSC; + + switch (clk_id) { + case CLK_I2C0: + rk_clrsetreg(&cru->pmuclksel_con[6], CLK_I2C0_SEL_MASK, + src_clk << CLK_I2C0_SEL_SHIFT); + break; + case CLK_I2C1: + rk_clrsetreg(&cru->clksel_con[57], CLK_I2C1_SEL_MASK, + src_clk << CLK_I2C1_SEL_SHIFT); + break; + case CLK_I2C2: + rk_clrsetreg(&cru->clksel_con[57], CLK_I2C2_SEL_MASK, + src_clk << CLK_I2C2_SEL_SHIFT); + break; + case CLK_I2C3: + rk_clrsetreg(&cru->clksel_con[57], CLK_I2C3_SEL_MASK, + src_clk << CLK_I2C3_SEL_SHIFT); + break; + case CLK_I2C4: + rk_clrsetreg(&cru->clksel_con[57], CLK_I2C4_SEL_MASK, + src_clk << CLK_I2C4_SEL_SHIFT); + break; + case CLK_I2C5: + rk_clrsetreg(&cru->clksel_con[57], CLK_I2C5_SEL_MASK, + src_clk << CLK_I2C5_SEL_SHIFT); + break; + case CLK_I2C6: + rk_clrsetreg(&cru->clksel_con[57], CLK_I2C6_SEL_MASK, + src_clk << CLK_I2C6_SEL_SHIFT); + break; + case CLK_I2C7: + rk_clrsetreg(&cru->clksel_con[57], CLK_I2C7_SEL_MASK, + src_clk << CLK_I2C7_SEL_SHIFT); + break; + case CLK_I2C8: + rk_clrsetreg(&cru->clksel_con[57], CLK_I2C8_SEL_MASK, + src_clk << CLK_I2C8_SEL_SHIFT); + case CLK_I2C9: + rk_clrsetreg(&cru->clksel_con[58], CLK_I2C9_SEL_MASK, + src_clk << CLK_I2C9_SEL_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3576_i2c_get_clk(priv, clk_id); +} + +static ulong rk3576_spi_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 sel, con; + + switch (clk_id) { + case CLK_SPI0: + con = readl(&cru->clksel_con[70]); + sel = (con & CLK_SPI0_SEL_MASK) >> CLK_SPI0_SEL_SHIFT; + break; + case CLK_SPI1: + con = readl(&cru->clksel_con[71]); + sel = (con & CLK_SPI1_SEL_MASK) >> CLK_SPI1_SEL_SHIFT; + break; + case CLK_SPI2: + con = readl(&cru->clksel_con[71]); + sel = (con & CLK_SPI2_SEL_MASK) >> CLK_SPI2_SEL_SHIFT; + break; + case CLK_SPI3: + con = readl(&cru->clksel_con[71]); + sel = (con & CLK_SPI3_SEL_MASK) >> CLK_SPI3_SEL_SHIFT; + break; + case CLK_SPI4: + con = readl(&cru->clksel_con[71]); + sel = (con & CLK_SPI4_SEL_MASK) >> CLK_SPI4_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + switch (sel) { + case CLK_SPI_SEL_200M: + return 200 * MHz; + case CLK_SPI_SEL_100M: + return 100 * MHz; + case CLK_SPI_SEL_50M: + return 50 * MHz; + case CLK_SPI_SEL_OSC: + return OSC_HZ; + default: + return -ENOENT; + } +} + +static ulong rk3576_spi_set_clk(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + int src_clk; + + if (rate >= 198 * MHz) + src_clk = CLK_SPI_SEL_200M; + else if (rate >= 99 * MHz) + src_clk = CLK_SPI_SEL_100M; + else if (rate >= 50 * MHz) + src_clk = CLK_SPI_SEL_50M; + else + src_clk = CLK_SPI_SEL_OSC; + + switch (clk_id) { + case CLK_SPI0: + rk_clrsetreg(&cru->clksel_con[70], + CLK_SPI0_SEL_MASK, + src_clk << CLK_SPI0_SEL_SHIFT); + break; + case CLK_SPI1: + rk_clrsetreg(&cru->clksel_con[71], + CLK_SPI1_SEL_MASK, + src_clk << CLK_SPI1_SEL_SHIFT); + break; + case CLK_SPI2: + rk_clrsetreg(&cru->clksel_con[71], + CLK_SPI2_SEL_MASK, + src_clk << CLK_SPI2_SEL_SHIFT); + break; + case CLK_SPI3: + rk_clrsetreg(&cru->clksel_con[71], + CLK_SPI3_SEL_MASK, + src_clk << CLK_SPI3_SEL_SHIFT); + break; + case CLK_SPI4: + rk_clrsetreg(&cru->clksel_con[71], + CLK_SPI4_SEL_MASK, + src_clk << CLK_SPI4_SEL_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3576_spi_get_clk(priv, clk_id); +} + +static ulong rk3576_pwm_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 sel, con; + + switch (clk_id) { + case CLK_PWM1: + con = readl(&cru->clksel_con[71]); + sel = (con & CLK_PWM1_SEL_MASK) >> CLK_PWM1_SEL_SHIFT; + break; + case CLK_PWM2: + con = readl(&cru->clksel_con[74]); + sel = (con & CLK_PWM2_SEL_MASK) >> CLK_PWM2_SEL_SHIFT; + break; + case CLK_PMU1PWM: + con = readl(&cru->pmuclksel_con[5]); + sel = (con & CLK_PMU1PWM_SEL_MASK) >> CLK_PMU1PWM_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + switch (sel) { + case CLK_PWM_SEL_100M: + return 100 * MHz; + case CLK_PWM_SEL_50M: + return 50 * MHz; + case CLK_PWM_SEL_OSC: + return OSC_HZ; + default: + return -ENOENT; + } +} + +static ulong rk3576_pwm_set_clk(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + int src_clk; + + if (rate >= 99 * MHz) + src_clk = CLK_PWM_SEL_100M; + else if (rate >= 50 * MHz) + src_clk = CLK_PWM_SEL_50M; + else + src_clk = CLK_PWM_SEL_OSC; + + switch (clk_id) { + case CLK_PWM1: + rk_clrsetreg(&cru->clksel_con[71], + CLK_PWM1_SEL_MASK, + src_clk << CLK_PWM1_SEL_SHIFT); + break; + case CLK_PWM2: + rk_clrsetreg(&cru->clksel_con[74], + CLK_PWM2_SEL_MASK, + src_clk << CLK_PWM2_SEL_SHIFT); + break; + case CLK_PMU1PWM: + rk_clrsetreg(&cru->pmuclksel_con[5], + CLK_PMU1PWM_SEL_MASK, + src_clk << CLK_PMU1PWM_SEL_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3576_pwm_get_clk(priv, clk_id); +} + +static ulong rk3576_adc_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 div, sel, con, prate; + + switch (clk_id) { + case CLK_SARADC: + con = readl(&cru->clksel_con[58]); + div = (con & CLK_SARADC_DIV_MASK) >> CLK_SARADC_DIV_SHIFT; + sel = (con & CLK_SARADC_SEL_MASK) >> + CLK_SARADC_SEL_SHIFT; + if (sel == CLK_SARADC_SEL_OSC) + prate = OSC_HZ; + else + prate = priv->gpll_hz; + return DIV_TO_RATE(prate, div); + case CLK_TSADC: + con = readl(&cru->clksel_con[59]); + div = (con & CLK_TSADC_DIV_MASK) >> + CLK_TSADC_DIV_SHIFT; + prate = OSC_HZ; + return DIV_TO_RATE(prate, div); + default: + return -ENOENT; + } +} + +static ulong rk3576_adc_set_clk(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + int src_clk_div; + + switch (clk_id) { + case CLK_SARADC: + if (!(OSC_HZ % rate)) { + src_clk_div = DIV_ROUND_UP(OSC_HZ, rate); + assert(src_clk_div - 1 <= 255); + rk_clrsetreg(&cru->clksel_con[58], + CLK_SARADC_SEL_MASK | + CLK_SARADC_DIV_MASK, + (CLK_SARADC_SEL_OSC << + CLK_SARADC_SEL_SHIFT) | + (src_clk_div - 1) << + CLK_SARADC_DIV_SHIFT); + } else { + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate); + assert(src_clk_div - 1 <= 255); + rk_clrsetreg(&cru->clksel_con[59], + CLK_SARADC_SEL_MASK | + CLK_SARADC_DIV_MASK, + (CLK_SARADC_SEL_GPLL << + CLK_SARADC_SEL_SHIFT) | + (src_clk_div - 1) << + CLK_SARADC_DIV_SHIFT); + } + break; + case CLK_TSADC: + src_clk_div = DIV_ROUND_UP(OSC_HZ, rate); + assert(src_clk_div - 1 <= 255); + rk_clrsetreg(&cru->clksel_con[58], + CLK_TSADC_DIV_MASK, + (src_clk_div - 1) << + CLK_TSADC_DIV_SHIFT); + break; + default: + return -ENOENT; + } + return rk3576_adc_get_clk(priv, clk_id); +} + +static ulong rk3576_mmc_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 sel, con, prate, div = 0; + + switch (clk_id) { + case CCLK_SRC_SDIO: + case HCLK_SDIO: + con = readl(&cru->clksel_con[104]); + div = (con & CCLK_SDIO_SRC_DIV_MASK) >> CCLK_SDIO_SRC_DIV_SHIFT; + sel = (con & CCLK_SDIO_SRC_SEL_MASK) >> + CCLK_SDIO_SRC_SEL_SHIFT; + if (sel == CCLK_SDIO_SRC_SEL_GPLL) + prate = priv->gpll_hz; + else if (sel == CCLK_SDIO_SRC_SEL_CPLL) + prate = priv->cpll_hz; + else + prate = OSC_HZ; + return DIV_TO_RATE(prate, div); + case CCLK_SRC_SDMMC0: + case HCLK_SDMMC0: + con = readl(&cru->clksel_con[105]); + div = (con & CCLK_SDMMC0_SRC_DIV_MASK) >> CCLK_SDMMC0_SRC_DIV_SHIFT; + sel = (con & CCLK_SDMMC0_SRC_SEL_MASK) >> + CCLK_SDMMC0_SRC_SEL_SHIFT; + if (sel == CCLK_SDMMC0_SRC_SEL_GPLL) + prate = priv->gpll_hz; + else if (sel == CCLK_SDMMC0_SRC_SEL_CPLL) + prate = priv->cpll_hz; + else + prate = OSC_HZ; + return DIV_TO_RATE(prate, div); + case CCLK_SRC_EMMC: + case HCLK_EMMC: + con = readl(&cru->clksel_con[89]); + div = (con & CCLK_EMMC_DIV_MASK) >> CCLK_EMMC_DIV_SHIFT; + sel = (con & CCLK_EMMC_SEL_MASK) >> + CCLK_EMMC_SEL_SHIFT; + if (sel == CCLK_EMMC_SEL_GPLL) + prate = priv->gpll_hz; + else if (sel == CCLK_EMMC_SEL_CPLL) + prate = priv->cpll_hz; + else + prate = OSC_HZ; + return DIV_TO_RATE(prate, div); + case BCLK_EMMC: + con = readl(&cru->clksel_con[90]); + sel = (con & BCLK_EMMC_SEL_MASK) >> + BCLK_EMMC_SEL_SHIFT; + if (sel == BCLK_EMMC_SEL_200M) + prate = 200 * MHz; + else if (sel == BCLK_EMMC_SEL_100M) + prate = 100 * MHz; + else if (sel == BCLK_EMMC_SEL_50M) + prate = 50 * MHz; + else + prate = OSC_HZ; + return DIV_TO_RATE(prate, div); + case SCLK_FSPI_X2: + con = readl(&cru->clksel_con[89]); + div = (con & SCLK_FSPI_DIV_MASK) >> SCLK_FSPI_DIV_SHIFT; + sel = (con & SCLK_FSPI_SEL_MASK) >> + SCLK_FSPI_SEL_SHIFT; + if (sel == SCLK_FSPI_SEL_GPLL) + prate = priv->gpll_hz; + else if (sel == SCLK_FSPI_SEL_CPLL) + prate = priv->cpll_hz; + else + prate = OSC_HZ; + return DIV_TO_RATE(prate, div); + case SCLK_FSPI1_X2: + con = readl(&cru->clksel_con[106]); + div = (con & SCLK_FSPI_DIV_MASK) >> SCLK_FSPI_DIV_SHIFT; + sel = (con & SCLK_FSPI_SEL_MASK) >> + SCLK_FSPI_SEL_SHIFT; + if (sel == SCLK_FSPI_SEL_GPLL) + prate = priv->gpll_hz; + else if (sel == SCLK_FSPI_SEL_CPLL) + prate = priv->cpll_hz; + else + prate = OSC_HZ; + return DIV_TO_RATE(prate, div); + case DCLK_DECOM: + con = readl(&cru->clksel_con[72]); + div = (con & DCLK_DECOM_DIV_MASK) >> DCLK_DECOM_DIV_SHIFT; + sel = (con & DCLK_DECOM_SEL_MASK) >> DCLK_DECOM_SEL_SHIFT; + if (sel == DCLK_DECOM_SEL_SPLL) + prate = priv->spll_hz; + else + prate = priv->gpll_hz; + return DIV_TO_RATE(prate, div); + + default: + return -ENOENT; + } +} + +static ulong rk3576_mmc_set_clk(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + int src_clk, div = 0; + + switch (clk_id) { + case CCLK_SRC_SDIO: + case CCLK_SRC_SDMMC0: + case CCLK_SRC_EMMC: + case SCLK_FSPI_X2: + case SCLK_FSPI1_X2: + case HCLK_SDMMC0: + case HCLK_EMMC: + case HCLK_SDIO: + if (!(OSC_HZ % rate)) { + src_clk = SCLK_FSPI_SEL_OSC; + div = DIV_ROUND_UP(OSC_HZ, rate); + } else if (!(priv->cpll_hz % rate)) { + src_clk = SCLK_FSPI_SEL_CPLL; + div = DIV_ROUND_UP(priv->cpll_hz, rate); + } else { + src_clk = SCLK_FSPI_SEL_GPLL; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } + break; + case BCLK_EMMC: + if (rate >= 198 * MHz) + src_clk = BCLK_EMMC_SEL_200M; + else if (rate >= 99 * MHz) + src_clk = BCLK_EMMC_SEL_100M; + else if (rate >= 50 * MHz) + src_clk = BCLK_EMMC_SEL_50M; + else + src_clk = BCLK_EMMC_SEL_OSC; + break; + case DCLK_DECOM: + if (!(priv->spll_hz % rate)) { + src_clk = DCLK_DECOM_SEL_SPLL; + div = DIV_ROUND_UP(priv->spll_hz, rate); + } else { + src_clk = DCLK_DECOM_SEL_GPLL; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } + break; + default: + return -ENOENT; + } + + switch (clk_id) { + case CCLK_SRC_SDIO: + case HCLK_SDIO: + rk_clrsetreg(&cru->clksel_con[104], + CCLK_SDIO_SRC_SEL_MASK | + CCLK_SDIO_SRC_DIV_MASK, + (src_clk << CCLK_SDIO_SRC_SEL_SHIFT) | + (div - 1) << CCLK_SDIO_SRC_DIV_SHIFT); + break; + case CCLK_SRC_SDMMC0: + case HCLK_SDMMC0: + rk_clrsetreg(&cru->clksel_con[105], + CCLK_SDMMC0_SRC_SEL_MASK | + CCLK_SDMMC0_SRC_DIV_MASK, + (src_clk << CCLK_SDMMC0_SRC_SEL_SHIFT) | + (div - 1) << CCLK_SDMMC0_SRC_DIV_SHIFT); + break; + case CCLK_SRC_EMMC: + case HCLK_EMMC: + rk_clrsetreg(&cru->clksel_con[89], + CCLK_EMMC_DIV_MASK | + CCLK_EMMC_SEL_MASK, + (src_clk << CCLK_EMMC_SEL_SHIFT) | + (div - 1) << CCLK_EMMC_DIV_SHIFT); + break; + case SCLK_FSPI_X2: + rk_clrsetreg(&cru->clksel_con[89], + SCLK_FSPI_DIV_MASK | + SCLK_FSPI_SEL_MASK, + (src_clk << SCLK_FSPI_SEL_SHIFT) | + (div - 1) << SCLK_FSPI_DIV_SHIFT); + break; + case SCLK_FSPI1_X2: + rk_clrsetreg(&cru->clksel_con[106], + SCLK_FSPI_DIV_MASK | + SCLK_FSPI_SEL_MASK, + (src_clk << SCLK_FSPI_SEL_SHIFT) | + (div - 1) << SCLK_FSPI_DIV_SHIFT); + break; + case BCLK_EMMC: + rk_clrsetreg(&cru->clksel_con[90], + BCLK_EMMC_SEL_MASK, + src_clk << BCLK_EMMC_SEL_SHIFT); + break; + case DCLK_DECOM: + rk_clrsetreg(&cru->clksel_con[72], + DCLK_DECOM_DIV_MASK | + DCLK_DECOM_SEL_MASK, + (src_clk << DCLK_DECOM_SEL_SHIFT) | + (div - 1) << DCLK_DECOM_DIV_SHIFT); + break; + + default: + return -ENOENT; + } + + return rk3576_mmc_get_clk(priv, clk_id); +} + +#ifndef CONFIG_SPL_BUILD + +static ulong rk3576_aclk_vop_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 div, sel, con, parent = 0; + + switch (clk_id) { + case ACLK_VOP_ROOT: + case ACLK_VOP: + con = readl(&cru->clksel_con[144]); + div = (con & ACLK_VOP_ROOT_DIV_MASK) >> ACLK_VOP_ROOT_DIV_SHIFT; + sel = (con & ACLK_VOP_ROOT_SEL_MASK) >> ACLK_VOP_ROOT_SEL_SHIFT; + if (sel == ACLK_VOP_ROOT_SEL_GPLL) + parent = priv->gpll_hz; + else if (sel == ACLK_VOP_ROOT_SEL_CPLL) + parent = priv->cpll_hz; + else if (sel == ACLK_VOP_ROOT_SEL_AUPLL) + parent = priv->aupll_hz; + else if (sel == ACLK_VOP_ROOT_SEL_SPLL) + parent = priv->spll_hz; + else if (sel == ACLK_VOP_ROOT_SEL_LPLL) + parent = priv->lpll_hz / 2; + return DIV_TO_RATE(parent, div); + case ACLK_VO0_ROOT: + con = readl(&cru->clksel_con[149]); + div = (con & ACLK_VO0_ROOT_DIV_MASK) >> ACLK_VO0_ROOT_DIV_SHIFT; + sel = (con & ACLK_VO0_ROOT_SEL_MASK) >> ACLK_VO0_ROOT_SEL_SHIFT; + if (sel == ACLK_VO0_ROOT_SEL_GPLL) + parent = priv->gpll_hz; + else if (sel == ACLK_VO0_ROOT_SEL_CPLL) + parent = priv->cpll_hz; + else if (sel == ACLK_VO0_ROOT_SEL_LPLL) + parent = priv->lpll_hz / 2; + else if (sel == ACLK_VO0_ROOT_SEL_BPLL) + parent = priv->bpll_hz / 4; + return DIV_TO_RATE(parent, div); + case ACLK_VO1_ROOT: + con = readl(&cru->clksel_con[158]); + div = (con & ACLK_VO0_ROOT_DIV_MASK) >> ACLK_VO0_ROOT_DIV_SHIFT; + sel = (con & ACLK_VO0_ROOT_SEL_MASK) >> ACLK_VO0_ROOT_SEL_SHIFT; + if (sel == ACLK_VO0_ROOT_SEL_GPLL) + parent = priv->gpll_hz; + else if (sel == ACLK_VO0_ROOT_SEL_CPLL) + parent = priv->cpll_hz; + else if (sel == ACLK_VO0_ROOT_SEL_LPLL) + parent = priv->lpll_hz / 2; + else if (sel == ACLK_VO0_ROOT_SEL_BPLL) + parent = priv->bpll_hz / 4; + return DIV_TO_RATE(parent, div); + case HCLK_VOP_ROOT: + con = readl(&cru->clksel_con[144]); + sel = (con & HCLK_VOP_ROOT_SEL_MASK) >> HCLK_VOP_ROOT_SEL_SHIFT; + if (sel == HCLK_VOP_ROOT_SEL_200M) + return 200 * MHz; + else if (sel == HCLK_VOP_ROOT_SEL_100M) + return 100 * MHz; + else if (sel == HCLK_VOP_ROOT_SEL_50M) + return 50 * MHz; + else + return OSC_HZ; + case PCLK_VOP_ROOT: + con = readl(&cru->clksel_con[144]); + sel = (con & PCLK_VOP_ROOT_SEL_MASK) >> PCLK_VOP_ROOT_SEL_SHIFT; + if (sel == PCLK_VOP_ROOT_SEL_100M) + return 100 * MHz; + else if (sel == PCLK_VOP_ROOT_SEL_50M) + return 50 * MHz; + else + return OSC_HZ; + + default: + return -ENOENT; + } +} + +static ulong rk3576_aclk_vop_set_clk(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + int src_clk, div; + + switch (clk_id) { + case ACLK_VOP_ROOT: + case ACLK_VOP: + if (rate == 700 * MHz) { + src_clk = ACLK_VOP_ROOT_SEL_SPLL; + div = 1; + } else if (!(priv->cpll_hz % rate)) { + src_clk = ACLK_VOP_ROOT_SEL_CPLL; + div = DIV_ROUND_UP(priv->cpll_hz, rate); + } else { + src_clk = ACLK_VOP_ROOT_SEL_GPLL; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } + rk_clrsetreg(&cru->clksel_con[144], + ACLK_VOP_ROOT_DIV_MASK | + ACLK_VOP_ROOT_SEL_MASK, + (src_clk << ACLK_VOP_ROOT_SEL_SHIFT) | + (div - 1) << ACLK_VOP_ROOT_DIV_SHIFT); + break; + case ACLK_VO0_ROOT: + if (!(priv->cpll_hz % rate)) { + src_clk = ACLK_VO0_ROOT_SEL_CPLL; + div = DIV_ROUND_UP(priv->cpll_hz, rate); + } else { + src_clk = ACLK_VO0_ROOT_SEL_GPLL; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } + rk_clrsetreg(&cru->clksel_con[149], + ACLK_VO0_ROOT_DIV_MASK | + ACLK_VO0_ROOT_SEL_MASK, + (src_clk << ACLK_VO0_ROOT_SEL_SHIFT) | + (div - 1) << ACLK_VO0_ROOT_DIV_SHIFT); + break; + case ACLK_VO1_ROOT: + if (!(priv->cpll_hz % rate)) { + src_clk = ACLK_VO0_ROOT_SEL_CPLL; + div = DIV_ROUND_UP(priv->cpll_hz, rate); + } else { + src_clk = ACLK_VO0_ROOT_SEL_GPLL; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } + rk_clrsetreg(&cru->clksel_con[158], + ACLK_VO0_ROOT_DIV_MASK | + ACLK_VO0_ROOT_SEL_MASK, + (src_clk << ACLK_VO0_ROOT_SEL_SHIFT) | + (div - 1) << ACLK_VO0_ROOT_DIV_SHIFT); + break; + case HCLK_VOP_ROOT: + if (rate == 200 * MHz) + src_clk = HCLK_VOP_ROOT_SEL_200M; + else if (rate == 100 * MHz) + src_clk = HCLK_VOP_ROOT_SEL_100M; + else if (rate == 50 * MHz) + src_clk = HCLK_VOP_ROOT_SEL_50M; + else + src_clk = HCLK_VOP_ROOT_SEL_OSC; + rk_clrsetreg(&cru->clksel_con[144], + HCLK_VOP_ROOT_SEL_MASK, + src_clk << HCLK_VOP_ROOT_SEL_SHIFT); + break; + case PCLK_VOP_ROOT: + if (rate == 100 * MHz) + src_clk = PCLK_VOP_ROOT_SEL_100M; + else if (rate == 50 * MHz) + src_clk = PCLK_VOP_ROOT_SEL_50M; + else + src_clk = PCLK_VOP_ROOT_SEL_OSC; + rk_clrsetreg(&cru->clksel_con[144], + PCLK_VOP_ROOT_SEL_MASK, + src_clk << PCLK_VOP_ROOT_SEL_SHIFT); + break; + + default: + return -ENOENT; + } + + return rk3576_aclk_vop_get_clk(priv, clk_id); +} + +static ulong rk3576_dclk_vop_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 div, sel, con, parent; + + switch (clk_id) { + case DCLK_VP0: + case DCLK_VP0_SRC: + con = readl(&cru->clksel_con[145]); + div = (con & DCLK0_VOP_SRC_DIV_MASK) >> DCLK0_VOP_SRC_DIV_SHIFT; + sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT; + break; + case DCLK_VP1: + case DCLK_VP1_SRC: + con = readl(&cru->clksel_con[146]); + div = (con & DCLK0_VOP_SRC_DIV_MASK) >> DCLK0_VOP_SRC_DIV_SHIFT; + sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT; + break; + case DCLK_VP2: + case DCLK_VP2_SRC: + con = readl(&cru->clksel_con[147]); + div = (con & DCLK0_VOP_SRC_DIV_MASK) >> DCLK0_VOP_SRC_DIV_SHIFT; + sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + if (sel == DCLK_VOP_SRC_SEL_VPLL) + parent = priv->vpll_hz; + else if (sel == DCLK_VOP_SRC_SEL_BPLL) + parent = priv->bpll_hz / 4; + else if (sel == DCLK_VOP_SRC_SEL_LPLL) + parent = priv->lpll_hz / 2; + else if (sel == DCLK_VOP_SRC_SEL_GPLL) + parent = priv->gpll_hz; + else + parent = priv->cpll_hz; + + return DIV_TO_RATE(parent, div); +} + +#define RK3576_VOP_PLL_LIMIT_FREQ 600000000 + +static ulong rk3576_dclk_vop_set_clk(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + ulong pll_rate, now, best_rate = 0; + u32 i, conid, con, sel, div, best_div = 0, best_sel = 0; + u32 mask, div_shift, sel_shift; + + switch (clk_id) { + case DCLK_VP0: + case DCLK_VP0_SRC: + conid = 145; + con = readl(&cru->clksel_con[conid]); + sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT; + mask = DCLK0_VOP_SRC_SEL_MASK | DCLK0_VOP_SRC_DIV_MASK; + div_shift = DCLK0_VOP_SRC_DIV_SHIFT; + sel_shift = DCLK0_VOP_SRC_SEL_SHIFT; + break; + case DCLK_VP1: + case DCLK_VP1_SRC: + conid = 146; + con = readl(&cru->clksel_con[conid]); + sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT; + mask = DCLK0_VOP_SRC_SEL_MASK | DCLK0_VOP_SRC_DIV_MASK; + div_shift = DCLK0_VOP_SRC_DIV_SHIFT; + sel_shift = DCLK0_VOP_SRC_SEL_SHIFT; + break; + case DCLK_VP2: + case DCLK_VP2_SRC: + conid = 147; + con = readl(&cru->clksel_con[conid]); + sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT; + mask = DCLK0_VOP_SRC_SEL_MASK | DCLK0_VOP_SRC_DIV_MASK; + div_shift = DCLK0_VOP_SRC_DIV_SHIFT; + sel_shift = DCLK0_VOP_SRC_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + if (sel == DCLK_VOP_SRC_SEL_VPLL) { + pll_rate = rockchip_pll_get_rate(&rk3576_pll_clks[VPLL], + priv->cru, VPLL); + if (pll_rate >= RK3576_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_VPLL << sel_shift | + ((div - 1) << div_shift)); + } else { + div = DIV_ROUND_UP(RK3576_VOP_PLL_LIMIT_FREQ, rate); + if (div % 2) + div = div + 1; + rk_clrsetreg(&cru->clksel_con[conid], + mask, + DCLK_VOP_SRC_SEL_VPLL << sel_shift | + ((div - 1) << div_shift)); + rockchip_pll_set_rate(&rk3576_pll_clks[VPLL], + priv->cru, VPLL, div * rate); + priv->vpll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[VPLL], + priv->cru, VPLL); + } + } else { + for (i = 0; i <= DCLK_VOP_SRC_SEL_LPLL; i++) { + switch (i) { + case DCLK_VOP_SRC_SEL_GPLL: + pll_rate = priv->gpll_hz; + break; + case DCLK_VOP_SRC_SEL_CPLL: + pll_rate = priv->cpll_hz; + break; + case DCLK_VOP_SRC_SEL_BPLL: + pll_rate = 0; + break; + case DCLK_VOP_SRC_SEL_LPLL: + pll_rate = 0; + break; + case DCLK_VOP_SRC_SEL_VPLL: + pll_rate = 0; + break; + default: + printf("do not support this vop pll sel\n"); + return -EINVAL; + } + + div = DIV_ROUND_UP(pll_rate, rate); + if (div > 255) + continue; + now = pll_rate / div; + if (abs(rate - now) < abs(rate - best_rate)) { + best_rate = now; + best_div = div; + best_sel = i; + } + debug("p_rate=%lu, best_rate=%lu, div=%u, sel=%u\n", + pll_rate, best_rate, best_div, best_sel); + } + + if (best_rate) { + rk_clrsetreg(&cru->clksel_con[conid], + mask, + best_sel << sel_shift | + (best_div - 1) << div_shift); + } else { + printf("do not support this vop freq %lu\n", rate); + return -EINVAL; + } + } + + return rk3576_dclk_vop_get_clk(priv, clk_id); +} + +static ulong rk3576_clk_csihost_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 div, sel, con, parent; + + switch (clk_id) { + case CLK_DSIHOST0: + con = readl(&cru->clksel_con[151]); + div = (con & CLK_DSIHOST0_DIV_MASK) >> CLK_DSIHOST0_DIV_SHIFT; + sel = (con & CLK_DSIHOST0_SEL_MASK) >> CLK_DSIHOST0_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + if (sel == CLK_DSIHOST0_SEL_VPLL) + parent = priv->vpll_hz; + else if (sel == CLK_DSIHOST0_SEL_BPLL) + parent = priv->bpll_hz / 4; + else if (sel == CLK_DSIHOST0_SEL_LPLL) + parent = priv->lpll_hz / 2; + else if (sel == CLK_DSIHOST0_SEL_GPLL) + parent = priv->gpll_hz; + else if (sel == CLK_DSIHOST0_SEL_SPLL) + parent = priv->spll_hz; + else + parent = priv->cpll_hz; + + return DIV_TO_RATE(parent, div); +} + +static ulong rk3576_clk_csihost_set_clk(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + ulong pll_rate, now, best_rate = 0; + u32 i, con, div, best_div = 0, best_sel = 0; + u32 mask, div_shift, sel_shift; + + switch (clk_id) { + case CLK_DSIHOST0: + con = 151; + mask = CLK_DSIHOST0_SEL_MASK | CLK_DSIHOST0_DIV_MASK; + div_shift = CLK_DSIHOST0_DIV_SHIFT; + sel_shift = CLK_DSIHOST0_SEL_SHIFT; + break; + default: + return -ENOENT; + } + for (i = 0; i <= CLK_DSIHOST0_SEL_LPLL; i++) { + switch (i) { + case CLK_DSIHOST0_SEL_GPLL: + pll_rate = priv->gpll_hz; + break; + case CLK_DSIHOST0_SEL_CPLL: + pll_rate = priv->cpll_hz; + break; + case CLK_DSIHOST0_SEL_BPLL: + pll_rate = 0; + break; + case CLK_DSIHOST0_SEL_LPLL: + pll_rate = 0; + break; + case CLK_DSIHOST0_SEL_VPLL: + pll_rate = 0; + break; + case CLK_DSIHOST0_SEL_SPLL: + pll_rate = priv->spll_hz; + break; + default: + printf("do not support this vop pll sel\n"); + return -EINVAL; + } + + div = DIV_ROUND_UP(pll_rate, rate); + if (div > 255) + continue; + now = pll_rate / div; + if (abs(rate - now) < abs(rate - best_rate)) { + best_rate = now; + best_div = div; + best_sel = i; + } + debug("p_rate=%lu, best_rate=%lu, div=%u, sel=%u\n", + pll_rate, best_rate, best_div, best_sel); + } + + if (best_rate) { + rk_clrsetreg(&cru->clksel_con[con], + mask, + best_sel << sel_shift | + (best_div - 1) << div_shift); + } else { + printf("do not support this vop freq %lu\n", rate); + return -EINVAL; + } + + return rk3576_clk_csihost_get_clk(priv, clk_id); +} + +static ulong rk3576_dclk_ebc_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 div, sel, con, parent; + unsigned long m = 0, n = 0; + + switch (clk_id) { + case DCLK_EBC: + con = readl(&cru->clksel_con[123]); + div = (con & DCLK_EBC_DIV_MASK) >> DCLK_EBC_DIV_SHIFT; + sel = (con & DCLK_EBC_SEL_MASK) >> DCLK_EBC_SEL_SHIFT; + if (sel == DCLK_EBC_SEL_CPLL) + parent = priv->cpll_hz; + else if (sel == DCLK_EBC_SEL_VPLL) + parent = priv->vpll_hz; + else if (sel == DCLK_EBC_SEL_AUPLL) + parent = priv->aupll_hz; + else if (sel == DCLK_EBC_SEL_LPLL) + parent = priv->lpll_hz / 2; + else if (sel == DCLK_EBC_SEL_GPLL) + parent = priv->gpll_hz; + else if (sel == DCLK_EBC_SEL_FRAC_SRC) + parent = rk3576_dclk_ebc_get_clk(priv, DCLK_EBC_FRAC_SRC); + else + parent = OSC_HZ; + return DIV_TO_RATE(parent, div); + case DCLK_EBC_FRAC_SRC: + con = readl(&cru->clksel_con[123]); + div = readl(&cru->clksel_con[122]); + sel = (con & DCLK_EBC_FRAC_SRC_SEL_MASK) >> DCLK_EBC_FRAC_SRC_SEL_SHIFT; + if (sel == DCLK_EBC_FRAC_SRC_SEL_GPLL) + parent = priv->gpll_hz; + else if (sel == DCLK_EBC_FRAC_SRC_SEL_CPLL) + parent = priv->cpll_hz; + else if (sel == DCLK_EBC_FRAC_SRC_SEL_VPLL) + parent = priv->vpll_hz; + else if (sel == DCLK_EBC_FRAC_SRC_SEL_AUPLL) + parent = priv->aupll_hz; + else + parent = OSC_HZ; + + n = div & CLK_UART_FRAC_NUMERATOR_MASK; + n >>= CLK_UART_FRAC_NUMERATOR_SHIFT; + m = div & CLK_UART_FRAC_DENOMINATOR_MASK; + m >>= CLK_UART_FRAC_DENOMINATOR_SHIFT; + return parent * n / m; + default: + return -ENOENT; + } +} + +static ulong rk3576_dclk_ebc_set_clk(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + ulong pll_rate, now, best_rate = 0; + u32 i, con, sel, div, best_div = 0, best_sel = 0; + unsigned long m = 0, n = 0, val; + + switch (clk_id) { + case DCLK_EBC: + con = readl(&cru->clksel_con[123]); + sel = (con & DCLK_EBC_SEL_MASK) >> DCLK_EBC_SEL_SHIFT; + if (sel == DCLK_EBC_SEL_VPLL) { + pll_rate = rockchip_pll_get_rate(&rk3576_pll_clks[VPLL], + priv->cru, VPLL); + if (pll_rate >= RK3576_VOP_PLL_LIMIT_FREQ && + pll_rate % rate == 0) { + div = DIV_ROUND_UP(pll_rate, rate); + rk_clrsetreg(&cru->clksel_con[123], + DCLK_EBC_DIV_MASK, + (div - 1) << DCLK_EBC_DIV_SHIFT); + } else { + div = DIV_ROUND_UP(RK3576_VOP_PLL_LIMIT_FREQ, + rate); + if (div % 2) + div = div + 1; + rk_clrsetreg(&cru->clksel_con[123], + DCLK_EBC_DIV_MASK, + (div - 1) << DCLK_EBC_DIV_SHIFT); + rockchip_pll_set_rate(&rk3576_pll_clks[VPLL], + priv->cru, + VPLL, div * rate); + priv->vpll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[VPLL], + priv->cru, + VPLL); + } + } else if (sel == DCLK_EBC_SEL_FRAC_SRC) { + rk3576_dclk_ebc_set_clk(priv, DCLK_EBC_FRAC_SRC, rate); + div = rk3576_dclk_ebc_get_clk(priv, DCLK_EBC_FRAC_SRC) / rate; + rk_clrsetreg(&cru->clksel_con[123], + DCLK_EBC_DIV_MASK, + (div - 1) << DCLK_EBC_DIV_SHIFT); + } else { + for (i = 0; i <= DCLK_EBC_SEL_LPLL; i++) { + switch (i) { + case DCLK_EBC_SEL_GPLL: + pll_rate = priv->gpll_hz; + break; + case DCLK_EBC_SEL_CPLL: + pll_rate = priv->cpll_hz; + break; + case DCLK_EBC_SEL_VPLL: + pll_rate = 0; + break; + case DCLK_EBC_SEL_AUPLL: + pll_rate = priv->aupll_hz; + break; + case DCLK_EBC_SEL_LPLL: + pll_rate = 0; + break; + default: + printf("not support ebc pll sel\n"); + return -EINVAL; + } + + div = DIV_ROUND_UP(pll_rate, rate); + if (div > 255) + continue; + now = pll_rate / div; + if (abs(rate - now) < abs(rate - best_rate)) { + best_rate = now; + best_div = div; + best_sel = i; + } + } + + if (best_rate) { + rk_clrsetreg(&cru->clksel_con[123], + DCLK_EBC_DIV_MASK | + DCLK_EBC_SEL_MASK, + best_sel << + DCLK_EBC_SEL_SHIFT | + (best_div - 1) << + DCLK_EBC_DIV_SHIFT); + } else { + printf("do not support this vop freq %lu\n", + rate); + return -EINVAL; + } + } + break; + case DCLK_EBC_FRAC_SRC: + sel = DCLK_EBC_FRAC_SRC_SEL_GPLL; + div = 1; + rational_best_approximation(rate, priv->gpll_hz, + GENMASK(16 - 1, 0), + GENMASK(16 - 1, 0), + &m, &n); + + if (m < 4 && m != 0) { + if (n % 2 == 0) + val = 1; + else + val = DIV_ROUND_UP(4, m); + + n *= val; + m *= val; + if (n > 0xffff) + n = 0xffff; + } + + rk_clrsetreg(&cru->clksel_con[123], + DCLK_EBC_FRAC_SRC_SEL_MASK, + (sel << DCLK_EBC_FRAC_SRC_SEL_SHIFT)); + if (m && n) { + val = m << CLK_UART_FRAC_NUMERATOR_SHIFT | n; + writel(val, &cru->clksel_con[122]); + } + break; + default: + return -ENOENT; + } + return rk3576_dclk_ebc_get_clk(priv, clk_id); +} + +static ulong rk3576_gmac_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 con, div, src, p_rate; + + switch (clk_id) { + case CLK_GMAC0_PTP_REF_SRC: + case CLK_GMAC0_PTP_REF: + con = readl(&cru->clksel_con[105]); + div = (con & CLK_GMAC0_PTP_DIV_MASK) >> CLK_GMAC0_PTP_DIV_SHIFT; + src = (con & CLK_GMAC0_PTP_SEL_MASK) >> CLK_GMAC0_PTP_SEL_SHIFT; + if (src == CLK_GMAC0_PTP_SEL_GPLL) + p_rate = priv->gpll_hz; + else if (src == CLK_GMAC0_PTP_SEL_CPLL) + p_rate = priv->cpll_hz; + else + p_rate = GMAC0_PTP_REFCLK_IN; + return DIV_TO_RATE(p_rate, div); + case CLK_GMAC1_PTP_REF_SRC: + case CLK_GMAC1_PTP_REF: + con = readl(&cru->clksel_con[104]); + div = (con & CLK_GMAC1_PTP_DIV_MASK) >> CLK_GMAC0_PTP_DIV_SHIFT; + src = (con & CLK_GMAC1_PTP_SEL_MASK) >> CLK_GMAC1_PTP_SEL_SHIFT; + if (src == CLK_GMAC1_PTP_SEL_GPLL) + p_rate = priv->gpll_hz; + else if (src == CLK_GMAC1_PTP_SEL_CPLL) + p_rate = priv->cpll_hz; + else + p_rate = GMAC1_PTP_REFCLK_IN; + return DIV_TO_RATE(p_rate, div); + case CLK_GMAC0_125M_SRC: + con = readl(&cru->clksel_con[30]); + div = (con & CLK_GMAC0_125M_DIV_MASK) >> CLK_GMAC0_125M_DIV_SHIFT; + return DIV_TO_RATE(priv->cpll_hz, div); + case CLK_GMAC1_125M_SRC: + con = readl(&cru->clksel_con[31]); + div = (con & CLK_GMAC1_125M_DIV_MASK) >> CLK_GMAC1_125M_DIV_SHIFT; + return DIV_TO_RATE(priv->cpll_hz, div); + default: + return -ENOENT; + } +} + +static ulong rk3576_gmac_set_clk(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + int div, src; + + div = DIV_ROUND_UP(priv->cpll_hz, rate); + + switch (clk_id) { + case CLK_GMAC0_PTP_REF_SRC: + case CLK_GMAC0_PTP_REF: + if (rate == GMAC0_PTP_REFCLK_IN) { + src = CLK_GMAC0_PTP_SEL_REFIN; + div = 1; + } else if (!(priv->gpll_hz % rate)) { + src = CLK_GMAC0_PTP_SEL_GPLL; + div = priv->gpll_hz / rate; + } else { + src = CLK_GMAC0_PTP_SEL_CPLL; + div = priv->cpll_hz / rate; + } + rk_clrsetreg(&cru->clksel_con[105], + CLK_GMAC0_PTP_DIV_MASK | CLK_GMAC0_PTP_SEL_MASK, + src << CLK_GMAC0_PTP_SEL_SHIFT | + (div - 1) << CLK_GMAC0_PTP_DIV_SHIFT); + break; + case CLK_GMAC1_PTP_REF_SRC: + case CLK_GMAC1_PTP_REF: + if (rate == GMAC1_PTP_REFCLK_IN) { + src = CLK_GMAC1_PTP_SEL_REFIN; + div = 1; + } else if (!(priv->gpll_hz % rate)) { + src = CLK_GMAC1_PTP_SEL_GPLL; + div = priv->gpll_hz / rate; + } else { + src = CLK_GMAC1_PTP_SEL_CPLL; + div = priv->cpll_hz / rate; + } + rk_clrsetreg(&cru->clksel_con[104], + CLK_GMAC1_PTP_DIV_MASK | CLK_GMAC1_PTP_SEL_MASK, + src << CLK_GMAC1_PTP_SEL_SHIFT | + (div - 1) << CLK_GMAC1_PTP_DIV_SHIFT); + break; + + case CLK_GMAC0_125M_SRC: + rk_clrsetreg(&cru->clksel_con[30], + CLK_GMAC0_125M_DIV_MASK, + (div - 1) << CLK_GMAC0_125M_DIV_SHIFT); + break; + case CLK_GMAC1_125M_SRC: + rk_clrsetreg(&cru->clksel_con[31], + CLK_GMAC1_125M_DIV_MASK, + (div - 1) << CLK_GMAC1_125M_DIV_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3576_gmac_get_clk(priv, clk_id); +} + +static ulong rk3576_uart_frac_get_rate(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 reg, con, fracdiv, p_src, p_rate; + unsigned long m, n; + + switch (clk_id) { + case CLK_UART_FRAC_0: + reg = 21; + break; + case CLK_UART_FRAC_1: + reg = 23; + break; + case CLK_UART_FRAC_2: + reg = 25; + break; + default: + return -ENOENT; + } + con = readl(&cru->clksel_con[reg + 1]); + p_src = (con & CLK_UART_SRC_SEL_MASK) >> CLK_UART_SRC_SEL_SHIFT; + if (p_src == CLK_UART_SRC_SEL_GPLL) + p_rate = priv->gpll_hz; + else if (p_src == CLK_UART_SRC_SEL_CPLL) + p_rate = priv->cpll_hz; + else if (p_src == CLK_UART_SRC_SEL_AUPLL) + p_rate = priv->aupll_hz; + else + p_rate = OSC_HZ; + + fracdiv = readl(&cru->clksel_con[reg]); + n = fracdiv & CLK_UART_FRAC_NUMERATOR_MASK; + n >>= CLK_UART_FRAC_NUMERATOR_SHIFT; + m = fracdiv & CLK_UART_FRAC_DENOMINATOR_MASK; + m >>= CLK_UART_FRAC_DENOMINATOR_SHIFT; + return p_rate * n / m; +} + +static ulong rk3576_uart_frac_set_rate(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + u32 reg, clk_src, p_rate; + unsigned long m = 0, n = 0, val; + + if (priv->cpll_hz % rate == 0) { + clk_src = CLK_UART_SRC_SEL_CPLL; + p_rate = priv->cpll_hz; + } else if (rate == OSC_HZ) { + clk_src = CLK_UART_SRC_SEL_OSC; + p_rate = OSC_HZ; + } else { + clk_src = CLK_UART_SRC_SEL_GPLL; + p_rate = priv->cpll_hz; + } + + rational_best_approximation(rate, p_rate, GENMASK(16 - 1, 0), + GENMASK(16 - 1, 0), &m, &n); + + if (m < 4 && m != 0) { + if (n % 2 == 0) + val = 1; + else + val = DIV_ROUND_UP(4, m); + + n *= val; + m *= val; + if (n > 0xffff) + n = 0xffff; + } + + switch (clk_id) { + case CLK_UART_FRAC_0: + reg = 21; + break; + case CLK_UART_FRAC_1: + reg = 23; + break; + case CLK_UART_FRAC_2: + reg = 25; + break; + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[reg + 1], + CLK_UART_SRC_SEL_MASK, + (clk_src << CLK_UART_SRC_SEL_SHIFT)); + if (m && n) { + val = m << CLK_UART_FRAC_NUMERATOR_SHIFT | n; + writel(val, &cru->clksel_con[reg]); + } + + return rk3576_uart_frac_get_rate(priv, clk_id); +} + +static ulong rk3576_uart_get_rate(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 con, div, src, p_rate; + + switch (clk_id) { + case SCLK_UART0: + con = readl(&cru->clksel_con[60]); + break; + case SCLK_UART1: + con = readl(&cru->pmuclksel_con[8]); + src = (con & CLK_UART1_SEL_MASK) >> CLK_UART1_SEL_SHIFT; + if (src == CLK_UART1_SEL_OSC) + return OSC_HZ; + con = readl(&cru->clksel_con[27]); + break; + case SCLK_UART2: + con = readl(&cru->clksel_con[61]); + break; + case SCLK_UART3: + con = readl(&cru->clksel_con[62]); + break; + case SCLK_UART4: + con = readl(&cru->clksel_con[63]); + break; + case SCLK_UART5: + con = readl(&cru->clksel_con[64]); + break; + case SCLK_UART6: + con = readl(&cru->clksel_con[65]); + break; + case SCLK_UART7: + con = readl(&cru->clksel_con[66]); + break; + case SCLK_UART8: + con = readl(&cru->clksel_con[67]); + break; + case SCLK_UART9: + con = readl(&cru->clksel_con[68]); + break; + case SCLK_UART10: + con = readl(&cru->clksel_con[69]); + break; + case SCLK_UART11: + con = readl(&cru->clksel_con[70]); + break; + default: + return -ENOENT; + } + if (clk_id == SCLK_UART1) { + src = (con & CLK_UART1_SRC_SEL_SHIFT) >> CLK_UART1_SRC_SEL_SHIFT; + div = (con & CLK_UART1_SRC_DIV_MASK) >> CLK_UART1_SRC_DIV_SHIFT; + } else { + src = (con & CLK_UART_SEL_MASK) >> CLK_UART_SEL_SHIFT; + div = (con & CLK_UART_DIV_MASK) >> CLK_UART_DIV_SHIFT; + } + if (src == CLK_UART_SEL_GPLL) + p_rate = priv->gpll_hz; + else if (src == CLK_UART_SEL_CPLL) + p_rate = priv->cpll_hz; + else if (src == CLK_UART_SEL_AUPLL) + p_rate = priv->aupll_hz; + else if (src == CLK_UART_SEL_FRAC0) + p_rate = rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_0); + else if (src == CLK_UART_SEL_FRAC1) + p_rate = rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_1); + else if (src == CLK_UART_SEL_FRAC2) + p_rate = rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_2); + else + p_rate = OSC_HZ; + + return DIV_TO_RATE(p_rate, div); +} + +static ulong rk3576_uart_set_rate(struct rk3576_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3576_cru *cru = priv->cru; + u32 reg, clk_src = 0, div = 0; + + if (!(priv->gpll_hz % rate)) { + clk_src = CLK_UART_SEL_GPLL; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } else if (!(priv->cpll_hz % rate)) { + clk_src = CLK_UART_SEL_CPLL; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } else if (!(rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_0) % rate)) { + clk_src = CLK_UART_SEL_FRAC0; + div = DIV_ROUND_UP(rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_0), rate); + } else if (!(rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_1) % rate)) { + clk_src = CLK_UART_SEL_FRAC1; + div = DIV_ROUND_UP(rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_1), rate); + } else if (!(rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_2) % rate)) { + clk_src = CLK_UART_SEL_FRAC2; + div = DIV_ROUND_UP(rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_2), rate); + } else if (!(OSC_HZ % rate)) { + clk_src = CLK_UART_SEL_OSC; + div = DIV_ROUND_UP(OSC_HZ, rate); + } + + switch (clk_id) { + case SCLK_UART0: + reg = 60; + break; + case SCLK_UART1: + if (rate == OSC_HZ) { + rk_clrsetreg(&cru->pmuclksel_con[8], + CLK_UART1_SEL_MASK, + CLK_UART1_SEL_OSC << CLK_UART1_SEL_SHIFT); + return 0; + } + + rk_clrsetreg(&cru->clksel_con[27], + CLK_UART1_SRC_SEL_MASK | CLK_UART1_SRC_DIV_MASK, + (clk_src << CLK_UART1_SRC_SEL_SHIFT) | + ((div - 1) << CLK_UART1_SRC_DIV_SHIFT)); + rk_clrsetreg(&cru->pmuclksel_con[8], + CLK_UART1_SEL_MASK, + CLK_UART1_SEL_TOP << CLK_UART1_SEL_SHIFT); + return 0; + case SCLK_UART2: + reg = 61; + break; + case SCLK_UART3: + reg = 62; + break; + case SCLK_UART4: + reg = 63; + break; + case SCLK_UART5: + reg = 64; + break; + case SCLK_UART6: + reg = 65; + break; + case SCLK_UART7: + reg = 66; + break; + case SCLK_UART8: + reg = 67; + break; + case SCLK_UART9: + reg = 68; + break; + case SCLK_UART10: + reg = 69; + break; + case SCLK_UART11: + reg = 70; + break; + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[reg], + CLK_UART_SEL_MASK | + CLK_UART_DIV_MASK, + (clk_src << CLK_UART_SEL_SHIFT) | + ((div - 1) << CLK_UART_DIV_SHIFT)); + + return rk3576_uart_get_rate(priv, clk_id); +} +#endif + +static ulong rk3576_ufs_ref_get_rate(struct rk3576_clk_priv *priv, ulong clk_id) +{ + struct rk3576_cru *cru = priv->cru; + u32 src, div; + + src = readl(&cru->pmuclksel_con[3]) & 0x3; + div = readl(&cru->pmuclksel_con[1]) & 0xff; + if (src == 0) + return OSC_HZ; + else if (src == 2) + return priv->ppll_hz / (div + 1); + else + return 26000000; +} + +static ulong rk3576_clk_get_rate(struct clk *clk) +{ + struct rk3576_clk_priv *priv = dev_get_priv(clk->dev); + ulong rate = 0; + + if (!priv->gpll_hz) { + printf("%s gpll=%lu\n", __func__, priv->gpll_hz); + return -ENOENT; + } + + if (!priv->ppll_hz) { + priv->ppll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[PPLL], + priv->cru, PPLL); + } + + switch (clk->id) { + case PLL_LPLL: + rate = rockchip_pll_get_rate(&rk3576_pll_clks[LPLL], priv->cru, + LPLL); + priv->lpll_hz = rate; + break; + case PLL_BPLL: + rate = rockchip_pll_get_rate(&rk3576_pll_clks[BPLL], priv->cru, + BPLL); + priv->bpll_hz = rate; + break; + case PLL_GPLL: + rate = rockchip_pll_get_rate(&rk3576_pll_clks[GPLL], priv->cru, + GPLL); + break; + case PLL_CPLL: + rate = rockchip_pll_get_rate(&rk3576_pll_clks[CPLL], priv->cru, + CPLL); + break; + case PLL_VPLL: + rate = rockchip_pll_get_rate(&rk3576_pll_clks[VPLL], priv->cru, + VPLL); + break; + case PLL_AUPLL: + rate = rockchip_pll_get_rate(&rk3576_pll_clks[AUPLL], priv->cru, + AUPLL); + break; + case PLL_PPLL: + rate = rockchip_pll_get_rate(&rk3576_pll_clks[PPLL], priv->cru, + PPLL) * 2; + break; + case ACLK_BUS_ROOT: + case HCLK_BUS_ROOT: + case PCLK_BUS_ROOT: + rate = rk3576_bus_get_clk(priv, clk->id); + break; + case ACLK_TOP: + case HCLK_TOP: + case PCLK_TOP_ROOT: + case ACLK_TOP_MID: + rate = rk3576_top_get_clk(priv, clk->id); + break; + case CLK_I2C0: + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + case CLK_I2C6: + case CLK_I2C7: + case CLK_I2C8: + case CLK_I2C9: + rate = rk3576_i2c_get_clk(priv, clk->id); + break; + case CLK_SPI0: + case CLK_SPI1: + case CLK_SPI2: + case CLK_SPI3: + case CLK_SPI4: + rate = rk3576_spi_get_clk(priv, clk->id); + break; + case CLK_PWM1: + case CLK_PWM2: + case CLK_PMU1PWM: + rate = rk3576_pwm_get_clk(priv, clk->id); + break; + case CLK_SARADC: + case CLK_TSADC: + rate = rk3576_adc_get_clk(priv, clk->id); + break; + case CCLK_SRC_SDIO: + case CCLK_SRC_SDMMC0: + case CCLK_SRC_EMMC: + case BCLK_EMMC: + case SCLK_FSPI_X2: + case SCLK_FSPI1_X2: + case DCLK_DECOM: + case HCLK_SDMMC0: + case HCLK_EMMC: + case HCLK_SDIO: + rate = rk3576_mmc_get_clk(priv, clk->id); + break; + case TCLK_EMMC: + case TCLK_WDT0: + rate = OSC_HZ; + break; +#ifndef CONFIG_SPL_BUILD + case ACLK_VOP_ROOT: + case ACLK_VOP: + case ACLK_VO0_ROOT: + case ACLK_VO1_ROOT: + case HCLK_VOP_ROOT: + case PCLK_VOP_ROOT: + rate = rk3576_aclk_vop_get_clk(priv, clk->id); + break; + case DCLK_VP0: + case DCLK_VP0_SRC: + case DCLK_VP1: + case DCLK_VP1_SRC: + case DCLK_VP2: + case DCLK_VP2_SRC: + rate = rk3576_dclk_vop_get_clk(priv, clk->id); + break; + case CLK_GMAC0_PTP_REF_SRC: + case CLK_GMAC1_PTP_REF_SRC: + case CLK_GMAC0_PTP_REF: + case CLK_GMAC1_PTP_REF: + case CLK_GMAC0_125M_SRC: + case CLK_GMAC1_125M_SRC: + rate = rk3576_gmac_get_clk(priv, clk->id); + break; + case CLK_UART_FRAC_0: + case CLK_UART_FRAC_1: + case CLK_UART_FRAC_2: + rate = rk3576_uart_frac_get_rate(priv, clk->id); + break; + case SCLK_UART0: + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + case SCLK_UART5: + case SCLK_UART6: + case SCLK_UART7: + case SCLK_UART8: + case SCLK_UART9: + case SCLK_UART10: + case SCLK_UART11: + rate = rk3576_uart_get_rate(priv, clk->id); + break; + case CLK_DSIHOST0: + rate = rk3576_clk_csihost_get_clk(priv, clk->id); + break; + case DCLK_EBC: + case DCLK_EBC_FRAC_SRC: + rate = rk3576_dclk_ebc_get_clk(priv, clk->id); + break; +#endif + case CLK_REF_UFS_CLKOUT: + case CLK_REF_OSC_MPHY: + rate = rk3576_ufs_ref_get_rate(priv, clk->id); + break; + + default: + return -ENOENT; + } + + return rate; +}; + +static ulong rk3576_clk_set_rate(struct clk *clk, ulong rate) +{ + struct rk3576_clk_priv *priv = dev_get_priv(clk->dev); + ulong ret = 0; + + if (!priv->ppll_hz) { + priv->ppll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[PPLL], + priv->cru, PPLL); + } + if (!priv->aupll_hz) { + priv->aupll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[AUPLL], + priv->cru, AUPLL); + } + + switch (clk->id) { + case PLL_CPLL: + ret = rockchip_pll_set_rate(&rk3576_pll_clks[CPLL], priv->cru, + CPLL, rate); + priv->cpll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[CPLL], + priv->cru, CPLL); + break; + case PLL_GPLL: + ret = rockchip_pll_set_rate(&rk3576_pll_clks[GPLL], priv->cru, + GPLL, rate); + priv->gpll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[GPLL], + priv->cru, GPLL); + break; + case PLL_VPLL: + ret = rockchip_pll_set_rate(&rk3576_pll_clks[VPLL], priv->cru, + VPLL, rate); + priv->vpll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[VPLL], + priv->cru, VPLL); + break; + case PLL_AUPLL: + ret = rockchip_pll_set_rate(&rk3576_pll_clks[AUPLL], priv->cru, + AUPLL, rate); + priv->aupll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[AUPLL], + priv->cru, AUPLL); + break; + case PLL_PPLL: + ret = rockchip_pll_set_rate(&rk3576_pll_clks[PPLL], priv->cru, + PPLL, rate); + priv->ppll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[PPLL], + priv->cru, PPLL) * 2; + break; + case ACLK_BUS_ROOT: + case HCLK_BUS_ROOT: + case PCLK_BUS_ROOT: + ret = rk3576_bus_set_clk(priv, clk->id, rate); + break; + case ACLK_TOP: + case HCLK_TOP: + case PCLK_TOP_ROOT: + case ACLK_TOP_MID: + ret = rk3576_top_set_clk(priv, clk->id, rate); + break; + case CLK_I2C0: + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + case CLK_I2C6: + case CLK_I2C7: + case CLK_I2C8: + case CLK_I2C9: + ret = rk3576_i2c_set_clk(priv, clk->id, rate); + break; + case CLK_SPI0: + case CLK_SPI1: + case CLK_SPI2: + case CLK_SPI3: + case CLK_SPI4: + ret = rk3576_spi_set_clk(priv, clk->id, rate); + break; + case CLK_PWM1: + case CLK_PWM2: + case CLK_PMU1PWM: + ret = rk3576_pwm_set_clk(priv, clk->id, rate); + break; + case CLK_SARADC: + case CLK_TSADC: + ret = rk3576_adc_set_clk(priv, clk->id, rate); + break; + case CCLK_SRC_SDIO: + case CCLK_SRC_SDMMC0: + case CCLK_SRC_EMMC: + case BCLK_EMMC: + case SCLK_FSPI_X2: + case SCLK_FSPI1_X2: + case DCLK_DECOM: + case HCLK_SDMMC0: + case HCLK_EMMC: + case HCLK_SDIO: + ret = rk3576_mmc_set_clk(priv, clk->id, rate); + break; + case TCLK_EMMC: + case TCLK_WDT0: + ret = OSC_HZ; + break; + + /* Might occur in cru assigned-clocks, can be ignored here */ + case CLK_AUDIO_FRAC_0: + case CLK_AUDIO_FRAC_1: + case CLK_AUDIO_FRAC_0_SRC: + case CLK_AUDIO_FRAC_1_SRC: + case CLK_CPLL_DIV2: + case CLK_CPLL_DIV4: + case CLK_CPLL_DIV10: + case FCLK_DDR_CM0_CORE: + case ACLK_PHP_ROOT: + ret = 0; + break; +#ifndef CONFIG_SPL_BUILD + case ACLK_VOP_ROOT: + case ACLK_VOP: + case ACLK_VO0_ROOT: + case ACLK_VO1_ROOT: + case HCLK_VOP_ROOT: + case PCLK_VOP_ROOT: + ret = rk3576_aclk_vop_set_clk(priv, clk->id, rate); + break; + case DCLK_VP0: + case DCLK_VP0_SRC: + case DCLK_VP1: + case DCLK_VP1_SRC: + case DCLK_VP2: + case DCLK_VP2_SRC: + ret = rk3576_dclk_vop_set_clk(priv, clk->id, rate); + break; + case CLK_GMAC0_PTP_REF_SRC: + case CLK_GMAC1_PTP_REF_SRC: + case CLK_GMAC0_PTP_REF: + case CLK_GMAC1_PTP_REF: + case CLK_GMAC0_125M_SRC: + case CLK_GMAC1_125M_SRC: + ret = rk3576_gmac_set_clk(priv, clk->id, rate); + break; + case CLK_UART_FRAC_0: + case CLK_UART_FRAC_1: + case CLK_UART_FRAC_2: + ret = rk3576_uart_frac_set_rate(priv, clk->id, rate); + break; + case SCLK_UART0: + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + case SCLK_UART5: + case SCLK_UART6: + case SCLK_UART7: + case SCLK_UART8: + case SCLK_UART9: + case SCLK_UART10: + case SCLK_UART11: + ret = rk3576_uart_set_rate(priv, clk->id, rate); + break; + case CLK_DSIHOST0: + ret = rk3576_clk_csihost_set_clk(priv, clk->id, rate); + break; + case DCLK_EBC: + case DCLK_EBC_FRAC_SRC: + ret = rk3576_dclk_ebc_set_clk(priv, clk->id, rate); + break; +#endif + default: + return -ENOENT; + } + + return ret; +}; + +#if (IS_ENABLED(OF_CONTROL)) || (!IS_ENABLED(OF_PLATDATA)) +static int __maybe_unused rk3576_dclk_vop_set_parent(struct clk *clk, + struct clk *parent) +{ + struct rk3576_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3576_cru *cru = priv->cru; + u32 sel; + const char *clock_dev_name = parent->dev->name; + + if (parent->id == PLL_VPLL) + sel = 2; + else if (parent->id == PLL_GPLL) + sel = 0; + else if (parent->id == PLL_CPLL) + sel = 1; + else if (parent->id == PLL_BPLL) + sel = 3; + else + sel = 4; + + switch (clk->id) { + case DCLK_VP0_SRC: + rk_clrsetreg(&cru->clksel_con[145], DCLK0_VOP_SRC_SEL_MASK, + sel << DCLK0_VOP_SRC_SEL_SHIFT); + break; + case DCLK_VP1_SRC: + rk_clrsetreg(&cru->clksel_con[146], DCLK0_VOP_SRC_SEL_MASK, + sel << DCLK0_VOP_SRC_SEL_SHIFT); + break; + case DCLK_VP2_SRC: + rk_clrsetreg(&cru->clksel_con[147], DCLK0_VOP_SRC_SEL_MASK, + sel << DCLK0_VOP_SRC_SEL_SHIFT); + break; + case DCLK_VP0: + if (!strcmp(clock_dev_name, "hdmiphypll_clk0")) + sel = 1; + else + sel = 0; + rk_clrsetreg(&cru->clksel_con[147], DCLK0_VOP_SEL_MASK, + sel << DCLK0_VOP_SEL_SHIFT); + break; + case DCLK_VP1: + if (!strcmp(clock_dev_name, "hdmiphypll_clk0")) + sel = 1; + else + sel = 0; + rk_clrsetreg(&cru->clksel_con[147], DCLK1_VOP_SEL_MASK, + sel << DCLK1_VOP_SEL_SHIFT); + break; + case DCLK_VP2: + if (!strcmp(clock_dev_name, "hdmiphypll_clk0")) + sel = 1; + else + sel = 0; + rk_clrsetreg(&cru->clksel_con[147], DCLK2_VOP_SEL_MASK, + sel << DCLK2_VOP_SEL_SHIFT); + break; + case DCLK_EBC: + if (parent->id == PLL_GPLL) + sel = 0; + else if (parent->id == PLL_CPLL) + sel = 1; + else if (parent->id == PLL_VPLL) + sel = 2; + else if (parent->id == PLL_AUPLL) + sel = 3; + else if (parent->id == PLL_LPLL) + sel = 4; + else if (parent->id == DCLK_EBC_FRAC_SRC) + sel = 5; + else + sel = 6; + rk_clrsetreg(&cru->clksel_con[123], DCLK_EBC_SEL_MASK, + sel << DCLK_EBC_SEL_SHIFT); + break; + default: + return -EINVAL; + } + return 0; +} + +static int __maybe_unused rk3576_ufs_ref_set_parent(struct clk *clk, + struct clk *parent) +{ + struct rk3576_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3576_cru *cru = priv->cru; + u32 sel; + const char *clock_dev_name = parent->dev->name; + + if (parent->id == CLK_REF_MPHY_26M) + sel = 2; + else if (!strcmp(clock_dev_name, "xin24m")) + sel = 0; + else + sel = 1; + + rk_clrsetreg(&cru->pmuclksel_con[3], 0x3, sel << 0); + return 0; +} + +static int rk3576_clk_set_parent(struct clk *clk, struct clk *parent) +{ + switch (clk->id) { + case DCLK_VP0_SRC: + case DCLK_VP1_SRC: + case DCLK_VP2_SRC: + case DCLK_VP0: + case DCLK_VP1: + case DCLK_VP2: + case DCLK_EBC: + return rk3576_dclk_vop_set_parent(clk, parent); + case CLK_REF_OSC_MPHY: + return rk3576_ufs_ref_set_parent(clk, parent); + case CLK_AUDIO_FRAC_0_SRC: + case CLK_AUDIO_FRAC_1_SRC: + /* Might occur in cru assigned-clocks, can be ignored here */ + return 0; + default: + return -ENOENT; + } + + return 0; +} +#endif + +static struct clk_ops rk3576_clk_ops = { + .get_rate = rk3576_clk_get_rate, + .set_rate = rk3576_clk_set_rate, +#if (IS_ENABLED(OF_CONTROL)) || (!IS_ENABLED(OF_PLATDATA)) + .set_parent = rk3576_clk_set_parent, +#endif +}; + +static void rk3576_clk_init(struct rk3576_clk_priv *priv) +{ + int ret; + + priv->spll_hz = 702000000; + + if (priv->cpll_hz != CPLL_HZ) { + ret = rockchip_pll_set_rate(&rk3576_pll_clks[CPLL], priv->cru, + CPLL, CPLL_HZ); + if (!ret) + priv->cpll_hz = CPLL_HZ; + } + if (priv->gpll_hz != GPLL_HZ) { + ret = rockchip_pll_set_rate(&rk3576_pll_clks[GPLL], priv->cru, + GPLL, GPLL_HZ); + if (!ret) + priv->gpll_hz = GPLL_HZ; + } + rk_clrsetreg(&priv->cru->clksel_con[123], + DCLK_EBC_FRAC_SRC_SEL_MASK, + (DCLK_EBC_FRAC_SRC_SEL_GPLL << + DCLK_EBC_FRAC_SRC_SEL_SHIFT)); +} + +static int rk3576_clk_probe(struct udevice *dev) +{ + struct rk3576_clk_priv *priv = dev_get_priv(dev); + int ret; + + priv->sync_kernel = false; + +#ifdef CONFIG_SPL_BUILD + /* relase presetn_bigcore_biu/cru/grf */ + writel(0x1c001c00, 0x26010010); + /* set spll to normal mode */ + writel(BITS_WITH_WMASK(2, 0x7U, 6), + RK3576_SCRU_BASE + RK3576_PLL_CON(137)); + writel(BITS_WITH_WMASK(1, 0x3U, 0), + RK3576_SCRU_BASE + RK3576_MODE_CON0); + /* fix ppll\aupll\cpll */ + writel(BITS_WITH_WMASK(2, 0x7U, 6), + RK3576_CRU_BASE + RK3576_PMU_PLL_CON(129)); + writel(BITS_WITH_WMASK(2, 0x7U, 6), + RK3576_CRU_BASE + RK3576_PLL_CON(97)); + writel(BITS_WITH_WMASK(2, 0x7U, 6), + RK3576_CRU_BASE + RK3576_PLL_CON(105)); + writel(BITS_WITH_WMASK(1, 0x3U, 6), + RK3576_CRU_BASE + RK3576_MODE_CON0); + writel(BITS_WITH_WMASK(1, 0x3U, 8), + RK3576_CRU_BASE + RK3576_MODE_CON0); + /* init cci */ + writel(0xffff0000, RK3576_CRU_BASE + RK3576_CCI_CLKSEL_CON(4)); + rockchip_pll_set_rate(&rk3576_pll_clks[BPLL], priv->cru, + BPLL, LPLL_HZ); + if (!priv->armclk_enter_hz) { + ret = rockchip_pll_set_rate(&rk3576_pll_clks[LPLL], priv->cru, + LPLL, LPLL_HZ); + priv->armclk_enter_hz = + rockchip_pll_get_rate(&rk3576_pll_clks[LPLL], + priv->cru, LPLL); + priv->armclk_init_hz = priv->armclk_enter_hz; + rk_clrsetreg(&priv->cru->litclksel_con[0], CLK_LITCORE_DIV_MASK, + 0 << CLK_LITCORE_DIV_SHIFT); + } + /* init cci */ + writel(0xffff20cb, RK3576_CRU_BASE + RK3576_CCI_CLKSEL_CON(4)); + + /* Change bigcore rm from 4 to 3 */ + writel(0x001c000c, RK3576_BIGCORE_GRF_BASE + 0x3c); + writel(0x001c000c, RK3576_BIGCORE_GRF_BASE + 0x44); + writel(0x00020002, RK3576_BIGCORE_GRF_BASE + 0x38); + udelay(1); + writel(0x00020000, RK3576_BIGCORE_GRF_BASE + 0x38); + /* Change litcore rm from 4 to 3 */ + writel(0x001c000c, RK3576_LITCORE_GRF_BASE + 0x3c); + writel(0x001c000c, RK3576_LITCORE_GRF_BASE + 0x44); + writel(0x00020002, RK3576_LITCORE_GRF_BASE + 0x38); + udelay(1); + writel(0x00020000, RK3576_LITCORE_GRF_BASE + 0x38); + /* Change cci rm form 4 to 3 */ + writel(0x001c000c, RK3576_CCI_GRF_BASE + 0x54); +#endif + + rk3576_clk_init(priv); + + /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */ + ret = clk_set_defaults(dev, 1); + if (ret) + debug("%s clk_set_defaults failed %d\n", __func__, ret); + else + priv->sync_kernel = true; + + return 0; +} + +static int rk3576_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct rk3576_clk_priv *priv = dev_get_priv(dev); + + priv->cru = dev_read_addr_ptr(dev); + + return 0; +} + +static int rk3576_clk_bind(struct udevice *dev) +{ + int ret; + struct udevice *sys_child; + struct sysreset_reg *priv; + + /* The reset driver does not have a device node, so bind it here */ + ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", + &sys_child); + if (ret) { + debug("Warning: No sysreset driver: ret=%d\n", ret); + } else { + priv = malloc(sizeof(struct sysreset_reg)); + priv->glb_srst_fst_value = offsetof(struct rk3576_cru, + glb_srst_fst); + priv->glb_srst_snd_value = offsetof(struct rk3576_cru, + glb_srsr_snd); + dev_set_priv(sys_child, priv); + } + +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) + ret = offsetof(struct rk3576_cru, softrst_con[0]); + ret = rk3576_reset_bind_lut(dev, ret, 32776); + if (ret) + debug("Warning: software reset driver bind failed\n"); +#endif + + return 0; +} + +static const struct udevice_id rk3576_clk_ids[] = { + { .compatible = "rockchip,rk3576-cru" }, + { } +}; + +U_BOOT_DRIVER(rockchip_rk3576_cru) = { + .name = "rockchip_rk3576_cru", + .id = UCLASS_CLK, + .of_match = rk3576_clk_ids, + .priv_auto = sizeof(struct rk3576_clk_priv), + .of_to_plat = rk3576_clk_ofdata_to_platdata, + .ops = &rk3576_clk_ops, + .bind = rk3576_clk_bind, + .probe = rk3576_clk_probe, +}; diff --git a/drivers/clk/stm32/clk-stm32mp1.c b/drivers/clk/stm32/clk-stm32mp1.c index 4044edfb768..9cb69a01f7f 100644 --- a/drivers/clk/stm32/clk-stm32mp1.c +++ b/drivers/clk/stm32/clk-stm32mp1.c @@ -551,6 +551,7 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = { STM32MP1_CLK_SET_CLR_F(RCC_MP_APB4ENSETR, 0, LTDC_PX, _PLL4_Q), STM32MP1_CLK_SET_CLR_F(RCC_MP_APB4ENSETR, 4, DSI_PX, _PLL4_Q), STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 4, DSI_K, _DSI_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 4, DSI, _DSI_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 8, DDRPERFM, _UNKNOWN_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 15, IWDG2, _UNKNOWN_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 16, USBPHY_K, _USBPHY_SEL), diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig index 8bdc0944896..f44db76c182 100644 --- a/drivers/clk/sunxi/Kconfig +++ b/drivers/clk/sunxi/Kconfig @@ -122,4 +122,11 @@ config CLK_SUN50I_A64 This enables common clock driver support for platforms based on Allwinner A64 SoC. +config CLK_SUN50I_A100 + bool "Clock driver for Allwinner A100/A133" + default MACH_SUN50I_A133 + help + This enables common clock driver support for platforms based + on Allwinner A100/A133 SoCs. + endif # CLK_SUNXI diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 90a277489dc..7ff71c756e0 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -24,3 +24,4 @@ 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 obj-$(CONFIG_CLK_SUN50I_A64) += clk_a64.o +obj-$(CONFIG_CLK_SUN50I_A100) += clk_a100.o diff --git a/drivers/clk/sunxi/clk_a100.c b/drivers/clk/sunxi/clk_a100.c new file mode 100644 index 00000000000..b641feb8612 --- /dev/null +++ b/drivers/clk/sunxi/clk_a100.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2023-2024 Arm Ltd. + */ + +#include <clk/sunxi.h> +#include <dt-bindings/clock/sun50i-a100-ccu.h> +#include <dt-bindings/reset/sun50i-a100-ccu.h> +#include <linux/bitops.h> + +static struct ccu_clk_gate a100_gates[] = { + [CLK_PLL_PERIPH0] = GATE(0x020, BIT(31) | BIT(27)), + + [CLK_APB1] = GATE_DUMMY, + + [CLK_DE] = GATE(0x600, BIT(31)), + [CLK_BUS_DE] = GATE(0x60c, BIT(0)), + + [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_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_SPI2] = GATE(0x948, BIT(31)), + + [CLK_BUS_SPI0] = GATE(0x96c, BIT(0)), + [CLK_BUS_SPI1] = GATE(0x96c, BIT(1)), + [CLK_BUS_SPI2] = GATE(0x96c, BIT(2)), + + [CLK_BUS_EMAC] = GATE(0x97c, BIT(0)), + + [CLK_USB_PHY0] = GATE(0xa70, BIT(29)), + [CLK_USB_OHCI0] = GATE(0xa70, BIT(31)), + + [CLK_USB_PHY1] = GATE(0xa74, BIT(29)), + [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_TCON_LCD] = GATE(0xb60, BIT(31)), + [CLK_BUS_TCON_LCD] = GATE(0xb7c, BIT(0)), +}; + +static struct ccu_reset a100_resets[] = { + [RST_BUS_DE] = RESET(0x60c, BIT(16)), + + [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_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_SPI2] = RESET(0x96c, BIT(18)), + + [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_TCON_LCD] = RESET(0xb7c, BIT(16)), +}; + +const struct ccu_desc a100_ccu_desc = { + .gates = a100_gates, + .resets = a100_resets, + .num_gates = ARRAY_SIZE(a100_gates), + .num_resets = ARRAY_SIZE(a100_resets), +}; diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c index 2ef4f45dacf..e0765cbc6dc 100644 --- a/drivers/clk/sunxi/clk_sunxi.c +++ b/drivers/clk/sunxi/clk_sunxi.c @@ -122,6 +122,7 @@ extern const struct ccu_desc f1c100s_ccu_desc; extern const struct ccu_desc h3_ccu_desc; extern const struct ccu_desc h6_ccu_desc; extern const struct ccu_desc h616_ccu_desc; +extern const struct ccu_desc a100_ccu_desc; extern const struct ccu_desc h6_r_ccu_desc; extern const struct ccu_desc r40_ccu_desc; extern const struct ccu_desc v3s_ccu_desc; @@ -215,6 +216,10 @@ static const struct udevice_id sunxi_clk_ids[] = { { .compatible = "allwinner,sun50i-h616-r-ccu", .data = (ulong)&h6_r_ccu_desc }, #endif +#ifdef CONFIG_CLK_SUN50I_A100 + { .compatible = "allwinner,sun50i-a100-ccu", + .data = (ulong)&a100_ccu_desc }, +#endif #ifdef CONFIG_CLK_SUNIV_F1C100S { .compatible = "allwinner,suniv-f1c100s-ccu", .data = (ulong)&f1c100s_ccu_desc }, diff --git a/drivers/clk/ti/clk-k3.c b/drivers/clk/ti/clk-k3.c index bf65f573cb8..b38e559e45a 100644 --- a/drivers/clk/ti/clk-k3.c +++ b/drivers/clk/ti/clk-k3.c @@ -82,6 +82,8 @@ static const struct soc_attr ti_k3_soc_clk_data[] = { .family = "J721E", .data = &j721e_clk_platdata, }, +#endif +#if IS_ENABLED(CONFIG_SOC_K3_J7200) { .family = "J7200", .data = &j7200_clk_platdata, @@ -104,6 +106,10 @@ static const struct soc_attr ti_k3_soc_clk_data[] = { .family = "J784S4", .data = &j784s4_clk_platdata, }, + { + .family = "J742S2", + .data = &j784s4_clk_platdata, + }, #endif { /* sentinel */ } }; diff --git a/drivers/core/Makefile b/drivers/core/Makefile index 9ea57911f89..a549890c22b 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -6,16 +6,16 @@ obj-y += device.o fdtaddr.o lists.o root.o uclass.o util.o tag.o obj-$(CONFIG_$(PHASE_)ACPIGEN) += acpi.o obj-$(CONFIG_$(PHASE_)DEVRES) += devres.o obj-$(CONFIG_$(PHASE_)DM_DEVICE_REMOVE) += device-remove.o -obj-$(CONFIG_$(XPL_)SIMPLE_BUS) += simple-bus.o +obj-$(CONFIG_$(PHASE_)SIMPLE_BUS) += simple-bus.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o obj-$(CONFIG_DM) += dump.o obj-$(CONFIG_$(PHASE_)REGMAP) += regmap.o obj-$(CONFIG_$(PHASE_)SYSCON) += syscon-uclass.o -obj-$(CONFIG_$(XPL_)OF_LIVE) += of_access.o of_addr.o +obj-$(CONFIG_$(PHASE_)OF_LIVE) += of_access.o of_addr.o ifndef CONFIG_DM_DEV_READ_INLINE obj-$(CONFIG_OF_CONTROL) += read.o endif -obj-$(CONFIG_$(XPL_)OF_PLATDATA) += read.o -obj-$(CONFIG_OF_CONTROL) += of_extra.o ofnode.o read_extra.o +obj-$(CONFIG_$(PHASE_)OF_PLATDATA) += read.o +obj-$(CONFIG_OF_CONTROL) += of_extra.o ofnode.o read_extra.o ofnode_graph.o ccflags-$(CONFIG_DM_DEBUG) += -DDEBUG diff --git a/drivers/core/ofnode_graph.c b/drivers/core/ofnode_graph.c new file mode 100644 index 00000000000..175ac768771 --- /dev/null +++ b/drivers/core/ofnode_graph.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#define LOG_CATEGORY LOGC_DT + +#include <dm.h> +#include <log.h> +#include <dm/ofnode.h> +#include <linux/err.h> + +/** + * ofnode_graph_get_endpoint_count() - get the number of endpoints in a device ofnode + * @parent: ofnode to the device containing ports and endpoints + * + * Return: count of endpoint of this device ofnode + */ +unsigned int ofnode_graph_get_endpoint_count(ofnode parent) +{ + ofnode ports, port, endpoint; + unsigned int num = 0; + + /* Check if ports node exists */ + ports = ofnode_find_subnode(parent, "ports"); + if (ofnode_valid(ports)) + parent = ports; + + ofnode_for_each_subnode(port, parent) { + if (!strncmp(ofnode_get_name(port), "port", 4)) { + /* Port node can only contain endpoints */ + ofnode_for_each_subnode(endpoint, port) + num++; + } + }; + + log_debug("%s: detected %d endpoints\n", __func__, num); + + return num++; +} + +/** + * ofnode_graph_get_port_count() - get the number of port in a device or ports ofnode + * @parent: ofnode to the device or ports node + * + * Return: count of port of this device or ports node + */ +unsigned int ofnode_graph_get_port_count(ofnode parent) +{ + ofnode ports, port; + unsigned int num = 0; + + /* Check if ports node exists */ + ports = ofnode_find_subnode(parent, "ports"); + if (ofnode_valid(ports)) + parent = ports; + + ofnode_for_each_subnode(port, parent) + if (!strncmp(ofnode_get_name(port), "port", 4)) + num++; + + log_debug("%s: detected %d ports\n", __func__, num); + + return num++; +} + +/** + * ofnode_graph_get_port_by_id() - get the port matching a given id + * @parent: parent ofnode + * @id: id of the port + * + * Return: ofnode in given port. + */ +ofnode ofnode_graph_get_port_by_id(ofnode parent, u32 id) +{ + ofnode ports, port; + u32 port_id; + + ports = ofnode_find_subnode(parent, "ports"); + if (!ofnode_valid(ports)) + return ofnode_null(); + + /* Check ports for node with desired id */ + ofnode_for_each_subnode(port, ports) { + ofnode_read_u32(port, "reg", &port_id); + log_debug("%s: detected port %d\n", __func__, port_id); + if (port_id == id) + return port; + } + + return ofnode_null(); +} + +/** + * ofnode_graph_get_endpoint_by_regs() - get the endpoint matching a given id + * @parent: parent ofnode + * @reg_id: id of the port + * @id: id for the endpoint + * + * Return: ofnode in given endpoint or ofnode_null() if not found. + * reg_id and id are ignored when they are -1. + */ +ofnode ofnode_graph_get_endpoint_by_regs(ofnode parent, int reg_id, int id) +{ + ofnode port, endpoint; + u32 ep_id; + + /* get the port to work with */ + if (reg_id < 0) + port = ofnode_find_subnode(parent, "port"); + else + port = ofnode_graph_get_port_by_id(parent, reg_id); + + if (!ofnode_valid(port)) { + log_debug("%s: port node is not found\n", __func__); + return ofnode_null(); + } + + if (id < 0) + return ofnode_find_subnode(port, "endpoint"); + + /* Check endpoints for node with desired id */ + ofnode_for_each_subnode(endpoint, port) { + ofnode_read_u32(endpoint, "reg", &ep_id); + log_debug("%s: detected endpoint %d\n", __func__, ep_id); + if (ep_id == id) + return endpoint; + } + + return ofnode_null(); +} + +/** + * ofnode_graph_get_remote_endpoint() - get remote endpoint node + * @endpoint: ofnode of a local endpoint + * + * Return: Remote endpoint ofnode linked with local endpoint. + */ +ofnode ofnode_graph_get_remote_endpoint(ofnode endpoint) +{ + /* Get remote endpoint node. */ + return ofnode_parse_phandle(endpoint, "remote-endpoint", 0); +} + +/** + * ofnode_graph_get_port_parent() - get port's parent node + * @endpoint: ofnode of a local endpoint + * + * Return: device ofnode associated with endpoint + */ +ofnode ofnode_graph_get_port_parent(ofnode endpoint) +{ + ofnode port = ofnode_get_parent(endpoint); + ofnode parent = ofnode_get_parent(port); + + /* check if we are on top level or in ports node */ + if (!strcmp(ofnode_get_name(parent), "ports")) + parent = ofnode_get_parent(parent); + + return parent; +} + +/** + * ofnode_graph_get_remote_port_parent() - get remote port's parent ofnode + * @endpoint: ofnode of a local endpoint + * + * Return: device ofnode associated with endpoint linked to local endpoint. + */ +ofnode ofnode_graph_get_remote_port_parent(ofnode endpoint) +{ + ofnode remote_endpoint = ofnode_graph_get_remote_endpoint(endpoint); + if (!ofnode_valid(remote_endpoint)) { + log_debug("%s: remote endpoint is not found\n", __func__); + return ofnode_null(); + } + + return ofnode_graph_get_port_parent(remote_endpoint); +} + +/** + * ofnode_graph_get_remote_port() - get remote port ofnode + * @endpoint: ofnode of a local endpoint + * + * Return: port ofnode associated with remote endpoint node linked + * to local endpoint. + */ +ofnode ofnode_graph_get_remote_port(ofnode endpoint) +{ + ofnode remote_endpoint = ofnode_graph_get_remote_endpoint(endpoint); + if (!ofnode_valid(remote_endpoint)) { + log_debug("%s: remote endpoint is not found\n", __func__); + return ofnode_null(); + } + + return ofnode_get_parent(remote_endpoint); +} + +/** + * ofnode_graph_get_remote_node() - get remote parent ofnode for given port/endpoint + * @parent: parent ofnode containing graph port/endpoint + * @port: identifier (value of reg property) of the parent port ofnode + * @endpoint: identifier (value of reg property) of the endpoint ofnode + * + * Return: device ofnode associated with endpoint linked to local endpoint. + */ +ofnode ofnode_graph_get_remote_node(ofnode parent, int port, int endpoint) +{ + ofnode endpoint_ofnode; + + endpoint_ofnode = ofnode_graph_get_endpoint_by_regs(parent, port, endpoint); + if (!ofnode_valid(endpoint_ofnode)) { + log_debug("%s: endpoint is not found\n", __func__); + return ofnode_null(); + } + + return ofnode_graph_get_remote_port_parent(endpoint_ofnode); +} diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index f846a35d6b2..ce5e61bbaa6 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -16,6 +16,7 @@ #include <dm/device.h> #include <dm/device-internal.h> #include <dm/lists.h> +#include <dm/ofnode_graph.h> #include <dm/uclass.h> #include <dm/uclass-internal.h> #include <dm/util.h> @@ -582,6 +583,24 @@ int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, ret = uclass_find_device_by_phandle(id, parent, name, &dev); return uclass_get_device_tail(dev, ret, devp); } + +int uclass_get_device_by_endpoint(enum uclass_id class_id, struct udevice *dev, + int port_idx, int ep_idx, struct udevice **devp) +{ + ofnode node_source = dev_ofnode(dev); + ofnode node_dest = ofnode_graph_get_remote_node(node_source, port_idx, ep_idx); + struct udevice *target = NULL; + int ret; + + if (!ofnode_valid(node_dest)) + return -EINVAL; + + ret = uclass_find_device_by_ofnode(class_id, node_dest, &target); + if (ret) + return -ENODEV; + + return uclass_get_device_tail(target, 0, devp); +} #endif /* diff --git a/drivers/cpu/imx8_cpu.c b/drivers/cpu/imx8_cpu.c index 53d31b3c0bf..4e1eccaa5b0 100644 --- a/drivers/cpu/imx8_cpu.c +++ b/drivers/cpu/imx8_cpu.c @@ -35,11 +35,49 @@ static const char *get_imx_type_str(u32 imxtype) { switch (imxtype) { case MXC_CPU_IMX8MM: - return "8MM"; + return "8MMQ"; /* Quad-core version of the imx8mm */ + case MXC_CPU_IMX8MML: + return "8MMQL"; /* Quad-core Lite version of the imx8mm */ + case MXC_CPU_IMX8MMD: + return "8MMD"; /* Dual-core version of the imx8mm */ + case MXC_CPU_IMX8MMDL: + return "8MMDL"; /* Dual-core Lite version of the imx8mm */ + case MXC_CPU_IMX8MMS: + return "8MMS"; /* Single-core version of the imx8mm */ + case MXC_CPU_IMX8MMSL: + return "8MMSL"; /* Single-core Lite version of the imx8mm */ case MXC_CPU_IMX8MN: - return "8MN"; + return "8MNano Quad"; /* Quad-core version */ + case MXC_CPU_IMX8MND: + return "8MNano Dual"; /* Dual-core version */ + case MXC_CPU_IMX8MNS: + return "8MNano Solo"; /* Single-core version */ + case MXC_CPU_IMX8MNL: + return "8MNano QuadLite"; /* Quad-core Lite version */ + case MXC_CPU_IMX8MNDL: + return "8MNano DualLite"; /* Dual-core Lite version */ + case MXC_CPU_IMX8MNSL: + return "8MNano SoloLite";/* Single-core Lite version of the imx8mn */ + case MXC_CPU_IMX8MNUQ: + return "8MNano UltraLite Quad";/* Quad-core UltraLite version of the imx8mn */ + case MXC_CPU_IMX8MNUD: + return "8MNano UltraLite Dual";/* Dual-core UltraLite version of the imx8mn */ + case MXC_CPU_IMX8MNUS: + return "8MNano UltraLite Solo";/* Single-core UltraLite version of the imx8mn */ case MXC_CPU_IMX8MP: - return "8MP"; + return "8MP[8]"; /* Quad-core version of the imx8mp */ + case MXC_CPU_IMX8MPD: + return "8MP Dual[3]"; /* Dual-core version of the imx8mp */ + case MXC_CPU_IMX8MPL: + return "8MP Lite[4]"; /* Quad-core Lite version of the imx8mp */ + case MXC_CPU_IMX8MP6: + return "8MP[6]"; /* Quad-core version of the imx8mp, NPU fused */ + case MXC_CPU_IMX8MQ: + return "8MQ"; /* Quad-core version of the imx8mq */ + case MXC_CPU_IMX8MQL: + return "8MQLite"; /* Quad-core Lite version of the imx8mq */ + case MXC_CPU_IMX8MD: + return "8MD"; /* Dual-core version of the imx8mq */ case MXC_CPU_IMX8QXP: case MXC_CPU_IMX8QXP_A0: return "8QXP"; @@ -177,19 +215,19 @@ static int cpu_imx_get_desc(const struct udevice *dev, char *buf, int size) ret = snprintf(buf, size, "NXP i.MX%s Rev%s %s at %u MHz", plat->type, plat->rev, plat->name, plat->freq_mhz); - if (IS_ENABLED(CONFIG_IMX9)) { + if (IS_ENABLED(CONFIG_IMX_TMU)) { switch (get_cpu_temp_grade(&minc, &maxc)) { case TEMP_AUTOMOTIVE: - grade = "Automotive temperature grade "; + grade = "Automotive temperature grade"; break; case TEMP_INDUSTRIAL: - grade = "Industrial temperature grade "; + grade = "Industrial temperature grade"; break; case TEMP_EXTCOMMERCIAL: - grade = "Extended Consumer temperature grade "; + grade = "Extended Consumer temperature grade"; break; default: - grade = "Consumer temperature grade "; + grade = "Consumer temperature grade"; break; } diff --git a/drivers/ddr/altera/Makefile b/drivers/ddr/altera/Makefile index c1d6a6b6c59..c627f16711e 100644 --- a/drivers/ddr/altera/Makefile +++ b/drivers/ddr/altera/Makefile @@ -4,12 +4,13 @@ # Wolfgang Denk, DENX Software Engineering, wd@denx.de. # # (C) Copyright 2010, Thomas Chou <thomas@wytron.com.tw> -# Copyright (C) 2014-2021 Altera Corporation <www.altera.com> +# Copyright (C) 2014-2025 Altera Corporation <www.altera.com> -ifdef CONFIG_$(XPL_)ALTERA_SDRAM +ifdef CONFIG_$(PHASE_)ALTERA_SDRAM obj-$(CONFIG_TARGET_SOCFPGA_GEN5) += sdram_gen5.o sequencer.o obj-$(CONFIG_TARGET_SOCFPGA_ARRIA10) += sdram_arria10.o obj-$(CONFIG_TARGET_SOCFPGA_STRATIX10) += sdram_soc64.o sdram_s10.o obj-$(CONFIG_TARGET_SOCFPGA_AGILEX) += sdram_soc64.o sdram_agilex.o obj-$(CONFIG_TARGET_SOCFPGA_N5X) += sdram_soc64.o sdram_n5x.o +obj-$(CONFIG_TARGET_SOCFPGA_AGILEX5) += sdram_soc64.o sdram_agilex5.o iossm_mailbox.o endif diff --git a/drivers/ddr/altera/iossm_mailbox.c b/drivers/ddr/altera/iossm_mailbox.c new file mode 100644 index 00000000000..fc09dde3f9e --- /dev/null +++ b/drivers/ddr/altera/iossm_mailbox.c @@ -0,0 +1,923 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Altera Corporation <www.altera.com> + * + */ + +#include <hang.h> +#include <string.h> +#include <wait_bit.h> +#include <asm/arch/base_addr_soc64.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include <linux/sizes.h> +#include "iossm_mailbox.h" + +#define TIMEOUT_120000MS 120000 +#define TIMEOUT_60000MS 60000 +#define TIMEOUT TIMEOUT_120000MS +#define IOSSM_STATUS_CAL_SUCCESS BIT(0) +#define IOSSM_STATUS_CAL_FAIL BIT(1) +#define IOSSM_STATUS_CAL_BUSY BIT(2) +#define IOSSM_STATUS_COMMAND_RESPONSE_READY BIT(0) +#define IOSSM_CMD_RESPONSE_STATUS_OFFSET 0x45C +#define IOSSM_CMD_RESPONSE_DATA_0_OFFSET 0x458 +#define IOSSM_CMD_RESPONSE_DATA_1_OFFSET 0x454 +#define IOSSM_CMD_RESPONSE_DATA_2_OFFSET 0x450 +#define IOSSM_CMD_REQ_OFFSET 0x43C +#define IOSSM_CMD_PARAM_0_OFFSET 0x438 +#define IOSSM_CMD_PARAM_1_OFFSET 0x434 +#define IOSSM_CMD_PARAM_2_OFFSET 0x430 +#define IOSSM_CMD_PARAM_3_OFFSET 0x42C +#define IOSSM_CMD_PARAM_4_OFFSET 0x428 +#define IOSSM_CMD_PARAM_5_OFFSET 0x424 +#define IOSSM_CMD_PARAM_6_OFFSET 0x420 +#define IOSSM_CMD_RESPONSE_DATA_SHORT_MASK GENMASK(31, 16) +#define IOSSM_CMD_RESPONSE_DATA_SHORT(n) FIELD_GET(IOSSM_CMD_RESPONSE_DATA_SHORT_MASK, n) +#define IOSSM_STATUS_CMD_RESPONSE_ERROR_MASK GENMASK(7, 5) +#define IOSSM_STATUS_CMD_RESPONSE_ERROR(n) FIELD_GET(IOSSM_STATUS_CMD_RESPONSE_ERROR_MASK, n) +#define IOSSM_STATUS_GENERAL_ERROR_MASK GENMASK(4, 1) +#define IOSSM_STATUS_GENERAL_ERROR(n) FIELD_GET(IOSSM_STATUS_GENERAL_ERROR_MASK, n) + +/* Offset of Mailbox Read-only Registers */ +#define IOSSM_MAILBOX_HEADER_OFFSET 0x0 +#define IOSSM_MEM_INTF_INFO_0_OFFSET 0X200 +#define IOSSM_MEM_INTF_INFO_1_OFFSET 0x280 +#define IOSSM_MEM_TECHNOLOGY_INTF0_OFFSET 0x210 +#define IOSSM_MEM_TECHNOLOGY_INTF1_OFFSET 0x290 +#define IOSSM_MEM_WIDTH_INFO_INTF0_OFFSET 0x230 +#define IOSSM_MEM_WIDTH_INFO_INTF1_OFFSET 0x2B0 +#define IOSSM_MEM_TOTAL_CAPACITY_INTF0_OFFSET 0x234 +#define IOSSM_MEM_TOTAL_CAPACITY_INTF1_OFFSET 0x2B4 +#define IOSSM_ECC_ENABLE_INTF0_OFFSET 0x240 +#define IOSSM_ECC_ENABLE_INTF1_OFFSET 0x2C0 +#define IOSSM_ECC_SCRUB_STATUS_INTF0_OFFSET 0x244 +#define IOSSM_ECC_SCRUB_STATUS_INTF1_OFFSET 0x2C4 +#define IOSSM_LP_MODE_INTF0_OFFSET 0x250 +#define IOSSM_LP_MODE_INTF1_OFFSET 0x2D0 +#define IOSSM_MEM_INIT_STATUS_INTF0_OFFSET 0x260 +#define IOSSM_MEM_INIT_STATUS_INTF1_OFFSET 0x2E0 +#define IOSSM_BIST_STATUS_INTF0_OFFSET 0x264 +#define IOSSM_BIST_STATUS_INTF1_OFFSET 0x2E4 +#define IOSSM_ECC_ERR_STATUS_OFFSET 0x300 +#define IOSSM_ECC_ERR_DATA_START_OFFSET 0x310 +#define IOSSM_STATUS_OFFSET 0x400 +#define IOSSM_STATUS_CAL_INTF0_OFFSET 0x404 +#define IOSSM_STATUS_CAL_INTF1_OFFSET 0x408 + +#define ECC_INTSTATUS_SERR SOCFPGA_SYSMGR_ADDRESS + 0x9C +#define ECC_INISTATUS_DERR SOCFPGA_SYSMGR_ADDRESS + 0xA0 +#define DDR_CSR_CLKGEN_LOCKED_IO96B0_MASK BIT(16) +#define DDR_CSR_CLKGEN_LOCKED_IO96B1_MASK BIT(17) + +/* offset info of GET_MEM_INTF_INFO */ +#define INTF_IP_TYPE_MASK GENMASK(31, 29) +#define INTF_INSTANCE_ID_MASK GENMASK(28, 24) + +/* offset info of GET_MEM_CAL_STATUS */ +#define INTF_UNUSED 0x0 +#define INTF_MEM_CAL_STATUS_SUCCESS 0x1 +#define INTF_MEM_CAL_STATUS_FAIL 0x2 +#define INTF_MEM_CAL_STATUS_ONGOING 0x4 + +/* offset info of MEM_TECHNOLOGY_INTF */ +#define INTF_DDR_TYPE_MASK GENMASK(2, 0) + +/* offset info of MEM_TOTAL_CAPACITY_INTF */ +#define INTF_CAPACITY_GBITS_MASK GENMASK(7, 0) + +/* offset info of ECC_ENABLE_INTF */ +#define INTF_ECC_ENABLE_TYPE_MASK GENMASK(1, 0) +#define INTF_ECC_TYPE_MASK BIT(8) + +/* cmd opcode BIST_MEM_INIT_START, BIST performed on full memory address range */ +#define BIST_FULL_MEM BIT(6) + +/* offset info of ECC_ENABLE_INTF */ +#define INTF_BIST_STATUS_MASK BIT(0) + +/* offset info of ECC_ERR_STATUS */ +#define ECC_ERR_COUNTER_MASK GENMASK(15, 0) +#define ECC_ERR_OVERFLOW_MASK GENMASK(31, 16) + +/* offset info of ECC_ERR_DATA */ +#define ECC_ERR_IP_TYPE_MASK GENMASK(24, 22) +#define ECC_ERR_INSTANCE_ID_MASK GENMASK(21, 17) +#define ECC_ERR_SOURCE_ID_MASK GENMASK(16, 10) +#define ECC_ERR_TYPE_MASK GENMASK(9, 6) +#define ECC_ERR_ADDR_UPPER_MASK GENMASK(5, 0) +#define ECC_ERR_ADDR_LOWER_MASK GENMASK(31, 0) +#define ECC_FULL_ADDR_UPPER_MASK GENMASK(63, 32) +#define ECC_FULL_ADDR_LOWER_MASK GENMASK(31, 0) + +#define MAX_ECC_ERR_INFO_COUNT 16 + +#define BIST_START_ADDR_SPACE_MASK GENMASK(5, 0) +#define BIST_START_ADDR_LOW_MASK GENMASK(31, 0) +#define BIST_START_ADDR_HIGH_MASK GENMASK(37, 32) + +#define IO96B_MB_REQ_SETUP(v, w, x, y, z) \ + usr_req.ip_type = v; \ + usr_req.ip_id = w; \ + usr_req.usr_cmd_type = x; \ + usr_req.usr_cmd_opcode = y; \ + usr_req.cmd_param[0] = z; \ + for (n = 1; n < NUM_CMD_PARAM; n++) \ + usr_req.cmd_param[n] = 0 +#define MAX_RETRY_COUNT 3 +#define NUM_CMD_RESPONSE_DATA 3 + +#define IO96B0_PLL_A_MASK BIT(0) +#define IO96B0_PLL_B_MASK BIT(1) +#define IO96B1_PLL_A_MASK BIT(2) +#define IO96B1_PLL_B_MASK BIT(3) + +/* supported DDR type list */ +static const char *ddr_type_list[7] = { + "DDR4", "DDR5", "DDR5_RDIMM", "LPDDR4", "LPDDR5", "QDRIV", "UNKNOWN" +}; + +/* Define an enumeration for ECC error types */ +enum ecc_error_type { + SINGLE_BIT_ERROR = 0, /* 0b0000 */ + MULTIPLE_SINGLE_BIT_ERRORS = 1, /* 0b0001 */ + DOUBLE_BIT_ERROR = 2, /* 0b0010 */ + MULTIPLE_DOUBLE_BIT_ERRORS = 3, /* 0b0011 */ + SINGLE_BIT_ERROR_SCRUBBING = 8, /* 0b1000 */ + WRITE_LINK_SINGLE_BIT_ERROR = 9, /* 0b1001 */ + WRITE_LINK_DOUBLE_BIT_ERROR = 10, /* 0b1010 */ + READ_LINK_SINGLE_BIT_ERROR = 11, /* 0b1011 */ + READ_LINK_DOUBLE_BIT_ERROR = 12, /* 0b1100 */ + READ_MODIFY_WRITE_DOUBLE_BIT_ERROR = 13 /* 0b1101 */ +}; + +/* + * ecc error info + * + * @ip_type: The IP type of the interface that produced the ECC interrupt. + * @instance_id: The instance ID of the interface that produced the ECC interrupt. + * @ecc_err_source_id: The source ID associated with the ECC event. + * @ecc_err_type: The ECC error type of the ECC event. + * @ecc_err_addr_upper: Upper 6 bits of the address of the read data that caused the ECC event. + * @ecc_err_addr_lower: Lower 32 bits of the address of the read data that caused the ECC event. + */ +struct ecc_err_info { + u32 ip_type; + u32 instance_id; + u32 source_id; + enum ecc_error_type err_type; + u32 addr_upper; + u32 addr_lower; +}; + +struct ecc_overflow_error_desc { + int bit; + const char *msg; +}; + +static const struct ecc_overflow_error_desc ecc_overflow_errors[] = { + { 0, " - Single-bit error\n" }, + { 1, " - Multiple single-bit errors\n" }, + { 2, " - Double-bit error\n" }, + { 3, " - Multiple double-bit errors\n" }, + { 8, " - Single-bit error during ECC scrubbing\n" }, + { 9, " - Write link ECC single-bit error (LPDDR5 only)\n" }, + { 10, " - Write link ECC double-bit error (LPDDR5 only)\n" }, + { 11, " - Read link ECC single-bit error (LPDDR5 only)\n" }, + { 12, " - Read link ECC double-bit error (LPDDR5 only)\n" }, + { 13, " - RMW read link ECC double-bit error (LPDDR5 only)\n" }, +}; + +static int is_ddr_csr_clkgen_locked(u8 io96b_pll) +{ + int ret = 0; + const char *pll_names[MAX_IO96B_SUPPORTED][2] = { + {"io96b_0 clkgenA", "io96b_0 clkgenB"}, + {"io96b_1 clkgenA", "io96b_1 clkgenB"} + }; + u32 masks[MAX_IO96B_SUPPORTED][2] = { + {IO96B0_PLL_A_MASK, IO96B0_PLL_B_MASK}, + {IO96B1_PLL_A_MASK, IO96B1_PLL_B_MASK} + }; + u32 lock_masks[MAX_IO96B_SUPPORTED] = { + DDR_CSR_CLKGEN_LOCKED_IO96B0_MASK, + DDR_CSR_CLKGEN_LOCKED_IO96B1_MASK + }; + + for (int i = 0; i < MAX_IO96B_SUPPORTED ; i++) { + /* Check for PLL_A */ + if (io96b_pll & masks[i][0]) { + ret = wait_for_bit_le32((const void *)(ECC_INTSTATUS_SERR), lock_masks[i], + true, TIMEOUT, false); + + if (ret) { + debug("%s: ddr csr %s locked is timeout\n", + __func__, pll_names[i][0]); + goto err; + } else { + debug("%s: ddr csr %s is successfully locked\n", + __func__, pll_names[i][0]); + } + } + + /* Check for PLL_B */ + if (io96b_pll & masks[i][1]) { + ret = wait_for_bit_le32((const void *)(ECC_INISTATUS_DERR), lock_masks[i], + true, TIMEOUT, false); + + if (ret) { + debug("%s: ddr csr %s locked is timeout\n", + __func__, pll_names[i][1]); + goto err; + } else { + debug("%s: ddr csr %s is successfully locked\n", + __func__, pll_names[i][1]); + } + } + } + +err: + return ret; +} + +/* + * Mailbox request function + * This function will send the request to IOSSM mailbox and wait for response return + * + * @io96b_csr_addr: CSR address for the target IO96B + * @req: Structure contain command request for IOSSM mailbox command + * @resp_data_len: User desire extra response data fields other than + * CMD_RESPONSE_DATA_SHORT field on CMD_RESPONSE_STATUS + * @resp: Structure contain responses returned from the requested IOSSM + * mailbox command + */ +int io96b_mb_req(phys_addr_t io96b_csr_addr, struct io96b_mb_req req, + u32 resp_data_len, struct io96b_mb_resp *resp) +{ + int i, ret; + u32 cmd_req; + + if (!resp) { + ret = -EINVAL; + goto err; + } + + /* Zero initialization for responses */ + resp->cmd_resp_status = 0; + + /* Ensure CMD_REQ is cleared before write any command request */ + ret = wait_for_bit_le32((const void *)(io96b_csr_addr + IOSSM_CMD_REQ_OFFSET), + GENMASK(31, 0), false, TIMEOUT, false); + if (ret) { + printf("%s: Timeout of waiting DDR mailbox ready to be functioned!\n", + __func__); + goto err; + } + + /* Write CMD_PARAM_* */ + for (i = 0; i < NUM_CMD_PARAM ; i++) { + switch (i) { + case 0: + if (req.cmd_param[0]) + writel(req.cmd_param[0], io96b_csr_addr + IOSSM_CMD_PARAM_0_OFFSET); + break; + case 1: + if (req.cmd_param[1]) + writel(req.cmd_param[1], io96b_csr_addr + IOSSM_CMD_PARAM_1_OFFSET); + break; + case 2: + if (req.cmd_param[2]) + writel(req.cmd_param[2], io96b_csr_addr + IOSSM_CMD_PARAM_2_OFFSET); + break; + case 3: + if (req.cmd_param[3]) + writel(req.cmd_param[3], io96b_csr_addr + IOSSM_CMD_PARAM_3_OFFSET); + break; + case 4: + if (req.cmd_param[4]) + writel(req.cmd_param[4], io96b_csr_addr + IOSSM_CMD_PARAM_4_OFFSET); + break; + case 5: + if (req.cmd_param[5]) + writel(req.cmd_param[5], io96b_csr_addr + IOSSM_CMD_PARAM_5_OFFSET); + break; + case 6: + if (req.cmd_param[6]) + writel(req.cmd_param[6], io96b_csr_addr + IOSSM_CMD_PARAM_6_OFFSET); + break; + } + } + + /* Write CMD_REQ (IP_TYPE, IP_INSTANCE_ID, CMD_TYPE and CMD_OPCODE) */ + cmd_req = FIELD_PREP(CMD_TARGET_IP_TYPE_MASK, req.ip_type) | + FIELD_PREP(CMD_TARGET_IP_INSTANCE_ID_MASK, req.ip_id) | + FIELD_PREP(CMD_TYPE_MASK, req.usr_cmd_type) | + FIELD_PREP(CMD_OPCODE_MASK, req.usr_cmd_opcode); + writel(cmd_req, io96b_csr_addr + IOSSM_CMD_REQ_OFFSET); + + debug("%s: Write 0x%x to IOSSM_CMD_REQ_OFFSET 0x%llx\n", __func__, cmd_req, + io96b_csr_addr + IOSSM_CMD_REQ_OFFSET); + + /* Read CMD_RESPONSE_READY in CMD_RESPONSE_STATUS */ + ret = wait_for_bit_le32((const void *)(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET), + IOSSM_STATUS_COMMAND_RESPONSE_READY, true, TIMEOUT, false); + + /* read CMD_RESPONSE_STATUS */ + resp->cmd_resp_status = readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + + debug("%s: CMD_RESPONSE_STATUS 0x%llx: 0x%x\n", __func__, io96b_csr_addr + + IOSSM_CMD_RESPONSE_STATUS_OFFSET, resp->cmd_resp_status); + + if (ret) { + printf("%s: CMD_RESPONSE ERROR:\n", __func__); + + printf("%s: STATUS_GENERAL_ERROR: 0x%lx\n", __func__, + IOSSM_STATUS_GENERAL_ERROR(resp->cmd_resp_status)); + printf("%s: STATUS_CMD_RESPONSE_ERROR: 0x%lx\n", __func__, + IOSSM_STATUS_CMD_RESPONSE_ERROR(resp->cmd_resp_status)); + goto err; + } + + /* read CMD_RESPONSE_DATA_* */ + for (i = 0; i < resp_data_len; i++) { + switch (i) { + case 0: + resp->cmd_resp_data[i] = + readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_0_OFFSET); + + debug("%s: IOSSM_CMD_RESPONSE_DATA_0_OFFSET 0x%llx: 0x%x\n", __func__, + io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_0_OFFSET, + resp->cmd_resp_data[i]); + break; + case 1: + resp->cmd_resp_data[i] = + readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_1_OFFSET); + + debug("%s: IOSSM_CMD_RESPONSE_DATA_1_OFFSET 0x%llx: 0x%x\n", __func__, + io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_1_OFFSET, + resp->cmd_resp_data[i]); + break; + case 2: + resp->cmd_resp_data[i] = + readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_2_OFFSET); + + debug("%s: IOSSM_CMD_RESPONSE_DATA_2_OFFSET 0x%llx: 0x%x\n", __func__, + io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_2_OFFSET, + resp->cmd_resp_data[i]); + break; + default: + resp->cmd_resp_data[i] = 0; + printf("%s: Invalid response data\n", __func__); + } + } + + /* write CMD_RESPONSE_READY = 0 */ + clrbits_le32((u32 *)(uintptr_t)(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET), + IOSSM_STATUS_COMMAND_RESPONSE_READY); + + debug("%s: After clear CMD_RESPONSE_READY bit: 0x%llx: 0x%x\n", __func__, + io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET, + readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET)); + +err: + return ret; +} + +/* + * Initial function to be called to set memory interface IP type and instance ID + * IP type and instance ID need to be determined before sending mailbox command + */ +void io96b_mb_init(struct io96b_info *io96b_ctrl) +{ + int i, j; + u32 mem_intf_info_0, mem_intf_info_1; + + debug("%s: num_instance %d\n", __func__, io96b_ctrl->num_instance); + + for (i = 0; i < io96b_ctrl->num_instance; i++) { + debug("%s: get memory interface IO96B %d\n", __func__, i); + io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface = 0; + + mem_intf_info_0 = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + IOSSM_MEM_INTF_INFO_0_OFFSET); + mem_intf_info_1 = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + IOSSM_MEM_INTF_INFO_1_OFFSET); + + io96b_ctrl->io96b[i].mb_ctrl.ip_type[0] = FIELD_GET(INTF_IP_TYPE_MASK, + mem_intf_info_0); + io96b_ctrl->io96b[i].mb_ctrl.ip_id[0] = FIELD_GET(INTF_INSTANCE_ID_MASK, + mem_intf_info_0); + io96b_ctrl->io96b[i].mb_ctrl.ip_type[1] = FIELD_GET(INTF_IP_TYPE_MASK, + mem_intf_info_1); + io96b_ctrl->io96b[i].mb_ctrl.ip_id[1] = FIELD_GET(INTF_INSTANCE_ID_MASK, + mem_intf_info_1); + + for (j = 0; j < MAX_MEM_INTERFACE_SUPPORTED; j++) { + if (io96b_ctrl->io96b[i].mb_ctrl.ip_type[j]) { + io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface++; + + debug("%s: IO96B %d mem_interface %d: ip_type_ret: 0x%x\n", + __func__, i, j, io96b_ctrl->io96b[i].mb_ctrl.ip_type[j]); + debug("%s: IO96B %d mem_interface %d: instance_id_ret: 0x%x\n", + __func__, i, j, io96b_ctrl->io96b[i].mb_ctrl.ip_id[j]); + } + } + + debug("%s: IO96B %d: num_mem_interface: 0x%x\n", __func__, i, + io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface); + } +} + +int io96b_cal_status(phys_addr_t addr) +{ + u32 cal_success, cal_fail; + phys_addr_t status_addr = addr + IOSSM_STATUS_OFFSET; + u32 start = get_timer(0); + + do { + if (get_timer(start) > TIMEOUT_60000MS) { + printf("%s: SDRAM calibration for IO96B instance 0x%llx timeout!\n", + __func__, status_addr); + hang(); + } + + udelay(1); + schedule(); + + /* Polling until getting any calibration result */ + cal_success = readl(status_addr) & IOSSM_STATUS_CAL_SUCCESS; + cal_fail = readl(status_addr) & IOSSM_STATUS_CAL_FAIL; + } while (!cal_success && !cal_fail); + + debug("%s: Calibration for IO96B instance 0x%llx done at %ld msec!\n", + __func__, status_addr, get_timer(start)); + + if (cal_success && !cal_fail) + return 0; + else + return -EPERM; +} + +void init_mem_cal(struct io96b_info *io96b_ctrl) +{ + int count, i, ret; + + /* Initialize overall calibration status */ + io96b_ctrl->overall_cal_status = false; + + if (io96b_ctrl->ckgen_lock) { + ret = is_ddr_csr_clkgen_locked(io96b_ctrl->io96b_pll); + if (ret) { + printf("%s: iossm IO96B ckgena_lock is not locked\n", __func__); + hang(); + } + } + + /* Check initial calibration status for the assigned IO96B */ + count = 0; + for (i = 0; i < io96b_ctrl->num_instance; i++) { + ret = io96b_cal_status(io96b_ctrl->io96b[i].io96b_csr_addr); + if (ret) { + io96b_ctrl->io96b[i].cal_status = false; + + printf("%s: Initial DDR calibration IO96B_%d failed %d\n", __func__, + i, ret); + + hang(); + } + + io96b_ctrl->io96b[i].cal_status = true; + + printf("%s: Initial DDR calibration IO96B_%d succeed\n", __func__, i); + + count++; + } + + if (count == io96b_ctrl->num_instance) + io96b_ctrl->overall_cal_status = true; +} + +int get_mem_technology(struct io96b_info *io96b_ctrl) +{ + int i, j, ret = 0; + u32 mem_technology_intf; + u8 ddr_type_ret; + + u32 mem_technology_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = { + IOSSM_MEM_TECHNOLOGY_INTF0_OFFSET, + IOSSM_MEM_TECHNOLOGY_INTF1_OFFSET + }; + + /* Initialize ddr type */ + io96b_ctrl->ddr_type = ddr_type_list[6]; + + /* Get and ensure all memory interface(s) same DDR type */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { + mem_technology_intf = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + mem_technology_intf_offset[j]); + + ddr_type_ret = FIELD_GET(INTF_DDR_TYPE_MASK, mem_technology_intf); + + if (!strcmp(io96b_ctrl->ddr_type, "UNKNOWN")) + io96b_ctrl->ddr_type = ddr_type_list[ddr_type_ret]; + + if (ddr_type_list[ddr_type_ret] != io96b_ctrl->ddr_type) { + printf("%s: Mismatch DDR type on IO96B_%d\n", __func__, i); + + ret = -EINVAL; + goto err; + } + } + } + +err: + return ret; +} + +int get_mem_width_info(struct io96b_info *io96b_ctrl) +{ + int i, j, ret = 0; + u32 mem_width_info; + phys_size_t memory_size, total_memory_size = 0; + + u32 mem_total_capacity_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = { + IOSSM_MEM_TOTAL_CAPACITY_INTF0_OFFSET, + IOSSM_MEM_TOTAL_CAPACITY_INTF1_OFFSET + }; + + /* Get all memory interface(s) total memory size on all instance(s) */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + memory_size = 0; + for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { + mem_width_info = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + mem_total_capacity_intf_offset[j]); + + io96b_ctrl->io96b[i].mb_ctrl.memory_size[j] = + FIELD_GET(INTF_CAPACITY_GBITS_MASK, mem_width_info) * SZ_1G / SZ_8; + + if (io96b_ctrl->io96b[i].mb_ctrl.memory_size[j] != 0) + memory_size += io96b_ctrl->io96b[i].mb_ctrl.memory_size[j]; + } + + if (!memory_size) { + printf("%s: Failed to get valid memory size\n", __func__); + ret = -EINVAL; + goto err; + } + + total_memory_size = total_memory_size + memory_size; + } + + if (!total_memory_size) { + printf("%s: Failed to get valid memory size\n", __func__); + ret = -EINVAL; + } + + io96b_ctrl->overall_size = total_memory_size; + +err: + return ret; +} + +int ecc_enable_status(struct io96b_info *io96b_ctrl) +{ + int i, j, ret = 0; + u32 ecc_enable_intf; + bool ecc_status, ecc_status_set = false, inline_ecc = false; + + u32 ecc_enable_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = { + IOSSM_ECC_ENABLE_INTF0_OFFSET, + IOSSM_ECC_ENABLE_INTF1_OFFSET + }; + + /* Initialize ECC status */ + io96b_ctrl->ecc_status = false; + io96b_ctrl->inline_ecc = false; + + /* Get and ensure all memory interface(s) same ECC status */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { + ecc_enable_intf = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + ecc_enable_intf_offset[j]); + + ecc_status = (FIELD_GET(INTF_ECC_ENABLE_TYPE_MASK, ecc_enable_intf) + == 0) ? false : true; + inline_ecc = FIELD_GET(INTF_ECC_TYPE_MASK, ecc_enable_intf); + + if (!ecc_status_set) { + io96b_ctrl->ecc_status = ecc_status; + + if (io96b_ctrl->ecc_status) + io96b_ctrl->inline_ecc = inline_ecc; + + ecc_status_set = true; + } + + if (ecc_status != io96b_ctrl->ecc_status || + (io96b_ctrl->ecc_status && inline_ecc != io96b_ctrl->inline_ecc)) { + printf("%s: Mismatch DDR ECC status on IO96B_%d\n", __func__, i); + + ret = -EINVAL; + goto err; + } + } + } + + debug("%s: ECC enable status: %d\n", __func__, io96b_ctrl->ecc_status); + +err: + return ret; +} + +bool is_double_bit_error(enum ecc_error_type err_type) +{ + switch (err_type) { + case DOUBLE_BIT_ERROR: + case MULTIPLE_DOUBLE_BIT_ERRORS: + case WRITE_LINK_DOUBLE_BIT_ERROR: + case READ_LINK_DOUBLE_BIT_ERROR: + case READ_MODIFY_WRITE_DOUBLE_BIT_ERROR: + return true; + + default: + return false; + } +} + +bool ecc_interrupt_status(struct io96b_info *io96b_ctrl) +{ + int i, j; + u32 ecc_err_status; + u16 ecc_err_counter, ecc_overflow_status; + bool ecc_error_flag = false; + + /* Get ECC double-bit error status */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + ecc_err_status = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + IOSSM_ECC_ERR_STATUS_OFFSET); + + ecc_err_counter = FIELD_GET(ECC_ERR_COUNTER_MASK, ecc_err_status); + log_err("%s: ECC error number detected on IO96B_%d: %d\n", + __func__, i, ecc_err_counter); + + ecc_overflow_status = FIELD_GET(ECC_ERR_OVERFLOW_MASK, ecc_err_status); + if (ecc_overflow_status != 0) { + log_err("ECC Error Overflow Flags:\n"); + + for (int i = 0; i < ARRAY_SIZE(ecc_overflow_errors); i++) { + if (ecc_overflow_status & BIT(ecc_overflow_errors[i].bit)) { + log_err("%s", ecc_overflow_errors[i].msg); + } + } + } + + if (ecc_err_counter != 0) { + phys_addr_t address; + u32 ecc_err_data; + struct ecc_err_info err_info; + + address = io96b_ctrl->io96b[i].io96b_csr_addr + + IOSSM_ECC_ERR_DATA_START_OFFSET; + + for (j = 0; j < ecc_err_counter && j < MAX_ECC_ERR_INFO_COUNT; j++) { + ecc_err_data = readl(address); + err_info.err_type = FIELD_GET(ECC_ERR_TYPE_MASK, + ecc_err_data); + err_info.ip_type = FIELD_GET(ECC_ERR_IP_TYPE_MASK, + ecc_err_data); + err_info.instance_id = FIELD_GET(ECC_ERR_INSTANCE_ID_MASK, + ecc_err_data); + err_info.source_id = FIELD_GET(ECC_ERR_SOURCE_ID_MASK, + ecc_err_data); + err_info.addr_upper = FIELD_GET(ECC_ERR_ADDR_UPPER_MASK, + ecc_err_data); + err_info.addr_lower = readl(address + sizeof(u32)); + + log_err(" %s: DDR ECC Error Detected on IO96B_%d number:%d\n", + __func__, i, j); + log_err(" - error info address :0x%llx\n", address); + log_err(" - error ip type: %d\n", err_info.ip_type); + log_err(" - error instance id: %d\n", err_info.instance_id); + log_err(" - error source id: %d\n", err_info.source_id); + log_err(" - error type: %s\n", + is_double_bit_error(err_info.err_type) ? + "Double-bit error" : "Single-bit error"); + log_err(" - error address: 0x%016llx\n", + (u64)FIELD_PREP(ECC_FULL_ADDR_UPPER_MASK, + err_info.addr_upper) | + FIELD_PREP(ECC_FULL_ADDR_LOWER_MASK, + err_info.addr_lower)); + + if (is_double_bit_error(err_info.err_type)) { + if (!ecc_error_flag) + ecc_error_flag = true; + } + + address += sizeof(u32) * 2; + } + } + } + + if (ecc_error_flag) + log_err("\n%s: ECC double-bit error detected!\n", __func__); + + return ecc_error_flag; +} + +int out_of_band_bist_mem_init_start(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_req usr_req; + struct io96b_mb_resp usr_resp; + int i, j, n, ret = 0; + bool bist_start, bist_success; + u32 mem_init_status_intf, start; + + u32 mem_init_status_offset[MAX_MEM_INTERFACE_SUPPORTED] = { + IOSSM_MEM_INIT_STATUS_INTF0_OFFSET, + IOSSM_MEM_INIT_STATUS_INTF1_OFFSET + }; + + /* Full memory initialization BIST performed on all memory interface(s) */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { + bist_start = false; + bist_success = false; + + /* Start memory initialization BIST on full memory address */ + IO96B_MB_REQ_SETUP(io96b_ctrl->io96b[i].mb_ctrl.ip_type[j], + io96b_ctrl->io96b[i].mb_ctrl.ip_id[j], + CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_START, + BIST_FULL_MEM); + + ret = io96b_mb_req(io96b_ctrl->io96b[i].io96b_csr_addr, + usr_req, 0, &usr_resp); + if (ret) + goto err; + + bist_start = IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & BIT(0); + + if (!bist_start) { + printf("%s: Failed to initialize memory on IO96B_%d\n", __func__, + i); + printf("%s: BIST_MEM_INIT_START Error code 0x%lx\n", __func__, + IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_status)); + + ret = -EINVAL; + goto err; + } + + /* Polling for the initiated memory initialization BIST status */ + start = get_timer(0); + while (!bist_success) { + udelay(1); + + mem_init_status_intf = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + mem_init_status_offset[j]); + + bist_success = FIELD_GET(INTF_BIST_STATUS_MASK, + mem_init_status_intf); + + if (!bist_success && (get_timer(start) > TIMEOUT)) { + printf("%s: Timeout initialize memory on IO96B_%d\n", + __func__, i); + printf("%s: BIST_MEM_INIT_STATUS Error code 0x%lx\n", + __func__, + IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_status)); + + ret = -ETIMEDOUT; + goto err; + } + } + } + + debug("%s: Memory initialized successfully on IO96B_%d\n", __func__, i); + } + +err: + return ret; +} + +int bist_mem_init_by_addr(struct io96b_info *io96b_ctrl, int inst_id, int intf_id, + phys_addr_t base_addr, phys_size_t size) +{ + struct io96b_mb_req usr_req; + struct io96b_mb_resp usr_resp; + int n, ret = 0; + bool bist_start, bist_success; + u32 mem_exp, mem_init_status_intf, start; + phys_size_t chunk_size; + + u32 mem_init_status_offset[MAX_MEM_INTERFACE_SUPPORTED] = { + IOSSM_MEM_INIT_STATUS_INTF0_OFFSET, + IOSSM_MEM_INIT_STATUS_INTF1_OFFSET + }; + + /* Check if size is a power of 2 */ + if (size == 0 || (size & (size - 1)) != 0) { + ret = -EINVAL; + goto err; + } + + mem_exp = 0; + chunk_size = size; + + while (chunk_size >>= 1) + mem_exp++; + + /* Start memory initialization BIST on the specified address range */ + IO96B_MB_REQ_SETUP(io96b_ctrl->io96b[inst_id].mb_ctrl.ip_type[intf_id], + io96b_ctrl->io96b[inst_id].mb_ctrl.ip_id[intf_id], + CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_START, 0); + + /* CMD_PARAM_0 bit[5:0] = mem_exp */ + /* CMD_PARAM_0 bit[6]: 0 - on the specified address range */ + usr_req.cmd_param[0] = FIELD_PREP(BIST_START_ADDR_SPACE_MASK, mem_exp); + /* Extract address fields START_ADDR[31:0] */ + usr_req.cmd_param[1] = FIELD_GET(BIST_START_ADDR_LOW_MASK, base_addr); + /* Extract address fields START_ADDR[37:32] */ + usr_req.cmd_param[2] = FIELD_GET(BIST_START_ADDR_HIGH_MASK, base_addr); + /* Initialize memory to all zeros */ + usr_req.cmd_param[3] = 0; + + bist_start = false; + bist_success = false; + + /* Send request to DDR controller */ + debug("%s:Initializing memory: Addr=0x%llx, Size=2^%u\n", __func__, + base_addr, mem_exp); + ret = io96b_mb_req(io96b_ctrl->io96b[inst_id].io96b_csr_addr, + usr_req, 0, &usr_resp); + if (ret) + goto err; + + bist_start = IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & BIT(0); + + if (!bist_start) { + printf("%s: Failed to initialize memory on IO96B_%d\n", __func__, + inst_id); + printf("%s: BIST_MEM_INIT_START Error code 0x%lx\n", __func__, + IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_status)); + + ret = -EINVAL; + goto err; + } + + /* Polling for the initiated memory initialization BIST status */ + start = get_timer(0); + while (!bist_success) { + udelay(1); + + mem_init_status_intf = readl(io96b_ctrl->io96b[inst_id].io96b_csr_addr + + mem_init_status_offset[intf_id]); + + bist_success = FIELD_GET(INTF_BIST_STATUS_MASK, mem_init_status_intf); + + if (!bist_success && (get_timer(start) > TIMEOUT)) { + printf("%s: Timeout initialize memory on IO96B_%d\n", + __func__, inst_id); + printf("%s: BIST_MEM_INIT_STATUS Error code 0x%lx\n", + __func__, + IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_status)); + + ret = -ETIMEDOUT; + goto err; + } + } + + debug("%s:DDR memory initializationat 0x%llx completed.\n", __func__, base_addr); + +err: + return ret; +} + +int inline_ecc_bist_mem_init(struct io96b_info *io96b_ctrl) +{ + int i, j, ret = 0; + + /* Memory initialization BIST performed on all memory interfaces */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { + ret = bist_mem_init_by_addr(io96b_ctrl, i, j, 0, + io96b_ctrl->io96b[i].mb_ctrl.memory_size[j]); + if (ret) { + printf("Error: Memory init failed at Instance %d, Interface %d\n", + i, j); + goto err; + } + } + } + +err: + return ret; +} + +int bist_mem_init_start(struct io96b_info *io96b_ctrl) +{ + if (io96b_ctrl->inline_ecc) + return inline_ecc_bist_mem_init(io96b_ctrl); + else + return out_of_band_bist_mem_init_start(io96b_ctrl); +} diff --git a/drivers/ddr/altera/iossm_mailbox.h b/drivers/ddr/altera/iossm_mailbox.h new file mode 100644 index 00000000000..02d1db28e20 --- /dev/null +++ b/drivers/ddr/altera/iossm_mailbox.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 Altera Corporation <www.altera.com> + * + */ + +#define MAX_IO96B_SUPPORTED 2 +#define MAX_MEM_INTERFACE_SUPPORTED 2 +#define NUM_CMD_RESPONSE_DATA 3 +#define NUM_CMD_PARAM 7 + +/* supported mailbox command type */ +enum iossm_mailbox_cmd_type { + CMD_NOP, + CMD_GET_SYS_INFO, + CMD_GET_MEM_INFO, + CMD_GET_MEM_CAL_INFO, + CMD_TRIG_CONTROLLER_OP, + CMD_TRIG_MEM_CAL_OP +}; + +/* supported mailbox command opcode */ +enum iossm_mailbox_cmd_opcode { + ECC_ENABLE_SET = 0x0101, + ECC_INTERRUPT_MASK = 0x0105, + ECC_WRITEBACK_ENABLE = 0x0106, + ECC_INJECT_ERROR = 0x0109, + ECC_SCRUB_MODE_0_START = 0x0202, + ECC_SCRUB_MODE_1_START = 0x0203, + BIST_STANDARD_MODE_START = 0x0301, + BIST_MEM_INIT_START = 0x0303, + BIST_SET_DATA_PATTERN_UPPER = 0x0305, + BIST_SET_DATA_PATTERN_LOWER = 0x0306, + TRIG_MEM_CAL = 0x000a +}; + +/* + * IOSSM mailbox required information + * + * @num_mem_interface: Number of memory interfaces instantiated + * @ip_type: IP type implemented on the IO96B + * @ip_instance_id: IP identifier for every IP instance implemented on the IO96B + * @memory_size[2]: Memory size for every IP instance implemented on the IO96B + */ +struct io96b_mb_ctrl { + u32 num_mem_interface; + u32 ip_type[2]; + u32 ip_id[2]; + phys_size_t memory_size[2]; +}; + +/* CMD_REQ Register Definition */ +#define CMD_TARGET_IP_TYPE_MASK GENMASK(31, 29) +#define CMD_TARGET_IP_INSTANCE_ID_MASK GENMASK(28, 24) +#define CMD_TYPE_MASK GENMASK(23, 16) +#define CMD_OPCODE_MASK GENMASK(15, 0) + +/* Computes the Inline ECC data region size */ +#define CALC_INLINE_ECC_HW_SIZE(size) (((size) * 7) / 8) + +/* + * IOSSM mailbox request + * @ip_type: IP type for the specified memory interface + * @ip_id: IP instance ID for the specified memory interface + * @usr_cmd_type: User desire IOSSM mailbox command type + * @usr_cmd_opcode: User desire IOSSM mailbox command opcode + * @cmd_param_*: Parameters (if applicable) for the requested IOSSM mailbox command + */ +struct io96b_mb_req { + u32 ip_type; + u32 ip_id; + u32 usr_cmd_type; + u32 usr_cmd_opcode; + u32 cmd_param[NUM_CMD_PARAM]; +}; + +/* + * IOSSM mailbox response outputs + * + * @cmd_resp_status: Command Interface status + * @cmd_resp_data_*: More spaces for command response + */ +struct io96b_mb_resp { + u32 cmd_resp_status; + u32 cmd_resp_data[NUM_CMD_RESPONSE_DATA]; +}; + +/* + * IO96B instance specific information + * + * @io96b_csr_addr: IO96B instance CSR address + * @cal_status: IO96B instance calibration status + * @mb_ctrl: IOSSM mailbox required information + */ +struct io96b_instance { + phys_addr_t io96b_csr_addr; + bool cal_status; + struct io96b_mb_ctrl mb_ctrl; +}; + +/* + * Overall IO96B instance(s) information + * + * @num_instance: Number of instance(s) assigned to HPS + * @overall_cal_status: Overall calibration status for all IO96B instance(s) + * @ddr_type: DDR memory type + * @ecc_status: ECC enable status (false = disabled, true = enabled) + * @inline_ecc: Inline ECC or Out of Band ECC (false = Out of Band ECC, true = Inline ECC) + * @overall_size: Total DDR memory size + * @io96b[]: IO96B instance specific information + * @ckgen_lock: IO96B GEN PLL lock (false = not locked, true = locked) + * @num_port: Number of IO96B port. + * @io96b_pll: Selected IO96B PLL. Example bit 0: EMIF0 PLL A selected, + * bit 1: EMIF0 PLL B selected, bit 2 - EMIF1 PLL A selected, + * bit 3: EMIF1 PLL B selected + */ +struct io96b_info { + u8 num_instance; + bool overall_cal_status; + const char *ddr_type; + bool ecc_status; + bool inline_ecc; + phys_size_t overall_size; + struct io96b_instance io96b[MAX_IO96B_SUPPORTED]; + bool ckgen_lock; + u8 num_port; + u8 io96b_pll; +}; + +int io96b_mb_req(phys_addr_t io96b_csr_addr, struct io96b_mb_req req, + u32 resp_data_len, struct io96b_mb_resp *resp); + +/* Supported IOSSM mailbox function */ +void io96b_mb_init(struct io96b_info *io96b_ctrl); +int io96b_cal_status(phys_addr_t addr); +void init_mem_cal(struct io96b_info *io96b_ctrl); +int get_mem_technology(struct io96b_info *io96b_ctrl); +int get_mem_width_info(struct io96b_info *io96b_ctrl); +int ecc_enable_status(struct io96b_info *io96b_ctrl); +int bist_mem_init_start(struct io96b_info *io96b_ctrl); +bool ecc_interrupt_status(struct io96b_info *io96b_ctrl); diff --git a/drivers/ddr/altera/sdram_agilex5.c b/drivers/ddr/altera/sdram_agilex5.c new file mode 100644 index 00000000000..ee66c72157a --- /dev/null +++ b/drivers/ddr/altera/sdram_agilex5.c @@ -0,0 +1,423 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Altera Corporation <www.altera.com> + * + */ + +#include <stdlib.h> +#include <div64.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <hang.h> +#include <log.h> +#include <ram.h> +#include <reset.h> +#include <wait_bit.h> +#include <wdt.h> +#include <linux/bitfield.h> +#include <linux/sizes.h> +#include <asm/arch/firewall.h> +#include <asm/arch/reset_manager.h> +#include <asm/arch/system_manager.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include "iossm_mailbox.h" +#include "sdram_soc64.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* MPFE NOC registers */ +#define F2SDRAM_SIDEBAND_FLAGOUTSET0 0x50 +#define F2SDRAM_SIDEBAND_FLAGOUTSTATUS0 0x58 +#define SIDEBANDMGR_FLAGOUTSET0_REG SOCFPGA_F2SDRAM_MGR_ADDRESS +\ + F2SDRAM_SIDEBAND_FLAGOUTSET0 +#define SIDEBANDMGR_FLAGOUTSTATUS0_REG SOCFPGA_F2SDRAM_MGR_ADDRESS +\ + F2SDRAM_SIDEBAND_FLAGOUTSTATUS0 +#define BOOT_SCRATCH_COLD3_REG (socfpga_get_sysmgr_addr() +\ + SYSMGR_SOC64_BOOT_SCRATCH_COLD3) +#define PORT_EMIF_CONFIG_OFFSET 4 +#define EMIF_PLL_MASK GENMASK(19, 16) + +#define IO96B0_DUAL_PORT_MASK BIT(0) +#define IO96B0_DUAL_EMIF_MASK BIT(1) + +#define FIREWALL_MPFE_SCR_IO96B0_REG 0x18000d00 +#define FIREWALL_MPFE_SCR_IO96B1_REG 0x18000d04 +#define FIREWALL_MPFE_NOC_CSR_REG 0x18000d08 + +#define MEMORY_BANK_MAX_COUNT 3 + +/* Reset type */ +enum reset_type { + POR_RESET, + WARM_RESET, + COLD_RESET, + NCONFIG, + JTAG_CONFIG, + RSU_RECONFIG +}; + +phys_addr_t io96b_csr_reg_addr[] = { + 0x18400000, /* IO96B_0 CSR registers address */ + 0x18800000 /* IO96B_1 CSR registers address */ +}; + +struct dram_bank_info_s { + phys_addr_t start; + phys_size_t max_size; +}; + +struct dram_bank_info_s dram_bank_info[MEMORY_BANK_MAX_COUNT] = { + {0x80000000, 0x80000000}, /* Memory Bank 0 */ + {0x880000000, 0x780000000}, /* Memory Bank 1 */ + {0x8800000000, 0x7800000000} /* Memory Bank 2 */ +}; + +static enum reset_type get_reset_type(u32 reg) +{ + return FIELD_GET(ALT_SYSMGR_SCRATCH_REG_3_DDR_RESET_TYPE_MASK, reg); +} + +static void update_io96b_assigned_to_hps(bool dual_port_flag, bool dual_emif_flag) +{ + clrsetbits_le32(BOOT_SCRATCH_COLD3_REG, + ALT_SYSMGR_SCRATCH_REG_3_DDR_PORT_EMIF_INFO_MASK, + FIELD_PREP(ALT_SYSMGR_SCRATCH_REG_3_DDR_PORT_INFO_MASK, dual_port_flag) | + FIELD_PREP(ALT_SYSMGR_SCRATCH_REG_3_DDR_EMIF_INFO_MASK, dual_emif_flag)); + + debug("%s: update dual port dual emif info: 0x%x\n", __func__, + readl(BOOT_SCRATCH_COLD3_REG)); +} + +static void set_mpfe_config(void) +{ + /* Set mpfe_lite_intfcsel */ + setbits_le32(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_MPFE_CONFIG, BIT(2)); + + /* Set mpfe_lite_active */ + setbits_le32(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_MPFE_CONFIG, BIT(8)); + + debug("%s: mpfe_config: 0x%x\n", __func__, + readl(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_MPFE_CONFIG)); +} + +static bool is_ddr_init_hang(void) +{ + u32 reg = readl(socfpga_get_sysmgr_addr() + + SYSMGR_SOC64_BOOT_SCRATCH_POR0); + + debug("%s: 0x%x\n", __func__, reg); + + if (reg & ALT_SYSMGR_SCRATCH_REG_POR_0_DDR_PROGRESS_MASK) + return true; + + return false; +} + +static void ddr_init_inprogress(bool start) +{ + if (start) + setbits_le32(socfpga_get_sysmgr_addr() + + SYSMGR_SOC64_BOOT_SCRATCH_POR0, + ALT_SYSMGR_SCRATCH_REG_POR_0_DDR_PROGRESS_MASK); + else + clrbits_le32(socfpga_get_sysmgr_addr() + + SYSMGR_SOC64_BOOT_SCRATCH_POR0, + ALT_SYSMGR_SCRATCH_REG_POR_0_DDR_PROGRESS_MASK); +} + +static void populate_ddr_handoff(struct udevice *dev, struct io96b_info *io96b_ctrl) +{ + struct altera_sdram_plat *plat = dev_get_plat(dev); + int i; + u32 len = SOC64_HANDOFF_SDRAM_LEN; + u32 handoff_table[len]; + + /* Read handoff for DDR configuration */ + socfpga_handoff_read((void *)SOC64_HANDOFF_SDRAM, handoff_table, len); + + /* Read handoff - dual port */ + plat->dualport = FIELD_GET(IO96B0_DUAL_PORT_MASK, handoff_table[PORT_EMIF_CONFIG_OFFSET]); + debug("%s: dualport from handoff: 0x%x\n", __func__, plat->dualport); + + if (plat->dualport) + io96b_ctrl->num_port = 2; + else + io96b_ctrl->num_port = 1; + + /* Read handoff - dual EMIF */ + plat->dualemif = FIELD_GET(IO96B0_DUAL_EMIF_MASK, handoff_table[PORT_EMIF_CONFIG_OFFSET]); + debug("%s: dualemif from handoff: 0x%x\n", __func__, plat->dualemif); + + if (plat->dualemif) + io96b_ctrl->num_instance = 2; + else + io96b_ctrl->num_instance = 1; + + io96b_ctrl->io96b_pll = FIELD_GET(EMIF_PLL_MASK, + handoff_table[PORT_EMIF_CONFIG_OFFSET]); + debug("%s: io96b enabled pll from handoff: 0x%x\n", __func__, io96b_ctrl->io96b_pll); + + update_io96b_assigned_to_hps(plat->dualport, plat->dualemif); + + /* Assign IO96B CSR base address if it is valid */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + io96b_ctrl->io96b[i].io96b_csr_addr = io96b_csr_reg_addr[i]; + debug("%s: IO96B 0x%llx CSR enabled\n", __func__, + io96b_ctrl->io96b[i].io96b_csr_addr); + } +} + +static void config_mpfe_sideband_mgr(struct udevice *dev) +{ + struct altera_sdram_plat *plat = dev_get_plat(dev); + + /* Dual port setting */ + if (plat->dualport) + setbits_le32(SIDEBANDMGR_FLAGOUTSET0_REG, BIT(4)); + + /* Dual EMIF setting */ + if (plat->dualemif) { + set_mpfe_config(); + setbits_le32(SIDEBANDMGR_FLAGOUTSET0_REG, BIT(5)); + } + + debug("%s: SIDEBANDMGR_FLAGOUTSTATUS0: 0x%x\n", __func__, + readl(SIDEBANDMGR_FLAGOUTSTATUS0_REG)); +} + +static void config_ccu_mgr(struct udevice *dev) +{ + int ret = 0; + struct altera_sdram_plat *plat = dev_get_plat(dev); + + if (plat->dualport || plat->dualemif) { + debug("%s: config interleaving on ccu reg\n", __func__); + ret = uclass_get_device_by_name(UCLASS_NOP, + "socfpga-ccu-ddr-interleaving-on", &dev); + } else { + debug("%s: config interleaving off ccu reg\n", __func__); + ret = uclass_get_device_by_name(UCLASS_NOP, + "socfpga-ccu-ddr-interleaving-off", &dev); + } + + if (ret) { + printf("interleaving on/off ccu settings init failed: %d\n", ret); + hang(); + } +} + +static void config_firewall_mpfe_csr(struct udevice *dev) +{ + int ret = 0; + + debug("%s: config Firewall setting for MPFE CSR\n", __func__); + ret = uclass_get_device_by_name(UCLASS_NOP, + "socfpga-noc-fw-mpfe-csr", &dev); + + if (ret) { + printf("Firewall setting for MPFE CSR init failed: %d\n", ret); + hang(); + } +} + +static bool hps_ocram_dbe_status(void) +{ + u32 reg = readl(BOOT_SCRATCH_COLD3_REG); + + if (reg & ALT_SYSMGR_SCRATCH_REG_3_OCRAM_DBE_MASK) + return true; + + return false; +} + +int sdram_mmr_init_full(struct udevice *dev) +{ + int i, ret = 0; + phys_size_t hw_size; + struct altera_sdram_plat *plat = dev_get_plat(dev); + struct altera_sdram_priv *priv = dev_get_priv(dev); + struct io96b_info *io96b_ctrl = malloc(sizeof(*io96b_ctrl)); + + u32 reg = readl(BOOT_SCRATCH_COLD3_REG); + enum reset_type reset_t = get_reset_type(reg); + bool full_mem_init = false; + + /* DDR initialization progress status tracking */ + bool is_ddr_hang_be4_rst = is_ddr_init_hang(); + + debug("DDR: SDRAM init in progress ...\n"); + ddr_init_inprogress(true); + + gd->bd = (struct bd_info *)malloc(sizeof(struct bd_info)); + memset(gd->bd, '\0', sizeof(struct bd_info)); + + debug("DDR: Address MPFE 0x%llx\n", plat->mpfe_base_addr); + + /* Populating DDR handoff data */ + debug("DDR: Checking SDRAM configuration in progress ...\n"); + populate_ddr_handoff(dev, io96b_ctrl); + + /* Configuring MPFE sideband manager registers - dual port & dual emif */ + config_mpfe_sideband_mgr(dev); + + /* Configuring Interleave/Non-interleave ccu registers */ + config_ccu_mgr(dev); + + /* Configure if polling is needed for IO96B GEN PLL locked */ + io96b_ctrl->ckgen_lock = true; + + /* Ensure calibration status passing */ + init_mem_cal(io96b_ctrl); + + printf("DDR: Calibration success\n"); + + /* Initiate IOSSM mailbox */ + io96b_mb_init(io96b_ctrl); + + /* DDR type, DDR size and ECC status) */ + ret = get_mem_technology(io96b_ctrl); + if (ret) { + printf("DDR: Failed to get DDR type\n"); + + goto err; + } + + ret = get_mem_width_info(io96b_ctrl); + if (ret) { + printf("DDR: Failed to get DDR size\n"); + + goto err; + } + + ret = ecc_enable_status(io96b_ctrl); + if (ret) { + printf("DDR: Failed to get ECC enabled status\n"); + + goto err; + } + + hw_size = io96b_ctrl->overall_size; + + /* Get bank configuration from devicetree */ + ret = fdtdec_decode_ram_size(gd->fdt_blob, NULL, 0, NULL, + (phys_size_t *)&gd->ram_size, gd->bd); + if (ret) { + puts("DDR: Failed to decode memory node\n"); + ret = -ENXIO; + + goto err; + } + + if (io96b_ctrl->inline_ecc) + hw_size = CALC_INLINE_ECC_HW_SIZE(hw_size); + + if (gd->ram_size > hw_size) { + printf("DDR: Warning: DRAM size from device tree (%lld MiB) exceeds\n", + gd->ram_size >> 20); + printf(" the actual hardware capacity(%lld MiB). Memory configuration will be\n", + hw_size >> 20); + printf(" adjusted to match the detected hardware size.\n"); + gd->ram_size = 0; + } + + if (gd->ram_size > 0 && gd->ram_size != hw_size) { + printf("DDR: Warning: DRAM size from device tree (%lld MiB)\n", + gd->ram_size >> 20); + printf(" mismatch with hardware capacity(%lld MiB).\n", + hw_size >> 20); + } + + if (gd->ram_size == 0 && hw_size > 0) { + phys_size_t remaining_size, size_counter = 0; + u8 config_dram_banks; + + if (CONFIG_NR_DRAM_BANKS > MEMORY_BANK_MAX_COUNT) { + printf("DDR: Warning: CONFIG_NR_DRAM_BANKS(%d) is bigger than Max Memory Bank count(%d).\n", + CONFIG_NR_DRAM_BANKS, MEMORY_BANK_MAX_COUNT); + printf(" Max Memory Bank count is in use instead of CONFIG_NR_DRAM_BANKS.\n"); + config_dram_banks = MEMORY_BANK_MAX_COUNT; + } else { + config_dram_banks = CONFIG_NR_DRAM_BANKS; + } + + for (i = 0; i < config_dram_banks; i++) { + remaining_size = hw_size - size_counter; + if (remaining_size <= dram_bank_info[i].max_size) { + gd->bd->bi_dram[i].start = dram_bank_info[i].start; + gd->bd->bi_dram[i].size = remaining_size; + debug("Memory bank[%d] Starting address: 0x%llx size: 0x%llx\n", + i, gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size); + break; + } + + gd->bd->bi_dram[i].start = dram_bank_info[i].start; + gd->bd->bi_dram[i].size = dram_bank_info[i].max_size; + + debug("Memory bank[%d] Starting address: 0x%llx size: 0x%llx\n", + i, gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size); + size_counter += gd->bd->bi_dram[i].size; + } + + gd->ram_size = hw_size; + } + + printf("%s: %lld MiB\n", io96b_ctrl->ddr_type, gd->ram_size >> 20); + + /* Is HPS cold or warm reset? If yes, Skip full memory initialization if ECC + * enabled to preserve memory content + */ + if (io96b_ctrl->ecc_status) { + if (ecc_interrupt_status(io96b_ctrl)) { + if (CONFIG_IS_ENABLED(WDT)) { + struct udevice *wdt; + + printf("DDR: ECC error recover start now\n"); + ret = uclass_first_device_err(UCLASS_WDT, &wdt); + if (ret) { + printf("DDR: Failed to trigger watchdog reset\n"); + hang(); + } + + wdt_expire_now(wdt, 0); + } + hang(); + } + + full_mem_init = hps_ocram_dbe_status() | is_ddr_hang_be4_rst; + if (full_mem_init || !(reset_t == WARM_RESET || reset_t == COLD_RESET)) { + ret = bist_mem_init_start(io96b_ctrl); + if (ret) { + printf("DDR: Failed to fully initialize DDR memory\n"); + + goto err; + } + } + + printf("SDRAM-ECC: Initialized success\n"); + } + + sdram_size_check(gd->bd); + printf("DDR: size check success\n"); + + sdram_set_firewall(gd->bd); + + /* Firewall setting for MPFE CSR */ + config_firewall_mpfe_csr(dev); + + printf("DDR: firewall init success\n"); + + priv->info.base = gd->bd->bi_dram[0].start; + priv->info.size = gd->ram_size; + + /* Ending DDR driver initialization success tracking */ + ddr_init_inprogress(false); + + printf("DDR: init success\n"); + +err: + free(io96b_ctrl); + + return ret; +} diff --git a/drivers/ddr/altera/sdram_soc64.c b/drivers/ddr/altera/sdram_soc64.c index 10a8e64af3d..27fbe80ed41 100644 --- a/drivers/ddr/altera/sdram_soc64.c +++ b/drivers/ddr/altera/sdram_soc64.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2016-2022 Intel Corporation <www.intel.com> + * Copyright (C) 2025 Altera Corporation <www.altera.com> * */ @@ -28,6 +29,7 @@ #define PGTABLE_OFF 0x4000 +#if !IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5) u32 hmc_readl(struct altera_sdram_plat *plat, u32 reg) { return readl(plat->iomhc + reg); @@ -99,8 +101,9 @@ int emif_reset(struct altera_sdram_plat *plat) debug("DDR: %s triggered successly\n", __func__); return 0; } +#endif -#if !IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X) +#if !(IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X) || IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5)) int poll_hmc_clock_status(void) { return wait_for_bit_le32((const void *)(socfpga_get_sysmgr_addr() + @@ -182,35 +185,51 @@ void sdram_init_ecc_bits(struct bd_info *bd) void sdram_size_check(struct bd_info *bd) { phys_size_t total_ram_check = 0; - phys_size_t ram_check = 0; - phys_addr_t start = 0; - phys_size_t size, remaining_size; int bank; /* Sanity check ensure correct SDRAM size specified */ debug("DDR: Running SDRAM size sanity check\n"); for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { + phys_size_t ram_check = 0; + phys_addr_t start = 0; + phys_size_t remaining_size; + start = bd->bi_dram[bank].start; remaining_size = bd->bi_dram[bank].size; + debug("Checking bank %d: start=0x%llx, size=0x%llx\n", + bank, start, remaining_size); + while (ram_check < bd->bi_dram[bank].size) { - size = min((phys_addr_t)SZ_1G, - (phys_addr_t)remaining_size); - - /* - * Ensure the size is power of two, this is requirement - * to run get_ram_size() / memory test - */ - if (size != 0 && ((size & (size - 1)) == 0)) { - ram_check += get_ram_size((void *) - (start + ram_check), size); - remaining_size = bd->bi_dram[bank].size - - ram_check; - } else { - puts("DDR: Memory test requires SDRAM size "); - puts("in power of two!\n"); + phys_size_t size, test_size, detected_size; + + size = min((phys_addr_t)SZ_1G, (phys_addr_t)remaining_size); + + if (size < SZ_8) { + puts("Invalid size: Memory size required to be multiple\n"); + puts("of 64-Bit word!\n"); + hang(); + } + + /* Adjust size to the nearest power of two to support get_ram_size() */ + test_size = SZ_8; + + while (test_size * 2 <= size) + test_size *= 2; + + debug("Testing memory at 0x%llx with size 0x%llx\n", + start + ram_check, test_size); + detected_size = get_ram_size((void *)(start + ram_check), test_size); + + if (detected_size != test_size) { + debug("Detected size 0x%llx doesn’t match the test size 0x%llx!\n", + detected_size, test_size); + puts("Memory testing failed!\n"); hang(); } + + ram_check += detected_size; + remaining_size = bd->bi_dram[bank].size - ram_check; } total_ram_check += ram_check; @@ -246,13 +265,13 @@ phys_size_t sdram_calculate_size(struct altera_sdram_plat *plat) DRAMADDRW_CFG_ROW_ADDR_WIDTH(dramaddrw) + DRAMADDRW_CFG_COL_ADDR_WIDTH(dramaddrw)); - size *= (2 << (hmc_ecc_readl(plat, DDRIOCTRL) & + size *= ((phys_size_t)2 << (hmc_ecc_readl(plat, DDRIOCTRL) & DDR_HMC_DDRIOCTRL_IOSIZE_MSK)); return size; } -void sdram_set_firewall(struct bd_info *bd) +static void sdram_set_firewall_non_f2sdram(struct bd_info *bd) { u32 i; phys_size_t value; @@ -288,7 +307,7 @@ void sdram_set_firewall(struct bd_info *bd) FW_MPU_DDR_SCR_NONMPUREGION0ADDR_BASEEXT + (i * 4 * sizeof(u32))); - /* Setting non-secure MPU limit and limit extexded */ + /* Setting non-secure MPU limit and limit extended */ value = bd->bi_dram[i].start + bd->bi_dram[i].size - 1; lower = lower_32_bits(value); @@ -301,7 +320,7 @@ void sdram_set_firewall(struct bd_info *bd) FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMITEXT + (i * 4 * sizeof(u32))); - /* Setting non-secure Non-MPU limit and limit extexded */ + /* Setting non-secure Non-MPU limit and limit extended */ FW_MPU_DDR_SCR_WRITEL(lower, FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMIT + (i * 4 * sizeof(u32))); @@ -314,15 +333,77 @@ void sdram_set_firewall(struct bd_info *bd) } } +#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5) +static void sdram_set_firewall_f2sdram(struct bd_info *bd) +{ + u32 i, lower, upper; + phys_size_t value; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + if (!bd->bi_dram[i].size) + continue; + + value = bd->bi_dram[i].start; + + /* Keep first 1MB of SDRAM memory region as secure region when + * using ATF flow, where the ATF code is located. + */ + if (IS_ENABLED(CONFIG_SPL_ATF) && i == 0) + value += SZ_1M; + + /* Setting base and base extended */ + lower = lower_32_bits(value); + upper = upper_32_bits(value); + FW_F2SDRAM_DDR_SCR_WRITEL(lower, + FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASE + + (i * 4 * sizeof(u32))); + FW_F2SDRAM_DDR_SCR_WRITEL(upper & 0xff, + FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASEEXT + + (i * 4 * sizeof(u32))); + + /* Setting limit and limit extended */ + value = bd->bi_dram[i].start + bd->bi_dram[i].size - 1; + + lower = lower_32_bits(value); + upper = upper_32_bits(value); + + FW_F2SDRAM_DDR_SCR_WRITEL(lower, + FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMIT + + (i * 4 * sizeof(u32))); + FW_F2SDRAM_DDR_SCR_WRITEL(upper & 0xff, + FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMITEXT + + (i * 4 * sizeof(u32))); + + FW_F2SDRAM_DDR_SCR_WRITEL(BIT(i), FW_F2SDRAM_DDR_SCR_EN_SET); + } +} +#endif + +void sdram_set_firewall(struct bd_info *bd) +{ + sdram_set_firewall_non_f2sdram(bd); + +#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5) + sdram_set_firewall_f2sdram(bd); +#endif +} + static int altera_sdram_of_to_plat(struct udevice *dev) { +#if !IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X) struct altera_sdram_plat *plat = dev_get_plat(dev); fdt_addr_t addr; +#endif /* These regs info are part of DDR handoff in bitstream */ #if IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X) return 0; -#endif +#elif IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5) + addr = dev_read_addr_index(dev, 0); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + plat->mpfe_base_addr = addr; +#else addr = dev_read_addr_index(dev, 0); if (addr == FDT_ADDR_T_NONE) @@ -338,7 +419,7 @@ static int altera_sdram_of_to_plat(struct udevice *dev) if (addr == FDT_ADDR_T_NONE) return -EINVAL; plat->hmc = (void __iomem *)addr; - +#endif return 0; } @@ -385,6 +466,7 @@ static const struct udevice_id altera_sdram_ids[] = { { .compatible = "altr,sdr-ctl-s10" }, { .compatible = "intel,sdr-ctl-agilex" }, { .compatible = "intel,sdr-ctl-n5x" }, + { .compatible = "intel,sdr-ctl-agilex5" }, { /* sentinel */ } }; diff --git a/drivers/ddr/altera/sdram_soc64.h b/drivers/ddr/altera/sdram_soc64.h index 87a70a861ba..183b1a33080 100644 --- a/drivers/ddr/altera/sdram_soc64.h +++ b/drivers/ddr/altera/sdram_soc64.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2017-2019 Intel Corporation <www.intel.com> + * Copyright (C) 2025 Altera Corporation <www.altera.com> + * */ #ifndef _SDRAM_SOC64_H_ @@ -13,11 +15,19 @@ struct altera_sdram_priv { struct reset_ctl_bulk resets; }; +#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5) +struct altera_sdram_plat { + fdt_addr_t mpfe_base_addr; + bool dualport; + bool dualemif; +}; +#else struct altera_sdram_plat { void __iomem *hmc; void __iomem *ddr_sch; void __iomem *iomhc; }; +#endif /* ECC HMC registers */ #define DDRIOCTRL 0x8 diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 6e1ab1c2ea5..eb4ae5e2074 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -3,12 +3,12 @@ # Copyright (C) 2012 Samsung Electronics # Lukasz Majewski <l.majewski@samsung.com> -obj-$(CONFIG_$(XPL_)DFU) += dfu.o -obj-$(CONFIG_$(XPL_)DFU_MMC) += dfu_mmc.o -obj-$(CONFIG_$(XPL_)DFU_MTD) += dfu_mtd.o -obj-$(CONFIG_$(XPL_)DFU_NAND) += dfu_nand.o -obj-$(CONFIG_$(XPL_)DFU_RAM) += dfu_ram.o -obj-$(CONFIG_$(XPL_)DFU_SF) += dfu_sf.o -obj-$(CONFIG_$(XPL_)DFU_WRITE_ALT) += dfu_alt.o -obj-$(CONFIG_$(XPL_)DFU_VIRT) += dfu_virt.o -obj-$(CONFIG_$(XPL_)DFU_SCSI) += dfu_scsi.o +obj-$(CONFIG_$(PHASE_)DFU) += dfu.o +obj-$(CONFIG_$(PHASE_)DFU_MMC) += dfu_mmc.o +obj-$(CONFIG_$(PHASE_)DFU_MTD) += dfu_mtd.o +obj-$(CONFIG_$(PHASE_)DFU_NAND) += dfu_nand.o +obj-$(CONFIG_$(PHASE_)DFU_RAM) += dfu_ram.o +obj-$(CONFIG_$(PHASE_)DFU_SF) += dfu_sf.o +obj-$(CONFIG_$(PHASE_)DFU_WRITE_ALT) += dfu_alt.o +obj-$(CONFIG_$(PHASE_)DFU_VIRT) += dfu_virt.o +obj-$(CONFIG_$(PHASE_)DFU_SCSI) += dfu_scsi.o diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 3c64e894646..4b47be6b016 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -76,6 +76,13 @@ config XILINX_DPDMA this file is used as placeholder for driver. The main reason is to record compatible string and calling power domain driver. +config ADI_DMA + bool "ADI DMA driver" + depends on DMA && DMA_CHANNELS + help + Enable DMA support for Analog Devices SOCs, such as the SC5xx. + Currently this is a minimalistic driver tested against OSPI use only. + if APBH_DMA config APBH_DMA_BURST bool "Enable DMA BURST" diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 48811eaaeb3..00d765864cd 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -13,5 +13,6 @@ obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o obj-$(CONFIG_TI_EDMA3) += ti-edma3.o obj-$(CONFIG_DMA_LPC32XX) += lpc32xx_dma.o obj-$(CONFIG_XILINX_DPDMA) += xilinx_dpdma.o +obj-$(CONFIG_ADI_DMA) += adi_dma.o obj-y += ti/ diff --git a/drivers/dma/adi_dma.c b/drivers/dma/adi_dma.c new file mode 100644 index 00000000000..28afe488db0 --- /dev/null +++ b/drivers/dma/adi_dma.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices DMA controller driver + * + * (C) Copyright 2024 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * Contact: Ian Roberts <ian.roberts@timesys.com> + * + */ +#include <dm.h> +#include <dma.h> +#include <dma-uclass.h> +#include <dm/device_compat.h> +#include <linux/errno.h> +#include <linux/io.h> + +#define HAS_MDMA BIT(0) + +#define REG_ADDRSTART 0x04 +#define REG_CFG 0x08 +#define REG_XCNT 0x0C +#define REG_XMOD 0x10 +#define REG_STAT 0x30 + +#define BITP_DMA_CFG_MSIZE 8 +#define BITP_DMA_CFG_PSIZE 4 +#define BITM_DMA_CFG_WNR 0x00000002 +#define BITM_DMA_CFG_EN 0x00000001 +#define ENUM_DMA_CFG_XCNT_INT 0x00100000 + +#define BITP_DMA_STAT_PBWID 12 +#define BITP_DMA_STAT_ERRC 4 +#define BITM_DMA_STAT_PBWID 0x00003000 +#define BITM_DMA_STAT_ERRC 0x00000070 +#define BITM_DMA_STAT_PIRQ 0x00000004 +#define BITM_DMA_STAT_IRQERR 0x00000002 +#define BITM_DMA_STAT_IRQDONE 0x00000001 + +#define DMA_MDMA_SRC_DEFAULT_CONFIG(psize, msize) \ + (BITM_DMA_CFG_EN | ((psize) << BITP_DMA_CFG_PSIZE) | ((msize) << BITP_DMA_CFG_MSIZE)) +#define DMA_MDMA_DST_DEFAULT_CONFIG(psize, msize) \ + (BITM_DMA_CFG_EN | BITM_DMA_CFG_WNR | ENUM_DMA_CFG_XCNT_INT | \ + ((psize) << BITP_DMA_CFG_PSIZE) | ((msize) << BITP_DMA_CFG_MSIZE)) + +struct adi_dma_channel { + int id; + struct adi_dma *dma; + void __iomem *iosrc; + void __iomem *iodest; +}; + +struct adi_dma { + struct udevice *dev; + struct adi_dma_channel channels[1]; + void __iomem *ioaddr; + unsigned long hw_cfg; +}; + +static const struct udevice_id dma_dt_ids[] = { + { .compatible = "adi,mdma-controller", .data = HAS_MDMA }, + { } +}; + +static u8 adi_dma_get_msize(u32 n_bytecount, u32 n_address) +{ + /* Calculate MSIZE, PSIZE, XCNT and XMOD */ + u8 n_msize = 0; + u32 n_value = n_bytecount | n_address; + u32 n_mask = 0x1; + + for (n_msize = 0; n_msize < 5; n_msize++, n_mask <<= 1) { + if ((n_value & n_mask) == n_mask) + break; + } + + return n_msize; +} + +static int adi_dma_get_ch_error(void __iomem *ch) +{ + u32 cause = (ioread32(ch + REG_STAT) & BITM_DMA_STAT_ERRC) >> + BITP_DMA_STAT_ERRC; + switch (cause) { + case 0: + return -EINVAL; + case 1: + return -EBUSY; + case 2: + return -EFAULT; + case 3: + fallthrough; + case 5: + fallthrough; + case 6: + fallthrough; + default: + return -EIO; + } +} + +static int adi_mdma_transfer(struct udevice *dev, int direction, + dma_addr_t dst, dma_addr_t src, size_t len) +{ + struct adi_dma *priv = dev_get_priv(dev); + void __iomem *chsrc = priv->channels[0].iosrc; + void __iomem *chdst = priv->channels[0].iodest; + + int result = 0; + u32 reg; + u32 bytecount = len; + + u8 n_srcmsize; + u8 n_dstmsize; + u8 n_srcpsize; + u8 n_dstpsize; + u8 n_psize; + u32 srcconfig; + u32 dstconfig; + u8 srcpsizemax = (ioread32(chsrc + REG_STAT) & BITM_DMA_STAT_PBWID) >> + BITP_DMA_STAT_PBWID; + u8 dstpsizemax = (ioread32(chdst + REG_STAT) & BITM_DMA_STAT_PBWID) >> + BITP_DMA_STAT_PBWID; + + const u32 CLRSTAT = (BITM_DMA_STAT_IRQDONE | BITM_DMA_STAT_IRQERR | + BITM_DMA_STAT_PIRQ); + + if (len == 0) + return -EINVAL; + + /* Clear DMA status */ + iowrite32(CLRSTAT, chsrc + REG_STAT); + iowrite32(CLRSTAT, chdst + REG_STAT); + + /* Calculate MSIZE, PSIZE, XCNT and XMOD */ + n_srcmsize = adi_dma_get_msize(bytecount, src); + n_dstmsize = adi_dma_get_msize(bytecount, dst); + n_srcpsize = min(n_srcmsize, srcpsizemax); + n_dstpsize = min(n_dstmsize, dstpsizemax); + n_psize = min(n_srcpsize, n_dstpsize); + + srcconfig = DMA_MDMA_SRC_DEFAULT_CONFIG(n_psize, n_srcmsize); + dstconfig = DMA_MDMA_DST_DEFAULT_CONFIG(n_psize, n_dstmsize); + + /* Load the DMA descriptors */ + iowrite32(src, chsrc + REG_ADDRSTART); + iowrite32(bytecount >> n_srcmsize, chsrc + REG_XCNT); + iowrite32(1 << n_srcmsize, chsrc + REG_XMOD); + iowrite32(dst, chdst + REG_ADDRSTART); + iowrite32(bytecount >> n_dstmsize, chdst + REG_XCNT); + iowrite32(1 << n_dstmsize, chdst + REG_XMOD); + + iowrite32(dstconfig, chdst + REG_CFG); + iowrite32(srcconfig, chsrc + REG_CFG); + + /* Wait for DMA to complete while checking for a DMA error */ + do { + reg = ioread32(chsrc + REG_STAT); + if ((reg & BITM_DMA_STAT_IRQERR) == BITM_DMA_STAT_IRQERR) { + result = adi_dma_get_ch_error(chsrc); + break; + } + reg = ioread32(chdst + REG_STAT); + if ((reg & BITM_DMA_STAT_IRQERR) == BITM_DMA_STAT_IRQERR) { + result = adi_dma_get_ch_error(chdst); + break; + } + } while ((reg & BITM_DMA_STAT_IRQDONE) == 0); + + clrbits_32(chsrc + REG_CFG, 1); + clrbits_32(chdst + REG_CFG, 1); + + return result; +} + +static int adi_dma_init_channel(struct adi_dma *dma, + struct adi_dma_channel *channel, ofnode node) +{ + u32 offset; + + if (ofnode_read_u32(node, "adi,id", &channel->id)) { + dev_err(dma->dev, "Missing adi,id for channel %s\n", + ofnode_get_name(node)); + return -ENOENT; + } + + if (ofnode_read_u32(node, "adi,src-offset", &offset)) { + dev_err(dma->dev, "Missing adi,src-offset for channel %s\n", + ofnode_get_name(node)); + return -ENOENT; + } + + channel->iosrc = dma->ioaddr + offset; + channel->dma = dma; + + if (dma->hw_cfg & HAS_MDMA) { + if (ofnode_read_u32(node, "adi,dest-offset", &offset)) { + dev_err(dma->dev, + "Missing adi,dest-offset for channel %s\n", + ofnode_get_name(node)); + return -ENOENT; + } + channel->iodest = dma->ioaddr + offset; + } + + return 0; +} + +static int adi_dma_probe(struct udevice *dev) +{ + struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct adi_dma *priv = dev_get_priv(dev); + ofnode node, child; + + priv->hw_cfg = dev_get_driver_data(dev); + if (priv->hw_cfg & HAS_MDMA) + uc_priv->supported = DMA_SUPPORTS_MEM_TO_MEM; + + priv->ioaddr = dev_remap_addr(dev); + if (!priv->ioaddr) + return -EINVAL; + + node = dev_read_first_subnode(dev); + if (!ofnode_valid(node)) { + dev_err(dev, + "Error: device tree DMA channel config missing!\n"); + return -ENODEV; + } + + node = dev_ofnode(dev); + ofnode_for_each_subnode(child, node) { + adi_dma_init_channel(priv, priv->channels, child); + break; //Only 1 channel supported for now + } + + return 0; +} + +static const struct dma_ops adi_dma_ops = { + .transfer = adi_mdma_transfer, +}; + +U_BOOT_DRIVER(adi_dma) = { + .name = "adi_dma", + .id = UCLASS_DMA, + .of_match = dma_dt_ids, + .ops = &adi_dma_ops, + .probe = adi_dma_probe, + .priv_auto = sizeof(struct adi_dma), +}; diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile index 94ec13ba7ca..90c20a6a3fa 100644 --- a/drivers/dma/ti/Makefile +++ b/drivers/dma/ti/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_TI_K3_PSIL) += k3-psil-data.o k3-psil-data-y += k3-psil.o k3-psil-data-$(CONFIG_SOC_K3_AM654) += k3-psil-am654.o k3-psil-data-$(CONFIG_SOC_K3_J721E) += k3-psil-j721e.o +k3-psil-data-$(CONFIG_SOC_K3_J7200) += 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 diff --git a/drivers/dma/ti/k3-psil.c b/drivers/dma/ti/k3-psil.c index 369e679886f..39798844a8a 100644 --- a/drivers/dma/ti/k3-psil.c +++ b/drivers/dma/ti/k3-psil.c @@ -20,6 +20,8 @@ struct psil_endpoint_config *psil_get_ep_config(u32 thread_id) soc_ep_map = &am654_ep_map; else if (IS_ENABLED(CONFIG_SOC_K3_J721E)) soc_ep_map = &j721e_ep_map; + else if (IS_ENABLED(CONFIG_SOC_K3_J7200)) + soc_ep_map = &j721e_ep_map; else if (IS_ENABLED(CONFIG_SOC_K3_J721S2)) soc_ep_map = &j721s2_ep_map; else if (IS_ENABLED(CONFIG_SOC_K3_AM642)) diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 3013c4741d0..723265ab2e5 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -36,6 +36,7 @@ #include "k3-psil-priv.h" #define K3_UDMA_MAX_RFLOWS 1024 +#define K3_UDMA_MAX_TR 2 struct udma_chan; @@ -74,7 +75,6 @@ struct udma_tchan { struct k3_nav_ring *t_ring; /* Transmit ring */ struct k3_nav_ring *tc_ring; /* Transmit Completion ring */ int tflow_id; /* applicable only for PKTDMA */ - }; #define udma_bchan udma_tchan @@ -175,6 +175,7 @@ struct udma_dev { struct udma_rflow *rflows; struct udma_match_data *match_data; + void *bc_desc; struct udma_chan *channels; u32 psil_base; @@ -1349,6 +1350,7 @@ static int udma_setup_resources(struct udma_dev *ud) struct ti_sci_resource_desc *rm_desc; struct ti_sci_resource *rm_res; struct udma_tisci_rm *tisci_rm = &ud->tisci_rm; + size_t desc_size; ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt), sizeof(unsigned long), GFP_KERNEL); @@ -1366,9 +1368,11 @@ static int udma_setup_resources(struct udma_dev *ud) ud->rflows = devm_kcalloc(dev, ud->rflow_cnt, sizeof(*ud->rflows), GFP_KERNEL); + desc_size = cppi5_trdesc_calc_size(K3_UDMA_MAX_TR, sizeof(struct cppi5_tr_type15_t)); + ud->bc_desc = devm_kzalloc(dev, ALIGN(desc_size, ARCH_DMA_MINALIGN), GFP_KERNEL); if (!ud->tchan_map || !ud->rchan_map || !ud->rflow_map || !ud->rflow_map_reserved || !ud->tchans || !ud->rchans || - !ud->rflows) + !ud->rflows || !ud->bc_desc) return -ENOMEM; /* @@ -1444,6 +1448,7 @@ static int bcdma_setup_resources(struct udma_dev *ud) struct ti_sci_resource_desc *rm_desc; struct ti_sci_resource *rm_res; struct udma_tisci_rm *tisci_rm = &ud->tisci_rm; + size_t desc_size; ud->bchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->bchan_cnt), sizeof(unsigned long), GFP_KERNEL); @@ -1460,9 +1465,12 @@ static int bcdma_setup_resources(struct udma_dev *ud) ud->rflows = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rflows), GFP_KERNEL); + desc_size = cppi5_trdesc_calc_size(K3_UDMA_MAX_TR, sizeof(struct cppi5_tr_type15_t)); + ud->bc_desc = devm_kzalloc(dev, ALIGN(desc_size, ARCH_DMA_MINALIGN), GFP_KERNEL); + if (!ud->bchan_map || !ud->tchan_map || !ud->rchan_map || !ud->bchans || !ud->tchans || !ud->rchans || - !ud->rflows) + !ud->rflows || !ud->bc_desc) return -ENOMEM; /* Get resource ranges from tisci */ @@ -1718,8 +1726,7 @@ static int *udma_prep_dma_memcpy(struct udma_chan *uc, dma_addr_t dest, int num_tr; size_t tr_size = sizeof(struct cppi5_tr_type15_t); u16 tr0_cnt0, tr0_cnt1, tr1_cnt0; - unsigned long dummy; - void *tr_desc; + void *tr_desc = uc->ud->bc_desc; size_t desc_size; if (len < SZ_64K) { @@ -1748,9 +1755,6 @@ static int *udma_prep_dma_memcpy(struct udma_chan *uc, dma_addr_t dest, } desc_size = cppi5_trdesc_calc_size(num_tr, tr_size); - tr_desc = dma_alloc_coherent(desc_size, &dummy); - if (!tr_desc) - return NULL; memset(tr_desc, 0, desc_size); cppi5_trdesc_init(tr_desc, num_tr, tr_size, 0, 0); diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index 1eb460f5a02..70207573de2 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -1,6 +1,5 @@ menu "Fastboot support" depends on CMDLINE - depends on !NET_LWIP config FASTBOOT bool diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c index e4484d65aca..2cdbac50ac4 100644 --- a/drivers/fastboot/fb_command.c +++ b/drivers/fastboot/fb_command.c @@ -186,6 +186,7 @@ void fastboot_multiresponse(int cmd, char *response) } break; } + fallthrough; default: fastboot_fail("Unknown multiresponse command", response); break; diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c index 12ffb463deb..68f92c4b887 100644 --- a/drivers/fastboot/fb_common.c +++ b/drivers/fastboot/fb_common.c @@ -183,11 +183,15 @@ void fastboot_handle_boot(int command, bool success) switch (command) { case FASTBOOT_COMMAND_BOOT: fastboot_boot(); +#if CONFIG_IS_ENABLED(NET) net_set_state(NETLOOP_SUCCESS); +#endif break; case FASTBOOT_COMMAND_CONTINUE: +#if CONFIG_IS_ENABLED(NET) net_set_state(NETLOOP_SUCCESS); +#endif break; case FASTBOOT_COMMAND_REBOOT: diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 8b979f69ed9..9e7b118b074 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_FIRMWARE) += firmware-uclass.o -obj-$(CONFIG_$(XPL_)ARM_PSCI_FW) += psci.o +obj-$(CONFIG_$(PHASE_)ARM_PSCI_FW) += psci.o obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o obj-$(CONFIG_SANDBOX) += firmware-sandbox.o obj-$(CONFIG_ZYNQMP_FIRMWARE) += firmware-zynqmp.o diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c index 4b1b80d7abe..2940181e83e 100644 --- a/drivers/firmware/firmware-zynqmp.c +++ b/drivers/firmware/firmware-zynqmp.c @@ -5,6 +5,8 @@ * Copyright (C) 2018-2019 Xilinx, Inc. */ +#include <asm/arch/hardware.h> +#include <asm/io.h> #include <cpu_func.h> #include <dm.h> #include <dm/device_compat.h> @@ -169,6 +171,32 @@ unsigned int zynqmp_firmware_version(void) return pm_api_version; }; +#if defined(CONFIG_ARCH_VERSAL2) +int zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value) +{ + *value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_TX_RX_CFG_RDY); + return 0; +} + +int zynqmp_pm_ufs_sram_csr_read(u32 *value) +{ + *value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR); + return 0; +} + +int zynqmp_pm_ufs_sram_csr_write(u32 *value) +{ + writel(*value, PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR); + return 0; +} + +int zynqmp_pm_ufs_cal_reg(u32 *value) +{ + *value = readl(PMXC_EFUSE_CACHE_BASE_ADDRESS + PMXC_UFS_CAL_1_OFFSET); + return 0; +} +#endif + int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, u32 value) { int ret; @@ -195,6 +223,52 @@ int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value) return ret; } +u32 zynqmp_pm_get_bootmode_reg(void) +{ + int ret; + u32 ret_payload[PAYLOAD_ARG_CNT]; + + ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_READ_REG); + if (ret) { + printf("%s: IOCTL_READ_REG is not supported failed with error code: %d\n" + , __func__, ret); + return 0; + } + + ret = xilinx_pm_request(PM_IOCTL, CRP_BOOT_MODE_REG_NODE, IOCTL_READ_REG, + CRP_BOOT_MODE_REG_OFFSET, 0, ret_payload); + if (ret) { + printf("%s: node 0x%x: get_bootmode 0x%x failed\n", + __func__, CRP_BOOT_MODE_REG_NODE, CRP_BOOT_MODE_REG_OFFSET); + return 0; + } + + return ret_payload[1]; +} + +u32 zynqmp_pm_get_pmc_multi_boot_reg(void) +{ + int ret; + u32 ret_payload[PAYLOAD_ARG_CNT]; + + ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_READ_REG); + if (ret) { + printf("%s: IOCTL_READ_REG is not supported failed with error code: %d\n" + , __func__, ret); + return 0; + } + + ret = xilinx_pm_request(PM_IOCTL, PM_REG_PMC_GLOBAL_NODE, IOCTL_READ_REG, + PMC_MULTI_BOOT_MODE_REG_OFFSET, 0, ret_payload); + if (ret) { + printf("%s: node 0x%x: get_bootmode 0x%x failed\n", + __func__, PM_REG_PMC_GLOBAL_NODE, PMC_MULTI_BOOT_MODE_REG_OFFSET); + return 0; + } + + return ret_payload[1]; +} + int zynqmp_pm_feature(const u32 api_id) { int ret; diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 8c907c3b032..e6e43ae936a 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -427,14 +427,8 @@ static int scmi_bind_protocols(struct udevice *dev) break; case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: 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"); - return -ENXIO; - } + scmi_protocol_is_supported(dev, protocol_id)) drv = DM_DRIVER_GET(scmi_voltage_domain); - } break; default: break; diff --git a/drivers/firmware/scmi/smt.c b/drivers/firmware/scmi/smt.c index 67d2f450024..3253f4211d6 100644 --- a/drivers/firmware/scmi/smt.c +++ b/drivers/firmware/scmi/smt.c @@ -20,6 +20,16 @@ #include "smt.h" +static void scmi_smt_enable_intr(struct scmi_smt *smt, bool enable) +{ + struct scmi_smt_header *hdr = (void *)smt->buf; + + if (enable) + hdr->flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; + else + hdr->flags &= ~SCMI_SHMEM_FLAG_INTR_ENABLED; +} + /** * Get shared memory configuration defined by the referred DT phandle * Return with a errno compliant value. @@ -48,6 +58,9 @@ int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt) if (!smt->buf) return -ENOMEM; + if (device_is_compatible(dev, "arm,scmi") && ofnode_has_property(dev_ofnode(dev), "mboxes")) + scmi_smt_enable_intr(smt, true); + #ifdef CONFIG_ARM if (dcache_status()) mmu_set_region_dcache_behaviour(ALIGN_DOWN((uintptr_t)smt->buf, MMU_SECTION_SIZE), diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 190a1e3f5fc..54d6689ce78 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -696,20 +696,25 @@ static int ti_sci_cmd_put_device(const struct ti_sci_handle *handle, u32 id) MSG_DEVICE_SW_STATE_AUTO_OFF); } -static -int ti_sci_cmd_release_exclusive_devices(const struct ti_sci_handle *handle) +static int ti_sci_cmd_release_exclusive_devices(void) { struct ti_sci_exclusive_dev *dev, *tmp; struct ti_sci_info *info; int i, cnt; - info = handle_to_ti_sci_info(handle); - - list_for_each_entry_safe(dev, tmp, &info->dev_list, list) { - cnt = dev->count; - debug("%s: id = %d, cnt = %d\n", __func__, dev->id, cnt); - for (i = 0; i < cnt; i++) - ti_sci_cmd_put_device(handle, dev->id); + /* + * Scan all ti_sci_list registrations, since with FIT images, we could + * have started with one device tree registration and switched over + * to a final version. This prevents exclusive devices identified + * during the first probe to be left orphan. + */ + list_for_each_entry(info, &ti_sci_list, list) { + list_for_each_entry_safe(dev, tmp, &info->dev_list, list) { + cnt = dev->count; + debug("%s: id = %d, cnt = %d\n", __func__, dev->id, cnt); + for (i = 0; i < cnt; i++) + ti_sci_cmd_put_device(&info->handle, dev->id); + } } return 0; diff --git a/drivers/firmware/ti_sci_static_data.h b/drivers/firmware/ti_sci_static_data.h index 3370f80231d..2e61e0c7de1 100644 --- a/drivers/firmware/ti_sci_static_data.h +++ b/drivers/firmware/ti_sci_static_data.h @@ -16,7 +16,7 @@ struct ti_sci_resource_static_data { #if IS_ENABLED(CONFIG_K3_DM_FW) -#if IS_ENABLED(CONFIG_SOC_K3_J721E) +#if IS_ENABLED(CONFIG_SOC_K3_J721E) || IS_ENABLED(CONFIG_SOC_K3_J7200) static struct ti_sci_resource_static_data rm_static_data[] = { /* Free rings */ { diff --git a/drivers/fpga/altera.c b/drivers/fpga/altera.c index ae06f0123a0..64fda3a307c 100644 --- a/drivers/fpga/altera.c +++ b/drivers/fpga/altera.c @@ -12,6 +12,10 @@ /* * Altera FPGA support */ +#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX) || \ + IS_ENABLED(CONFIG_TARGET_SOCFPGA_STRATIX10) +#include <asm/arch/misc.h> +#endif #include <errno.h> #include <ACEX1K.h> #include <log.h> @@ -47,6 +51,43 @@ static const struct altera_fpga { #endif }; +#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX) || \ + IS_ENABLED(CONFIG_TARGET_SOCFPGA_STRATIX10) +int fpga_is_partial_data(int devnum, size_t img_len) +{ + /* + * The FPGA data (full or partial) is checked by + * the SDM hardware, for Intel SDM Mailbox based + * devices. Hence always return full bitstream. + * + * For Cyclone V and Arria 10 family, the bitstream + * type parameter is not handled by the driver. + */ + return 0; +} + +int fpga_loadbitstream(int devnum, char *fpgadata, size_t size, + bitstream_type bstype) +{ + int ret_val; + int flags = 0; + + ret_val = fpga_load(devnum, (void *)fpgadata, size, bstype, flags); + + /* + * Enable the HPS to FPGA bridges when FPGA load is completed + * successfully. This is to ensure the FPGA is accessible + * by the HPS. + */ + if (!ret_val) { + printf("Enable FPGA bridges\n"); + do_bridge_reset(1, ~0); + } + + return ret_val; +} +#endif + static int altera_validate(Altera_desc *desc, const char *fn) { if (!desc) { diff --git a/drivers/fpga/versalpl.c b/drivers/fpga/versalpl.c index 1957e8dcaca..d691f135e89 100644 --- a/drivers/fpga/versalpl.c +++ b/drivers/fpga/versalpl.c @@ -41,8 +41,15 @@ static int versal_load(xilinx_desc *desc, const void *buf, size_t bsize, buf_lo = lower_32_bits(bin_buf); buf_hi = upper_32_bits(bin_buf); - ret = xilinx_pm_request(VERSAL_PM_LOAD_PDI, VERSAL_PM_PDI_TYPE, buf_lo, - buf_hi, 0, ret_payload); + + if (desc->family == xilinx_versal2) { + ret = xilinx_pm_request(VERSAL_PM_LOAD_PDI, VERSAL_PM_PDI_TYPE, buf_hi, + buf_lo, 0, ret_payload); + } else { + ret = xilinx_pm_request(VERSAL_PM_LOAD_PDI, VERSAL_PM_PDI_TYPE, buf_lo, + buf_hi, 0, ret_payload); + } + if (ret) printf("PL FPGA LOAD failed with err: 0x%08x\n", ret); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 21361f56e69..e11109fb56d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -97,6 +97,15 @@ config SPL_DM_GPIO_LOOKUP_LABEL different gpios on different hardware versions for the same functionality in board code. +config ADI_GPIO + bool "ADI GPIO driver" + depends on DM_GPIO && ARCH_SC5XX + help + This driver supports GPIO banks on SC5xx processors. It + supports inputs and outputs but does not support pin + interrupt functionality (PINT) or other features in the + Linux version of the driver. + config ALTERA_PIO bool "Altera PIO driver" depends on DM_GPIO @@ -545,6 +554,14 @@ config DM_PCA953X Now, max 24 bits chips and PCA953X compatible chips are supported +config ADP5588_GPIO + bool "ADP5588 GPIO expander driver" + depends on DM_GPIO && DM_I2C + help + Say yes here to support GPIO functionality of ADI ADP5588 chips. + + The ADP5588 is an 18-port I2C GPIO expander and keypad controller. + config SPL_DM_PCA953X bool "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports in SPL" depends on SPL_DM_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 3f882c065d8..d64c14db5cf 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,8 +10,9 @@ obj-$(CONFIG_DM_74X164) += 74x164_gpio.o endif obj-$(CONFIG_$(PHASE_)DM_GPIO) += gpio-uclass.o -obj-$(CONFIG_$(XPL_)DM_PCA953X) += pca953x_gpio.o +obj-$(CONFIG_$(PHASE_)DM_PCA953X) += pca953x_gpio.o +obj-$(CONFIG_ADI_GPIO) += gpio-adi-adsp.o obj-$(CONFIG_ASPEED_GPIO) += gpio-aspeed.o obj-$(CONFIG_ASPEED_G7_GPIO) += gpio-aspeed-g7.o obj-$(CONFIG_ASPEED_SGPIO) += gpio-aspeed-sgpio.o @@ -58,12 +59,12 @@ obj-$(CONFIG_VYBRID_GPIO) += vybrid_gpio.o obj-$(CONFIG_HIKEY_GPIO) += hi6220_gpio.o obj-$(CONFIG_HSDK_CREG_GPIO) += hsdk-creg-gpio.o obj-$(CONFIG_IMX_RGPIO2P) += imx_rgpio2p.o -obj-$(CONFIG_$(XPL_)PALMAS_GPIO) += palmas_gpio.o +obj-$(CONFIG_$(PHASE_)PALMAS_GPIO) += palmas_gpio.o obj-$(CONFIG_PIC32_GPIO) += pic32_gpio.o obj-$(CONFIG_OCTEON_GPIO) += octeon_gpio.o obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o obj-$(CONFIG_MSM_GPIO) += msm_gpio.o -obj-$(CONFIG_$(XPL_)PCF8575_GPIO) += pcf8575_gpio.o +obj-$(CONFIG_$(PHASE_)PCF8575_GPIO) += pcf8575_gpio.o obj-$(CONFIG_$(PHASE_)QCOM_PMIC_GPIO) += qcom_pmic_gpio.o obj-$(CONFIG_MT7620_GPIO) += mt7620_gpio.o obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o @@ -72,10 +73,11 @@ obj-$(CONFIG_NX_GPIO) += nx_gpio.o obj-$(CONFIG_SIFIVE_GPIO) += sifive-gpio.o obj-$(CONFIG_NOMADIK_GPIO) += nmk_gpio.o obj-$(CONFIG_MAX7320_GPIO) += max7320_gpio.o -obj-$(CONFIG_$(XPL_)MAX77663_GPIO) += max77663_gpio.o +obj-$(CONFIG_$(PHASE_)MAX77663_GPIO) += max77663_gpio.o obj-$(CONFIG_SL28CPLD_GPIO) += sl28cpld-gpio.o +obj-$(CONFIG_ADP5588_GPIO) += adp5588_gpio.o obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN) += zynqmp_gpio_modepin.o obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o obj-$(CONFIG_FTGPIO010) += ftgpio010.o -obj-$(CONFIG_$(SPL_)ADP5585_GPIO) += adp5585_gpio.o +obj-$(CONFIG_$(PHASE_)ADP5585_GPIO) += adp5585_gpio.o obj-$(CONFIG_RZG2L_GPIO) += rzg2l-gpio.o diff --git a/drivers/gpio/adp5588_gpio.c b/drivers/gpio/adp5588_gpio.c new file mode 100644 index 00000000000..d081e169897 --- /dev/null +++ b/drivers/gpio/adp5588_gpio.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * GPIO Chip driver for Analog Devices + * ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * + * Based on Michael Hennerich's Linux driver: + * Michael Hennerich <michael.hennerich@analog.com> + * + */ + +#include <dm.h> +#include <i2c.h> +#include <asm-generic/gpio.h> + +#define ADP5588_MAXGPIO 18 +#define ADP5588_BANK(offs) ((offs) >> 3) +#define ADP5588_BIT(offs) (1u << ((offs) & 0x7)) + +#define DEV_ID 0x00 /* Device ID */ +#define GPIO_DAT_STAT1 0x14 /* GPIO Data Status, Read twice to clear */ +#define GPIO_DAT_STAT2 0x15 /* GPIO Data Status, Read twice to clear */ +#define GPIO_DAT_STAT3 0x16 /* GPIO Data Status, Read twice to clear */ +#define GPIO_DAT_OUT1 0x17 /* GPIO DATA OUT */ +#define GPIO_DAT_OUT2 0x18 /* GPIO DATA OUT */ +#define GPIO_DAT_OUT3 0x19 /* GPIO DATA OUT */ +#define GPIO_INT_EN1 0x1A /* GPIO Interrupt Enable */ +#define GPIO_INT_EN2 0x1B /* GPIO Interrupt Enable */ +#define GPIO_INT_EN3 0x1C /* GPIO Interrupt Enable */ +#define KP_GPIO1 0x1D /* Keypad or GPIO Selection */ +#define KP_GPIO2 0x1E /* Keypad or GPIO Selection */ +#define KP_GPIO3 0x1F /* Keypad or GPIO Selection */ +#define GPIO_DIR1 0x23 /* GPIO Data Direction */ +#define GPIO_DIR2 0x24 /* GPIO Data Direction */ +#define GPIO_DIR3 0x25 /* GPIO Data Direction */ +#define GPIO_PULL1 0x2C /* GPIO Pull Disable */ +#define GPIO_PULL2 0x2D /* GPIO Pull Disable */ +#define GPIO_PULL3 0x2E /* GPIO Pull Disable */ +#define ID_MASK 0x0F + +struct adp5588_gpio { + u8 dat_out[3]; + u8 dir[3]; +}; + +static int adp5588_gpio_read(struct udevice *dev, u8 reg) +{ + int ret; + u8 val; + + ret = dm_i2c_read(dev, reg, &val, 1); + + if (ret < 0) { + pr_err("%s: read error\n", __func__); + return ret; + } + + return val; +} + +static int adp5588_gpio_write(struct udevice *dev, u8 reg, u8 val) +{ + int ret; + + ret = dm_i2c_write(dev, reg, &val, 1); + if (ret < 0) { + pr_err("%s: write error\n", __func__); + return ret; + } + + return 0; +} + +static int adp5588_get_value(struct udevice *dev, u32 offset) +{ + struct adp5588_gpio *plat = dev_get_plat(dev); + unsigned int bank = ADP5588_BANK(offset); + unsigned int bit = ADP5588_BIT(offset); + int val; + + if (plat->dir[bank] & bit) + val = plat->dat_out[bank]; + else + val = adp5588_gpio_read(dev, GPIO_DAT_STAT1 + bank); + + return !!(val & bit); +} + +static int adp5588_set_value(struct udevice *dev, u32 offset, + int32_t value) +{ + unsigned int bank, bit; + int ret; + struct adp5588_gpio *plat = dev_get_plat(dev); + + bank = ADP5588_BANK(offset); + bit = ADP5588_BIT(offset); + + if (value) + plat->dat_out[bank] |= bit; + else + plat->dat_out[bank] &= ~bit; + + ret = adp5588_gpio_write(dev, GPIO_DAT_OUT1 + bank, + plat->dat_out[bank]); + + return ret; +} + +static int adp5588_direction_input(struct udevice *dev, u32 offset) +{ + int ret; + unsigned int bank; + struct adp5588_gpio *plat = dev_get_plat(dev); + + bank = ADP5588_BANK(offset); + + plat->dir[bank] &= ~ADP5588_BIT(offset); + ret = adp5588_gpio_write(dev, GPIO_DIR1 + bank, plat->dir[bank]); + + return ret; +} + +static int adp5588_direction_output(struct udevice *dev, + u32 offset, int value) +{ + int ret; + unsigned int bank, bit; + struct adp5588_gpio *plat = dev_get_plat(dev); + + bank = ADP5588_BANK(offset); + bit = ADP5588_BIT(offset); + + plat->dir[bank] |= bit; + + if (value) + plat->dat_out[bank] |= bit; + else + plat->dat_out[bank] &= ~bit; + + ret = adp5588_gpio_write(dev, GPIO_DAT_OUT1 + bank, + plat->dat_out[bank]); + ret |= adp5588_gpio_write(dev, GPIO_DIR1 + bank, + plat->dir[bank]); + + return ret; +} + +static int adp5588_ofdata_platdata(struct udevice *dev) +{ + struct adp5588_gpio *plat = dev_get_plat(dev); + struct gpio_dev_priv *priv = dev_get_uclass_priv(dev); + int node = dev_of_offset(dev); + int ret, i, revid; + + priv->gpio_count = ADP5588_MAXGPIO; + priv->bank_name = fdt_get_name(gd->fdt_blob, node, NULL); + + ret = adp5588_gpio_read(dev, DEV_ID); + if (ret < 0) + return ret; + + revid = ret & ID_MASK; + + printf("ADP5588 Detected: Rev %x, Rev ID %x\n", ret, revid); + + for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { + plat->dat_out[i] = adp5588_gpio_read(dev, GPIO_DAT_OUT1 + i); + plat->dir[i] = adp5588_gpio_read(dev, GPIO_DIR1 + i); + ret |= adp5588_gpio_write(dev, KP_GPIO1 + i, 0); + ret |= adp5588_gpio_write(dev, GPIO_PULL1 + i, 0); + ret |= adp5588_gpio_write(dev, GPIO_INT_EN1 + i, 0); + if (ret) { + pr_err("%s: Initialization error\n", __func__); + return ret; + } + } + + return 0; +} + +static const struct dm_gpio_ops adp5588_ops = { + .direction_input = adp5588_direction_input, + .direction_output = adp5588_direction_output, + .get_value = adp5588_get_value, + .set_value = adp5588_set_value, +}; + +static const struct udevice_id adp5588_of_match_list[] = { + { .compatible = "adi,adp5588"}, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(gpio_adp5588) = { + .name = "gpio_adp5588", + .id = UCLASS_GPIO, + .ops = &adp5588_ops, + .of_match = adp5588_of_match_list, + .of_to_plat = adp5588_ofdata_platdata, + .plat_auto = sizeof(struct adp5588_gpio), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/gpio/axp_gpio.c b/drivers/gpio/axp_gpio.c index 6e632c8fc73..181c53bfe72 100644 --- a/drivers/gpio/axp_gpio.c +++ b/drivers/gpio/axp_gpio.c @@ -15,6 +15,9 @@ #include <errno.h> #include <sunxi_gpio.h> +#define AXP_GPIO_PREFIX "AXP0-" +#define AXP_GPIO_COUNT 4 + static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val); static u8 axp_get_gpio_ctrl_reg(unsigned pin) @@ -46,28 +49,14 @@ static int axp_gpio_direction_input(struct udevice *dev, unsigned pin) static int axp_gpio_direction_output(struct udevice *dev, unsigned pin, int val) { - __maybe_unused int ret; u8 reg; - switch (pin) { -#ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC - /* Only available on later PMICs */ - case SUNXI_GPIO_AXP0_VBUS_ENABLE: - ret = pmic_bus_clrbits(AXP_MISC_CTRL, - AXP_MISC_CTRL_N_VBUSEN_FUNC); - if (ret) - return ret; - - return axp_gpio_set_value(dev, pin, val); -#endif - default: - reg = axp_get_gpio_ctrl_reg(pin); - if (reg == 0) - return -EINVAL; + reg = axp_get_gpio_ctrl_reg(pin); + if (reg == 0) + return -EINVAL; - return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH : - AXP_GPIO_CTRL_OUTPUT_LOW); - } + return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH : + AXP_GPIO_CTRL_OUTPUT_LOW); } static int axp_gpio_get_value(struct udevice *dev, unsigned pin) @@ -75,25 +64,16 @@ static int axp_gpio_get_value(struct udevice *dev, unsigned pin) u8 reg, val, mask; int ret; - switch (pin) { -#ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC - /* Only available on later PMICs */ - case SUNXI_GPIO_AXP0_VBUS_ENABLE: - ret = pmic_bus_read(AXP_VBUS_IPSOUT, &val); - mask = AXP_VBUS_IPSOUT_DRIVEBUS; - break; -#endif - default: - reg = axp_get_gpio_ctrl_reg(pin); - if (reg == 0) - return -EINVAL; + reg = axp_get_gpio_ctrl_reg(pin); + if (reg == 0) + return -EINVAL; - ret = pmic_bus_read(AXP_GPIO_STATE, &val); - mask = 1 << (pin + AXP_GPIO_STATE_OFFSET); - } + ret = pmic_bus_read(AXP_GPIO_STATE, &val); if (ret) return ret; + mask = 1 << (pin + AXP_GPIO_STATE_OFFSET); + return (val & mask) ? 1 : 0; } @@ -101,25 +81,12 @@ static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val) { u8 reg; - switch (pin) { -#ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC - /* Only available on later PMICs */ - case SUNXI_GPIO_AXP0_VBUS_ENABLE: - if (val) - return pmic_bus_setbits(AXP_VBUS_IPSOUT, - AXP_VBUS_IPSOUT_DRIVEBUS); - else - return pmic_bus_clrbits(AXP_VBUS_IPSOUT, - AXP_VBUS_IPSOUT_DRIVEBUS); -#endif - default: - reg = axp_get_gpio_ctrl_reg(pin); - if (reg == 0) - return -EINVAL; + reg = axp_get_gpio_ctrl_reg(pin); + if (reg == 0) + return -EINVAL; - return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH : - AXP_GPIO_CTRL_OUTPUT_LOW); - } + return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH : + AXP_GPIO_CTRL_OUTPUT_LOW); } static const struct dm_gpio_ops gpio_axp_ops = { @@ -134,8 +101,8 @@ static int gpio_axp_probe(struct udevice *dev) struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); /* Tell the uclass how many GPIOs we have */ - uc_priv->bank_name = strdup(SUNXI_GPIO_AXP0_PREFIX); - uc_priv->gpio_count = SUNXI_GPIO_AXP0_GPIO_COUNT; + uc_priv->bank_name = AXP_GPIO_PREFIX; + uc_priv->gpio_count = AXP_GPIO_COUNT; return 0; } diff --git a/drivers/gpio/gpio-adi-adsp.c b/drivers/gpio/gpio-adi-adsp.c new file mode 100644 index 00000000000..0ce00572e08 --- /dev/null +++ b/drivers/gpio/gpio-adi-adsp.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa <greg.malysa@timesys.com> + * Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + */ + +#include <dm.h> +#include <asm-generic/gpio.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/io.h> + +#define ADSP_PORT_MMIO_SIZE 0x80 +#define ADSP_PORT_PIN_SIZE 16 + +#define ADSP_PORT_REG_FER 0x00 +#define ADSP_PORT_REG_FER_SET 0x04 +#define ADSP_PORT_REG_FER_CLEAR 0x08 +#define ADSP_PORT_REG_DATA 0x0c +#define ADSP_PORT_REG_DATA_SET 0x10 +#define ADSP_PORT_REG_DATA_CLEAR 0x14 +#define ADSP_PORT_REG_DIR 0x18 +#define ADSP_PORT_REG_DIR_SET 0x1c +#define ADSP_PORT_REG_DIR_CLEAR 0x20 +#define ADSP_PORT_REG_INEN 0x24 +#define ADSP_PORT_REG_INEN_SET 0x28 +#define ADSP_PORT_REG_INEN_CLEAR 0x2c +#define ADSP_PORT_REG_PORT_MUX 0x30 +#define ADSP_PORT_REG_DATA_TGL 0x34 +#define ADSP_PORT_REG_POLAR 0x38 +#define ADSP_PORT_REG_POLAR_SET 0x3c +#define ADSP_PORT_REG_POLAR_CLEAR 0x40 +#define ADSP_PORT_REG_LOCK 0x44 +#define ADSP_PORT_REG_TRIG_TGL 0x48 + +struct adsp_gpio_priv { + void __iomem *base; + int ngpio; +}; + +static u32 get_port(unsigned int pin) +{ + return pin / ADSP_PORT_PIN_SIZE; +} + +static u32 get_offset(unsigned int pin) +{ + return pin % ADSP_PORT_PIN_SIZE; +} + +static int adsp_gpio_input(struct udevice *udev, unsigned int pin) +{ + struct adsp_gpio_priv *priv = dev_get_priv(udev); + u32 port, offset; + void __iomem *portbase; + + if (pin < priv->ngpio) { + port = get_port(pin); + offset = get_offset(pin); + portbase = priv->base + port * ADSP_PORT_MMIO_SIZE; + + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_FER_CLEAR); + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DIR_CLEAR); + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_INEN_SET); + return 0; + } + + return -EINVAL; +} + +static int adsp_gpio_output(struct udevice *udev, unsigned int pin, int value) +{ + struct adsp_gpio_priv *priv = dev_get_priv(udev); + u32 port, offset; + void __iomem *portbase; + + if (pin < priv->ngpio) { + port = get_port(pin); + offset = get_offset(pin); + portbase = priv->base + port * ADSP_PORT_MMIO_SIZE; + + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_FER_CLEAR); + + if (value) + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_SET); + else + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_CLEAR); + + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DIR_SET); + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_INEN_CLEAR); + return 0; + } + + return -EINVAL; +} + +static int adsp_gpio_get_value(struct udevice *udev, unsigned int pin) +{ + struct adsp_gpio_priv *priv = dev_get_priv(udev); + u32 port, offset; + u16 val; + void __iomem *portbase; + + if (pin < priv->ngpio) { + port = get_port(pin); + offset = get_offset(pin); + portbase = priv->base + port * ADSP_PORT_MMIO_SIZE; + + val = ioread16(portbase + ADSP_PORT_REG_DATA); + return !!(val & BIT(offset)); + } + + return 0; +} + +static int adsp_gpio_set_value(struct udevice *udev, unsigned int pin, int value) +{ + struct adsp_gpio_priv *priv = dev_get_priv(udev); + u32 port, offset; + void __iomem *portbase; + + if (pin < priv->ngpio) { + port = get_port(pin); + offset = get_offset(pin); + portbase = priv->base + port * ADSP_PORT_MMIO_SIZE; + + if (value) + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_SET); + else + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_CLEAR); + } + + return 0; +} + +static const struct dm_gpio_ops adsp_gpio_ops = { + .direction_input = adsp_gpio_input, + .direction_output = adsp_gpio_output, + .get_value = adsp_gpio_get_value, + .set_value = adsp_gpio_set_value, +}; + +static int adsp_gpio_probe(struct udevice *udev) +{ + struct adsp_gpio_priv *priv = dev_get_priv(udev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(udev); + + uc_priv->bank_name = "adsp gpio"; + uc_priv->gpio_count = dev_read_u32_default(udev, "adi,ngpios", 0); + + if (!uc_priv->gpio_count) { + dev_err(udev, "Missing adi,ngpios property!\n"); + return -ENOENT; + } + + priv->base = dev_read_addr_ptr(udev); + priv->ngpio = uc_priv->gpio_count; + + return 0; +} + +static const struct udevice_id adsp_gpio_match[] = { + { .compatible = "adi,adsp-gpio" }, + { }, +}; + +U_BOOT_DRIVER(adi_adsp_gpio) = { + .name = "adi_adsp_gpio", + .id = UCLASS_GPIO, + .ops = &adsp_gpio_ops, + .probe = adsp_gpio_probe, + .priv_auto = sizeof(struct adsp_gpio_priv), + .of_match = adsp_gpio_match, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/gpio/msm_gpio.c b/drivers/gpio/msm_gpio.c index cea073b3297..6783fc756f4 100644 --- a/drivers/gpio/msm_gpio.c +++ b/drivers/gpio/msm_gpio.c @@ -151,6 +151,9 @@ static int msm_gpio_direction_output(struct udevice *dev, unsigned int gpio, static int msm_gpio_set_flags(struct udevice *dev, unsigned int gpio, ulong flags) { + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return -EPERM; + if (flags & GPIOD_IS_OUT_ACTIVE) { return msm_gpio_direction_output(dev, gpio, 1); } else if (flags & GPIOD_IS_OUT) { @@ -172,12 +175,19 @@ static int msm_gpio_get_value_special(struct msm_gpio_bank *priv, unsigned int g const struct msm_special_pin_data *data; if (!priv->pin_data->special_pins_data) - return 0; + return -EINVAL; data = &priv->pin_data->special_pins_data[offset]; - if (!data->io_reg || data->in_bit >= 31) - return 0; + if (!data->io_reg) + return -EINVAL; + + if (data->in_bit >= 31) { + if (data->out_bit >= 31) + return -EINVAL; + + return !!(readl(priv->base + data->io_reg) >> data->out_bit); + } return !!(readl(priv->base + data->io_reg) >> data->in_bit); } @@ -186,19 +196,54 @@ static int msm_gpio_get_value(struct udevice *dev, unsigned int gpio) { struct msm_gpio_bank *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return -EPERM; + if (qcom_is_special_pin(priv->pin_data, gpio)) return msm_gpio_get_value_special(priv, gpio); return !!(readl(priv->base + GPIO_IN_OUT_REG(dev, gpio)) >> GPIO_IN); } +static int msm_gpio_get_function_special(struct msm_gpio_bank *priv, + unsigned int gpio) +{ + unsigned int offset = gpio - priv->pin_data->special_pins_start; + const struct msm_special_pin_data *data; + + if (!priv->pin_data->special_pins_data) + return GPIOF_UNKNOWN; + + data = &priv->pin_data->special_pins_data[offset]; + + /* No I/O fields, cannot control/read the I/O value */ + if (!data->io_reg || (data->out_bit >= 31 && data->in_bit >= 31)) + return GPIOF_FUNC; + + /* No Output-Enable register, cannot control I/O direction */ + if (!data->ctl_reg || data->oe_bit >= 31) { + if (data->out_bit >= 31) + return GPIOF_INPUT; + else + return GPIOF_OUTPUT; + } + + if (readl(priv->base + data->ctl_reg) & BIT(data->oe_bit)) + return GPIOF_OUTPUT; + + return GPIOF_INPUT; +} + static int msm_gpio_get_function(struct udevice *dev, unsigned int gpio) { struct msm_gpio_bank *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return GPIOF_UNKNOWN; + /* Always NOP for special pins, assume they're in the correct state */ if (qcom_is_special_pin(priv->pin_data, gpio)) - return 0; + return msm_gpio_get_function_special(priv, gpio); if (readl(priv->base + GPIO_CONFIG_REG(dev, gpio)) & GPIO_OE_ENABLE) return GPIOF_OUTPUT; diff --git a/drivers/gpio/pca953x_gpio.c b/drivers/gpio/pca953x_gpio.c index e84038f312e..523ca8473a8 100644 --- a/drivers/gpio/pca953x_gpio.c +++ b/drivers/gpio/pca953x_gpio.c @@ -393,6 +393,8 @@ static const struct udevice_id pca953x_ids[] = { { .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), }, { .compatible = "nxp,pca9698", .data = OF_953X(40, 0), }, + { .compatible = "nxp,pcal6408", .data = OF_953X(8, PCA_LATCH_INT), }, + { .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), }, { .compatible = "maxim,max7310", .data = OF_953X(8, 0), }, diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c index 2ca4960f17a..094c45a6927 100644 --- a/drivers/gpio/sunxi_gpio.c +++ b/drivers/gpio/sunxi_gpio.c @@ -244,17 +244,7 @@ int sunxi_name_to_gpio(const char *name) int sunxi_name_to_gpio(const char *name) { unsigned int gpio; - int ret; -#if !defined CONFIG_XPL_BUILD && defined CONFIG_AXP_GPIO - char lookup[8]; - - if (strcasecmp(name, "AXP0-VBUS-ENABLE") == 0) { - sprintf(lookup, SUNXI_GPIO_AXP0_PREFIX "%d", - SUNXI_GPIO_AXP0_VBUS_ENABLE); - name = lookup; - } -#endif - ret = gpio_lookup_name(name, NULL, NULL, &gpio); + int ret = gpio_lookup_name(name, NULL, NULL, &gpio); return ret ? ret : gpio; } diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index cdae6825736..146bc621c7e 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -154,6 +154,13 @@ config SPL_DM_I2C_GPIO bindings are supported. Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt +config SYS_I2C_ADI + bool "ADI I2C driver" + depends on DM_I2C && ARCH_SC5XX + help + Add support for the ADI (Analog Devices) I2C driver as used + in SC57X, SC58X, SC59X, SC59X_64. + config SYS_I2C_AT91 bool "Atmel I2C driver" depends on DM_I2C && ARCH_AT91 @@ -503,6 +510,15 @@ config SYS_I2C_OMAP24XX help Add support for the OMAP2+ I2C driver. +config SYS_I2C_OMAP24XX_REPEATED_START + bool "Enable I2C repeated start" + depends on SYS_I2C_OMAP24XX + default y if ARCH_K3 + help + Enable support for repeated start. Updates driver defaults to not + send a Stop condition and issue Repeated Start (Sr) for subsequent + i2c msgs. + config SYS_I2C_RCAR_I2C bool "Renesas R-Car I2C driver" depends on (RCAR_GEN2 || RCAR_64) && DM_I2C diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index bebd728e7da..42326db85f6 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -2,15 +2,16 @@ # # (C) Copyright 2000-2007 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. -obj-$(CONFIG_$(XPL_)DM_I2C) += i2c-uclass.o -ifdef CONFIG_$(XPL_)ACPIGEN -obj-$(CONFIG_$(XPL_)DM_I2C) += acpi_i2c.o +obj-$(CONFIG_$(PHASE_)DM_I2C) += i2c-uclass.o +ifdef CONFIG_$(PHASE_)ACPIGEN +obj-$(CONFIG_$(PHASE_)DM_I2C) += acpi_i2c.o endif -obj-$(CONFIG_$(XPL_)DM_I2C_GPIO) += i2c-gpio.o -obj-$(CONFIG_$(XPL_)I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o -obj-$(CONFIG_$(XPL_)I2C_CROS_EC_LDO) += cros_ec_ldo.o +obj-$(CONFIG_$(PHASE_)DM_I2C_GPIO) += i2c-gpio.o +obj-$(CONFIG_$(PHASE_)I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o +obj-$(CONFIG_$(PHASE_)I2C_CROS_EC_LDO) += cros_ec_ldo.o -obj-$(CONFIG_$(XPL_)SYS_I2C_LEGACY) += i2c_core.o +obj-$(CONFIG_$(PHASE_)SYS_I2C_LEGACY) += i2c_core.o +obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o obj-$(CONFIG_SYS_I2C_AST2600) += ast2600_i2c.o obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o @@ -57,4 +58,4 @@ obj-$(CONFIG_SYS_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_SYS_I2C_XILINX_XIIC) += xilinx_xiic.o obj-$(CONFIG_TEGRA186_BPMP_I2C) += tegra186_bpmp_i2c.o -obj-$(CONFIG_$(XPL_)I2C_MUX) += muxes/ +obj-$(CONFIG_$(PHASE_)I2C_MUX) += muxes/ diff --git a/drivers/i2c/adi_i2c.c b/drivers/i2c/adi_i2c.c new file mode 100644 index 00000000000..4cddcfa6b7f --- /dev/null +++ b/drivers/i2c/adi_i2c.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Converted to driver model by Nathan Barrett-Morrison + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + */ + +#include <clk.h> +#include <dm.h> +#include <i2c.h> +#include <mapmem.h> +#include <linux/io.h> + +#define CLKLOW(x) ((x) & 0xFF) // Periods Clock Is Held Low +#define CLKHI(y) (((y) & 0xFF) << 0x8) // Periods Clock Is High + +#define PRESCALE 0x007F // SCLKs Per Internal Time Reference (10MHz) +#define TWI_ENA 0x0080 // TWI Enable +#define SCCB 0x0200 // SCCB Compatibility Enable + +#define SEN 0x0001 // Slave Enable +#define SADD_LEN 0x0002 // Slave Address Length +#define STDVAL 0x0004 // Slave Transmit Data Valid +#define TSC_NAK 0x0008 // NAK Generated At Conclusion Of Transfer +#define GEN 0x0010 // General Call Adrress Matching Enabled + +#define SDIR 0x0001 // Slave Transfer Direction +#define GCALL 0x0002 // General Call Indicator + +#define MEN 0x0001 // Master Mode Enable +#define MADD_LEN 0x0002 // Master Address Length +#define MDIR 0x0004 // Master Transmit Direction (RX/TX*) +#define FAST 0x0008 // Use Fast Mode Timing Specs +#define STOP 0x0010 // Issue Stop Condition +#define RSTART 0x0020 // Repeat Start or Stop* At End Of Transfer +#define DCNT 0x3FC0 // Data Bytes To Transfer +#define SDAOVR 0x4000 // Serial Data Override +#define SCLOVR 0x8000 // Serial Clock Override + +#define MPROG 0x0001 // Master Transfer In Progress +#define LOSTARB 0x0002 // Lost Arbitration Indicator (Xfer Aborted) +#define ANAK 0x0004 // Address Not Acknowledged +#define DNAK 0x0008 // Data Not Acknowledged +#define BUFRDERR 0x0010 // Buffer Read Error +#define BUFWRERR 0x0020 // Buffer Write Error +#define SDASEN 0x0040 // Serial Data Sense +#define SCLSEN 0x0080 // Serial Clock Sense +#define BUSBUSY 0x0100 // Bus Busy Indicator + +#define SINIT 0x0001 // Slave Transfer Initiated +#define SCOMP 0x0002 // Slave Transfer Complete +#define SERR 0x0004 // Slave Transfer Error +#define SOVF 0x0008 // Slave Overflow +#define MCOMP 0x0010 // Master Transfer Complete +#define MERR 0x0020 // Master Transfer Error +#define XMTSERV 0x0040 // Transmit FIFO Service +#define RCVSERV 0x0080 // Receive FIFO Service + +#define XMTFLUSH 0x0001 // Transmit Buffer Flush +#define RCVFLUSH 0x0002 // Receive Buffer Flush +#define XMTINTLEN 0x0004 // Transmit Buffer Interrupt Length +#define RCVINTLEN 0x0008 // Receive Buffer Interrupt Length + +#define XMTSTAT 0x0003 // Transmit FIFO Status +#define XMT_EMPTY 0x0000 // Transmit FIFO Empty +#define XMT_HALF 0x0001 // Transmit FIFO Has 1 Byte To Write +#define XMT_FULL 0x0003 // Transmit FIFO Full (2 Bytes To Write) + +#define RCVSTAT 0x000C // Receive FIFO Status +#define RCV_EMPTY 0x0000 // Receive FIFO Empty +#define RCV_HALF 0x0004 // Receive FIFO Has 1 Byte To Read +#define RCV_FULL 0x000C // Receive FIFO Full (2 Bytes To Read) + +/* Every register is 32bit aligned, but only 16bits in size */ +#define ureg(name) u16 name; u16 __pad_##name + +struct twi_regs { + ureg(clkdiv); + ureg(control); + ureg(slave_ctl); + ureg(slave_stat); + ureg(slave_addr); + ureg(master_ctl); + ureg(master_stat); + ureg(master_addr); + ureg(int_stat); + ureg(int_mask); + ureg(fifo_ctl); + ureg(fifo_stat); + u8 __pad[0x50]; + + ureg(xmt_data8); + ureg(xmt_data16); + ureg(rcv_data8); + ureg(rcv_data16); +}; + +#undef ureg + +/* + * The way speed is changed into duty often results in integer truncation + * with 50% duty, so we'll force rounding up to the next duty by adding 1 + * to the max. In practice this will get us a speed of something like + * 385 KHz. The other limit is easy to handle as it is only 8 bits. + */ +#define I2C_SPEED_MAX 400000 +#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed)) +#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1) +#define I2C_DUTY_MIN 0xff /* 8 bit limited */ + +#define I2C_M_COMBO 0x4 +#define I2C_M_STOP 0x2 +#define I2C_M_READ 0x1 + +/* + * All transfers are described by this data structure + */ +struct adi_i2c_msg { + u8 flags; + u32 len; /* msg length */ + u8 *buf; /* pointer to msg data */ + u32 olen; /* addr length */ + u8 *obuf; /* addr buffer */ +}; + +struct adi_i2c_dev { + struct twi_regs __iomem *base; + u32 i2c_clk; + uint speed; +}; + +/* Allow msec timeout per ~byte transfer */ +#define I2C_TIMEOUT 10 + +/** + * wait_for_completion - manage the actual i2c transfer + * @msg: the i2c msg + */ +static int wait_for_completion(struct twi_regs *twi, struct adi_i2c_msg *msg) +{ + u16 int_stat; + ulong timebase = get_timer(0); + + do { + int_stat = ioread16(&twi->int_stat); + + if (int_stat & XMTSERV) { + iowrite16(XMTSERV, &twi->int_stat); + if (msg->olen) { + iowrite16(*(msg->obuf++), &twi->xmt_data8); + --msg->olen; + } else if (!(msg->flags & I2C_M_COMBO) && msg->len) { + iowrite16(*(msg->buf++), &twi->xmt_data8); + --msg->len; + } else { + if (msg->flags & I2C_M_COMBO) + setbits_16(&twi->master_ctl, RSTART | MDIR); + else + setbits_16(&twi->master_ctl, STOP); + } + } + if (int_stat & RCVSERV) { + iowrite16(RCVSERV, &twi->int_stat); + if (msg->len) { + *(msg->buf++) = ioread16(&twi->rcv_data8); + --msg->len; + } else if (msg->flags & I2C_M_STOP) { + setbits_16(&twi->master_ctl, STOP); + } + } + if (int_stat & MERR) { + pr_err("%s: master transmit terror: %d\n", __func__, + ioread16(&twi->master_stat)); + iowrite16(MERR, &twi->int_stat); + return -EIO; + } + if (int_stat & MCOMP) { + iowrite16(MCOMP, &twi->int_stat); + if (msg->flags & I2C_M_COMBO && msg->len) { + u16 mlen = min(msg->len, 0xffu) << 6; + clrsetbits_16(&twi->master_ctl, RSTART, mlen | MEN | MDIR); + } else { + break; + } + } + + /* If we were able to do something, reset timeout */ + if (int_stat) + timebase = get_timer(0); + + } while (get_timer(timebase) < I2C_TIMEOUT); + + return 0; +} + +static int i2c_transfer(struct twi_regs *twi, u8 chip, u8 *offset, + int olen, u8 *buffer, int len, u8 flags) +{ + int ret; + u16 ctl; + + struct adi_i2c_msg msg = { + .flags = flags | (len >= 0xff ? I2C_M_STOP : 0), + .buf = buffer, + .len = len, + .obuf = offset, + .olen = olen, + }; + + /* wait for things to settle */ + while (ioread16(&twi->master_stat) & BUSBUSY) + if (!IS_ENABLED(CONFIG_SPL_BUILD) && ctrlc()) + return -EINTR; + + /* Set Transmit device address */ + iowrite16(chip, &twi->master_addr); + + /* Clear the FIFO before starting things */ + iowrite16(XMTFLUSH | RCVFLUSH, &twi->fifo_ctl); + iowrite16(0, &twi->fifo_ctl); + + /* Prime the pump */ + if (msg.olen) { + len = (msg.flags & I2C_M_COMBO) ? msg.olen : msg.olen + len; + iowrite16(*(msg.obuf++), &twi->xmt_data8); + --msg.olen; + } else if (!(msg.flags & I2C_M_READ) && msg.len) { + iowrite16(*(msg.buf++), &twi->xmt_data8); + --msg.len; + } + + /* clear int stat */ + iowrite16(-1, &twi->master_stat); + iowrite16(-1, &twi->int_stat); + iowrite16(0, &twi->int_mask); + + /* Master enable */ + ctl = ioread16(&twi->master_ctl); + ctl = (ctl & FAST) | (min(len, 0xff) << 6) | MEN | + ((msg.flags & I2C_M_READ) ? MDIR : 0); + iowrite16(ctl, &twi->master_ctl); + + /* Process the rest */ + ret = wait_for_completion(twi, &msg); + + clrbits_16(&twi->master_ctl, MEN); + clrbits_16(&twi->control, TWI_ENA); + setbits_16(&twi->control, TWI_ENA); + return ret; +} + +static int adi_i2c_read(struct twi_regs *twi, u8 chip, + u8 *offset, int olen, u8 *buffer, int len) +{ + return i2c_transfer(twi, chip, offset, olen, buffer, + len, olen ? I2C_M_COMBO : I2C_M_READ); +} + +static int adi_i2c_write(struct twi_regs *twi, u8 chip, + u8 *offset, int olen, u8 *buffer, int len) +{ + return i2c_transfer(twi, chip, offset, olen, buffer, len, 0); +} + +static int adi_i2c_set_bus_speed(struct udevice *bus, uint speed) +{ + struct adi_i2c_dev *dev = dev_get_priv(bus); + struct twi_regs *twi = dev->base; + u16 clkdiv = I2C_SPEED_TO_DUTY(speed); + + /* Set TWI interface clock */ + if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN) + return -1; + clkdiv = (clkdiv << 8) | (clkdiv & 0xff); + iowrite16(clkdiv, &twi->clkdiv); + + /* Don't turn it on */ + iowrite16(speed > 100000 ? FAST : 0, &twi->master_ctl); + + return 0; +} + +static int adi_i2c_of_to_plat(struct udevice *bus) +{ + struct adi_i2c_dev *dev = dev_get_priv(bus); + struct clk clock; + u32 ret; + + dev->base = map_sysmem(dev_read_addr(bus), sizeof(struct twi_regs)); + + if (!dev->base) + return -ENOMEM; + + dev->speed = dev_read_u32_default(bus, "clock-frequency", + I2C_SPEED_FAST_RATE); + + ret = clk_get_by_name(bus, "i2c", &clock); + if (ret < 0) + printf("%s: Can't get I2C clk: %d\n", __func__, ret); + else + dev->i2c_clk = clk_get_rate(&clock); + + return 0; +} + +static int adi_i2c_probe_chip(struct udevice *bus, u32 chip_addr, + u32 chip_flags) +{ + struct adi_i2c_dev *dev = dev_get_priv(bus); + u8 byte; + + return adi_i2c_read(dev->base, chip_addr, NULL, 0, &byte, 1); +} + +static int adi_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) +{ + struct adi_i2c_dev *dev = dev_get_priv(bus); + struct i2c_msg *dmsg, *omsg, dummy; + + memset(&dummy, 0, sizeof(struct i2c_msg)); + + /* + * We expect either two messages (one with an offset and one with the + * actual data) or one message (just data) + */ + if (nmsgs > 2 || nmsgs == 0) { + debug("%s: Only one or two messages are supported.", __func__); + return -EINVAL; + } + + omsg = nmsgs == 1 ? &dummy : msg; + dmsg = nmsgs == 1 ? msg : msg + 1; + + if (dmsg->flags & I2C_M_RD) + return adi_i2c_read(dev->base, dmsg->addr, omsg->buf, omsg->len, + dmsg->buf, dmsg->len); + else + return adi_i2c_write(dev->base, dmsg->addr, omsg->buf, omsg->len, + dmsg->buf, dmsg->len); +} + +int adi_i2c_probe(struct udevice *bus) +{ + struct adi_i2c_dev *dev = dev_get_priv(bus); + struct twi_regs *twi = dev->base; + + u16 prescale = ((dev->i2c_clk / 1000 / 1000 + 5) / 10) & 0x7F; + + /* Set TWI internal clock as 10MHz */ + iowrite16(prescale, &twi->control); + + /* Set TWI interface clock as specified */ + adi_i2c_set_bus_speed(bus, dev->speed); + + /* Enable it */ + iowrite16(TWI_ENA | prescale, &twi->control); + + return 0; +} + +static const struct dm_i2c_ops adi_i2c_ops = { + .xfer = adi_i2c_xfer, + .probe_chip = adi_i2c_probe_chip, + .set_bus_speed = adi_i2c_set_bus_speed, +}; + +static const struct udevice_id adi_i2c_ids[] = { + { .compatible = "adi-i2c", }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(i2c_adi) = { + .name = "i2c_adi", + .id = UCLASS_I2C, + .of_match = adi_i2c_ids, + .probe = adi_i2c_probe, + .of_to_plat = adi_i2c_of_to_plat, + .priv_auto = sizeof(struct adi_i2c_dev), + .ops = &adi_i2c_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/i2c/mtk_i2c.c b/drivers/i2c/mtk_i2c.c index 3450177741a..55381dbeced 100644 --- a/drivers/i2c/mtk_i2c.c +++ b/drivers/i2c/mtk_i2c.c @@ -143,7 +143,6 @@ static const uint mt_i2c_regs_v1[] = { [REG_RSV_DEBUG] = 0x44, [REG_HS] = 0x48, [REG_SOFTRESET] = 0x50, - [REG_SOFTRESET] = 0x50, [REG_DCM_EN] = 0x54, [REG_DEBUGSTAT] = 0x64, [REG_DEBUGCTRL] = 0x68, @@ -879,7 +878,7 @@ static const struct udevice_id mtk_i2c_ids[] = { }, { .compatible = "mediatek,mt8518-i2c", .data = (ulong)&mt8518_soc_data, - } + }, {} }; U_BOOT_DRIVER(mtk_i2c) = { diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index ebe472e20cd..a6361d3d17d 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -535,12 +535,16 @@ pr_exit: return res; } +#if !CONFIG_IS_ENABLED(DM_I2C) +/* + * The legacy I2C functions. These need to get removed once + * all users of this driver are converted to DM. + */ + /* * i2c_read: Function now uses a single I2C read transaction with bulk transfer * of the requested number of bytes (note that the 'i2c md' command - * limits this to 16 bytes anyway). If CONFIG_I2C_REPEATED_START is - * defined in the board config header, this transaction shall be with - * Repeated Start (Sr) between the address and data phases; otherwise + * limits this to 16 bytes anyway). * Stop-Start (P-S) shall be used (some I2C chips do require a P-S). * The address (reg offset) may be 0, 1 or 2 bytes long. * Function now reads correctly from chips that return more than one @@ -608,16 +612,10 @@ static int __omap24_i2c_read(void __iomem *i2c_base, int ip_rev, int waitdelay, if (alen) { /* Must write reg offset first */ -#ifdef CONFIG_I2C_REPEATED_START - /* No stop bit, use Repeated Start (Sr) */ - omap_i2c_write_reg(i2c_base, ip_rev, I2C_CON_EN | I2C_CON_MST | - I2C_CON_STT | I2C_CON_TRX, OMAP_I2C_CON_REG); -#else /* Stop - Start (P-S) */ omap_i2c_write_reg(i2c_base, ip_rev, I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP | I2C_CON_TRX, OMAP_I2C_CON_REG); -#endif /* Send register offset */ while (1) { status = wait_for_event(i2c_base, ip_rev, waitdelay); @@ -836,11 +834,6 @@ wr_exit: return i2c_error; } -#if !CONFIG_IS_ENABLED(DM_I2C) -/* - * The legacy I2C functions. These need to get removed once - * all users of this driver are converted to DM. - */ static void __iomem *omap24_get_base(struct i2c_adapter *adap) { switch (adap->hwadapnr) { @@ -971,28 +964,140 @@ U_BOOT_I2C_ADAP_COMPLETE(omap24_4, omap24_i2c_init, omap24_i2c_probe, #else /* CONFIG_DM_I2C */ +static int __omap24_i2c_xfer_msg(void __iomem *i2c_base, int ip_rev, int waitdelay, + uchar chip, uchar *buffer, int len, u16 i2c_con_reg) +{ + int i; + u16 status; + int i2c_error = 0; + int timeout = I2C_TIMEOUT; + + if (len < 0) { + printf("%s: data len < 0\n", __func__); + return -EINVAL; + } + + if (!buffer) { + printf("%s: NULL pointer passed\n", __func__); + return -EINVAL; + } + + if (!(i2c_con_reg & I2C_CON_EN)) { + printf("%s: I2C_CON_EN not set\n", __func__); + return -EINVAL; + } + + /* Set slave address */ + omap_i2c_write_reg(i2c_base, ip_rev, chip, OMAP_I2C_SA_REG); + /* Read/Write len bytes data */ + omap_i2c_write_reg(i2c_base, ip_rev, len, OMAP_I2C_CNT_REG); + /* Configure the I2C_CON register */ + omap_i2c_write_reg(i2c_base, ip_rev, i2c_con_reg, OMAP_I2C_CON_REG); + + /* read/write data bytewise */ + for (i = 0; i < len; i++) { + status = wait_for_event(i2c_base, ip_rev, waitdelay); + /* Ignore I2C_STAT_RRDY in transmitter mode */ + if (i2c_con_reg & I2C_CON_TRX) + status &= ~I2C_STAT_RRDY; + else + status &= ~I2C_STAT_XRDY; + + /* Try to identify bus that is not padconf'd for I2C */ + if (status == I2C_STAT_XRDY) { + i2c_error = -EREMOTEIO; + printf("%s: pads on bus probably not configured (status=0x%x)\n", + __func__, status); + goto xfer_exit; + } + if (status == 0 || (status & I2C_STAT_NACK)) { + i2c_error = -EREMOTEIO; + printf("%s: error waiting for ACK (status=0x%x)\n", + __func__, status); + goto xfer_exit; + } + if (status & I2C_STAT_XRDY) { + /* Transmit data */ + omap_i2c_write_reg(i2c_base, ip_rev, + buffer[i], OMAP_I2C_DATA_REG); + omap_i2c_write_reg(i2c_base, ip_rev, + I2C_STAT_XRDY, OMAP_I2C_STAT_REG); + } + if (status & I2C_STAT_RRDY) { + /* Receive data */ + *buffer++ = omap_i2c_read_reg(i2c_base, ip_rev, + OMAP_I2C_DATA_REG); + omap_i2c_write_reg(i2c_base, ip_rev, + I2C_STAT_RRDY, OMAP_I2C_STAT_REG); + } + } + + /* + * poll ARDY bit for making sure that last byte really has been + * transferred on the bus. + */ + do { + status = wait_for_event(i2c_base, ip_rev, waitdelay); + } while (!(status & I2C_STAT_ARDY) && timeout--); + if (timeout <= 0) { + printf("%s: timed out on last byte!\n", __func__); + i2c_error = -EREMOTEIO; + goto xfer_exit; + } else { + omap_i2c_write_reg(i2c_base, ip_rev, I2C_STAT_ARDY, OMAP_I2C_STAT_REG); + } + + /* If Stop bit set, flush FIFO. */ + if (i2c_con_reg & I2C_CON_STP) + goto xfer_exit; + + return 0; + +xfer_exit: + flush_fifo(i2c_base, ip_rev); + omap_i2c_write_reg(i2c_base, ip_rev, 0xFFFF, OMAP_I2C_STAT_REG); + return i2c_error; +} + static int omap_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) { struct omap_i2c *priv = dev_get_priv(bus); int ret; + u16 i2c_con_reg = 0; - debug("i2c_xfer: %d messages\n", nmsgs); - for (; nmsgs > 0; nmsgs--, msg++) { - debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); - if (msg->flags & I2C_M_RD) { - ret = __omap24_i2c_read(priv->regs, priv->ip_rev, - priv->waitdelay, - msg->addr, 0, 0, msg->buf, - msg->len); - } else { - ret = __omap24_i2c_write(priv->regs, priv->ip_rev, - priv->waitdelay, - msg->addr, 0, 0, msg->buf, - msg->len); - } + debug("%s: %d messages\n", __func__, nmsgs); + for (int i = 0; i < nmsgs; i++, msg++) { + /* + * If previous msg sent a Stop or if this is the first msg + * Wait until bus not busy + */ + if ((i2c_con_reg & I2C_CON_STP) || (i == 0)) + if (wait_for_bb(priv->regs, priv->ip_rev, priv->waitdelay)) + return -EREMOTEIO; + + /* Set Controller mode with Start bit */ + i2c_con_reg = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT; + /* Set Transmitter/Receiver mode if it is a write/read msg */ + if (msg->flags & I2C_M_RD) + i2c_con_reg &= ~I2C_CON_TRX; + else + i2c_con_reg |= I2C_CON_TRX; + /* Send Stop condition (P) by default */ + if (!IS_ENABLED(CONFIG_SYS_I2C_OMAP24XX_REPEATED_START)) + i2c_con_reg |= I2C_CON_STP; + /* Send Stop if explicitly requested or if this is the last msg */ + if ((msg->flags & I2C_M_STOP) || (i == nmsgs - 1)) + i2c_con_reg |= I2C_CON_STP; + + debug("%s: chip=0x%x, len=0x%x, i2c_con_reg=0x%x\n", + __func__, msg->addr, msg->len, i2c_con_reg); + + ret = __omap24_i2c_xfer_msg(priv->regs, priv->ip_rev, priv->waitdelay, + msg->addr, msg->buf, msg->len, + i2c_con_reg); if (ret) { - debug("i2c_write: error sending\n"); - return -EREMOTEIO; + printf("%s: errored out at msg %d: %d\n", __func__, i, ret); + return ret; } } diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index c2b365af11d..c09f0ae795e 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -55,6 +55,12 @@ config BUTTON_KEYBOARD dt node to define button-event mapping. For example, an arrows and enter may be implemented to navigate boot menu. +config CPCAP_POWER_BUTTON + bool "Enable power button of CPCAP PMIC support" + depends on DM_KEYBOARD && DM_PMIC_CPCAP + help + Enable support for a dedicated power button of the CPCAP PMIC. + config CROS_EC_KEYB bool "Enable Chrome OS EC keyboard support" depends on INPUT diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 8d4107b8848..1303fcdb0b7 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_$(PHASE_)CROS_EC_KEYB) += cros_ec_keyb.o obj-$(CONFIG_$(PHASE_)OF_CONTROL) += key_matrix.o obj-$(CONFIG_$(PHASE_)DM_KEYBOARD) += input.o keyboard-uclass.o obj-$(CONFIG_BUTTON_KEYBOARD) += button_kbd.o +obj-$(CONFIG_CPCAP_POWER_BUTTON) += cpcap_pwrbutton.o ifndef CONFIG_XPL_BUILD diff --git a/drivers/input/cpcap_pwrbutton.c b/drivers/input/cpcap_pwrbutton.c new file mode 100644 index 00000000000..c8ad39d33ca --- /dev/null +++ b/drivers/input/cpcap_pwrbutton.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <stdlib.h> +#include <dm.h> +#include <input.h> +#include <keyboard.h> +#include <power/pmic.h> +#include <power/cpcap.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/input.h> + +static const unsigned int cpcpap_to_reg[] = { + CPCAP_REG_INT1, + CPCAP_REG_INT2, + CPCAP_REG_INT3, + CPCAP_REG_INT4, +}; + +/** + * struct cpcap_pwrbutton_priv + * + * @bank: id of interrupt bank co-responding to an IRQ register + * @id: id of interrupt pin co-responding to the bit in IRQ register + * @keycode: linux key code + * @old_state: holder of last button state + * @skip: holder of keycode skip state. This is required since both pressing + * and releasing generate same event and cause key send duplication. + */ +struct cpcap_pwrbutton_priv { + u32 bank; + u32 id; + + u32 keycode; + + bool old_state; + bool skip; +}; + +static int cpcap_pwrbutton_read_keys(struct input_config *input) +{ + struct udevice *dev = input->dev; + struct cpcap_pwrbutton_priv *priv = dev_get_priv(dev); + u32 value, state_changed; + bool state; + + value = pmic_reg_read(dev->parent, cpcpap_to_reg[priv->bank]) & + BIT(priv->id); + + /* Interrupt bit is cleared by writing it to interrupt reg */ + pmic_reg_write(dev->parent, cpcpap_to_reg[priv->bank], BIT(priv->id)); + + state = value >> priv->id; + state_changed = state != priv->old_state; + + if (state_changed && !priv->skip) { + priv->old_state = state; + input_add_keycode(input, priv->keycode, state); + } + + if (state) + priv->skip = !priv->skip; + + return 0; +} + +static int cpcap_pwrbutton_of_to_plat(struct udevice *dev) +{ + struct cpcap_pwrbutton_priv *priv = dev_get_priv(dev); + ofnode irq_parent; + u32 irq_desc; + int ret; + + /* Check interrupt parent, driver supports only CPCAP as parent */ + irq_parent = ofnode_parse_phandle(dev_ofnode(dev), "interrupt-parent", 0); + if (!ofnode_device_is_compatible(irq_parent, "motorola,cpcap")) + return -EINVAL; + + ret = dev_read_u32(dev, "interrupts", &irq_desc); + if (ret) + return ret; + + /* IRQ registers are 16 bit wide */ + priv->bank = irq_desc / 16; + priv->id = irq_desc % 16; + + ret = dev_read_u32(dev, "linux,code", &priv->keycode); + if (ret) + return ret; + + priv->old_state = false; + priv->skip = false; + return 0; +} + +static int cpcap_pwrbutton_probe(struct udevice *dev) +{ + struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); + struct stdio_dev *sdev = &uc_priv->sdev; + struct input_config *input = &uc_priv->input; + int ret; + + input_init(input, false); + input_add_tables(input, false); + + /* Register the device */ + input->dev = dev; + input->read_keys = cpcap_pwrbutton_read_keys; + strcpy(sdev->name, "cpcap-pwrbutton"); + ret = input_stdio_register(sdev); + if (ret) { + log_debug("%s: input_stdio_register() failed\n", __func__); + return ret; + } + + return 0; +} + +static const struct udevice_id cpcap_pwrbutton_ids[] = { + { .compatible = "motorola,cpcap-pwrbutton" }, + { } +}; + +U_BOOT_DRIVER(cpcap_pwrbutton) = { + .name = "cpcap_pwrbutton", + .id = UCLASS_KEYBOARD, + .of_match = cpcap_pwrbutton_ids, + .of_to_plat = cpcap_pwrbutton_of_to_plat, + .probe = cpcap_pwrbutton_probe, + .priv_auto = sizeof(struct cpcap_pwrbutton_priv), +}; diff --git a/drivers/led/Makefile b/drivers/led/Makefile index aa64a38b4e1..996753b88ae 100644 --- a/drivers/led/Makefile +++ b/drivers/led/Makefile @@ -10,6 +10,6 @@ obj-$(CONFIG_LED_BCM6358) += led_bcm6358.o obj-$(CONFIG_LED_BCM6753) += led_bcm6753.o obj-$(CONFIG_LED_BCM6858) += led_bcm6858.o obj-$(CONFIG_LED_PWM) += led_pwm.o -obj-$(CONFIG_$(XPL_)LED_GPIO) += led_gpio.o +obj-$(CONFIG_$(PHASE_)LED_GPIO) += led_gpio.o obj-$(CONFIG_LED_CORTINA) += led_cortina.o obj-$(CONFIG_LED_LP5562) += led_lp5562.o diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 67d5ac1a742..4d9f004ebad 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -21,6 +21,13 @@ config APPLE_MBOX such as the System Management Controller (SMC) and NVMe and this driver is required to get that functionality up and running. +config IMX_MU_MBOX + bool "Enable i.MX MU MBOX support" + depends on DM_MAILBOX + help + Enable support for i.MX Messaging Unit for communication with other + processors on the SoC using mailbox interface + config SANDBOX_MBOX bool "Enable the sandbox mailbox test driver" depends on DM_MAILBOX && SANDBOX diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 6072fa1956b..e8c745f7d79 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -3,8 +3,9 @@ # Copyright (c) 2016, NVIDIA CORPORATION. # -obj-$(CONFIG_$(XPL_)DM_MAILBOX) += mailbox-uclass.o +obj-$(CONFIG_$(PHASE_)DM_MAILBOX) += mailbox-uclass.o obj-$(CONFIG_APPLE_MBOX) += apple-mbox.o +obj-$(CONFIG_IMX_MU_MBOX) += imx-mailbox.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox-test.o obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c new file mode 100644 index 00000000000..b1e0465e7a8 --- /dev/null +++ b/drivers/mailbox/imx-mailbox.c @@ -0,0 +1,443 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + */ + +#include <asm/io.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <mailbox-uclass.h> +#include <linux/bitfield.h> +#include <linux/bug.h> +#include <linux/iopoll.h> +#include <linux/compat.h> + +/* This driver only exposes the status bits to keep with the + * polling methodology of u-boot. + */ +DECLARE_GLOBAL_DATA_PTR; + +#define IMX_MU_CHANS 24 + +#define IMX_MU_V2_PAR_OFF 0x4 +#define IMX_MU_V2_TR_MASK GENMASK(7, 0) +#define IMX_MU_V2_RR_MASK GENMASK(15, 8) + +enum imx_mu_chan_type { + IMX_MU_TYPE_TX = 0, /* Tx */ + IMX_MU_TYPE_RX = 1, /* Rx */ + IMX_MU_TYPE_TXDB = 2, /* Tx doorbell */ + IMX_MU_TYPE_RXDB = 3, /* Rx doorbell */ + IMX_MU_TYPE_RST = 4, /* Reset */ + IMX_MU_TYPE_TXDB_V2 = 5, /* Tx doorbell with S/W ACK */ +}; + +enum imx_mu_xcr { + IMX_MU_CR, + IMX_MU_GIER, + IMX_MU_GCR, + IMX_MU_TCR, + IMX_MU_RCR, + IMX_MU_xCR_MAX, +}; + +enum imx_mu_xsr { + IMX_MU_SR, + IMX_MU_GSR, + IMX_MU_TSR, + IMX_MU_RSR, + IMX_MU_xSR_MAX, +}; + +struct imx_mu_con_priv { + unsigned int idx; + enum imx_mu_chan_type type; + struct mbox_chan *chan; +}; + +enum imx_mu_type { + IMX_MU_V1, + IMX_MU_V2 = BIT(1), + IMX_MU_V2_S4 = BIT(15), + IMX_MU_V2_IRQ = BIT(16), +}; + +struct imx_mu { + void __iomem *base; + const struct imx_mu_dcfg *dcfg; + u32 num_tr; + u32 num_rr; + /* use pointers to channel as a way to reserve channels */ + struct mbox_chan *channels[IMX_MU_CHANS]; + struct imx_mu_con_priv con_priv[IMX_MU_CHANS]; +}; + +struct imx_mu_dcfg { + int (*tx)(struct imx_mu *plat, struct imx_mu_con_priv *cp, const void *data); + int (*rx)(struct imx_mu *plat, struct imx_mu_con_priv *cp); + int (*rxdb)(struct imx_mu *plat, struct imx_mu_con_priv *cp); + int (*init)(struct imx_mu *plat); + int (*of_xlate)(struct mbox_chan *chan, struct ofnode_phandle_args *args); + enum imx_mu_type type; + u32 xTR; /* Transmit Register0 */ + u32 xRR; /* Receive Register0 */ + u32 xSR[IMX_MU_xSR_MAX]; /* Status Registers */ + u32 xCR[IMX_MU_xCR_MAX]; /* Control Registers */ +}; + +#define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) +#define IMX_MU_xSR_RFn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) +#define IMX_MU_xSR_TEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x)))) + +/* General Purpose Interrupt Enable */ +#define IMX_MU_xCR_GIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) +/* Receive Interrupt Enable */ +#define IMX_MU_xCR_RIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) +/* Transmit Interrupt Enable */ +#define IMX_MU_xCR_TIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x)))) +/* General Purpose Interrupt Request */ +#define IMX_MU_xCR_GIRn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x)))) +/* MU reset */ +#define IMX_MU_xCR_RST(type) (type & IMX_MU_V2 ? BIT(0) : BIT(5)) +#define IMX_MU_xSR_RST(type) (type & IMX_MU_V2 ? BIT(0) : BIT(7)) + +static void imx_mu_write(struct imx_mu *plat, u32 val, u32 offs) +{ + iowrite32(val, plat->base + offs); +} + +static u32 imx_mu_read(struct imx_mu *plat, u32 offs) +{ + return ioread32(plat->base + offs); +} + +static u32 imx_mu_xcr_rmw(struct imx_mu *plat, enum imx_mu_xcr type, u32 set, u32 clr) +{ + u32 val; + + val = imx_mu_read(plat, plat->dcfg->xCR[type]); + val &= ~clr; + val |= set; + imx_mu_write(plat, val, plat->dcfg->xCR[type]); + + return val; +} + +/* check that the channel is open or owned by caller */ +static int imx_mu_check_channel(struct mbox_chan *chan) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + + if (plat->channels[chan->id]) { + /* if reserved check that caller owns */ + if (plat->channels[chan->id] == chan) + return 1; /* caller owns the channel */ + + return -EACCES; + } + + return 0; /* channel empty */ +} + +static int imx_mu_chan_request(struct mbox_chan *chan) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + struct imx_mu_con_priv *cp; + enum imx_mu_chan_type type; + int idx; + + type = chan->id / 4; + idx = chan->id % 4; + + if (imx_mu_check_channel(chan) < 0) /* check if channel already in use */ + return -EPERM; + + plat->channels[chan->id] = chan; + chan->con_priv = kcalloc(1, sizeof(struct imx_mu_con_priv), 0); + if (!chan->con_priv) + return -ENOMEM; + cp = chan->con_priv; + cp->idx = idx; + cp->type = type; + cp->chan = chan; + + switch (type) { + case IMX_MU_TYPE_RX: + imx_mu_xcr_rmw(plat, IMX_MU_RCR, IMX_MU_xCR_RIEn(plat->dcfg->type, idx), 0); + break; + case IMX_MU_TYPE_TXDB_V2: + case IMX_MU_TYPE_TXDB: + case IMX_MU_TYPE_RXDB: + imx_mu_xcr_rmw(plat, IMX_MU_GIER, IMX_MU_xCR_GIEn(plat->dcfg->type, idx), 0); + break; + default: + break; + } + + return 0; +} + +static int imx_mu_chan_free(struct mbox_chan *chan) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + struct imx_mu_con_priv *cp = chan->con_priv; + + if (imx_mu_check_channel(chan) <= 0) /* check that the channel is also not empty */ + return -EINVAL; + + /* if you own channel and channel is NOT empty */ + plat->channels[chan->id] = NULL; + switch (cp->type) { + case IMX_MU_TYPE_TX: + imx_mu_xcr_rmw(plat, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(plat->dcfg->type, cp->idx)); + break; + case IMX_MU_TYPE_RX: + imx_mu_xcr_rmw(plat, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(plat->dcfg->type, cp->idx)); + break; + case IMX_MU_TYPE_TXDB_V2: + case IMX_MU_TYPE_TXDB: + case IMX_MU_TYPE_RXDB: + imx_mu_xcr_rmw(plat, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(plat->dcfg->type, cp->idx)); + break; + default: + break; + } + + kfree(cp); + + return 0; +} + +static int imx_mu_send(struct mbox_chan *chan, const void *data) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + struct imx_mu_con_priv *cp = chan->con_priv; + + if (imx_mu_check_channel(chan) < 1) /* return if channel isn't owned */ + return -EPERM; + + return plat->dcfg->tx(plat, cp, data); +} + +static int imx_mu_recv(struct mbox_chan *chan, void *data) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + struct imx_mu_con_priv *cp = chan->con_priv; + u32 ctrl, val; + + if (imx_mu_check_channel(chan) < 1) /* return if channel isn't owned */ + return -EPERM; + + switch (cp->type) { + case IMX_MU_TYPE_TXDB_V2: + case IMX_MU_TYPE_RXDB: + /* check if GSR[GIRn] bit is set */ + if (readx_poll_timeout(ioread32, plat->base + plat->dcfg->xSR[IMX_MU_GSR], + val, val & BIT(cp->idx), 1000000) < 0) + return -EBUSY; + + ctrl = imx_mu_read(plat, plat->dcfg->xCR[IMX_MU_GIER]); + val = imx_mu_read(plat, plat->dcfg->xSR[IMX_MU_GSR]); + val &= IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx) & + (ctrl & IMX_MU_xCR_GIEn(plat->dcfg->type, cp->idx)); + break; + default: + dev_warn(chan->dev, "Unhandled channel type %d\n", cp->type); + return -EOPNOTSUPP; + }; + + if (val == IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx)) + plat->dcfg->rxdb(plat, cp); + + return 0; +} + +static int imx_mu_of_to_plat(struct udevice *dev) +{ + struct imx_mu *plat = dev_get_plat(dev); + fdt_addr_t addr; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -ENODEV; + + plat->base = (struct mu_type *)addr; + + return 0; +} + +static int imx_mu_init_generic(struct imx_mu *plat) +{ + unsigned int i; + unsigned int val; + + if (plat->num_rr > 4 || plat->num_tr > 4) { + WARN_ONCE(true, "%s not support TR/RR larger than 4\n", __func__); + return -EOPNOTSUPP; + } + + /* Set default MU configuration */ + for (i = 0; i < IMX_MU_xCR_MAX; i++) + imx_mu_write(plat, 0, plat->dcfg->xCR[i]); + + /* Clear any pending GIP */ + val = imx_mu_read(plat, plat->dcfg->xSR[IMX_MU_GSR]); + imx_mu_write(plat, val, plat->dcfg->xSR[IMX_MU_GSR]); + + /* Clear any pending RSR */ + for (i = 0; i < plat->num_rr; i++) + imx_mu_read(plat, plat->dcfg->xRR + i * 4); + + return 0; +} + +static int imx_mu_generic_of_xlate(struct mbox_chan *chan, struct ofnode_phandle_args *args) +{ + enum imx_mu_chan_type type; + int idx, cid; + + if (args->args_count != 2) { + dev_err(chan->dev, "Invalid argument count %d\n", args->args_count); + return -EINVAL; + } + + type = args->args[0]; /* channel type */ + idx = args->args[1]; /* index */ + + cid = type * 4 + idx; + if (cid >= IMX_MU_CHANS) { + dev_err(chan->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", + cid, type, idx); + return -EINVAL; + } + + chan->id = cid; + + return 0; +} + +static int imx_mu_generic_tx(struct imx_mu *plat, struct imx_mu_con_priv *cp, + const void *data) +{ + switch (cp->type) { + case IMX_MU_TYPE_TXDB_V2: + imx_mu_xcr_rmw(plat, IMX_MU_GCR, IMX_MU_xCR_GIRn(plat->dcfg->type, cp->idx), 0); + break; + default: + dev_warn(cp->chan->dev, "Send data on wrong channel type: %d\n", cp->type); + return -EINVAL; + } + + return 0; +} + +static int imx_mu_generic_rxdb(struct imx_mu *plat, struct imx_mu_con_priv *cp) +{ + imx_mu_write(plat, IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx), + plat->dcfg->xSR[IMX_MU_GSR]); + + return 0; +} + +static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = { + .tx = imx_mu_generic_tx, + .rxdb = imx_mu_generic_rxdb, + .init = imx_mu_init_generic, + .of_xlate = imx_mu_generic_of_xlate, + .type = IMX_MU_V1, + .xTR = 0x0, + .xRR = 0x10, + .xSR = {0x20, 0x20, 0x20, 0x20}, + .xCR = {0x24, 0x24, 0x24, 0x24, 0x24}, +}; + +static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = { + .tx = imx_mu_generic_tx, + .rxdb = imx_mu_generic_rxdb, + .init = imx_mu_init_generic, + .of_xlate = imx_mu_generic_of_xlate, + .type = IMX_MU_V1, + .xTR = 0x20, + .xRR = 0x40, + .xSR = {0x60, 0x60, 0x60, 0x60}, + .xCR = {0x64, 0x64, 0x64, 0x64, 0x64}, +}; + +static const struct imx_mu_dcfg imx_mu_cfg_imx95 = { + .tx = imx_mu_generic_tx, + .rxdb = imx_mu_generic_rxdb, + .init = imx_mu_init_generic, + .of_xlate = imx_mu_generic_of_xlate, + .type = IMX_MU_V2, + .xTR = 0x200, + .xRR = 0x280, + .xSR = {0xC, 0x118, 0x124, 0x12C}, + .xCR = {0x8, 0x110, 0x114, 0x120, 0x128}, +}; + +static const struct udevice_id ids[] = { + { .compatible = "fsl,imx6sx-mu", .data = (ulong)&imx_mu_cfg_imx6sx }, + { .compatible = "fsl,imx7ulp-mu", .data = (ulong)&imx_mu_cfg_imx7ulp }, + { .compatible = "fsl,imx95-mu", .data = (ulong)&imx_mu_cfg_imx95 }, + { } +}; + +int imx_mu_of_xlate(struct mbox_chan *chan, struct ofnode_phandle_args *args) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + + return plat->dcfg->of_xlate(chan, args); +} + +struct mbox_ops imx_mu_ops = { + .of_xlate = imx_mu_of_xlate, + .request = imx_mu_chan_request, + .rfree = imx_mu_chan_free, + .send = imx_mu_send, + .recv = imx_mu_recv, +}; + +static void imx_mu_get_tr_rr(struct imx_mu *plat) +{ + u32 val; + + if (plat->dcfg->type & IMX_MU_V2) { + val = imx_mu_read(plat, IMX_MU_V2_PAR_OFF); + plat->num_tr = FIELD_GET(IMX_MU_V2_TR_MASK, val); + plat->num_rr = FIELD_GET(IMX_MU_V2_RR_MASK, val); + } else { + plat->num_tr = 4; + plat->num_rr = 4; + } +} + +static int imx_mu_probe(struct udevice *dev) +{ + struct imx_mu *plat = dev_get_plat(dev); + int ret; + + debug("%s(dev=%p)\n", __func__, dev); + + plat->dcfg = (void *)dev_get_driver_data(dev); + + imx_mu_get_tr_rr(plat); + + ret = plat->dcfg->init(plat); + if (ret) { + dev_err(dev, "Failed to init MU\n"); + return ret; + } + + return 0; +} + +U_BOOT_DRIVER(imx_mu) = { + .name = "imx-mu", + .id = UCLASS_MAILBOX, + .of_match = ids, + .of_to_plat = imx_mu_of_to_plat, + .plat_auto = sizeof(struct imx_mu), + .probe = imx_mu_probe, + .ops = &imx_mu_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/mailbox/zynqmp-ipi.c b/drivers/mailbox/zynqmp-ipi.c index 713d93a200c..851aa737c03 100644 --- a/drivers/mailbox/zynqmp-ipi.c +++ b/drivers/mailbox/zynqmp-ipi.c @@ -270,5 +270,4 @@ U_BOOT_DRIVER(zynqmp_ipi) = { .id = UCLASS_NOP, .of_match = zynqmp_ipi_ids, .probe = zynqmp_ipi_probe, - .flags = DM_FLAG_PROBE_AFTER_BIND, }; diff --git a/drivers/memory/ti-gpmc.c b/drivers/memory/ti-gpmc.c index e979c431e33..29e02f12ae0 100644 --- a/drivers/memory/ti-gpmc.c +++ b/drivers/memory/ti-gpmc.c @@ -1242,4 +1242,5 @@ U_BOOT_DRIVER(ti_gpmc) = { .of_match = gpmc_dt_ids, .probe = gpmc_probe, .flags = DM_FLAG_ALLOC_PRIV_DMA, + .priv_auto = sizeof(struct ti_gpmc), }; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index da84b35e804..0911d2fc0cc 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -468,6 +468,13 @@ config STM32MP_FUSE for STM32MP architecture. This API is needed for CMD_FUSE. +config K3_FUSE + bool "Enable TI K3 fuse wrapper providing the fuse API" + depends on MISC && CMD_FUSE && CMD_FUSE_WRITEBUFF + help + If you say Y here, you will get support for the fuse API (OTP) + for TI K3 architecture. + config STM32_RCC bool "Enable RCC driver for the STM32 SoC's family" depends on (ARCH_STM32 || ARCH_STM32MP) && MISC @@ -569,6 +576,7 @@ config QFW_SMBIOS bool default y depends on QFW && SMBIOS && !SANDBOX && !SYSINFO_SMBIOS + select BLOBLIST help Hidden option to read SMBIOS tables from QEMU. diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index dac805e4cdd..248068d5b43 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_SANDBOX) += p2sb_sandbox.o p2sb_emul.o obj-$(CONFIG_SANDBOX) += swap_case.o endif -ifdef CONFIG_$(XPL_)DM_I2C +ifdef CONFIG_$(PHASE_)DM_I2C ifndef CONFIG_XPL_BUILD obj-$(CONFIG_SANDBOX) += i2c_eeprom_emul.o obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o @@ -37,29 +37,30 @@ obj-$(CONFIG_FSL_DEVICE_DISABLE) += fsl_devdis.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_FSL_IIM) += fsl_iim.o obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o -obj-$(CONFIG_$(XPL_)FS_LOADER) += fs_loader.o +obj-$(CONFIG_$(PHASE_)FS_LOADER) += fs_loader.o obj-$(CONFIG_GATEWORKS_SC) += gsc.o obj-$(CONFIG_GDSYS_IOEP) += gdsys_ioep.o obj-$(CONFIG_GDSYS_RXAUI_CTRL) += gdsys_rxaui_ctrl.o obj-$(CONFIG_GDSYS_SOC) += gdsys_soc.o obj-$(CONFIG_IRQ) += irq-uclass.o obj-$(CONFIG_SANDBOX) += irq_sandbox.o irq_sandbox_test.o -obj-$(CONFIG_$(XPL_)I2C_EEPROM) += i2c_eeprom.o +obj-$(CONFIG_$(PHASE_)I2C_EEPROM) += i2c_eeprom.o obj-$(CONFIG_IHS_FPGA) += ihs_fpga.o obj-$(CONFIG_IMX8) += imx8/ obj-$(CONFIG_IMX_ELE) += imx_ele/ +obj-$(CONFIG_K3_FUSE) += k3_fuse.o obj-$(CONFIG_LED_STATUS) += status_led.o obj-$(CONFIG_LED_STATUS_GPIO) += gpio_led.o obj-$(CONFIG_MPC83XX_SERDES) += mpc83xx_serdes.o obj-$(CONFIG_$(PHASE_)LS2_SFP) += ls2_sfp.o -obj-$(CONFIG_$(XPL_)MXC_OCOTP) += mxc_ocotp.o +obj-$(CONFIG_$(PHASE_)MXC_OCOTP) += mxc_ocotp.o obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o obj-$(CONFIG_NPCM_OTP) += npcm_otp.o obj-$(CONFIG_NPCM_HOST) += npcm_host_intf.o obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o obj-$(CONFIG_P2SB) += p2sb-uclass.o obj-$(CONFIG_PCA9551_LED) += pca9551_led.o -obj-$(CONFIG_$(XPL_)PWRSEQ) += pwrseq-uclass.o +obj-$(CONFIG_$(PHASE_)PWRSEQ) += pwrseq-uclass.o ifdef CONFIG_QFW obj-y += qfw.o obj-$(CONFIG_QFW_ACPI) += qfw_acpi.o diff --git a/drivers/misc/k3_fuse.c b/drivers/misc/k3_fuse.c new file mode 100644 index 00000000000..4a8ff1f2523 --- /dev/null +++ b/drivers/misc/k3_fuse.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Texas Instruments Incorporated, <www.ti.com> + */ + +#include <errno.h> +#include <stdio.h> +#include <fuse.h> +#include <linux/arm-smccc.h> +#include <string.h> + +#define K3_SIP_OTP_WRITEBUFF 0xC2000000 +#define K3_SIP_OTP_WRITE 0xC2000001 +#define K3_SIP_OTP_READ 0xC2000002 + +int fuse_read(u32 bank, u32 word, u32 *val) +{ + struct arm_smccc_res res; + + if (bank != 0U) { + printf("Invalid bank argument, ONLY bank 0 is supported\n"); + return -EINVAL; + } + + /* Make SiP SMC call and send the word in the parameter register */ + arm_smccc_smc(K3_SIP_OTP_READ, word, + 0, 0, 0, 0, 0, 0, &res); + + *val = res.a1; + if (res.a0 != 0) + printf("SMC call failed: Error code %lu\n", res.a0); + + return res.a0; +} + +int fuse_sense(u32 bank, u32 word, u32 *val) +{ + return -EPERM; +} + +int fuse_prog(u32 bank, u32 word, u32 val) +{ + struct arm_smccc_res res; + u32 mask = val; + + if (bank != 0U) { + printf("Invalid bank argument, ONLY bank 0 is supported\n"); + return -EINVAL; + } + + /* Make SiP SMC call and send the word, val and mask in the parameter register */ + arm_smccc_smc(K3_SIP_OTP_WRITE, word, + val, mask, 0, 0, 0, 0, &res); + + if (res.a0 != 0) + printf("SMC call failed: Error code %lu\n", res.a0); + + return res.a0; +} + +int fuse_override(u32 bank, u32 word, u32 val) +{ + return -EPERM; +} + +int fuse_writebuff(ulong addr) +{ + struct arm_smccc_res res; + + /* Make SiP SMC call and send the addr in the parameter register */ + arm_smccc_smc(K3_SIP_OTP_WRITEBUFF, (unsigned long)addr, + 0, 0, 0, 0, 0, 0, &res); + + if (res.a0 != 0) + printf("SMC call failed: Error code %lu\n", res.a0); + + return res.a0; +} diff --git a/drivers/misc/qfw_acpi.c b/drivers/misc/qfw_acpi.c index 0d0cf764689..c6c052ac6c3 100644 --- a/drivers/misc/qfw_acpi.c +++ b/drivers/misc/qfw_acpi.c @@ -25,17 +25,18 @@ DECLARE_GLOBAL_DATA_PTR; * * @entry : BIOS linker command entry which tells where to allocate memory * (either high memory or low memory) - * @addr : The address that should be used for low memory allcation. If the + * @addr : The address that should be used for low memory allocation. If the * memory allocation request is 'ZONE_HIGH' then this parameter will * be ignored. * @return: 0 on success, or negative value on failure */ -static int bios_linker_allocate(struct udevice *dev, +static int bios_linker_allocate(struct acpi_ctx *ctx, struct udevice *dev, struct bios_linker_entry *entry, ulong *addr) { uint32_t size, align; struct fw_file *file; unsigned long aligned_addr; + struct acpi_rsdp *rsdp; align = le32_to_cpu(entry->alloc.align); /* align must be power of 2 */ @@ -58,7 +59,9 @@ static int bios_linker_allocate(struct udevice *dev, * If allocation zone is ZONE_FSEG, then we use the 'addr' passed * in which is low memory */ - if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) { + if (IS_ENABLED(CONFIG_BLOBLIST_TABLES)) { + aligned_addr = ALIGN(*addr, align); + } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) { aligned_addr = (unsigned long)memalign(align, size); if (!aligned_addr) { printf("error: allocating resource\n"); @@ -83,8 +86,13 @@ static int bios_linker_allocate(struct udevice *dev, (void *)aligned_addr); file->addr = aligned_addr; + rsdp = (void *)aligned_addr; + if (!strncmp(rsdp->signature, RSDP_SIG, sizeof(rsdp->signature))) + ctx->rsdp = rsdp; + /* adjust address for low memory allocation */ - if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) + if (IS_ENABLED(CONFIG_BLOBLIST_TABLES) || + entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) *addr = (aligned_addr + size); return 0; @@ -209,19 +217,23 @@ ulong write_acpi_tables(ulong addr) qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader); for (i = 0; i < (size / sizeof(*entry)); i++) { + log_content("entry %d: addr %lx\n", i, addr); entry = table_loader + i; switch (le32_to_cpu(entry->command)) { case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: - ret = bios_linker_allocate(dev, entry, &addr); + log_content(" - %s\n", entry->alloc.file); + ret = bios_linker_allocate(ctx, dev, entry, &addr); if (ret) goto out; break; case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: + log_content(" - %s\n", entry->pointer.src_file); ret = bios_linker_add_pointer(dev, entry); if (ret) goto out; break; case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: + log_content(" - %s\n", entry->cksum.file); ret = bios_linker_add_checksum(dev, entry); if (ret) goto out; @@ -246,6 +258,16 @@ out: free(table_loader); + if (!ctx->rsdp) { + printf("error: no RSDP found\n"); + return addr; + } + struct acpi_rsdp *rsdp = ctx->rsdp; + + rsdp->length = sizeof(*rsdp); + rsdp->xsdt_address = 0; + rsdp->ext_checksum = table_compute_checksum((u8 *)rsdp, sizeof(*rsdp)); + gd_set_acpi_start(acpi_get_rsdp_addr()); return addr; @@ -305,7 +327,7 @@ static int evt_write_acpi_tables(void) /* Generate ACPI tables */ end = write_acpi_tables(addr); gd->arch.table_start = addr; - gd->arch.table_end = addr; + gd->arch.table_end = end; return 0; } diff --git a/drivers/misc/rockchip-otp.c b/drivers/misc/rockchip-otp.c index 2123c31038f..46820425a84 100644 --- a/drivers/misc/rockchip-otp.c +++ b/drivers/misc/rockchip-otp.c @@ -361,6 +361,13 @@ static const struct rockchip_otp_data rk3568_data = { .block_size = 2, }; +static const struct rockchip_otp_data rk3576_data = { + .read = rockchip_rk3588_otp_read, + .offset = 0x700, + .size = 0x100, + .block_size = 4, +}; + static const struct rockchip_otp_data rk3588_data = { .read = rockchip_rk3588_otp_read, .offset = 0xC00, @@ -384,10 +391,18 @@ static const struct udevice_id rockchip_otp_ids[] = { .data = (ulong)&px30_data, }, { + .compatible = "rockchip,rk3528-otp", + .data = (ulong)&rk3568_data, + }, + { .compatible = "rockchip,rk3568-otp", .data = (ulong)&rk3568_data, }, { + .compatible = "rockchip,rk3576-otp", + .data = (ulong)&rk3576_data, + }, + { .compatible = "rockchip,rk3588-otp", .data = (ulong)&rk3588_data, }, diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 4827834b4aa..38867f30a7e 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -292,6 +292,15 @@ config MMC_DW_ROCKCHIP SD 3.0, SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well as removeable SD and micro-SD cards. +config MMC_SDHCI_ADI + bool "ADI SD/MMC controller support" + depends on ARCH_SC5XX + depends on DM_MMC && OF_CONTROL + depends on MMC_SDHCI && MMC_SDHCI_ADMA + help + This enables support for the SD/MMC controller included in some Analog + Devices SC5XX Socs. + config MMC_DW_SOCFPGA bool "SOCFPGA specific extensions for Synopsys DW Memory Card Interface" depends on ARCH_SOCFPGA @@ -519,6 +528,7 @@ config SPL_MMC_SDHCI_ADMA config MMC_SDHCI_ADMA_FORCE_32BIT bool "Force 32 bit mode for ADMA on 64 bit platforms" + depends on MMC_SDHCI_ADMA || SPL_MMC_SDHCI_ADMA help This forces SDHCI ADMA to be built for 32 bit descriptors, even on a 64 bit platform where they would otherwise be assumed to @@ -528,6 +538,7 @@ config MMC_SDHCI_ADMA_FORCE_32BIT config MMC_SDHCI_ADMA_64BIT bool "Use SHDCI ADMA with 64 bit descriptors" + depends on MMC_SDHCI_ADMA || SPL_MMC_SDHCI_ADMA depends on !MMC_SDHCI_ADMA_FORCE_32BIT default y if DMA_ADDR_T_64BIT help @@ -860,7 +871,7 @@ config FTSDC010_SDIO config MMC_MTK bool "MediaTek SD/MMC Card Interface support" - depends on ARCH_MEDIATEK || ARCH_MTMIPS + depends on ARCH_MEDIATEK || ARCH_MTMIPS || ARCH_AIROHA depends on OF_CONTROL help This selects the MediaTek(R) Secure digital and Multimedia card Interface. @@ -919,6 +930,12 @@ config ESDHC_DETECT_QUIRK bool "QIXIS-based eSDHC quirk detection" depends on FSL_ESDHC && FSL_QIXIS +config ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE + bool + depends on FSL_ESDHC || FSL_ESDHC_IMX + def_bool y if ARCH_T1024 || ARCH_T1040 || ARCH_T1042 || ARCH_T2080 \ + || FSL_ESDHC_IMX + config FSL_ESDHC_IMX bool "Freescale/NXP i.MX eSDHC controller support" help diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 90e76f90769..360706f53d2 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -4,17 +4,17 @@ # Wolfgang Denk, DENX Software Engineering, wd@denx.de. obj-y += mmc.o -obj-$(CONFIG_$(XPL_)DM_MMC) += mmc-uclass.o +obj-$(CONFIG_$(PHASE_)DM_MMC) += mmc-uclass.o ifdef CONFIG_$(PHASE_)DM_MMC obj-$(CONFIG_$(PHASE_)BOOTSTD) += mmc_bootdev.o endif obj-$(CONFIG_$(PHASE_)MMC_WRITE) += mmc_write.o -obj-$(CONFIG_$(XPL_)MMC_PWRSEQ) += mmc-pwrseq.o +obj-$(CONFIG_$(PHASE_)MMC_PWRSEQ) += mmc-pwrseq.o obj-$(CONFIG_MMC_SDHCI_ADMA_HELPERS) += sdhci-adma.o -ifndef CONFIG_$(XPL_)BLK +ifndef CONFIG_$(PHASE_)BLK obj-y += mmc_legacy.o endif @@ -70,6 +70,7 @@ obj-$(CONFIG_MMC_SDHCI_MV) += mv_sdhci.o obj-$(CONFIG_MMC_SDHCI_NPCM) += npcm_sdhci.o obj-$(CONFIG_MMC_SDHCI_PIC32) += pic32_sdhci.o obj-$(CONFIG_MMC_SDHCI_ROCKCHIP) += rockchip_sdhci.o +obj-$(CONFIG_MMC_SDHCI_ADI) += adi_sdhci.o obj-$(CONFIG_MMC_SDHCI_S5P) += s5p_sdhci.o obj-$(CONFIG_MMC_SDHCI_SNPS) += snps_sdhci.o obj-$(CONFIG_MMC_SDHCI_STI) += sti_sdhci.o diff --git a/drivers/mmc/adi_sdhci.c b/drivers/mmc/adi_sdhci.c new file mode 100644 index 00000000000..65a22cefb71 --- /dev/null +++ b/drivers/mmc/adi_sdhci.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * + * Based on Rockchip's sdhci.c file + */ + +#include <clk.h> +#include <dm.h> +#include <malloc.h> +#include <sdhci.h> +#include <asm/cache.h> + +/* 400KHz is max freq for card ID etc. Use that as min */ +#define EMMC_MIN_FREQ 400000 + +/* Check if an operation crossed a boundary of size ADMA_BOUNDARY_ALIGN */ +#define ADMA_BOUNDARY_ALGN SZ_128M +#define BOUNDARY_OK(addr, len) \ + (((addr) | (ADMA_BOUNDARY_ALGN - 1)) == (((addr) + (len) - 1) | \ + (ADMA_BOUNDARY_ALGN - 1))) + +/* We split a descriptor for every crossing of the ADMA alignment boundary, + * so we need an additional descriptor for every expected crossing. + * As I understand it, the max expected transaction size is: + * CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN + * + * With the way the SDHCI-ADMA driver is implemented, if ADMA_MAX_LEN was a + * clean power of two, we'd only ever need +1 descriptor as the first + * descriptor that got split would then bring the remaining DMA + * destination addresses into alignment. Unfortunately, it's currently + * hardcoded to a non-power-of-two value. + * + * If that ever becomes parameterized, ADMA max length can be set to + * 0x10000, and set this to 1. + */ +#define ADMA_POTENTIAL_CROSSINGS \ + DIV_ROUND_UP((CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN), \ + ADMA_BOUNDARY_ALGN) +/* +1 descriptor for each crossing. + */ +#define ADMA_TABLE_EXTRA_SZ (ADMA_POTENTIAL_CROSSINGS * ADMA_DESC_LEN) + +struct adi_sdhc_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +void adi_dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc, + dma_addr_t addr, int len, bool end) +{ + int tmplen, offset; + + if (likely(!len || BOUNDARY_OK(addr, len))) { + sdhci_adma_write_desc(host, desc, addr, len, end); + return; + } + + offset = addr & (ADMA_BOUNDARY_ALGN - 1); + tmplen = ADMA_BOUNDARY_ALGN - offset; + sdhci_adma_write_desc(host, desc, addr, tmplen, false); + + addr += tmplen; + len -= tmplen; + sdhci_adma_write_desc(host, desc, addr, len, end); +} + +struct sdhci_ops adi_dwcmshc_sdhci_ops = { + .adma_write_desc = adi_dwcmshc_adma_write_desc, +}; + +static int adi_dwcmshc_sdhci_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct adi_sdhc_plat *plat = dev_get_plat(dev); + struct sdhci_host *host = dev_get_priv(dev); + int max_frequency, ret; + struct clk clk; + + max_frequency = dev_read_u32_default(dev, "max-frequency", 0); + ret = clk_get_by_index(dev, 0, &clk); + + host->quirks = 0; + host->max_clk = max_frequency; + /* + * The sdhci-driver only supports 4bit and 8bit, as sdhci_setup_cfg + * doesn't allow us to clear MMC_MODE_4BIT. Consequently, we don't + * check for other bus-width values. + */ + if (host->bus_width == 8) + host->host_caps |= MMC_MODE_8BIT; + + host->mmc = &plat->mmc; + host->mmc->priv = host; + host->mmc->dev = dev; + upriv->mmc = host->mmc; + + host->ops = &adi_dwcmshc_sdhci_ops; + host->adma_desc_table = memalign(ARCH_DMA_MINALIGN, + ADMA_TABLE_SZ + ADMA_TABLE_EXTRA_SZ); + host->adma_addr = virt_to_phys(host->adma_desc_table); + + ret = sdhci_setup_cfg(&plat->cfg, host, 0, EMMC_MIN_FREQ); + if (ret) + return ret; + + return sdhci_probe(dev); +} + +static int adi_dwcmshc_sdhci_of_to_plat(struct udevice *dev) +{ + struct sdhci_host *host = dev_get_priv(dev); + + host->name = dev->name; + host->ioaddr = dev_read_addr_ptr(dev); + host->bus_width = dev_read_u32_default(dev, "bus-width", 4); + + return 0; +} + +static int adi_sdhci_bind(struct udevice *dev) +{ + struct adi_sdhc_plat *plat = dev_get_plat(dev); + + return sdhci_bind(dev, &plat->mmc, &plat->cfg); +} + +static const struct udevice_id adi_dwcmshc_sdhci_ids[] = { + { .compatible = "adi,dwc-sdhci" }, + { } +}; + +U_BOOT_DRIVER(adi_dwcmshc_sdhci_drv) = { + .name = "adi_sdhci", + .id = UCLASS_MMC, + .of_match = adi_dwcmshc_sdhci_ids, + .of_to_plat = adi_dwcmshc_sdhci_of_to_plat, + .ops = &sdhci_ops, + .bind = adi_sdhci_bind, + .probe = adi_dwcmshc_sdhci_probe, + .priv_auto = sizeof(struct sdhci_host), + .plat_auto = sizeof(struct adi_sdhc_plat), +}; diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c index b4c60a48d2e..0df3568f073 100644 --- a/drivers/mmc/am654_sdhci.c +++ b/drivers/mmc/am654_sdhci.c @@ -125,12 +125,15 @@ static const struct timing_data td[] = { [MMC_LEGACY] = {"ti,otap-del-sel-legacy", "ti,itap-del-sel-legacy", 0}, - [MMC_HS] = {"ti,otap-del-sel-mmc-hs", - "ti,itap-del-sel-mms-hs", + [MMC_HS] = {"ti,otap-del-sel-mmc-hs26", + "ti,itap-del-sel-mmc-hs26", MMC_CAP(MMC_HS)}, [SD_HS] = {"ti,otap-del-sel-sd-hs", "ti,itap-del-sel-sd-hs", MMC_CAP(SD_HS)}, + [MMC_HS_52] = {"ti,otap-del-sel-mmc-hs", + "ti,itap-del-sel-mmc-hs", + MMC_CAP(MMC_HS_52)}, [UHS_SDR12] = {"ti,otap-del-sel-sdr12", "ti,itap-del-sel-sdr12", MMC_CAP(UHS_SDR12)}, @@ -409,8 +412,7 @@ static void am654_sdhci_write_b(struct sdhci_host *host, u8 val, int reg) */ case SD_HS: case MMC_HS: - case UHS_SDR12: - case UHS_SDR25: + case MMC_HS_52: val &= ~SDHCI_CTRL_HISPD; default: break; @@ -521,13 +523,24 @@ static int am654_sdhci_execute_tuning(struct mmc *mmc, u8 opcode) return 0; } #endif + +void am654_sdhci_set_control_reg(struct sdhci_host *host) +{ + struct mmc *mmc = host->mmc; + + sdhci_set_voltage(host); + + if (mmc->selected_mode > MMC_HS_52) + sdhci_set_uhs_timing(host); +} + const struct sdhci_ops am654_sdhci_ops = { #if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING) .platform_execute_tuning = am654_sdhci_execute_tuning, #endif .deferred_probe = am654_sdhci_deferred_probe, .set_ios_post = &am654_sdhci_set_ios_post, - .set_control_reg = sdhci_set_control_reg, + .set_control_reg = am654_sdhci_set_control_reg, .write_b = am654_sdhci_write_b, }; @@ -587,7 +600,7 @@ const struct sdhci_ops j721e_4bit_sdhci_ops = { #endif .deferred_probe = am654_sdhci_deferred_probe, .set_ios_post = &j721e_4bit_sdhci_set_ios_post, - .set_control_reg = sdhci_set_control_reg, + .set_control_reg = am654_sdhci_set_control_reg, .write_b = am654_sdhci_write_b, }; diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index d7a45ef0ad0..926113f79d3 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -40,12 +40,6 @@ #include <linux/iopoll.h> #include <linux/dma-mapping.h> -#ifndef ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE -#ifdef CONFIG_FSL_USDHC -#define ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE 1 -#endif -#endif - DECLARE_GLOBAL_DATA_PTR; #define SDHCI_IRQ_EN_BITS (IRQSTATEN_CC | IRQSTATEN_TC | \ @@ -376,7 +370,7 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, (timeout == 4 || timeout == 8 || timeout == 12)) timeout++; - if (IS_ENABLED(ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE)) + if (IS_ENABLED(CONFIG_ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE)) timeout = 0xE; esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16); diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index c8db4f811c2..2f4dc5bd887 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -83,6 +83,19 @@ int mmc_wait_dat0(struct mmc *mmc, int state, int timeout_us) return dm_mmc_wait_dat0(mmc->dev, state, timeout_us); } +void dm_mmc_send_init_stream(struct udevice *dev) +{ + struct dm_mmc_ops *ops = mmc_get_ops(dev); + + if (ops->send_init_stream) + ops->send_init_stream(dev); +} + +void mmc_send_init_stream(struct mmc *mmc) +{ + dm_mmc_send_init_stream(mmc->dev); +} + static int dm_mmc_get_wp(struct udevice *dev) { struct dm_mmc_ops *ops = mmc_get_ops(dev); @@ -498,22 +511,12 @@ static int mmc_blk_probe(struct udevice *dev) return ret; } - ret = device_probe(dev); - if (ret) { - debug("Probing %s failed (err=%d)\n", dev->name, ret); - - mmc_deinit(mmc); - - return ret; - } - return 0; } -static int mmc_blk_remove(struct udevice *dev) +static int mmc_remove(struct udevice *dev) { - struct udevice *mmc_dev = dev_get_parent(dev); - struct mmc_uclass_priv *upriv = dev_get_uclass_priv(mmc_dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct mmc *mmc = upriv->mmc; return mmc_deinit(mmc); @@ -533,7 +536,6 @@ U_BOOT_DRIVER(mmc_blk) = { .id = UCLASS_BLK, .ops = &mmc_blk_ops, .probe = mmc_blk_probe, - .remove = mmc_blk_remove, .flags = DM_FLAG_OS_PREPARE, }; #endif /* CONFIG_BLK */ @@ -543,4 +545,5 @@ UCLASS_DRIVER(mmc) = { .name = "mmc", .flags = DM_UC_FLAG_SEQ_ALIAS, .per_device_auto = sizeof(struct mmc_uclass_priv), + .pre_remove = mmc_remove, }; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 31a72366206..cdcf2e0c8fe 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1663,6 +1663,10 @@ static int mmc_execute_tuning(struct mmc *mmc, uint opcode) } #endif +static void mmc_send_init_stream(struct mmc *mmc) +{ +} + static int mmc_set_ios(struct mmc *mmc) { int ret = 0; @@ -2550,7 +2554,7 @@ static int mmc_startup(struct mmc *mmc) /* * For MMC cards, set the Relative Address. - * For SD cards, get the Relatvie Address. + * For SD cards, get the Relative Address. * This also puts the cards into Standby State */ if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ @@ -2929,6 +2933,8 @@ int mmc_get_op_cond(struct mmc *mmc, bool quiet) retry: mmc_set_initial_state(mmc); + mmc_send_init_stream(mmc); + /* Reset the Card */ err = mmc_go_idle(mmc); @@ -3040,9 +3046,9 @@ static int mmc_complete_init(struct mmc *mmc) return err; } -static void __maybe_unused mmc_cyclic_cd_poll(struct cyclic_info *c) +static void mmc_cyclic_cd_poll(struct cyclic_info *c) { - struct mmc *m = CONFIG_IS_ENABLED(CYCLIC, (container_of(c, struct mmc, cyclic)), (NULL)); + struct mmc *m = container_of(c, struct mmc, cyclic); if (!m->has_init) return; @@ -3073,15 +3079,15 @@ int mmc_init(struct mmc *mmc) if (!err) err = mmc_complete_init(mmc); - if (err) + if (err) { pr_info("%s: %d, time %lu\n", __func__, err, get_timer(start)); + return err; + } if (CONFIG_IS_ENABLED(CYCLIC, (!mmc->cyclic.func), (NULL))) { /* Register cyclic function for card detect polling */ - CONFIG_IS_ENABLED(CYCLIC, (cyclic_register(&mmc->cyclic, - mmc_cyclic_cd_poll, - 100 * 1000, - mmc->cfg->name))); + cyclic_register(&mmc->cyclic, mmc_cyclic_cd_poll, 100 * 1000, + mmc->cfg->name); } return err; @@ -3092,7 +3098,7 @@ int mmc_deinit(struct mmc *mmc) u32 caps_filtered; if (CONFIG_IS_ENABLED(CYCLIC, (mmc->cyclic.func), (NULL))) - CONFIG_IS_ENABLED(CYCLIC, (cyclic_unregister(&mmc->cyclic))); + cyclic_unregister(&mmc->cyclic); if (!CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) && !CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) && diff --git a/drivers/mmc/mmc_boot.c b/drivers/mmc/mmc_boot.c index 367c957b518..986e6c500b1 100644 --- a/drivers/mmc/mmc_boot.c +++ b/drivers/mmc/mmc_boot.c @@ -8,20 +8,107 @@ #include <mmc.h> #include "mmc_private.h" -/* - * This function changes the size of boot partition and the size of rpmb - * partition present on EMMC devices. - * - * Input Parameters: - * struct *mmc: pointer for the mmc device strcuture - * bootsize: size of boot partition - * rpmbsize: size of rpmb partition - * - * Returns 0 on success. - */ +static int mmc_resize_boot_micron(struct mmc *mmc, unsigned long bootsize, + unsigned long rpmbsize) +{ + int err; -int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, - unsigned long rpmbsize) + /* Micron eMMC doesn't support resizing RPMB partition */ + (void)rpmbsize; + + /* BOOT partition size is multiple of 128KB */ + bootsize = (bootsize * 1024) / 128; + + if (bootsize > 0xff) + bootsize = 0xff; + + /* Set EXT_CSD[175] ERASE_GROUP_DEF to 0x01 */ + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_ERASE_GROUP_DEF, 0x01); + if (err) + goto error; + + /* Set EXT_CSD[127:125] for BOOT partition size, [125] is low byte */ + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BOOT_SIZE_MULT_MICRON, bootsize); + if (err) + goto error; + + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BOOT_SIZE_MULT_MICRON + 1, 0x00); + if (err) + goto error; + + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BOOT_SIZE_MULT_MICRON + 2, 0x00); + if (err) + goto error; + + /* Set EXT_CSD[155] PARTITION_SETTING_COMPLETE to 0x01 */ + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_PARTITION_SETTING, 0x01); + if (err) + goto error; + + return 0; + +error: + debug("%s: Error = %d\n", __func__, err); + return err; +} + +static int mmc_resize_boot_sandisk(struct mmc *mmc, unsigned long bootsize, + unsigned long rpmbsize) +{ + int err; + struct mmc_cmd cmd; + + /* BOOT/RPMB partition size is multiple of 128KB */ + bootsize = (bootsize * 1024) / 128; + rpmbsize = (rpmbsize * 1024) / 128; + + if (bootsize > 0xff) + bootsize = 0xff; + + if (rpmbsize > 0xff) + rpmbsize = 0xff; + + /* Send BOOT/RPMB resize op code */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = MMC_CMD62_ARG_SANDISK; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto error; + + /* Arg: BOOT partition size */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = bootsize; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto error; + + /* Arg: RPMB partition size */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = rpmbsize; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto error; + + return 0; + +error: + debug("%s: Error = %d\n", __func__, err); + return err; +} + +static int mmc_resize_boot_samsung(struct mmc *mmc, unsigned long bootsize, + unsigned long rpmbsize) { int err; struct mmc_cmd cmd; @@ -32,10 +119,8 @@ int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, cmd.cmdarg = MMC_CMD62_ARG1; err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) { - debug("mmc_boot_partition_size_change: Error1 = %d\n", err); - return err; - } + if (err) + goto error; /* Boot partition changing mode */ cmd.cmdidx = MMC_CMD_RES_MAN; @@ -43,10 +128,9 @@ int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, cmd.cmdarg = MMC_CMD62_ARG2; err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) { - debug("mmc_boot_partition_size_change: Error2 = %d\n", err); - return err; - } + if (err) + goto error; + /* boot partition size is multiple of 128KB */ bootsize = (bootsize * 1024) / 128; @@ -56,10 +140,9 @@ int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, cmd.cmdarg = bootsize; err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) { - debug("mmc_boot_partition_size_change: Error3 = %d\n", err); - return err; - } + if (err) + goto error; + /* RPMB partition size is multiple of 128KB */ rpmbsize = (rpmbsize * 1024) / 128; /* Arg: RPMB partition size */ @@ -68,11 +151,43 @@ int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, cmd.cmdarg = rpmbsize; err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) { - debug("mmc_boot_partition_size_change: Error4 = %d\n", err); - return err; - } + if (err) + goto error; + return 0; + +error: + debug("%s: Error = %d\n", __func__, err); + return err; +} + +/* + * This function changes the size of BOOT partition and the size of RPMB + * partition present on eMMC devices. + * + * Input Parameters: + * struct *mmc: pointer for the mmc device strcuture + * bootsize: size of BOOT partition + * rpmbsize: size of RPMB partition + * + * Returns 0 on success. + */ + +int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, + unsigned long rpmbsize) +{ + switch (mmc->cid[0] >> 24) { + case CID_MANFID_MICRON: + return mmc_resize_boot_micron(mmc, bootsize, rpmbsize); + case CID_MANFID_SAMSUNG: + return mmc_resize_boot_samsung(mmc, bootsize, rpmbsize); + case CID_MANFID_SANDISK: + return mmc_resize_boot_sandisk(mmc, bootsize, rpmbsize); + default: + printf("Unsupported manufacturer id 0x%02x\n", + mmc->cid[0] >> 24); + return -EPERM; + } } /* diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c index c023d15e52a..90fcf2679bb 100644 --- a/drivers/mmc/mmc_write.c +++ b/drivers/mmc/mmc_write.c @@ -80,6 +80,8 @@ ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt) struct mmc *mmc = find_mmc_device(dev_num); lbaint_t blk = 0, blk_r = 0; int timeout_ms = 1000; + u32 grpcnt; + if (!mmc) return -1; @@ -123,6 +125,15 @@ ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt) } else { blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? mmc->erase_grp_size : (blkcnt - blk); + + grpcnt = (blkcnt - blk) / mmc->erase_grp_size; + /* Max 2GB per spec */ + if ((blkcnt - blk) > 0x400000) + blk_r = 0x400000; + else if (grpcnt) + blk_r = grpcnt * mmc->erase_grp_size; + else + blk_r = blkcnt - blk; } err = mmc_erase_t(mmc, start + blk, blk_r, erase_args); if (err) diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c index 27bb7052fca..ac77fb06bf7 100644 --- a/drivers/mmc/msm_sdhci.c +++ b/drivers/mmc/msm_sdhci.c @@ -10,6 +10,7 @@ #include <clk.h> #include <dm.h> #include <malloc.h> +#include <reset.h> #include <sdhci.h> #include <wait_bit.h> #include <asm/global_data.h> @@ -153,9 +154,18 @@ static int msm_sdc_probe(struct udevice *dev) const struct msm_sdhc_variant_info *var_info; struct sdhci_host *host = &prv->host; u32 core_version, core_minor, core_major; + struct reset_ctl bcr_rst; u32 caps; int ret; + ret = reset_get_by_index(dev, 0, &bcr_rst); + if (!ret) { + reset_assert(&bcr_rst); + udelay(200); + reset_deassert(&bcr_rst); + udelay(200); + } + host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_BROKEN_R1B; host->max_clk = 0; diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index e66ab25d02a..92bc72b267c 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -780,6 +780,14 @@ tuning_error: return ret; } #endif + +static void omap_hsmmc_send_init_stream(struct udevice *dev) +{ + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct hsmmc *mmc_base = priv->base_addr; + + mmc_init_stream(mmc_base); +} #endif static void mmc_enable_irq(struct mmc *mmc, struct mmc_cmd *cmd) @@ -1515,9 +1523,10 @@ static const struct dm_mmc_ops omap_hsmmc_ops = { .get_wp = omap_hsmmc_getwp, #endif #if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING) - .execute_tuning = omap_hsmmc_execute_tuning, + .execute_tuning = omap_hsmmc_execute_tuning, #endif - .wait_dat0 = omap_hsmmc_wait_dat0, + .send_init_stream = omap_hsmmc_send_init_stream, + .wait_dat0 = omap_hsmmc_wait_dat0, }; #else static const struct mmc_ops omap_hsmmc_ops = { diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c index 422b8f7e4c8..7a72abaa38a 100644 --- a/drivers/mmc/rockchip_dw_mmc.c +++ b/drivers/mmc/rockchip_dw_mmc.c @@ -171,6 +171,7 @@ static int rockchip_dwmmc_bind(struct udevice *dev) static const struct udevice_id rockchip_dwmmc_ids[] = { { .compatible = "rockchip,rk2928-dw-mshc" }, { .compatible = "rockchip,rk3288-dw-mshc" }, + { .compatible = "rockchip,rk3576-dw-mshc" }, { } }; diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index da630b9d97a..761e3619329 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -50,6 +50,10 @@ #define DWCMSHC_EMMC_EMMC_CTRL 0x52c #define DWCMSHC_CARD_IS_EMMC BIT(0) #define DWCMSHC_ENHANCED_STROBE BIT(8) +#define DWCMSHC_EMMC_AT_CTRL 0x540 +#define EMMC_AT_CTRL_TUNE_CLK_STOP_EN BIT(16) +#define EMMC_AT_CTRL_PRE_CHANGE_DLY 17 +#define EMMC_AT_CTRL_POST_CHANGE_DLY 19 #define DWCMSHC_EMMC_DLL_CTRL 0x800 #define DWCMSHC_EMMC_DLL_CTRL_RESET BIT(1) #define DWCMSHC_EMMC_DLL_RXCLK 0x804 @@ -156,6 +160,9 @@ struct sdhci_data { u32 flags; u8 hs200_txclk_tapnum; u8 hs400_txclk_tapnum; + u8 hs400_cmdout_tapnum; + u8 hs400_strbin_tapnum; + u8 ddr50_strbin_delay_num; }; static void rk3399_emmc_phy_power_on(struct rockchip_emmc_phy *phy, u32 clock) @@ -323,6 +330,11 @@ static int rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enab udelay(1); sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL); + extra = 0x3 << EMMC_AT_CTRL_POST_CHANGE_DLY | + 0x3 << EMMC_AT_CTRL_PRE_CHANGE_DLY | + EMMC_AT_CTRL_TUNE_CLK_STOP_EN; + sdhci_writel(host, extra, DWCMSHC_EMMC_AT_CTRL); + /* Init DLL settings */ extra = DWCMSHC_EMMC_DLL_START_DEFAULT << DWCMSHC_EMMC_DLL_START_POINT | DWCMSHC_EMMC_DLL_INC_VALUE << DWCMSHC_EMMC_DLL_INC | @@ -348,7 +360,7 @@ static int rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enab extra = DLL_CMDOUT_SRC_CLK_NEG | DLL_CMDOUT_BOTH_CLK_EDGE | DWCMSHC_EMMC_DLL_DLYENA | - DLL_CMDOUT_TAPNUM_90_DEGREES | + data->hs400_cmdout_tapnum | DLL_CMDOUT_TAPNUM_FROM_SW; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_CMDOUT); } @@ -360,7 +372,7 @@ static int rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enab sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK); extra = DWCMSHC_EMMC_DLL_DLYENA | - DLL_STRBIN_TAPNUM_DEFAULT | + data->hs400_strbin_tapnum | DLL_STRBIN_TAPNUM_FROM_SW; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); } else { @@ -380,7 +392,7 @@ static int rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enab */ extra = DWCMSHC_EMMC_DLL_DLYENA | DLL_STRBIN_DELAY_NUM_SEL | - DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET; + data->ddr50_strbin_delay_num << DLL_STRBIN_DELAY_NUM_OFFSET; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); } @@ -647,6 +659,17 @@ static const struct sdhci_data rk3399_data = { .set_enhanced_strobe = rk3399_sdhci_set_enhanced_strobe, }; +static const struct sdhci_data rk3528_data = { + .set_ios_post = rk3568_sdhci_set_ios_post, + .set_clock = rk3568_sdhci_set_clock, + .config_dll = rk3568_sdhci_config_dll, + .hs200_txclk_tapnum = 0xc, + .hs400_txclk_tapnum = 0x6, + .hs400_cmdout_tapnum = 0x6, + .hs400_strbin_tapnum = 0x3, + .ddr50_strbin_delay_num = 0xa, +}; + static const struct sdhci_data rk3568_data = { .set_ios_post = rk3568_sdhci_set_ios_post, .set_clock = rk3568_sdhci_set_clock, @@ -654,6 +677,20 @@ static const struct sdhci_data rk3568_data = { .flags = FLAG_INVERTER_FLAG_IN_RXCLK, .hs200_txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT, .hs400_txclk_tapnum = 0x8, + .hs400_cmdout_tapnum = DLL_CMDOUT_TAPNUM_90_DEGREES, + .hs400_strbin_tapnum = DLL_STRBIN_TAPNUM_DEFAULT, + .ddr50_strbin_delay_num = DLL_STRBIN_DELAY_NUM_DEFAULT, +}; + +static const struct sdhci_data rk3576_data = { + .set_ios_post = rk3568_sdhci_set_ios_post, + .set_clock = rk3568_sdhci_set_clock, + .config_dll = rk3568_sdhci_config_dll, + .hs200_txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT, + .hs400_txclk_tapnum = 0x7, + .hs400_cmdout_tapnum = 0x7, + .hs400_strbin_tapnum = 0x5, + .ddr50_strbin_delay_num = 0xa, }; static const struct sdhci_data rk3588_data = { @@ -662,6 +699,9 @@ static const struct sdhci_data rk3588_data = { .config_dll = rk3568_sdhci_config_dll, .hs200_txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT, .hs400_txclk_tapnum = 0x9, + .hs400_cmdout_tapnum = DLL_CMDOUT_TAPNUM_90_DEGREES, + .hs400_strbin_tapnum = DLL_STRBIN_TAPNUM_DEFAULT, + .ddr50_strbin_delay_num = DLL_STRBIN_DELAY_NUM_DEFAULT, }; static const struct udevice_id sdhci_ids[] = { @@ -670,10 +710,18 @@ static const struct udevice_id sdhci_ids[] = { .data = (ulong)&rk3399_data, }, { + .compatible = "rockchip,rk3528-dwcmshc", + .data = (ulong)&rk3528_data, + }, + { .compatible = "rockchip,rk3568-dwcmshc", .data = (ulong)&rk3568_data, }, { + .compatible = "rockchip,rk3576-dwcmshc", + .data = (ulong)&rk3576_data, + }, + { .compatible = "rockchip,rk3588-dwcmshc", .data = (ulong)&rk3588_data, }, diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 4833b5158c7..648dfa4b5ef 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -177,8 +177,10 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data) } while (!(stat & SDHCI_INT_DATA_END)); #if (CONFIG_IS_ENABLED(MMC_SDHCI_SDMA) || CONFIG_IS_ENABLED(MMC_SDHCI_ADMA)) - dma_unmap_single(host->start_addr, data->blocks * data->blocksize, - mmc_get_dma_dir(data)); + if (host->flags & USE_DMA) { + dma_unmap_single(host->start_addr, data->blocks * data->blocksize, + mmc_get_dma_dir(data)); + } #endif return 0; @@ -547,7 +549,7 @@ void sdhci_set_uhs_timing(struct sdhci_host *host) sdhci_writew(host, reg, SDHCI_HOST_CONTROL2); } -static void sdhci_set_voltage(struct sdhci_host *host) +void sdhci_set_voltage(struct sdhci_host *host) { if (IS_ENABLED(CONFIG_MMC_IO_VOLTAGE)) { struct mmc *mmc = (struct mmc *)host->mmc; diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index 0b56d1405be..06c1e09bf26 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -92,6 +92,13 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz) pll = CCM_MMC_CTRL_PLL6; pll_hz = clock_get_pll6(); #endif + /* + * On the D1/R528/T113 mux source 1 refers to PLL_PERIPH0(1x), + * like for the older SoCs. However we still have the hidden + * divider of 2x, so compensate for that here. + */ + if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) + pll_hz /= 2; } div = pll_hz / hz; @@ -442,6 +449,26 @@ out: return error; } +static void sunxi_mmc_reset(void *regs) +{ + /* Reset controller */ + writel(SUNXI_MMC_GCTRL_RESET, regs + SUNXI_MMC_GCTRL); + udelay(1000); + + if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) { + /* Reset card */ + writel(SUNXI_MMC_HWRST_ASSERT, regs + SUNXI_MMC_HWRST); + udelay(10); + writel(SUNXI_MMC_HWRST_DEASSERT, regs + SUNXI_MMC_HWRST); + udelay(300); + + /* Setup FIFO R/W threshold. Needed on H616. */ + writel(SUNXI_MMC_THLDC_READ_THLD(512) | + SUNXI_MMC_THLDC_WRITE_EN | + SUNXI_MMC_THLDC_READ_EN, regs + SUNXI_MMC_THLDC); + } +} + /* non-DM code here is used by the (ARM) SPL only */ #if !CONFIG_IS_ENABLED(DM_MMC) @@ -451,29 +478,29 @@ struct sunxi_mmc_priv mmc_host[4]; static int mmc_resource_init(int sdc_no) { struct sunxi_mmc_priv *priv = &mmc_host[sdc_no]; - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + void *ccm = (void *)SUNXI_CCM_BASE; debug("init mmc %d resource\n", sdc_no); switch (sdc_no) { case 0: priv->reg = (struct sunxi_mmc *)SUNXI_MMC0_BASE; - priv->mclkreg = &ccm->sd0_clk_cfg; + priv->mclkreg = ccm + CCU_MMC0_CLK_CFG; break; case 1: priv->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE; - priv->mclkreg = &ccm->sd1_clk_cfg; + priv->mclkreg = ccm + CCU_MMC1_CLK_CFG; break; #ifdef SUNXI_MMC2_BASE case 2: priv->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE; - priv->mclkreg = &ccm->sd2_clk_cfg; + priv->mclkreg = ccm + CCU_MMC2_CLK_CFG; break; #endif #ifdef SUNXI_MMC3_BASE case 3: priv->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE; - priv->mclkreg = &ccm->sd3_clk_cfg; + priv->mclkreg = ccm + CCU_MMC3_CLK_CFG; break; #endif default: @@ -489,9 +516,7 @@ static int sunxi_mmc_core_init(struct mmc *mmc) { struct sunxi_mmc_priv *priv = mmc->priv; - /* Reset controller */ - writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); - udelay(1000); + sunxi_mmc_reset(priv->reg); return 0; } @@ -520,7 +545,7 @@ static const struct mmc_ops sunxi_mmc_ops = { struct mmc *sunxi_mmc_init(int sdc_no) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + void *ccm = (void *)SUNXI_CCM_BASE; struct sunxi_mmc_priv *priv = &mmc_host[sdc_no]; struct mmc_config *cfg = &priv->cfg; int ret; @@ -549,11 +574,11 @@ 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) && !defined(CONFIG_SUNXI_GEN_NCAT2) - setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no)); + setbits_le32(ccm + CCU_AHB_GATE0, 1 << AHB_GATE_OFFSET_MMC(sdc_no)); #ifdef CONFIG_SUNXI_GEN_SUN6I /* unassert reset */ - setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no)); + setbits_le32(ccm + CCU_AHB_RESET0_CFG, 1 << AHB_RESET_OFFSET_MMC(sdc_no)); #endif #if defined(CONFIG_MACH_SUN9I) /* sun9i has a mmc-common module, also set the gate and reset there */ @@ -561,9 +586,9 @@ struct mmc *sunxi_mmc_init(int sdc_no) SUNXI_MMC_COMMON_BASE + 4 * sdc_no); #endif #else /* CONFIG_SUN50I_GEN_H6 */ - setbits_le32(&ccm->sd_gate_reset, 1 << sdc_no); + setbits_le32(ccm + CCU_H6_MMC_GATE_RESET, 1 << sdc_no); /* unassert reset */ - setbits_le32(&ccm->sd_gate_reset, 1 << (RESET_SHIFT + sdc_no)); + setbits_le32(ccm + CCU_H6_MMC_GATE_RESET, 1 << (RESET_SHIFT + sdc_no)); #endif ret = mmc_set_mod_clk(priv, 24000000); if (ret) @@ -684,9 +709,7 @@ static int sunxi_mmc_probe(struct udevice *dev) upriv->mmc = &plat->mmc; - /* Reset controller */ - writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); - udelay(1000); + sunxi_mmc_reset(priv->reg); return 0; } diff --git a/drivers/mmc/sunxi_mmc.h b/drivers/mmc/sunxi_mmc.h index f4ae5a790c8..71865160319 100644 --- a/drivers/mmc/sunxi_mmc.h +++ b/drivers/mmc/sunxi_mmc.h @@ -37,7 +37,9 @@ struct sunxi_mmc { u32 res0; /* 0x54 reserved */ u32 a12a; /* 0x58 Auto command 12 argument */ u32 ntsr; /* 0x5c New timing set register */ - u32 res1[8]; + u32 res1[6]; + u32 hwrst; /* 0x78 Hardware Reset */ + u32 res5; u32 dmac; /* 0x80 internal DMA control */ u32 dlba; /* 0x84 internal DMA descr list base address */ u32 idst; /* 0x88 internal DMA status */ @@ -46,7 +48,8 @@ struct sunxi_mmc { 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 thldc; /* 0x100 Threshold control */ + u32 res3[16]; u32 samp_dl; u32 res4[46]; #endif @@ -57,6 +60,7 @@ struct sunxi_mmc { #define SUNXI_MMC_CLK_ENABLE (0x1 << 16) #define SUNXI_MMC_CLK_DIVIDER_MASK (0xff) +#define SUNXI_MMC_GCTRL 0x000 #define SUNXI_MMC_GCTRL_SOFT_RESET (0x1 << 0) #define SUNXI_MMC_GCTRL_FIFO_RESET (0x1 << 1) #define SUNXI_MMC_GCTRL_DMA_RESET (0x1 << 2) @@ -123,6 +127,10 @@ struct sunxi_mmc { #define SUNXI_MMC_NTSR_MODE_SEL_NEW (0x1 << 31) +#define SUNXI_MMC_HWRST 0x078 +#define SUNXI_MMC_HWRST_ASSERT (0x0 << 0) +#define SUNXI_MMC_HWRST_DEASSERT (0x1 << 0) + #define SUNXI_MMC_IDMAC_RESET (0x1 << 0) #define SUNXI_MMC_IDMAC_FIXBURST (0x1 << 1) #define SUNXI_MMC_IDMAC_ENABLE (0x1 << 7) @@ -133,6 +141,12 @@ struct sunxi_mmc { #define SUNXI_MMC_COMMON_CLK_GATE (1 << 16) #define SUNXI_MMC_COMMON_RESET (1 << 18) +#define SUNXI_MMC_THLDC 0x100 +#define SUNXI_MMC_THLDC_READ_EN (0x1 << 0) +#define SUNXI_MMC_THLDC_BSY_CLR_INT_EN (0x1 << 1) +#define SUNXI_MMC_THLDC_WRITE_EN (0x1 << 2) +#define SUNXI_MMC_THLDC_READ_THLD(x) (((x) & 0xfff) << 16) + #define SUNXI_MMC_CAL_DL_SW_EN (0x1 << 7) #endif /* _SUNXI_MMC_H */ diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 88094b81e7a..3f8edeb5093 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -208,7 +208,7 @@ int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts, { struct mtd_partition partition = {}, *parts; const char *mtdparts = *_mtdparts; - uint64_t cur_off = 0, cur_sz = 0; + uint64_t cur_off = 0; int nparts = 0; int ret, idx; u64 sz; @@ -237,8 +237,7 @@ int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts, return ret; if (parts[idx].size == MTD_SIZE_REMAINING) - parts[idx].size = parent->size - cur_sz; - cur_sz += parts[idx].size; + parts[idx].size = parent->size - parts[idx].offset; sz = parts[idx].size; if (sz < parent->writesize || do_div(sz, parent->writesize)) { diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 609bdffbf77..adb271dfb8f 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -190,6 +190,21 @@ config SPL_NAND_LOAD def_bool y depends on NAND_DAVINCI && ARCH_DAVINCI && SPL_NAND_SUPPORT +config NAND_CADENCE + bool "Support Cadence NAND controller as a DT device" + depends on OF_CONTROL && DM_MTD + select SYS_NAND_SELF_INIT + select SPL_SYS_NAND_SELF_INIT + select SPL_NAND_BASE + select SPL_NAND_DRIVERS + select SPL_NAND_IDENT + select SPL_NAND_INIT + select SPL_NAND_ECC + imply CMD_NAND + help + Enable the driver for NAND flash on platforms using a Cadence NAND + controller as a DT device. + config NAND_DENALI bool select SYS_NAND_SELF_INIT @@ -673,7 +688,7 @@ config SYS_NAND_PAGE_SIZE SPL_NAND_SIMPLE || (NAND_MXC && SPL_NAND_SUPPORT) || \ MVEBU_SPL_BOOT_DEVICE_NAND || \ (NAND_ATMEL && SPL_NAND_SUPPORT) || \ - SPL_GENERATE_ATMEL_PMECC_HEADER || NAND_SANDBOX + SPL_GENERATE_ATMEL_PMECC_HEADER || NAND_SANDBOX || NAND_CADENCE 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 @@ -775,6 +790,13 @@ config SPL_NAND_AM33XX_BCH so those platforms should use CONFIG_SPL_NAND_SIMPLE for enabling SPL-NAND driver with software ECC correction support. +config SPL_NAND_CADENCE + bool "Support Cadence NAND controller for SPL" + depends on SPL_NAND_SUPPORT + help + This is a small implementation of the Cadence NAND controller + for use on SPL. + config SPL_NAND_DENALI bool "Support Denali NAND controller for SPL" depends on SPL_NAND_SUPPORT diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index b47a3d787ce..34cba77046a 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -10,6 +10,7 @@ NORMAL_DRIVERS=y endif obj-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o +obj-$(CONFIG_SPL_NAND_CADENCE) += cadence_spl.o obj-$(CONFIG_SPL_NAND_DENALI) += denali_spl.o obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o @@ -51,6 +52,7 @@ obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o obj-$(CONFIG_DM_NAND_ATMEL) += atmel/ obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o obj-$(CONFIG_NAND_BRCMNAND) += brcmnand/ +obj-$(CONFIG_NAND_CADENCE) += cadence_nand.o obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o obj-$(CONFIG_NAND_DENALI) += denali.o obj-$(CONFIG_NAND_DENALI_DT) += denali_dt.o diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 56fbd64ef68..c90a4eab8df 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -1127,7 +1127,7 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, const struct nand_data_interface *conf, struct atmel_smc_cs_conf *smcconf) { - u32 ncycles, totalcycles, timeps, mckperiodps; + u32 ncycles, totalcycles, timeps, mckperiodps, pulse; struct atmel_nand_controller *nc; int ret; @@ -1253,11 +1253,16 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, ATMEL_SMC_MODE_TDFMODE_OPTIMIZED; /* - * Read pulse timing directly matches tRP: + * Read pulse timing would directly match tRP, + * but some NAND flash chips (S34ML01G2 and W29N02KVxxAF) + * do not work properly in timing mode 3. + * The workaround is to extend the SMC NRD pulse to meet tREA + * timing. * - * NRD_PULSE = tRP + * NRD_PULSE = max(tRP, tREA) */ - ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps); + pulse = max(conf->timings.sdr.tRP_min, conf->timings.sdr.tREA_max); + ncycles = DIV_ROUND_UP(pulse, mckperiodps); totalcycles += ncycles; ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT, ncycles); diff --git a/drivers/mtd/nand/raw/cadence_nand.c b/drivers/mtd/nand/raw/cadence_nand.c new file mode 100644 index 00000000000..27aa7f97a45 --- /dev/null +++ b/drivers/mtd/nand/raw/cadence_nand.c @@ -0,0 +1,2423 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Cadence NAND flash controller driver + * + * Copyright (C) 2019 Cadence + * + * Author: Piotr Sroka <piotrs@cadence.com> + * + */ + +#include <cadence-nand.h> +#include <clk.h> +#include <dm.h> +#include <hang.h> +#include <malloc.h> +#include <memalign.h> +#include <nand.h> +#include <reset.h> +#include <wait_bit.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <linux/bitfield.h> +#include <linux/bug.h> +#include <linux/delay.h> +#include <linux/dma-direction.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/ioport.h> +#include <linux/printk.h> +#include <linux/sizes.h> + +static inline struct +cdns_nand_chip *to_cdns_nand_chip(struct nand_chip *chip) +{ + return container_of(chip, struct cdns_nand_chip, chip); +} + +static inline struct +cadence_nand_info *to_cadence_nand_info(struct nand_hw_control *controller) +{ + return container_of(controller, struct cadence_nand_info, controller); +} + +static bool +cadence_nand_dma_buf_ok(struct cadence_nand_info *cadence, const void *buf, + u32 buf_len) +{ + u8 data_dma_width = cadence->caps2.data_dma_width; + + return buf && + likely(IS_ALIGNED((uintptr_t)buf, data_dma_width)) && + likely(IS_ALIGNED(buf_len, DMA_DATA_SIZE_ALIGN)); +} + +static int cadence_nand_wait_for_value(struct cadence_nand_info *cadence, + u32 reg_offset, u32 timeout_us, + u32 mask, bool is_clear) +{ + u32 val; + int ret; + + ret = readl_poll_sleep_timeout(cadence->reg + reg_offset, + val, !(val & mask) == is_clear, + 10, timeout_us); + + if (ret < 0) { + dev_err(cadence->dev, + "Timeout while waiting for reg %x with mask %x is clear %d\n", + reg_offset, mask, is_clear); + } + + return ret; +} + +static int cadence_nand_set_ecc_enable(struct cadence_nand_info *cadence, + bool enable) +{ + u32 reg; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + reg = readl_relaxed(cadence->reg + ECC_CONFIG_0); + + if (enable) + reg |= ECC_CONFIG_0_ECC_EN; + else + reg &= ~ECC_CONFIG_0_ECC_EN; + + writel_relaxed(reg, cadence->reg + ECC_CONFIG_0); + + return 0; +} + +static void cadence_nand_set_ecc_strength(struct cadence_nand_info *cadence, + u8 corr_str_idx) +{ + u32 reg; + + if (cadence->curr_corr_str_idx == corr_str_idx) + return; + + reg = readl_relaxed(cadence->reg + ECC_CONFIG_0); + reg &= ~ECC_CONFIG_0_CORR_STR; + reg |= FIELD_PREP(ECC_CONFIG_0_CORR_STR, corr_str_idx); + writel_relaxed(reg, cadence->reg + ECC_CONFIG_0); + + cadence->curr_corr_str_idx = corr_str_idx; +} + +static int cadence_nand_get_ecc_strength_idx(struct cadence_nand_info *cadence, + u8 strength) +{ + int i, corr_str_idx = -1; + + for (i = 0; i < BCH_MAX_NUM_CORR_CAPS; i++) { + if (cadence->ecc_strengths[i] == strength) { + corr_str_idx = i; + break; + } + } + + return corr_str_idx; +} + +static int cadence_nand_set_skip_marker_val(struct cadence_nand_info *cadence, + u16 marker_value) +{ + u32 reg; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + reg = readl_relaxed(cadence->reg + SKIP_BYTES_CONF); + reg &= ~SKIP_BYTES_MARKER_VALUE; + reg |= FIELD_PREP(SKIP_BYTES_MARKER_VALUE, + marker_value); + + writel_relaxed(reg, cadence->reg + SKIP_BYTES_CONF); + + return 0; +} + +static int cadence_nand_set_skip_bytes_conf(struct cadence_nand_info *cadence, + u8 num_of_bytes, + u32 offset_value, + int enable) +{ + u32 reg, skip_bytes_offset; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + if (!enable) { + num_of_bytes = 0; + offset_value = 0; + } + + reg = readl_relaxed(cadence->reg + SKIP_BYTES_CONF); + reg &= ~SKIP_BYTES_NUM_OF_BYTES; + reg |= FIELD_PREP(SKIP_BYTES_NUM_OF_BYTES, + num_of_bytes); + skip_bytes_offset = FIELD_PREP(SKIP_BYTES_OFFSET_VALUE, + offset_value); + + writel_relaxed(reg, cadence->reg + SKIP_BYTES_CONF); + writel_relaxed(skip_bytes_offset, cadence->reg + SKIP_BYTES_OFFSET); + + return 0; +} + +/* Functions enables/disables hardware detection of erased data */ +static void cadence_nand_set_erase_detection(struct cadence_nand_info *cadence, + bool enable, + u8 bitflips_threshold) +{ + u32 reg; + + reg = readl_relaxed(cadence->reg + ECC_CONFIG_0); + + if (enable) + reg |= ECC_CONFIG_0_ERASE_DET_EN; + else + reg &= ~ECC_CONFIG_0_ERASE_DET_EN; + + writel_relaxed(reg, cadence->reg + ECC_CONFIG_0); + writel_relaxed(bitflips_threshold, cadence->reg + ECC_CONFIG_1); +} + +static int cadence_nand_set_access_width16(struct cadence_nand_info *cadence, + bool bit_bus16) +{ + u32 reg; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + reg = readl_relaxed(cadence->reg + COMMON_SET); + if (!bit_bus16) + reg &= ~COMMON_SET_DEVICE_16BIT; + else + reg |= COMMON_SET_DEVICE_16BIT; + writel_relaxed(reg, cadence->reg + COMMON_SET); + + return 0; +} + +static void +cadence_nand_clear_interrupt(struct cadence_nand_info *cadence, + struct cadence_nand_irq_status *irq_status) +{ + writel_relaxed(irq_status->status, cadence->reg + INTR_STATUS); + writel_relaxed(irq_status->trd_status, + cadence->reg + TRD_COMP_INT_STATUS); + writel_relaxed(irq_status->trd_error, + cadence->reg + TRD_ERR_INT_STATUS); +} + +static void +cadence_nand_read_int_status(struct cadence_nand_info *cadence, + struct cadence_nand_irq_status *irq_status) +{ + irq_status->status = readl_relaxed(cadence->reg + INTR_STATUS); + irq_status->trd_status = readl_relaxed(cadence->reg + + TRD_COMP_INT_STATUS); + irq_status->trd_error = readl_relaxed(cadence->reg + + TRD_ERR_INT_STATUS); +} + +static u32 irq_detected(struct cadence_nand_info *cadence, + struct cadence_nand_irq_status *irq_status) +{ + cadence_nand_read_int_status(cadence, irq_status); + + return irq_status->status || irq_status->trd_status || + irq_status->trd_error; +} + +static void cadence_nand_reset_irq(struct cadence_nand_info *cadence) +{ + memset(&cadence->irq_status, 0, sizeof(cadence->irq_status)); + memset(&cadence->irq_mask, 0, sizeof(cadence->irq_mask)); +} + +/* + * This is the interrupt service routine. It handles all interrupts + * sent to this device. + */ +static irqreturn_t cadence_nand_isr(struct cadence_nand_info *cadence) +{ + struct cadence_nand_irq_status irq_status; + irqreturn_t result = IRQ_NONE; + + if (irq_detected(cadence, &irq_status)) { + /* Handle interrupt. */ + /* First acknowledge it. */ + cadence_nand_clear_interrupt(cadence, &irq_status); + /* Status in the device context for someone to read. */ + cadence->irq_status.status |= irq_status.status; + cadence->irq_status.trd_status |= irq_status.trd_status; + cadence->irq_status.trd_error |= irq_status.trd_error; + /* Tell the OS that we've handled this. */ + result = IRQ_HANDLED; + } + return result; +} + +static void cadence_nand_set_irq_mask(struct cadence_nand_info *cadence, + struct cadence_nand_irq_status *irq_mask) +{ + writel_relaxed(INTR_ENABLE_INTR_EN | irq_mask->status, + cadence->reg + INTR_ENABLE); + + writel_relaxed(irq_mask->trd_error, + cadence->reg + TRD_ERR_INT_STATUS_EN); +} + +static void +cadence_nand_wait_for_irq(struct cadence_nand_info *cadence, + struct cadence_nand_irq_status *irq_mask, + struct cadence_nand_irq_status *irq_status) +{ + irqreturn_t result = IRQ_NONE; + u32 start = get_timer(0); + + while (get_timer(start) < TIMEOUT_US) { + result = cadence_nand_isr(cadence); + + if (result == IRQ_HANDLED) { + *irq_status = cadence->irq_status; + break; + } + udelay(1); + } + + if (!result) { + /* Timeout error. */ + dev_err(cadence->dev, "timeout occurred:\n"); + dev_err(cadence->dev, "\tstatus = 0x%x, mask = 0x%x\n", + irq_status->status, irq_mask->status); + dev_err(cadence->dev, + "\ttrd_status = 0x%x, trd_status mask = 0x%x\n", + irq_status->trd_status, irq_mask->trd_status); + dev_err(cadence->dev, + "\t trd_error = 0x%x, trd_error mask = 0x%x\n", + irq_status->trd_error, irq_mask->trd_error); + } +} + +/* Execute generic command on NAND controller. */ +static int cadence_nand_generic_cmd_send(struct cadence_nand_info *cadence, + u8 chip_nr, + u64 mini_ctrl_cmd) +{ + u32 mini_ctrl_cmd_l, mini_ctrl_cmd_h, reg; + + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_CS, chip_nr); + mini_ctrl_cmd_l = mini_ctrl_cmd & 0xFFFFFFFF; + mini_ctrl_cmd_h = mini_ctrl_cmd >> 32; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + cadence_nand_reset_irq(cadence); + + writel_relaxed(mini_ctrl_cmd_l, cadence->reg + CMD_REG2); + writel_relaxed(mini_ctrl_cmd_h, cadence->reg + CMD_REG3); + + /* Select generic command. */ + reg = FIELD_PREP(CMD_REG0_CT, CMD_REG0_CT_GEN); + /* Thread number. */ + reg |= FIELD_PREP(CMD_REG0_TN, 0); + + /* Issue command. */ + writel_relaxed(reg, cadence->reg + CMD_REG0); + cadence->buf_index = 0; + + return 0; +} + +/* Wait for data on slave DMA interface. */ +static int cadence_nand_wait_on_sdma(struct cadence_nand_info *cadence, u8 *out_sdma_trd, + u32 *out_sdma_size) +{ + struct cadence_nand_irq_status irq_mask, irq_status; + + irq_mask.trd_status = 0; + irq_mask.trd_error = 0; + irq_mask.status = INTR_STATUS_SDMA_TRIGG + | INTR_STATUS_SDMA_ERR + | INTR_STATUS_UNSUPP_CMD; + + cadence_nand_set_irq_mask(cadence, &irq_mask); + cadence_nand_wait_for_irq(cadence, &irq_mask, &irq_status); + if (irq_status.status == 0) { + dev_err(cadence->dev, "Timeout while waiting for SDMA\n"); + return -ETIMEDOUT; + } + + if (irq_status.status & INTR_STATUS_SDMA_TRIGG) { + *out_sdma_size = readl_relaxed(cadence->reg + SDMA_SIZE); + *out_sdma_trd = readl_relaxed(cadence->reg + SDMA_TRD_NUM); + *out_sdma_trd = + FIELD_GET(SDMA_TRD_NUM_SDMA_TRD, *out_sdma_trd); + } else { + dev_err(cadence->dev, "SDMA error - irq_status %x\n", + irq_status.status); + return -EIO; + } + + return 0; +} + +static void cadence_nand_get_caps(struct cadence_nand_info *cadence) +{ + u32 reg; + + reg = readl_relaxed(cadence->reg + CTRL_FEATURES); + + cadence->caps2.max_banks = 1 << FIELD_GET(CTRL_FEATURES_N_BANKS, reg); + + if (FIELD_GET(CTRL_FEATURES_DMA_DWITH64, reg)) + cadence->caps2.data_dma_width = 8; + else + cadence->caps2.data_dma_width = 4; + + if (reg & CTRL_FEATURES_CONTROL_DATA) + cadence->caps2.data_control_supp = true; + + if (reg & (CTRL_FEATURES_NVDDR_2_3 + | CTRL_FEATURES_NVDDR)) + cadence->caps2.is_phy_type_dll = true; +} + +/* Prepare CDMA descriptor. */ +static void +cadence_nand_cdma_desc_prepare(struct cadence_nand_info *cadence, + char nf_mem, u32 flash_ptr, dma_addr_t mem_ptr, + dma_addr_t ctrl_data_ptr, u16 ctype) +{ + struct cadence_nand_cdma_desc *cdma_desc = cadence->cdma_desc; + + memset(cdma_desc, 0, sizeof(struct cadence_nand_cdma_desc)); + + /* Set fields for one descriptor. */ + cdma_desc->flash_pointer = flash_ptr; + if (cadence->ctrl_rev >= 13) + cdma_desc->bank = nf_mem; + else + cdma_desc->flash_pointer |= (nf_mem << CDMA_CFPTR_MEM_SHIFT); + + cdma_desc->command_flags |= CDMA_CF_DMA_MASTER; + cdma_desc->command_flags |= CDMA_CF_INT; + + cdma_desc->memory_pointer = mem_ptr; + cdma_desc->status = 0; + cdma_desc->sync_flag_pointer = 0; + cdma_desc->sync_arguments = 0; + + cdma_desc->command_type = ctype; + cdma_desc->ctrl_data_ptr = ctrl_data_ptr; + + flush_cache((dma_addr_t)cadence->cdma_desc, + ROUND(sizeof(struct cadence_nand_cdma_desc), + ARCH_DMA_MINALIGN)); +} + +static u8 cadence_nand_check_desc_error(struct cadence_nand_info *cadence, + u32 desc_status) +{ + if (desc_status & CDMA_CS_ERP) + return STAT_ERASED; + + if (desc_status & CDMA_CS_UNCE) + return STAT_ECC_UNCORR; + + if (desc_status & CDMA_CS_ERR) { + dev_err(cadence->dev, ":CDMA desc error flag detected.\n"); + return STAT_FAIL; + } + + if (FIELD_GET(CDMA_CS_MAXERR, desc_status)) + return STAT_ECC_CORR; + + return STAT_FAIL; +} + +static int cadence_nand_cdma_finish(struct cadence_nand_info *cadence) +{ + struct cadence_nand_cdma_desc *desc_ptr = cadence->cdma_desc; + u8 status = STAT_BUSY; + + invalidate_dcache_range((dma_addr_t)cadence->cdma_desc, + (dma_addr_t)cadence->cdma_desc + + ROUND(sizeof(struct cadence_nand_cdma_desc), + ARCH_DMA_MINALIGN)); + + if (desc_ptr->status & CDMA_CS_FAIL) { + status = cadence_nand_check_desc_error(cadence, + desc_ptr->status); + dev_err(cadence->dev, ":CDMA error %x\n", desc_ptr->status); + } else if (desc_ptr->status & CDMA_CS_COMP) { + /* Descriptor finished with no errors. */ + if (desc_ptr->command_flags & CDMA_CF_CONT) { + dev_info(cadence->dev, "DMA unsupported flag is set"); + status = STAT_UNKNOWN; + } else { + /* Last descriptor. */ + status = STAT_OK; + } + } + + return status; +} + +static int cadence_nand_cdma_send(struct cadence_nand_info *cadence, + u8 thread) +{ + u32 reg; + int status; + + /* Wait for thread ready. */ + status = cadence_nand_wait_for_value(cadence, TRD_STATUS, + TIMEOUT_US, + BIT(thread), true); + if (status) + return status; + + cadence_nand_reset_irq(cadence); + + writel_relaxed((u32)cadence->dma_cdma_desc, + cadence->reg + CMD_REG2); + writel_relaxed(0, cadence->reg + CMD_REG3); + + /* Select CDMA mode. */ + reg = FIELD_PREP(CMD_REG0_CT, CMD_REG0_CT_CDMA); + /* Thread number. */ + reg |= FIELD_PREP(CMD_REG0_TN, thread); + /* Issue command. */ + writel_relaxed(reg, cadence->reg + CMD_REG0); + + return 0; +} + +/* Send SDMA command and wait for finish. */ +static u32 +cadence_nand_cdma_send_and_wait(struct cadence_nand_info *cadence, + u8 thread) +{ + struct cadence_nand_irq_status irq_mask, irq_status = {0}; + int status; + u32 val; + + irq_mask.trd_status = BIT(thread); + irq_mask.trd_error = BIT(thread); + irq_mask.status = INTR_STATUS_CDMA_TERR; + + cadence_nand_set_irq_mask(cadence, &irq_mask); + + status = cadence_nand_cdma_send(cadence, thread); + if (status) + return status; + + /* Make sure the descriptor processing is complete */ + status = readl_poll_timeout(cadence->reg + TRD_COMP_INT_STATUS, val, + (val & BIT(thread)), TIMEOUT_US); + if (status) { + pr_err("cmd thread completion timeout!\n"); + return status; + } + + cadence_nand_wait_for_irq(cadence, &irq_mask, &irq_status); + + if (irq_status.status == 0 && irq_status.trd_status == 0 && + irq_status.trd_error == 0) { + dev_err(cadence->dev, "CDMA command timeout\n"); + return -ETIMEDOUT; + } + if (irq_status.status & irq_mask.status) { + dev_err(cadence->dev, "CDMA command failed\n"); + return -EIO; + } + + return 0; +} + +/* + * ECC size depends on configured ECC strength and on maximum supported + * ECC step size. + */ +static int cadence_nand_calc_ecc_bytes(int max_step_size, int strength) +{ + int nbytes = DIV_ROUND_UP(fls(8 * max_step_size) * strength, 8); + + return ALIGN(nbytes, 2); +} + +#define CADENCE_NAND_CALC_ECC_BYTES(max_step_size) \ + static int \ + cadence_nand_calc_ecc_bytes_##max_step_size(int step_size, \ + int strength)\ + {\ + return cadence_nand_calc_ecc_bytes(max_step_size, strength);\ + } + +CADENCE_NAND_CALC_ECC_BYTES(256) +CADENCE_NAND_CALC_ECC_BYTES(512) +CADENCE_NAND_CALC_ECC_BYTES(1024) +CADENCE_NAND_CALC_ECC_BYTES(2048) +CADENCE_NAND_CALC_ECC_BYTES(4096) + +/* Function reads BCH capabilities. */ +static int cadence_nand_read_bch_caps(struct cadence_nand_info *cadence) +{ + struct nand_ecc_caps *ecc_caps = &cadence->ecc_caps; + int max_step_size = 0, nstrengths, i; + u32 reg; + + reg = readl_relaxed(cadence->reg + BCH_CFG_3); + cadence->bch_metadata_size = FIELD_GET(BCH_CFG_3_METADATA_SIZE, reg); + if (cadence->bch_metadata_size < 4) { + dev_err(cadence->dev, + "Driver needs at least 4 bytes of BCH meta data\n"); + return -EIO; + } + + reg = readl_relaxed(cadence->reg + BCH_CFG_0); + cadence->ecc_strengths[0] = FIELD_GET(BCH_CFG_0_CORR_CAP_0, reg); + cadence->ecc_strengths[1] = FIELD_GET(BCH_CFG_0_CORR_CAP_1, reg); + cadence->ecc_strengths[2] = FIELD_GET(BCH_CFG_0_CORR_CAP_2, reg); + cadence->ecc_strengths[3] = FIELD_GET(BCH_CFG_0_CORR_CAP_3, reg); + + reg = readl_relaxed(cadence->reg + BCH_CFG_1); + cadence->ecc_strengths[4] = FIELD_GET(BCH_CFG_1_CORR_CAP_4, reg); + cadence->ecc_strengths[5] = FIELD_GET(BCH_CFG_1_CORR_CAP_5, reg); + cadence->ecc_strengths[6] = FIELD_GET(BCH_CFG_1_CORR_CAP_6, reg); + cadence->ecc_strengths[7] = FIELD_GET(BCH_CFG_1_CORR_CAP_7, reg); + + reg = readl_relaxed(cadence->reg + BCH_CFG_2); + cadence->ecc_stepinfos[0].stepsize = + FIELD_GET(BCH_CFG_2_SECT_0, reg); + + cadence->ecc_stepinfos[1].stepsize = + FIELD_GET(BCH_CFG_2_SECT_1, reg); + + nstrengths = 0; + for (i = 0; i < BCH_MAX_NUM_CORR_CAPS; i++) { + if (cadence->ecc_strengths[i] != 0) + nstrengths++; + } + + ecc_caps->nstepinfos = 0; + for (i = 0; i < BCH_MAX_NUM_SECTOR_SIZES; i++) { + /* ECC strengths are common for all step infos. */ + cadence->ecc_stepinfos[i].nstrengths = nstrengths; + cadence->ecc_stepinfos[i].strengths = + cadence->ecc_strengths; + + if (cadence->ecc_stepinfos[i].stepsize != 0) + ecc_caps->nstepinfos++; + + if (cadence->ecc_stepinfos[i].stepsize > max_step_size) + max_step_size = cadence->ecc_stepinfos[i].stepsize; + } + ecc_caps->stepinfos = &cadence->ecc_stepinfos[0]; + + switch (max_step_size) { + case 256: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_256; + break; + case 512: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_512; + break; + case 1024: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_1024; + break; + case 2048: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_2048; + break; + case 4096: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_4096; + break; + default: + dev_err(cadence->dev, + "Unsupported sector size(ecc step size) %d\n", + max_step_size); + return -EIO; + } + + return 0; +} + +/* Hardware initialization. */ +static int cadence_nand_hw_init(struct cadence_nand_info *cadence) +{ + int status; + u32 reg; + + status = cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_INIT_COMP, false); + if (status) + return status; + + reg = readl_relaxed(cadence->reg + CTRL_VERSION); + cadence->ctrl_rev = FIELD_GET(CTRL_VERSION_REV, reg); + + dev_info(cadence->dev, + "%s: cadence nand controller version reg %x\n", + __func__, reg); + + /* Disable cache and multiplane. */ + writel_relaxed(0, cadence->reg + MULTIPLANE_CFG); + writel_relaxed(0, cadence->reg + CACHE_CFG); + + /* Clear all interrupts. */ + writel_relaxed(0xFFFFFFFF, cadence->reg + INTR_STATUS); + + cadence_nand_get_caps(cadence); + if (cadence_nand_read_bch_caps(cadence)) + return -EIO; + + /* + * Set IO width access to 8. + * It is because during SW device discovering width access + * is expected to be 8. + */ + status = cadence_nand_set_access_width16(cadence, false); + + return status; +} + +#define TT_MAIN_OOB_AREAS 2 +#define TT_RAW_PAGE 3 +#define TT_BBM 4 +#define TT_MAIN_OOB_AREA_EXT 5 + +/* Prepare size of data to transfer. */ +static void +cadence_nand_prepare_data_size(struct mtd_info *mtd, + int transfer_type) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + u32 sec_size = 0, offset = 0, sec_cnt = 1; + u32 last_sec_size = cdns_chip->sector_size; + u32 data_ctrl_size = 0; + u32 reg = 0; + + if (cadence->curr_trans_type == transfer_type) + return; + + switch (transfer_type) { + case TT_MAIN_OOB_AREA_EXT: + sec_cnt = cdns_chip->sector_count; + sec_size = cdns_chip->sector_size; + data_ctrl_size = cdns_chip->avail_oob_size; + break; + case TT_MAIN_OOB_AREAS: + sec_cnt = cdns_chip->sector_count; + last_sec_size = cdns_chip->sector_size + + cdns_chip->avail_oob_size; + sec_size = cdns_chip->sector_size; + break; + case TT_RAW_PAGE: + last_sec_size = mtd->writesize + mtd->oobsize; + break; + case TT_BBM: + offset = mtd->writesize + cdns_chip->bbm_offs; + last_sec_size = 8; + break; + } + + reg = 0; + reg |= FIELD_PREP(TRAN_CFG_0_OFFSET, offset); + reg |= FIELD_PREP(TRAN_CFG_0_SEC_CNT, sec_cnt); + writel_relaxed(reg, cadence->reg + TRAN_CFG_0); + + reg = 0; + reg |= FIELD_PREP(TRAN_CFG_1_LAST_SEC_SIZE, last_sec_size); + reg |= FIELD_PREP(TRAN_CFG_1_SECTOR_SIZE, sec_size); + writel_relaxed(reg, cadence->reg + TRAN_CFG_1); + + if (cadence->caps2.data_control_supp) { + reg = readl_relaxed(cadence->reg + CONTROL_DATA_CTRL); + reg &= ~CONTROL_DATA_CTRL_SIZE; + reg |= FIELD_PREP(CONTROL_DATA_CTRL_SIZE, data_ctrl_size); + writel_relaxed(reg, cadence->reg + CONTROL_DATA_CTRL); + } + + cadence->curr_trans_type = transfer_type; +} + +static int +cadence_nand_cdma_transfer(struct cadence_nand_info *cadence, u8 chip_nr, + int page, void *buf, void *ctrl_dat, u32 buf_size, + u32 ctrl_dat_size, enum dma_data_direction dir, + bool with_ecc) +{ + dma_addr_t dma_buf, dma_ctrl_dat = 0; + u8 thread_nr = chip_nr; + int status; + u16 ctype; + + if (dir == DMA_FROM_DEVICE) + ctype = CDMA_CT_RD; + else + ctype = CDMA_CT_WR; + + cadence_nand_set_ecc_enable(cadence, with_ecc); + + dma_buf = dma_map_single(buf, buf_size, dir); + if (dma_mapping_error(cadence->dev, dma_buf)) { + dev_err(cadence->dev, "Failed to map DMA buffer\n"); + return -EIO; + } + + if (ctrl_dat && ctrl_dat_size) { + dma_ctrl_dat = dma_map_single(ctrl_dat, + ctrl_dat_size, dir); + if (dma_mapping_error(cadence->dev, dma_ctrl_dat)) { + dma_unmap_single(dma_buf, + buf_size, dir); + dev_err(cadence->dev, "Failed to map DMA buffer\n"); + return -EIO; + } + } + + cadence_nand_cdma_desc_prepare(cadence, chip_nr, page, + dma_buf, dma_ctrl_dat, ctype); + + status = cadence_nand_cdma_send_and_wait(cadence, thread_nr); + + dma_unmap_single(dma_buf, + buf_size, dir); + + if (ctrl_dat && ctrl_dat_size) + dma_unmap_single(dma_ctrl_dat, + ctrl_dat_size, dir); + if (status) + return status; + + return cadence_nand_cdma_finish(cadence); +} + +static void cadence_nand_set_timings(struct cadence_nand_info *cadence, + struct cadence_nand_timings *t) +{ + writel_relaxed(t->async_toggle_timings, + cadence->reg + ASYNC_TOGGLE_TIMINGS); + writel_relaxed(t->timings0, cadence->reg + TIMINGS0); + writel_relaxed(t->timings1, cadence->reg + TIMINGS1); + writel_relaxed(t->timings2, cadence->reg + TIMINGS2); + + if (cadence->caps2.is_phy_type_dll) + writel_relaxed(t->dll_phy_ctrl, cadence->reg + DLL_PHY_CTRL); + + writel_relaxed(t->phy_ctrl, cadence->reg + PHY_CTRL); + + if (cadence->caps2.is_phy_type_dll) { + writel_relaxed(0, cadence->reg + PHY_TSEL); + writel_relaxed(2, cadence->reg + PHY_DQ_TIMING); + writel_relaxed(t->phy_dqs_timing, + cadence->reg + PHY_DQS_TIMING); + writel_relaxed(t->phy_gate_lpbk_ctrl, + cadence->reg + PHY_GATE_LPBK_CTRL); + writel_relaxed(PHY_DLL_MASTER_CTRL_BYPASS_MODE, + cadence->reg + PHY_DLL_MASTER_CTRL); + writel_relaxed(0, cadence->reg + PHY_DLL_SLAVE_CTRL); + } +} + +static int cadence_nand_select_target(struct nand_chip *chip) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + + if (chip == cadence->selected_chip) + return 0; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + cadence_nand_set_timings(cadence, &cdns_chip->timings); + + cadence_nand_set_ecc_strength(cadence, + cdns_chip->corr_str_idx); + + cadence_nand_set_erase_detection(cadence, true, + chip->ecc.strength); + + cadence->curr_trans_type = -1; + cadence->selected_chip = chip; + + return 0; +} + +static int cadence_nand_erase(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int status; + u8 thread_nr = cdns_chip->cs[chip->cur_cs]; + + cadence_nand_cdma_desc_prepare(cadence, + cdns_chip->cs[chip->cur_cs], + page, 0, 0, + CDMA_CT_ERASE); + status = cadence_nand_cdma_send_and_wait(cadence, thread_nr); + if (status) { + dev_err(cadence->dev, "erase operation failed\n"); + return -EIO; + } + + status = cadence_nand_cdma_finish(cadence); + if (status) + return status; + + return 0; +} + +static int cadence_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip, int oobavail) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + int ret; + + /* + * If .size and .strength are already set (usually by DT), + * check if they are supported by this controller. + */ + if (chip->ecc.size && chip->ecc.strength) + return nand_check_ecc_caps(chip, &cadence->ecc_caps, oobavail); + + /* + * We want .size and .strength closest to the chip's requirement + * unless NAND_ECC_MAXIMIZE is requested. + */ + if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) { + ret = nand_match_ecc_req(chip, &cadence->ecc_caps, oobavail); + if (!ret) + return 0; + } + + /* Max ECC strength is the last thing we can do */ + return nand_maximize_ecc(chip, &cadence->ecc_caps, oobavail); +} + +static int cadence_nand_read_bbm(struct mtd_info *mtd, struct nand_chip *chip, int page, u8 *buf) +{ + int status; + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + + cadence_nand_prepare_data_size(mtd, TT_BBM); + + cadence_nand_set_skip_bytes_conf(cadence, 0, 0, 0); + + /* + * Read only bad block marker from offset + * defined by a memory manufacturer. + */ + status = cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, cadence->buf, NULL, + mtd->oobsize, + 0, DMA_FROM_DEVICE, false); + if (status) { + dev_err(cadence->dev, "read BBM failed\n"); + return -EIO; + } + + memcpy(buf + cdns_chip->bbm_offs, cadence->buf, cdns_chip->bbm_len); + + return 0; +} + +static int cadence_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int status; + u16 marker_val = 0xFFFF; + + status = cadence_nand_select_target(chip); + if (status) + return status; + + cadence_nand_set_skip_bytes_conf(cadence, cdns_chip->bbm_len, + mtd->writesize + + cdns_chip->bbm_offs, + 1); + + if (oob_required) { + marker_val = *(u16 *)(chip->oob_poi + + cdns_chip->bbm_offs); + } else { + /* Set oob data to 0xFF. */ + memset(cadence->buf + mtd->writesize, 0xFF, + cdns_chip->avail_oob_size); + } + + cadence_nand_set_skip_marker_val(cadence, marker_val); + + cadence_nand_prepare_data_size(mtd, TT_MAIN_OOB_AREA_EXT); + + if (cadence_nand_dma_buf_ok(cadence, buf, mtd->writesize) && + cadence->caps2.data_control_supp && !(chip->options & NAND_USE_BOUNCE_BUFFER)) { + u8 *oob; + + if (oob_required) + oob = chip->oob_poi; + else + oob = cadence->buf + mtd->writesize; + + status = cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, (void *)buf, oob, + mtd->writesize, + cdns_chip->avail_oob_size, + DMA_TO_DEVICE, true); + if (status) { + dev_err(cadence->dev, "write page failed\n"); + return -EIO; + } + + return 0; + } + + if (oob_required) { + /* Transfer the data to the oob area. */ + memcpy(cadence->buf + mtd->writesize, chip->oob_poi, + cdns_chip->avail_oob_size); + } + + memcpy(cadence->buf, buf, mtd->writesize); + + cadence_nand_prepare_data_size(mtd, TT_MAIN_OOB_AREAS); + + return cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, cadence->buf, NULL, + mtd->writesize + + cdns_chip->avail_oob_size, + 0, DMA_TO_DEVICE, true); +} + +static int cadence_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + + memset(cadence->buf, 0xFF, mtd->writesize); + + return cadence_nand_write_page(mtd, chip, cadence->buf, 1, page); +} + +static int cadence_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int writesize = mtd->writesize; + int oobsize = mtd->oobsize; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + void *tmp_buf = cadence->buf; + int oob_skip = cdns_chip->bbm_len; + size_t size = writesize + oobsize; + int i, pos, len; + int status; + + status = cadence_nand_select_target(chip); + if (status) + return status; + + /* + * Fill the buffer with 0xff first except the full page transfer. + * This simplifies the logic. + */ + if (!buf || !oob_required) + memset(tmp_buf, 0xff, size); + + cadence_nand_set_skip_bytes_conf(cadence, 0, 0, 0); + + /* Arrange the buffer for syndrome payload/ecc layout. */ + if (buf) { + for (i = 0; i < ecc_steps; i++) { + pos = i * (ecc_size + ecc_bytes); + len = ecc_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(tmp_buf + pos, buf, len); + buf += len; + if (len < ecc_size) { + len = ecc_size - len; + memcpy(tmp_buf + writesize + oob_skip, buf, + len); + buf += len; + } + } + } + + if (oob_required) { + const u8 *oob = chip->oob_poi; + u32 oob_data_offset = (cdns_chip->sector_count - 1) * + (cdns_chip->sector_size + chip->ecc.bytes) + + cdns_chip->sector_size + oob_skip; + + /* BBM at the beginning of the OOB area. */ + memcpy(tmp_buf + writesize, oob, oob_skip); + + /* OOB free. */ + memcpy(tmp_buf + oob_data_offset, oob, + cdns_chip->avail_oob_size); + oob += cdns_chip->avail_oob_size; + + /* OOB ECC. */ + for (i = 0; i < ecc_steps; i++) { + pos = ecc_size + i * (ecc_size + ecc_bytes); + if (i == (ecc_steps - 1)) + pos += cdns_chip->avail_oob_size; + + len = ecc_bytes; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(tmp_buf + pos, oob, len); + oob += len; + if (len < ecc_bytes) { + len = ecc_bytes - len; + memcpy(tmp_buf + writesize + oob_skip, oob, + len); + oob += len; + } + } + } + + cadence_nand_prepare_data_size(mtd, TT_RAW_PAGE); + + return cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, cadence->buf, NULL, + mtd->writesize + + mtd->oobsize, + 0, DMA_TO_DEVICE, false); +} + +static int cadence_nand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return cadence_nand_write_page_raw(mtd, chip, NULL, true, page); +} + +static int cadence_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int status; + int ecc_err_count = 0; + + status = cadence_nand_select_target(chip); + if (status) + return status; + + cadence_nand_set_skip_bytes_conf(cadence, cdns_chip->bbm_len, + mtd->writesize + + cdns_chip->bbm_offs, 1); + + /* + * If data buffer can be accessed by DMA and data_control feature + * is supported then transfer data and oob directly. + */ + if (cadence_nand_dma_buf_ok(cadence, buf, mtd->writesize) && + cadence->caps2.data_control_supp && !(chip->options & NAND_USE_BOUNCE_BUFFER)) { + u8 *oob; + + if (oob_required) + oob = chip->oob_poi; + else + oob = cadence->buf + mtd->writesize; + + cadence_nand_prepare_data_size(mtd, TT_MAIN_OOB_AREA_EXT); + status = cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, buf, oob, + mtd->writesize, + cdns_chip->avail_oob_size, + DMA_FROM_DEVICE, true); + /* Otherwise use bounce buffer. */ + } else { + cadence_nand_prepare_data_size(mtd, TT_MAIN_OOB_AREAS); + status = cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, cadence->buf, + NULL, mtd->writesize + + cdns_chip->avail_oob_size, + 0, DMA_FROM_DEVICE, true); + + memcpy(buf, cadence->buf, mtd->writesize); + if (oob_required) + memcpy(chip->oob_poi, + cadence->buf + mtd->writesize, + mtd->oobsize); + } + + switch (status) { + case STAT_ECC_UNCORR: + mtd->ecc_stats.failed++; + ecc_err_count++; + break; + case STAT_ECC_CORR: + ecc_err_count = FIELD_GET(CDMA_CS_MAXERR, + cadence->cdma_desc->status); + mtd->ecc_stats.corrected += ecc_err_count; + break; + case STAT_ERASED: + case STAT_OK: + break; + default: + dev_err(cadence->dev, "read page failed\n"); + return -EIO; + } + + if (oob_required) + if (cadence_nand_read_bbm(mtd, chip, page, chip->oob_poi)) + return -EIO; + + return ecc_err_count; +} + +static int cadence_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + + return cadence_nand_read_page(mtd, chip, cadence->buf, 1, page); +} + +static int cadence_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int oob_skip = cdns_chip->bbm_len; + int writesize = mtd->writesize; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + void *tmp_buf = cadence->buf; + int i, pos, len; + int status; + + status = cadence_nand_select_target(chip); + if (status) + return status; + + cadence_nand_set_skip_bytes_conf(cadence, 0, 0, 0); + + cadence_nand_prepare_data_size(mtd, TT_RAW_PAGE); + status = cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, cadence->buf, NULL, + mtd->writesize + + mtd->oobsize, + 0, DMA_FROM_DEVICE, false); + + switch (status) { + case STAT_ERASED: + case STAT_OK: + break; + default: + dev_err(cadence->dev, "read raw page failed\n"); + return -EIO; + } + + /* Arrange the buffer for syndrome payload/ecc layout. */ + if (buf) { + for (i = 0; i < ecc_steps; i++) { + pos = i * (ecc_size + ecc_bytes); + len = ecc_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(buf, tmp_buf + pos, len); + buf += len; + if (len < ecc_size) { + len = ecc_size - len; + memcpy(buf, tmp_buf + writesize + oob_skip, + len); + buf += len; + } + } + } + + if (oob_required) { + u8 *oob = chip->oob_poi; + u32 oob_data_offset = (cdns_chip->sector_count - 1) * + (cdns_chip->sector_size + chip->ecc.bytes) + + cdns_chip->sector_size + oob_skip; + + /* OOB free. */ + memcpy(oob, tmp_buf + oob_data_offset, + cdns_chip->avail_oob_size); + + /* BBM at the beginning of the OOB area. */ + memcpy(oob, tmp_buf + writesize, oob_skip); + + oob += cdns_chip->avail_oob_size; + + /* OOB ECC */ + for (i = 0; i < ecc_steps; i++) { + pos = ecc_size + i * (ecc_size + ecc_bytes); + len = ecc_bytes; + + if (i == (ecc_steps - 1)) + pos += cdns_chip->avail_oob_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(oob, tmp_buf + pos, len); + oob += len; + if (len < ecc_bytes) { + len = ecc_bytes - len; + memcpy(oob, tmp_buf + writesize + oob_skip, + len); + oob += len; + } + } + } + return 0; +} + +static int cadence_nand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return cadence_nand_read_page_raw(mtd, chip, NULL, true, page); +} + +static void cadence_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + u8 thread_nr = 0; + u32 sdma_size; + int status; + int len_in_words = len >> 2; + + /* Wait until slave DMA interface is ready to data transfer. */ + status = cadence_nand_wait_on_sdma(cadence, &thread_nr, &sdma_size); + if (status) { + pr_err("Wait on sdma failed:%x\n", status); + hang(); + } + + if (!cadence->caps1->has_dma) { + readsq(cadence->io.virt, buf, len_in_words); + + if (sdma_size > len) { + memcpy(cadence->buf, buf + (len_in_words << 2), + len - (len_in_words << 2)); + readsl(cadence->io.virt, cadence->buf, + sdma_size / 4 - len_in_words); + } + } +} + +static void cadence_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + u8 thread_nr = 0; + u32 sdma_size; + int status; + int len_in_words = len >> 2; + + /* Wait until slave DMA interface is ready to data transfer. */ + status = cadence_nand_wait_on_sdma(cadence, &thread_nr, &sdma_size); + if (status) { + pr_err("Wait on sdma failed:%x\n", status); + hang(); + } + + if (!cadence->caps1->has_dma) { + writesq(cadence->io.virt, buf, len_in_words); + + if (sdma_size > len) { + memcpy(cadence->buf, buf + (len_in_words << 2), + len - (len_in_words << 2)); + writesl(cadence->io.virt, cadence->buf, + sdma_size / 4 - len_in_words); + } + } +} + +static int cadence_nand_cmd_opcode(struct nand_chip *chip, unsigned int op_id) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + u64 mini_ctrl_cmd = 0; + int ret; + + mini_ctrl_cmd |= GCMD_LAY_TWB; + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INSTR, GCMD_LAY_INSTR_CMD); + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INPUT_CMD, op_id); + + ret = cadence_nand_generic_cmd_send(cadence, + cdns_chip->cs[chip->cur_cs], + mini_ctrl_cmd); + + if (ret) + dev_err(cadence->dev, "send cmd %x failed\n", + op_id); + + return ret; +} + +static int cadence_nand_cmd_address(struct nand_chip *chip, + unsigned int naddrs, const u8 *addrs) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + u64 address = 0; + u64 mini_ctrl_cmd = 0; + int ret; + int i; + + mini_ctrl_cmd |= GCMD_LAY_TWB; + + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INSTR, + GCMD_LAY_INSTR_ADDR); + + for (i = 0; i < naddrs; i++) + address |= (u64)addrs[i] << (8 * i); + + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INPUT_ADDR, + address); + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INPUT_ADDR_SIZE, + naddrs - 1); + + ret = cadence_nand_generic_cmd_send(cadence, + cdns_chip->cs[chip->cur_cs], + mini_ctrl_cmd); + + if (ret) + pr_err("send address %llx failed\n", address); + + return ret; +} + +static int cadence_nand_cmd_data(struct nand_chip *chip, + unsigned int len, u8 mode) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + u64 mini_ctrl_cmd = 0; + int ret; + + mini_ctrl_cmd |= GCMD_LAY_TWB; + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INSTR, + GCMD_LAY_INSTR_DATA); + + if (mode) + mini_ctrl_cmd |= FIELD_PREP(GCMD_DIR, GCMD_DIR_WRITE); + + mini_ctrl_cmd |= FIELD_PREP(GCMD_SECT_CNT, 1); + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAST_SIZE, len); + + ret = cadence_nand_generic_cmd_send(cadence, + cdns_chip->cs[chip->cur_cs], + mini_ctrl_cmd); + + if (ret) { + pr_err("send generic data cmd failed\n"); + return ret; + } + + return ret; +} + +static int cadence_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int status; + + status = cadence_nand_wait_for_value(cadence, RBN_SETINGS, + TIMEOUT_US, + BIT(cdns_chip->cs[chip->cur_cs]), + false); + return status; +} + +static int cadence_nand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + + if (section) + return -ERANGE; + + oobregion->offset = cdns_chip->bbm_len; + oobregion->length = cdns_chip->avail_oob_size + - cdns_chip->bbm_len; + + return 0; +} + +static int cadence_nand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + + if (section) + return -ERANGE; + + oobregion->offset = cdns_chip->avail_oob_size; + oobregion->length = chip->ecc.total; + + return 0; +} + +static const struct mtd_ooblayout_ops cadence_nand_ooblayout_ops = { + .rfree = cadence_nand_ooblayout_free, + .ecc = cadence_nand_ooblayout_ecc, +}; + +static int calc_cycl(u32 timing, u32 clock) +{ + if (timing == 0 || clock == 0) + return 0; + + if ((timing % clock) > 0) + return timing / clock; + else + return timing / clock - 1; +} + +/* Calculate max data valid window. */ +static inline u32 calc_tdvw_max(u32 trp_cnt, u32 clk_period, u32 trhoh_min, + u32 board_delay_skew_min, u32 ext_mode) +{ + if (ext_mode == 0) + clk_period /= 2; + + return (trp_cnt + 1) * clk_period + trhoh_min + + board_delay_skew_min; +} + +/* Calculate data valid window. */ +static inline u32 calc_tdvw(u32 trp_cnt, u32 clk_period, u32 trhoh_min, + u32 trea_max, u32 ext_mode) +{ + if (ext_mode == 0) + clk_period /= 2; + + return (trp_cnt + 1) * clk_period + trhoh_min - trea_max; +} + +static inline int of_get_child_count(const ofnode node) +{ + return fdtdec_get_child_count(gd->fdt_blob, ofnode_to_offset(node)); +} + +static int cadence_setup_data_interface(struct mtd_info *mtd, int chipnr, + const struct nand_data_interface *conf) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(mtd_to_nand(mtd)); + const struct nand_sdr_timings *sdr; + struct cadence_nand_timings *t = &cdns_chip->timings; + u32 reg; + u32 board_delay = cadence->board_delay; + u32 clk_period = DIV_ROUND_DOWN_ULL(1000000000000ULL, + cadence->nf_clk_rate); + u32 tceh_cnt, tcs_cnt, tadl_cnt, tccs_cnt; + u32 tfeat_cnt, trhz_cnt, tvdly_cnt; + u32 trhw_cnt, twb_cnt, twh_cnt = 0, twhr_cnt; + u32 twp_cnt = 0, trp_cnt = 0, trh_cnt = 0; + u32 if_skew = cadence->caps1->if_skew; + u32 board_delay_skew_min = board_delay - if_skew; + u32 board_delay_skew_max = board_delay + if_skew; + u32 dqs_sampl_res, phony_dqs_mod; + u32 tdvw, tdvw_min, tdvw_max; + u32 ext_rd_mode, ext_wr_mode; + u32 dll_phy_dqs_timing = 0, phony_dqs_timing = 0, rd_del_sel = 0; + u32 sampling_point; + + sdr = nand_get_sdr_timings(conf); + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + memset(t, 0, sizeof(*t)); + /* Sampling point calculation. */ + if (cadence->caps2.is_phy_type_dll) + phony_dqs_mod = 2; + else + phony_dqs_mod = 1; + + dqs_sampl_res = clk_period / phony_dqs_mod; + + tdvw_min = sdr->tREA_max + board_delay_skew_max; + /* + * The idea of those calculation is to get the optimum value + * for tRP and tRH timings. If it is NOT possible to sample data + * with optimal tRP/tRH settings, the parameters will be extended. + * If clk_period is 50ns (the lowest value) this condition is met + * for SDR timing modes 1, 2, 3, 4 and 5. + * If clk_period is 20ns the condition is met only for SDR timing + * mode 5. + */ + if (sdr->tRC_min <= clk_period && + sdr->tRP_min <= (clk_period / 2) && + sdr->tREH_min <= (clk_period / 2)) { + /* Performance mode. */ + ext_rd_mode = 0; + tdvw = calc_tdvw(trp_cnt, clk_period, sdr->tRHOH_min, + sdr->tREA_max, ext_rd_mode); + tdvw_max = calc_tdvw_max(trp_cnt, clk_period, sdr->tRHOH_min, + board_delay_skew_min, + ext_rd_mode); + /* + * Check if data valid window and sampling point can be found + * and is not on the edge (ie. we have hold margin). + * If not extend the tRP timings. + */ + if (tdvw > 0) { + if (tdvw_max <= tdvw_min || + (tdvw_max % dqs_sampl_res) == 0) { + /* + * No valid sampling point so the RE pulse need + * to be widen widening by half clock cycle. + */ + ext_rd_mode = 1; + } + } else { + /* + * There is no valid window + * to be able to sample data the tRP need to be widen. + * Very safe calculations are performed here. + */ + trp_cnt = (sdr->tREA_max + board_delay_skew_max + + dqs_sampl_res) / clk_period; + ext_rd_mode = 1; + } + + } else { + /* Extended read mode. */ + u32 trh; + + ext_rd_mode = 1; + trp_cnt = calc_cycl(sdr->tRP_min, clk_period); + trh = sdr->tRC_min - ((trp_cnt + 1) * clk_period); + if (sdr->tREH_min >= trh) + trh_cnt = calc_cycl(sdr->tREH_min, clk_period); + else + trh_cnt = calc_cycl(trh, clk_period); + + tdvw = calc_tdvw(trp_cnt, clk_period, sdr->tRHOH_min, + sdr->tREA_max, ext_rd_mode); + /* + * Check if data valid window and sampling point can be found + * or if it is at the edge check if previous is valid + * - if not extend the tRP timings. + */ + if (tdvw > 0) { + tdvw_max = calc_tdvw_max(trp_cnt, clk_period, + sdr->tRHOH_min, + board_delay_skew_min, + ext_rd_mode); + + if ((((tdvw_max / dqs_sampl_res) + * dqs_sampl_res) <= tdvw_min) || + (((tdvw_max % dqs_sampl_res) == 0) && + (((tdvw_max / dqs_sampl_res - 1) + * dqs_sampl_res) <= tdvw_min))) { + /* + * Data valid window width is lower than + * sampling resolution and do not hit any + * sampling point to be sure the sampling point + * will be found the RE low pulse width will be + * extended by one clock cycle. + */ + trp_cnt = trp_cnt + 1; + } + } else { + /* + * There is no valid window to be able to sample data. + * The tRP need to be widen. + * Very safe calculations are performed here. + */ + trp_cnt = (sdr->tREA_max + board_delay_skew_max + + dqs_sampl_res) / clk_period; + } + } + + tdvw_max = calc_tdvw_max(trp_cnt, clk_period, + sdr->tRHOH_min, + board_delay_skew_min, ext_rd_mode); + + if (sdr->tWC_min <= clk_period && + (sdr->tWP_min + if_skew) <= (clk_period / 2) && + (sdr->tWH_min + if_skew) <= (clk_period / 2)) { + ext_wr_mode = 0; + } else { + u32 twh; + + ext_wr_mode = 1; + twp_cnt = calc_cycl(sdr->tWP_min + if_skew, clk_period); + if ((twp_cnt + 1) * clk_period < (sdr->tALS_min + if_skew)) + twp_cnt = calc_cycl(sdr->tALS_min + if_skew, + clk_period); + + twh = (sdr->tWC_min - (twp_cnt + 1) * clk_period); + if (sdr->tWH_min >= twh) + twh = sdr->tWH_min; + + twh_cnt = calc_cycl(twh + if_skew, clk_period); + } + + reg = FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TRH, trh_cnt); + reg |= FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TRP, trp_cnt); + reg |= FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TWH, twh_cnt); + reg |= FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TWP, twp_cnt); + t->async_toggle_timings = reg; + dev_dbg(cadence->dev, "ASYNC_TOGGLE_TIMINGS_SDR\t%x\n", reg); + + tadl_cnt = calc_cycl((sdr->tADL_min + if_skew), clk_period); + tccs_cnt = calc_cycl((sdr->tCCS_min + if_skew), clk_period); + twhr_cnt = calc_cycl((sdr->tWHR_min + if_skew), clk_period); + trhw_cnt = calc_cycl((sdr->tRHW_min + if_skew), clk_period); + reg = FIELD_PREP(TIMINGS0_TADL, tadl_cnt); + + /* + * If timing exceeds delay field in timing register + * then use maximum value. + */ + if (FIELD_FIT(TIMINGS0_TCCS, tccs_cnt)) + reg |= FIELD_PREP(TIMINGS0_TCCS, tccs_cnt); + else + reg |= TIMINGS0_TCCS; + + reg |= FIELD_PREP(TIMINGS0_TWHR, twhr_cnt); + reg |= FIELD_PREP(TIMINGS0_TRHW, trhw_cnt); + t->timings0 = reg; + dev_dbg(cadence->dev, "TIMINGS0_SDR\t%x\n", reg); + + /* The following is related to single signal so skew is not needed. */ + trhz_cnt = calc_cycl(sdr->tRHZ_max, clk_period); + trhz_cnt = trhz_cnt + 1; + twb_cnt = calc_cycl((sdr->tWB_max + board_delay), clk_period); + /* + * Because of the two stage syncflop the value must be increased by 3 + * first value is related with sync, second value is related + * with output if delay. + */ + twb_cnt = twb_cnt + 3 + 5; + /* + * The following is related to the we edge of the random data input + * sequence so skew is not needed. + */ + tvdly_cnt = calc_cycl(500000 + if_skew, clk_period); + reg = FIELD_PREP(TIMINGS1_TRHZ, trhz_cnt); + reg |= FIELD_PREP(TIMINGS1_TWB, twb_cnt); + reg |= FIELD_PREP(TIMINGS1_TVDLY, tvdly_cnt); + t->timings1 = reg; + dev_dbg(cadence->dev, "TIMINGS1_SDR\t%x\n", reg); + + tfeat_cnt = calc_cycl(sdr->tFEAT_max, clk_period); + if (tfeat_cnt < twb_cnt) + tfeat_cnt = twb_cnt; + + tceh_cnt = calc_cycl(sdr->tCEH_min, clk_period); + tcs_cnt = calc_cycl((sdr->tCS_min + if_skew), clk_period); + + reg = FIELD_PREP(TIMINGS2_TFEAT, tfeat_cnt); + reg |= FIELD_PREP(TIMINGS2_CS_HOLD_TIME, tceh_cnt); + reg |= FIELD_PREP(TIMINGS2_CS_SETUP_TIME, tcs_cnt); + t->timings2 = reg; + dev_dbg(cadence->dev, "TIMINGS2_SDR\t%x\n", reg); + + if (cadence->caps2.is_phy_type_dll) { + reg = DLL_PHY_CTRL_DLL_RST_N; + if (ext_wr_mode) + reg |= DLL_PHY_CTRL_EXTENDED_WR_MODE; + if (ext_rd_mode) + reg |= DLL_PHY_CTRL_EXTENDED_RD_MODE; + + reg |= FIELD_PREP(DLL_PHY_CTRL_RS_HIGH_WAIT_CNT, 7); + reg |= FIELD_PREP(DLL_PHY_CTRL_RS_IDLE_CNT, 7); + t->dll_phy_ctrl = reg; + dev_dbg(cadence->dev, "DLL_PHY_CTRL_SDR\t%x\n", reg); + } + + /* Sampling point calculation. */ + if ((tdvw_max % dqs_sampl_res) > 0) + sampling_point = tdvw_max / dqs_sampl_res; + else + sampling_point = (tdvw_max / dqs_sampl_res - 1); + + if (sampling_point * dqs_sampl_res > tdvw_min) { + dll_phy_dqs_timing = + FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_OE_END, 4); + dll_phy_dqs_timing |= PHY_DQS_TIMING_USE_PHONY_DQS; + phony_dqs_timing = sampling_point / phony_dqs_mod; + + if ((sampling_point % 2) > 0) { + dll_phy_dqs_timing |= PHY_DQS_TIMING_PHONY_DQS_SEL; + if ((tdvw_max % dqs_sampl_res) == 0) + /* + * Calculation for sampling point at the edge + * of data and being odd number. + */ + phony_dqs_timing = (tdvw_max / dqs_sampl_res) + / phony_dqs_mod - 1; + + if (!cadence->caps2.is_phy_type_dll) + phony_dqs_timing--; + + } else { + phony_dqs_timing--; + } + rd_del_sel = phony_dqs_timing + 3; + } else { + dev_warn(cadence->dev, + "ERROR : cannot find valid sampling point\n"); + } + + reg = FIELD_PREP(PHY_CTRL_PHONY_DQS, phony_dqs_timing); + if (cadence->caps2.is_phy_type_dll) + reg |= PHY_CTRL_SDR_DQS; + t->phy_ctrl = reg; + dev_dbg(cadence->dev, "PHY_CTRL_REG_SDR\t%x\n", reg); + + if (cadence->caps2.is_phy_type_dll) { + dev_dbg(cadence->dev, "PHY_TSEL_REG_SDR\t%x\n", 0); + dev_dbg(cadence->dev, "PHY_DQ_TIMING_REG_SDR\t%x\n", 2); + dev_dbg(cadence->dev, "PHY_DQS_TIMING_REG_SDR\t%x\n", + dll_phy_dqs_timing); + t->phy_dqs_timing = dll_phy_dqs_timing; + + reg = FIELD_PREP(PHY_GATE_LPBK_CTRL_RDS, rd_del_sel); + dev_dbg(cadence->dev, "PHY_GATE_LPBK_CTRL_REG_SDR\t%x\n", + reg); + t->phy_gate_lpbk_ctrl = reg; + + dev_dbg(cadence->dev, "PHY_DLL_MASTER_CTRL_REG_SDR\t%lx\n", + PHY_DLL_MASTER_CTRL_BYPASS_MODE); + dev_dbg(cadence->dev, "PHY_DLL_SLAVE_CTRL_REG_SDR\t%x\n", 0); + } + return 0; +} + +static int cadence_nand_attach_chip(struct nand_chip *chip) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + static struct nand_ecclayout nand_oob; + u32 ecc_size; + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + if (chip->options & NAND_BUSWIDTH_16) { + ret = cadence_nand_set_access_width16(cadence, true); + if (ret) + return ret; + } + + chip->options |= NAND_USE_BOUNCE_BUFFER; + chip->bbt_options |= NAND_BBT_USE_FLASH; + chip->bbt_options |= NAND_BBT_NO_OOB; + chip->ecc.mode = NAND_ECC_HW_SYNDROME; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + + cdns_chip->bbm_offs = chip->badblockpos; + cdns_chip->bbm_offs &= ~0x01; + /* this value should be even number */ + cdns_chip->bbm_len = 2; + + ret = cadence_ecc_setup(mtd, chip, mtd->oobsize - cdns_chip->bbm_len); + if (ret) { + dev_err(cadence->dev, "ECC configuration failed\n"); + return ret; + } + + dev_dbg(cadence->dev, + "chosen ECC settings: step=%d, strength=%d, bytes=%d\n", + chip->ecc.size, chip->ecc.strength, chip->ecc.bytes); + + /* Error correction configuration. */ + cdns_chip->sector_size = chip->ecc.size; + cdns_chip->sector_count = mtd->writesize / cdns_chip->sector_size; + ecc_size = cdns_chip->sector_count * chip->ecc.bytes; + + cdns_chip->avail_oob_size = mtd->oobsize - ecc_size; + + if (cdns_chip->avail_oob_size > cadence->bch_metadata_size) + cdns_chip->avail_oob_size = cadence->bch_metadata_size; + + if ((cdns_chip->avail_oob_size + cdns_chip->bbm_len + ecc_size) + > mtd->oobsize) + cdns_chip->avail_oob_size -= 4; + + ret = cadence_nand_get_ecc_strength_idx(cadence, chip->ecc.strength); + if (ret < 0) + return -EINVAL; + + cdns_chip->corr_str_idx = (u8)ret; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + cadence_nand_set_ecc_strength(cadence, + cdns_chip->corr_str_idx); + + cadence_nand_set_erase_detection(cadence, true, + chip->ecc.strength); + + dev_dbg(cadence->dev, + "chosen ECC settings: step=%d, strength=%d, bytes=%d\n", + chip->ecc.size, chip->ecc.strength, chip->ecc.bytes); + + /* Override the default read operations. */ + chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS; + chip->ecc.read_page = cadence_nand_read_page; + chip->ecc.read_page_raw = cadence_nand_read_page_raw; + chip->ecc.write_page = cadence_nand_write_page; + chip->ecc.write_page_raw = cadence_nand_write_page_raw; + chip->ecc.read_oob = cadence_nand_read_oob; + chip->ecc.write_oob = cadence_nand_write_oob; + chip->ecc.read_oob_raw = cadence_nand_read_oob_raw; + chip->ecc.write_oob_raw = cadence_nand_write_oob_raw; + chip->erase = cadence_nand_erase; + + if ((mtd->writesize + mtd->oobsize) > cadence->buf_size) + cadence->buf_size = mtd->writesize + mtd->oobsize; + + mtd_set_ooblayout(mtd, &cadence_nand_ooblayout_ops); + + nand_oob.eccbytes = cdns_chip->chip.ecc.bytes; + cdns_chip->chip.ecc.layout = &nand_oob; + + return 0; +} + +/* Dummy implementation: we don't support multiple chips */ +static void cadence_nand_select_chip(struct mtd_info *mtd, int chipnr) +{ + switch (chipnr) { + case -1: + case 0: + break; + + default: + WARN_ON(chipnr); + } +} + +static int cadence_nand_status(struct mtd_info *mtd, unsigned int command) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; + + ret = cadence_nand_cmd_opcode(chip, command); + if (ret) + return ret; + + ret = cadence_nand_cmd_data(chip, 1, GCMD_DIR_READ); + if (ret) + return ret; + + return 0; +} + +static int cadence_nand_readid(struct mtd_info *mtd, int offset_in_page, unsigned int command) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u8 addrs = (u8)offset_in_page; + int ret = 0; + + ret = cadence_nand_cmd_opcode(chip, command); + if (ret) + return ret; + + ret = cadence_nand_cmd_address(chip, ONE_CYCLE, &addrs); + if (ret) + return ret; + + ret = cadence_nand_cmd_data(chip, 8, GCMD_DIR_READ); + if (ret) + return ret; + + return 0; +} + +static int cadence_nand_param(struct mtd_info *mtd, u8 offset_in_page, unsigned int command) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; + + ret = cadence_nand_cmd_opcode(chip, command); + if (ret) + return ret; + + ret = cadence_nand_cmd_address(chip, ONE_CYCLE, &offset_in_page); + if (ret) + return ret; + + ret = cadence_nand_waitfunc(mtd, chip); + if (ret) + return ret; + + ret = cadence_nand_cmd_data(chip, sizeof(struct nand_jedec_params), GCMD_DIR_READ); + if (ret) + return ret; + + return 0; +} + +static int cadence_nand_reset(struct mtd_info *mtd, unsigned int command) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; + + ret = cadence_nand_cmd_opcode(chip, command); + if (ret) + return ret; + + ret = cadence_nand_waitfunc(mtd, chip); + if (ret) + return ret; + + return 0; +} + +static int cadence_nand_features(struct mtd_info *mtd, u8 offset_in_page, u32 command) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; + + ret = cadence_nand_cmd_opcode(chip, command); + if (ret) + return ret; + + ret = cadence_nand_cmd_address(chip, ONE_CYCLE, &offset_in_page); + if (ret) + return ret; + + if (command == NAND_CMD_GET_FEATURES) + ret = cadence_nand_cmd_data(chip, ONFI_SUBFEATURE_PARAM_LEN, + GCMD_DIR_READ); + else + ret = cadence_nand_cmd_data(chip, ONFI_SUBFEATURE_PARAM_LEN, + GCMD_DIR_WRITE); + + return ret; +} + +static void cadence_nand_cmdfunc(struct mtd_info *mtd, unsigned int command, + int offset_in_page, int page) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + int ret = 0; + + cadence->cmd = command; + switch (command) { + case NAND_CMD_STATUS: + ret = cadence_nand_status(mtd, command); + break; + + case NAND_CMD_READID: + ret = cadence_nand_readid(mtd, offset_in_page, command); + break; + + case NAND_CMD_PARAM: + ret = cadence_nand_param(mtd, offset_in_page, command); + break; + + case NAND_CMD_RESET: + ret = cadence_nand_reset(mtd, command); + break; + + case NAND_CMD_SET_FEATURES: + case NAND_CMD_GET_FEATURES: + ret = cadence_nand_features(mtd, offset_in_page, command); + break; + /* + * ecc will override other command for read, write and erase + */ + default: + break; + } + + if (cadence->cmd == NAND_CMD_RESET) { + ret = cadence_nand_select_target(chip); + if (ret) + dev_err(cadence->dev, "Chip select failure after reset\n"); + } + + if (ret != 0) + printf("ERROR:%s:command:0x%x\n", __func__, cadence->cmd); +} + +static int cadence_nand_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + return 0; +} + +static u8 cadence_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + u32 size = 1; + u8 val; + + if (cadence->buf_index == 0) { + if (cadence->cmd == NAND_CMD_READID) + size = 8; + else if (cadence->cmd == NAND_CMD_PARAM) + size = sizeof(struct nand_jedec_params); + else if (cadence->cmd == NAND_CMD_GET_FEATURES) + size = ONFI_SUBFEATURE_PARAM_LEN; + + cadence_nand_read_buf(mtd, &cadence->buf[0], size); + } + + val = *(&cadence->buf[0] + cadence->buf_index); + cadence->buf_index++; + + return val; +} + +static void cadence_nand_write_byte(struct mtd_info *mtd, u8 byte) +{ + cadence_nand_write_buf(mtd, &byte, 1); +} + +static int cadence_nand_chip_init(struct cadence_nand_info *cadence, ofnode node) +{ + struct cdns_nand_chip *cdns_chip; + struct nand_chip *chip; + struct mtd_info *mtd; + int ret, i; + int nsels; + u32 cs; + + if (!ofnode_get_property(node, "reg", &nsels)) + return -ENODEV; + + nsels /= sizeof(u32); + if (nsels <= 0) { + dev_err(cadence->dev, "invalid reg property size %d\n", nsels); + return -EINVAL; + } + + cdns_chip = devm_kzalloc(cadence->dev, sizeof(*cdns_chip) + + (nsels * sizeof(u8)), GFP_KERNEL); + if (!cdns_chip) + return -ENODEV; + + cdns_chip->nsels = nsels; + for (i = 0; i < nsels; i++) { + /* Retrieve CS id. */ + ret = ofnode_read_u32_index(node, "reg", i, &cs); + if (ret) { + dev_err(cadence->dev, + "could not retrieve reg property: %d\n", + ret); + goto free_buf; + } + + if (cs >= cadence->caps2.max_banks) { + dev_err(cadence->dev, + "invalid reg value: %u (max CS = %d)\n", + cs, cadence->caps2.max_banks); + ret = -EINVAL; + goto free_buf; + } + + if (test_and_set_bit(cs, &cadence->assigned_cs)) { + dev_err(cadence->dev, + "CS %d already assigned\n", cs); + ret = -EINVAL; + goto free_buf; + } + + cdns_chip->cs[i] = cs; + } + + chip = &cdns_chip->chip; + chip->controller = &cadence->controller; + nand_set_flash_node(chip, node); + mtd = nand_to_mtd(chip); + mtd->dev->parent = cadence->dev; + + chip->options |= NAND_BUSWIDTH_AUTO; + chip->select_chip = cadence_nand_select_chip; + chip->cmdfunc = cadence_nand_cmdfunc; + chip->dev_ready = cadence_nand_dev_ready; + chip->read_byte = cadence_nand_read_byte; + chip->write_byte = cadence_nand_write_byte; + chip->waitfunc = cadence_nand_waitfunc; + chip->read_buf = cadence_nand_read_buf; + chip->write_buf = cadence_nand_write_buf; + chip->setup_data_interface = cadence_setup_data_interface; + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) { + dev_err(cadence->dev, "Chip identification failure\n"); + goto free_buf; + } + + ret = cadence_nand_attach_chip(chip); + if (ret) { + dev_err(cadence->dev, "Chip not able to attached\n"); + goto free_buf; + } + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(cadence->dev, "could not scan the nand chip\n"); + goto free_buf; + } + + ret = nand_register(0, mtd); + if (ret) { + dev_err(cadence->dev, "Failed to register MTD: %d\n", ret); + goto free_buf; + } + + return 0; + +free_buf: + devm_kfree(cadence->dev, cdns_chip); + return ret; +} + +static int cadence_nand_chips_init(struct cadence_nand_info *cadence) +{ + struct udevice *dev = cadence->dev; + ofnode node = dev_ofnode(dev); + ofnode nand_node; + int max_cs = cadence->caps2.max_banks; + int nchips, ret; + + nchips = of_get_child_count(node); + + if (nchips > max_cs) { + dev_err(cadence->dev, + "too many NAND chips: %d (max = %d CS)\n", + nchips, max_cs); + return -EINVAL; + } + + ofnode_for_each_subnode(nand_node, node) { + ret = cadence_nand_chip_init(cadence, nand_node); + if (ret) + return ret; + } + + return 0; +} + +static int cadence_nand_init(struct cadence_nand_info *cadence) +{ + int ret; + + cadence->cdma_desc = dma_alloc_coherent(sizeof(*cadence->cdma_desc), + (unsigned long *)&cadence->dma_cdma_desc); + if (!cadence->cdma_desc) + return -ENOMEM; + + cadence->buf_size = SZ_16K; + cadence->buf = kmalloc(cadence->buf_size, GFP_KERNEL); + if (!cadence->buf) { + ret = -ENOMEM; + goto free_buf_desc; + } + + //Hardware initialization + ret = cadence_nand_hw_init(cadence); + if (ret) + goto free_buf; + + cadence->curr_corr_str_idx = 0xFF; + + ret = cadence_nand_chips_init(cadence); + if (ret) { + dev_err(cadence->dev, "Failed to register MTD: %d\n", + ret); + goto free_buf; + } + + kfree(cadence->buf); + cadence->buf = kzalloc(cadence->buf_size, GFP_KERNEL); + if (!cadence->buf) { + ret = -ENOMEM; + goto free_buf_desc; + } + + return 0; + +free_buf: + kfree(cadence->buf); + +free_buf_desc: + dma_free_coherent(cadence->cdma_desc); + + return ret; +} + +static const struct cadence_nand_dt_devdata cadence_nand_default = { + .if_skew = 0, + .has_dma = 0, +}; + +static const struct udevice_id cadence_nand_dt_ids[] = { + { + .compatible = "cdns,nand", + .data = (unsigned long)&cadence_nand_default + }, {} +}; + +static int cadence_nand_dt_probe(struct udevice *dev) +{ + struct cadence_nand_info *cadence = dev_get_priv(dev); + const struct udevice_id *of_id; + const struct cadence_nand_dt_devdata *devdata; + struct resource res; + int ret; + u32 val; + + if (!dev) { + dev_warn(dev, "Device ptr null\n"); + return -EINVAL; + } + + of_id = &cadence_nand_dt_ids[0]; + devdata = (struct cadence_nand_dt_devdata *)of_id->data; + + cadence->caps1 = devdata; + cadence->dev = dev; + + ret = clk_get_by_index(dev, 0, &cadence->clk); + if (ret) + return ret; + + ret = clk_enable(&cadence->clk); + if (ret && ret != -ENOSYS && ret != -ENOMEM) { + dev_err(dev, "failed to enable clock\n"); + return ret; + } + cadence->nf_clk_rate = clk_get_rate(&cadence->clk); + + ret = reset_get_by_index(dev, 1, &cadence->softphy_reset); + if (ret) { + if (ret != -ENOMEM) + dev_warn(dev, "Can't get softphy_reset: %d\n", ret); + } else { + reset_deassert(&cadence->softphy_reset); + } + + ret = reset_get_by_index(dev, 0, &cadence->nand_reset); + if (ret) { + if (ret != -ENOMEM) + dev_warn(dev, "Can't get nand_reset: %d\n", ret); + } else { + reset_deassert(&cadence->nand_reset); + } + + ret = dev_read_resource_byname(dev, "reg", &res); + if (ret) + return ret; + cadence->reg = devm_ioremap(dev, res.start, resource_size(&res)); + + ret = dev_read_resource_byname(dev, "sdma", &res); + if (ret) + return ret; + cadence->io.dma = res.start; + cadence->io.virt = devm_ioremap(dev, res.start, resource_size(&res)); + + ret = ofnode_read_u32(dev_ofnode(dev->parent), + "cdns,board-delay-ps", &val); + if (ret) { + val = 4830; + dev_info(cadence->dev, + "missing cdns,board-delay-ps property, %d was set\n", + val); + } + cadence->board_delay = val; + + ret = cadence_nand_init(cadence); + if (ret) + return ret; + + return 0; +} + +U_BOOT_DRIVER(cadence_nand_dt) = { + .name = "cadence-nand-dt", + .id = UCLASS_MTD, + .of_match = cadence_nand_dt_ids, + .probe = cadence_nand_dt_probe, + .priv_auto = sizeof(struct cadence_nand_info), +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MTD, + DM_DRIVER_GET(cadence_nand_dt), + &dev); + if (ret && ret != -ENODEV) + pr_err("Failed to initialize Cadence NAND controller. (error %d)\n", + ret); +} diff --git a/drivers/mtd/nand/raw/cadence_spl.c b/drivers/mtd/nand/raw/cadence_spl.c new file mode 100644 index 00000000000..17058e49faa --- /dev/null +++ b/drivers/mtd/nand/raw/cadence_spl.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Intel Corporation <www.intel.com> + */ + +#include <cadence-nand.h> +#include <dm.h> +#include <hang.h> +#include <nand.h> +#include <system-constants.h> + +/* Unselect after operation */ +void nand_deselect(void) +{ + struct mtd_info *mtd; + struct nand_chip *chip; + + mtd = get_nand_dev_by_index(nand_curr_device); + if (!mtd) + hang(); + chip = mtd_to_nand(mtd); + + if (chip->select_chip) + chip->select_chip(mtd, -1); +} + +static int nand_is_bad_block(int block) +{ + struct mtd_info *mtd; + struct nand_chip *chip; + loff_t ofs = block * CONFIG_SYS_NAND_BLOCK_SIZE; + + mtd = get_nand_dev_by_index(nand_curr_device); + if (!mtd) + hang(); + chip = mtd_to_nand(mtd); + + return chip->block_bad(mtd, ofs); +} + +static int nand_read_page(int block, int page, uchar *dst) +{ + struct mtd_info *mtd; + 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; + + mtd = get_nand_dev_by_index(nand_curr_device); + if (!mtd) + hang(); + + ret = nand_read(mtd, ofs, &len, dst); + if (ret) + printf("nand_read failed %d\n", ret); + + return ret; +} +#include "nand_spl_loaders.c" diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 28c851f103b..7e683f49c5e 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -618,9 +618,7 @@ static int meson_nfc_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *ch memcpy(meson_chip->data_buf, buf, mtd->writesize); memset(meson_chip->info_buf, 0, chip->ecc.steps * PER_INFO_BYTE); - - if (oob_required) - meson_nfc_set_user_byte(chip, chip->oob_poi); + meson_nfc_set_user_byte(chip, chip->oob_poi); return meson_nfc_write_page_sub(chip, page, false); } diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 1b65c6f6443..d3d1b93947b 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -306,6 +306,35 @@ void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) ioread16_rep(chip->IO_ADDR_R, p, len >> 1); } +/* + * nand_bbm_get_next_page - Get the next page for bad block markers + * @chip: The NAND chip + * @page: First page to start checking for bad block marker usage + * + * Returns an integer that corresponds to the page offset within a block, for + * a page that is used to store bad block markers. If no more pages are + * available, -EINVAL is returned. + */ +int nand_bbm_get_next_page(struct nand_chip *chip, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int last_page = ((mtd->erasesize - mtd->writesize) >> + chip->page_shift) & chip->pagemask; + unsigned int bbm_flags = NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE + | NAND_BBM_LASTPAGE; + + if (page == 0 && !(chip->options & bbm_flags)) + return 0; + if (page == 0 && chip->options & NAND_BBM_FIRSTPAGE) + return 0; + if (page <= 1 && chip->options & NAND_BBM_SECONDPAGE) + return 1; + if (page <= last_page && chip->options & NAND_BBM_LASTPAGE) + return last_page; + + return -EINVAL; +} + /** * nand_block_bad - [DEFAULT] Read bad block marker from the chip * @mtd: MTD device structure @@ -315,40 +344,32 @@ void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) */ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs) { - int page, res = 0, i = 0; struct nand_chip *chip = mtd_to_nand(mtd); - u16 bad; + int first_page, page_offset; + int res; + u8 bad; - if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) - ofs += mtd->erasesize - mtd->writesize; + first_page = (int)(ofs >> chip->page_shift) & chip->pagemask; + page_offset = nand_bbm_get_next_page(chip, 0); - page = (int)(ofs >> chip->page_shift) & chip->pagemask; + while (page_offset >= 0) { + res = chip->ecc.read_oob(mtd, chip, first_page + page_offset); + if (res < 0) + return res; - do { - if (chip->options & NAND_BUSWIDTH_16) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, - chip->badblockpos & 0xFE, page); - bad = cpu_to_le16(chip->read_word(mtd)); - if (chip->badblockpos & 0x1) - bad >>= 8; - else - bad &= 0xFF; - } else { - chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, - page); - bad = chip->read_byte(mtd); - } + bad = chip->oob_poi[chip->badblockpos]; if (likely(chip->badblockbits == 8)) res = bad != 0xFF; else res = hweight8(bad) < chip->badblockbits; - ofs += mtd->writesize; - page = (int)(ofs >> chip->page_shift) & chip->pagemask; - i++; - } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)); + if (res) + return res; - return res; + page_offset = nand_bbm_get_next_page(chip, page_offset + 1); + } + + return 0; } /** @@ -774,6 +795,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, NAND_NCE | NAND_CTRL_CHANGE); /* This applies to read commands */ + fallthrough; default: /* * If we don't have access to the busy pin, we apply the given @@ -4974,6 +4996,7 @@ int nand_scan_tail(struct mtd_info *mtd) if (!ecc->read_page) ecc->read_page = nand_read_page_hwecc_oob_first; + fallthrough; case NAND_ECC_HW: /* Use standard hwecc read page function? */ if (!ecc->read_page) @@ -4993,6 +5016,7 @@ int nand_scan_tail(struct mtd_info *mtd) if (!ecc->write_subpage && ecc->hwctl && ecc->calculate) ecc->write_subpage = nand_write_subpage_hwecc; + fallthrough; case NAND_ECC_HW_SYNDROME: if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) && (!ecc->read_page || @@ -5027,6 +5051,7 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->size, mtd->writesize); ecc->mode = NAND_ECC_SOFT; + fallthrough; case NAND_ECC_SOFT: ecc->calculate = nand_calculate_ecc; ecc->correct = nand_correct_data; diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index f5ddfbf4b83..3a1e7e18736 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -941,6 +941,19 @@ spinand_select_op_variant(struct spinand_device *spinand, return NULL; } +static int spinand_setup_slave(struct spinand_device *spinand, + const struct spinand_info *spinand_info) +{ + struct spi_slave *slave = spinand->slave; + struct udevice *bus = slave->dev->parent; + struct dm_spi_ops *ops = spi_get_ops(bus); + + if (!ops->setup_for_spinand) + return 0; + + return ops->setup_for_spinand(slave, spinand_info); +} + /** * spinand_match_and_init() - Try to find a match between a device ID and an * entry in a spinand_info table @@ -964,6 +977,7 @@ int spinand_match_and_init(struct spinand_device *spinand, u8 *id = spinand->id.data; struct nand_device *nand = spinand_to_nand(spinand); unsigned int i; + int ret; for (i = 0; i < table_size; i++) { const struct spinand_info *info = &table[i]; @@ -975,6 +989,10 @@ int spinand_match_and_init(struct spinand_device *spinand, if (memcmp(id + 1, info->devid.id, info->devid.len)) continue; + ret = spinand_setup_slave(spinand, info); + if (ret) + return ret; + nand->memorg = table[i].memorg; nand->eccreq = table[i].eccreq; spinand->eccinfo = table[i].eccinfo; diff --git a/drivers/mtd/spi/spi-nor-tiny.c b/drivers/mtd/spi/spi-nor-tiny.c index 5755c5eed29..23de64a1520 100644 --- a/drivers/mtd/spi/spi-nor-tiny.c +++ b/drivers/mtd/spi/spi-nor-tiny.c @@ -219,6 +219,7 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, case SNOR_MFR_MICRON: /* Some Micron need WREN command; all will accept it */ need_wren = true; + fallthrough; case SNOR_MFR_MACRONIX: case SNOR_MFR_WINBOND: if (need_wren) diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c index 2ef8fde3d32..306da509ec4 100644 --- a/drivers/mtd/ubi/attach.c +++ b/drivers/mtd/ubi/attach.c @@ -934,6 +934,7 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai, * be a result of power cut during erasure. */ ai->maybe_bad_peb_count += 1; + fallthrough; case UBI_IO_BAD_HDR: if (ec_err) /* diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 104537784ab..50e43928af0 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -1400,12 +1400,15 @@ static int __init bytes_str_to_int(const char *str) switch (*endp) { case 'G': result *= 1024; + fallthrough; case 'M': result *= 1024; + fallthrough; case 'K': result *= 1024; if (endp[1] == 'i' && endp[2] == 'B') endp += 2; + fallthrough; case '\0': break; default: diff --git a/drivers/mtd/ubi/part.c b/drivers/mtd/ubi/part.c index 13d1f165c30..6c017eb7299 100644 --- a/drivers/mtd/ubi/part.c +++ b/drivers/mtd/ubi/part.c @@ -47,7 +47,7 @@ static int __maybe_unused part_get_info_ubi(struct blk_desc *dev_desc, int part_ */ vol = ubi_get_volume_by_index(part_idx - 1); if (!vol) - return 0; + return -ENOENT; snprintf(info->name, PART_NAME_LEN, vol->name); diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index f15ee4f833f..2b95eb02177 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -10,6 +10,16 @@ config MULTIPLEXER if MULTIPLEXER +config SPL_MUX_MMIO + bool "MMIO register bitfield-controlled Multiplexer" + depends on MULTIPLEXER && SYSCON + help + MMIO register bitfield-controlled Multiplexer controller. + + The driver builds multiplexer controllers for bitfields in a syscon + register. For N bit wide bitfields, there will be 2^N possible + multiplexer states. + config MUX_MMIO bool "MMIO register bitfield-controlled Multiplexer" depends on MULTIPLEXER && SYSCON diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile index 63770e12bd1..b61f713c301 100644 --- a/drivers/mux/Makefile +++ b/drivers/mux/Makefile @@ -4,4 +4,4 @@ # Jean-Jacques Hiblot <jjhiblot@ti.com> obj-$(CONFIG_MULTIPLEXER) += mux-uclass.o -obj-$(CONFIG_$(XPL_)MUX_MMIO) += mmio.o +obj-$(CONFIG_$(PHASE_)MUX_MMIO) += mmio.o diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 1563404ca17..4434d364777 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -48,7 +48,6 @@ config DM_DSA bool "Enable Driver Model for DSA switches" depends on DM_MDIO depends on PHY_FIXED - depends on !NET_LWIP help Enable driver model for DSA switches @@ -122,6 +121,14 @@ config AG7XXX This driver supports the Atheros AG7xxx Ethernet MAC. This MAC is present in the Atheros AR7xxx, AR9xxx and QCA9xxx MIPS chips. +config AIROHA_ETH + bool "Airoha Ethernet QDMA Driver" + depends on ARCH_AIROHA + select PHYLIB + select DM_RESET + help + This Driver support Airoha Ethernet QDMA Driver + Say Y to enable support for the Airoha Ethernet QDMA. config ALTERA_TSE bool "Altera Triple-Speed Ethernet MAC support" @@ -237,6 +244,13 @@ config DWC_ETH_QOS Of Service) IP block. The IP supports many options for bus type, clocking/reset structure, and feature list. +config DWC_ETH_QOS_ADI + bool "Synopsys DWC Ethernet QOS device support for ADI SC59x-64 parts" + depends on DWC_ETH_QOS + help + The Synopsis Designware Ethernet QoS IP block with the specific + configuration used in the ADI ADSP-SC59X 64 bit SoCs + config DWC_ETH_QOS_IMX bool "Synopsys DWC Ethernet QOS device support for IMX" depends on DWC_ETH_QOS @@ -343,7 +357,7 @@ config ESSEDMA config ETH_SANDBOX depends on SANDBOX - depends on NET + depends on NET || NET_LWIP default y bool "Sandbox: Mocked Ethernet driver" help @@ -352,17 +366,6 @@ config ETH_SANDBOX This driver is particularly useful in the test/dm/eth.c tests -config ETH_SANDBOX_LWIP - depends on SANDBOX - depends on NET_LWIP - default y - bool "Sandbox: Mocked Ethernet driver (for NET_LWIP)" - help - This driver is meant as a replacement for ETH_SANDBOX when - the network stack is NET_LWIP rather than NET. It currently - does nothing, i.e. it drops the sent packets and never receives - data. - config ETH_SANDBOX_RAW depends on SANDBOX depends on NET @@ -852,6 +855,7 @@ config RENESAS_ETHER_SWITCH config RENESAS_RAVB bool "Renesas Ethernet AVB MAC" depends on RCAR_64 + select BITBANGMII select PHYLIB select PHY_ETHERNET_ID help diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 80d70212971..67bba3a8536 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_AG7XXX) += ag7xxx.o +obj-$(CONFIG_AIROHA_ETH) += airoha_eth.o obj-$(CONFIG_ALTERA_TSE) += altera_tse.o obj-$(CONFIG_ASPEED_MDIO) += aspeed_mdio.o obj-$(CONFIG_BCM6348_ETH) += bcm6348-eth.o @@ -19,6 +20,7 @@ obj-$(CONFIG_DM_ETH_PHY) += eth-phy-uclass.o obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o obj-$(CONFIG_DSA_SANDBOX) += dsa_sandbox.o obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o +obj-$(CONFIG_DWC_ETH_QOS_ADI) += dwc_eth_qos_adi.o obj-$(CONFIG_DWC_ETH_QOS_IMX) += dwc_eth_qos_imx.o obj-$(CONFIG_DWC_ETH_QOS_INTEL) += dwc_eth_qos_intel.o obj-$(CONFIG_DWC_ETH_QOS_ROCKCHIP) += dwc_eth_qos_rockchip.o @@ -39,7 +41,6 @@ obj-$(CONFIG_ETH_DESIGNWARE_SOCFPGA) += dwmac_socfpga.o obj-$(CONFIG_ETH_SANDBOX) += sandbox.o obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw-bus.o obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw.o -obj-$(CONFIG_ETH_SANDBOX_LWIP) += sandbox-lwip.o obj-$(CONFIG_FEC_MXC) += fec_mxc.o obj-$(CONFIG_FMAN_ENET) += fm/ obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o diff --git a/drivers/net/airoha_eth.c b/drivers/net/airoha_eth.c new file mode 100644 index 00000000000..7e35e1fd41d --- /dev/null +++ b/drivers/net/airoha_eth.c @@ -0,0 +1,948 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Based on Linux airoha_eth.c majorly rewritten + * and simplified for U-Boot usage for single TX/RX ring. + * + * Copyright (c) 2024 AIROHA Inc + * Author: Lorenzo Bianconi <lorenzo@kernel.org> + * Christian Marangi <ansuelsmth@gmail.org> + */ + +#include <dm.h> +#include <dm/devres.h> +#include <mapmem.h> +#include <net.h> +#include <regmap.h> +#include <reset.h> +#include <syscon.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/time.h> + +#define AIROHA_MAX_NUM_GDM_PORTS 1 +#define AIROHA_MAX_NUM_QDMA 1 +#define AIROHA_MAX_NUM_RSTS 3 +#define AIROHA_MAX_NUM_XSI_RSTS 4 + +#define AIROHA_MAX_PACKET_SIZE 2048 +#define AIROHA_NUM_TX_RING 1 +#define AIROHA_NUM_RX_RING 1 +#define AIROHA_NUM_TX_IRQ 1 +#define HW_DSCP_NUM 32 +#define IRQ_QUEUE_LEN 1 +#define TX_DSCP_NUM 16 +#define RX_DSCP_NUM PKTBUFSRX + +/* SCU */ +#define SCU_SHARE_FEMEM_SEL 0x958 + +/* SWITCH */ +#define SWITCH_MFC 0x10 +#define SWITCH_BC_FFP GENMASK(31, 24) +#define SWITCH_UNM_FFP GENMASK(23, 16) +#define SWITCH_UNU_FFP GENMASK(15, 8) +#define SWITCH_PMCR(_n) 0x3000 + ((_n) * 0x100) +#define SWITCH_IPG_CFG GENMASK(19, 18) +#define SWITCH_IPG_CFG_NORMAL FIELD_PREP(SWITCH_IPG_CFG, 0x0) +#define SWITCH_IPG_CFG_SHORT FIELD_PREP(SWITCH_IPG_CFG, 0x1) +#define SWITCH_IPG_CFG_SHRINK FIELD_PREP(SWITCH_IPG_CFG, 0x2) +#define SWITCH_MAC_MODE BIT(16) +#define SWITCH_FORCE_MODE BIT(15) +#define SWITCH_MAC_TX_EN BIT(14) +#define SWITCH_MAC_RX_EN BIT(13) +#define SWITCH_BKOFF_EN BIT(9) +#define SWITCH_BKPR_EN BIT(8) +#define SWITCH_FORCE_RX_FC BIT(5) +#define SWITCH_FORCE_TX_FC BIT(4) +#define SWITCH_FORCE_SPD GENMASK(3, 2) +#define SWITCH_FORCE_SPD_10 FIELD_PREP(SWITCH_FORCE_SPD, 0x0) +#define SWITCH_FORCE_SPD_100 FIELD_PREP(SWITCH_FORCE_SPD, 0x1) +#define SWITCH_FORCE_SPD_1000 FIELD_PREP(SWITCH_FORCE_SPD, 0x2) +#define SWITCH_FORCE_DPX BIT(1) +#define SWITCH_FORCE_LNK BIT(0) +#define SWITCH_SMACCR0 0x30e4 +#define SMACCR0_MAC2 GENMASK(31, 24) +#define SMACCR0_MAC3 GENMASK(23, 16) +#define SMACCR0_MAC4 GENMASK(15, 8) +#define SMACCR0_MAC5 GENMASK(7, 0) +#define SWITCH_SMACCR1 0x30e8 +#define SMACCR1_MAC0 GENMASK(15, 8) +#define SMACCR1_MAC1 GENMASK(7, 0) +#define SWITCH_PHY_POLL 0x7018 +#define SWITCH_PHY_AP_EN GENMASK(30, 24) +#define SWITCH_EEE_POLL_EN GENMASK(22, 16) +#define SWITCH_PHY_PRE_EN BIT(15) +#define SWITCH_PHY_END_ADDR GENMASK(12, 8) +#define SWITCH_PHY_ST_ADDR GENMASK(4, 0) + +/* FE */ +#define PSE_BASE 0x0100 +#define CSR_IFC_BASE 0x0200 +#define CDM1_BASE 0x0400 +#define GDM1_BASE 0x0500 +#define PPE1_BASE 0x0c00 + +#define CDM2_BASE 0x1400 +#define GDM2_BASE 0x1500 + +#define GDM3_BASE 0x1100 +#define GDM4_BASE 0x2500 + +#define GDM_BASE(_n) \ + ((_n) == 4 ? GDM4_BASE : \ + (_n) == 3 ? GDM3_BASE : \ + (_n) == 2 ? GDM2_BASE : GDM1_BASE) + +#define REG_GDM_FWD_CFG(_n) GDM_BASE(_n) +#define GDM_DROP_CRC_ERR BIT(23) +#define GDM_IP4_CKSUM BIT(22) +#define GDM_TCP_CKSUM BIT(21) +#define GDM_UDP_CKSUM BIT(20) +#define GDM_UCFQ_MASK GENMASK(15, 12) +#define GDM_BCFQ_MASK GENMASK(11, 8) +#define GDM_MCFQ_MASK GENMASK(7, 4) +#define GDM_OCFQ_MASK GENMASK(3, 0) + +/* QDMA */ +#define REG_QDMA_GLOBAL_CFG 0x0004 +#define GLOBAL_CFG_RX_2B_OFFSET_MASK BIT(31) +#define GLOBAL_CFG_DMA_PREFERENCE_MASK GENMASK(30, 29) +#define GLOBAL_CFG_CPU_TXR_RR_MASK BIT(28) +#define GLOBAL_CFG_DSCP_BYTE_SWAP_MASK BIT(27) +#define GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK BIT(26) +#define GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK BIT(25) +#define GLOBAL_CFG_OAM_MODIFY_MASK BIT(24) +#define GLOBAL_CFG_RESET_MASK BIT(23) +#define GLOBAL_CFG_RESET_DONE_MASK BIT(22) +#define GLOBAL_CFG_MULTICAST_EN_MASK BIT(21) +#define GLOBAL_CFG_IRQ1_EN_MASK BIT(20) +#define GLOBAL_CFG_IRQ0_EN_MASK BIT(19) +#define GLOBAL_CFG_LOOPCNT_EN_MASK BIT(18) +#define GLOBAL_CFG_RD_BYPASS_WR_MASK BIT(17) +#define GLOBAL_CFG_QDMA_LOOPBACK_MASK BIT(16) +#define GLOBAL_CFG_LPBK_RXQ_SEL_MASK GENMASK(13, 8) +#define GLOBAL_CFG_CHECK_DONE_MASK BIT(7) +#define GLOBAL_CFG_TX_WB_DONE_MASK BIT(6) +#define GLOBAL_CFG_MAX_ISSUE_NUM_MASK GENMASK(5, 4) +#define GLOBAL_CFG_RX_DMA_BUSY_MASK BIT(3) +#define GLOBAL_CFG_RX_DMA_EN_MASK BIT(2) +#define GLOBAL_CFG_TX_DMA_BUSY_MASK BIT(1) +#define GLOBAL_CFG_TX_DMA_EN_MASK BIT(0) + +#define REG_FWD_DSCP_BASE 0x0010 +#define REG_FWD_BUF_BASE 0x0014 + +#define REG_HW_FWD_DSCP_CFG 0x0018 +#define HW_FWD_DSCP_PAYLOAD_SIZE_MASK GENMASK(29, 28) +#define HW_FWD_DSCP_SCATTER_LEN_MASK GENMASK(17, 16) +#define HW_FWD_DSCP_MIN_SCATTER_LEN_MASK GENMASK(15, 0) + +#define REG_INT_STATUS(_n) \ + (((_n) == 4) ? 0x0730 : \ + ((_n) == 3) ? 0x0724 : \ + ((_n) == 2) ? 0x0720 : \ + ((_n) == 1) ? 0x0024 : 0x0020) + +#define REG_TX_IRQ_BASE(_n) ((_n) ? 0x0048 : 0x0050) + +#define REG_TX_IRQ_CFG(_n) ((_n) ? 0x004c : 0x0054) +#define TX_IRQ_THR_MASK GENMASK(27, 16) +#define TX_IRQ_DEPTH_MASK GENMASK(11, 0) + +#define REG_IRQ_CLEAR_LEN(_n) ((_n) ? 0x0064 : 0x0058) +#define IRQ_CLEAR_LEN_MASK GENMASK(7, 0) + +#define REG_TX_RING_BASE(_n) \ + (((_n) < 8) ? 0x0100 + ((_n) << 5) : 0x0b00 + (((_n) - 8) << 5)) + +#define REG_TX_CPU_IDX(_n) \ + (((_n) < 8) ? 0x0108 + ((_n) << 5) : 0x0b08 + (((_n) - 8) << 5)) + +#define TX_RING_CPU_IDX_MASK GENMASK(15, 0) + +#define REG_TX_DMA_IDX(_n) \ + (((_n) < 8) ? 0x010c + ((_n) << 5) : 0x0b0c + (((_n) - 8) << 5)) + +#define TX_RING_DMA_IDX_MASK GENMASK(15, 0) + +#define IRQ_RING_IDX_MASK GENMASK(20, 16) +#define IRQ_DESC_IDX_MASK GENMASK(15, 0) + +#define REG_RX_RING_BASE(_n) \ + (((_n) < 16) ? 0x0200 + ((_n) << 5) : 0x0e00 + (((_n) - 16) << 5)) + +#define REG_RX_RING_SIZE(_n) \ + (((_n) < 16) ? 0x0204 + ((_n) << 5) : 0x0e04 + (((_n) - 16) << 5)) + +#define RX_RING_THR_MASK GENMASK(31, 16) +#define RX_RING_SIZE_MASK GENMASK(15, 0) + +#define REG_RX_CPU_IDX(_n) \ + (((_n) < 16) ? 0x0208 + ((_n) << 5) : 0x0e08 + (((_n) - 16) << 5)) + +#define RX_RING_CPU_IDX_MASK GENMASK(15, 0) + +#define REG_RX_DMA_IDX(_n) \ + (((_n) < 16) ? 0x020c + ((_n) << 5) : 0x0e0c + (((_n) - 16) << 5)) + +#define REG_RX_DELAY_INT_IDX(_n) \ + (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5)) + +#define RX_DELAY_INT_MASK GENMASK(15, 0) + +#define RX_RING_DMA_IDX_MASK GENMASK(15, 0) + +#define REG_LMGR_INIT_CFG 0x1000 +#define LMGR_INIT_START BIT(31) +#define LMGR_SRAM_MODE_MASK BIT(30) +#define HW_FWD_PKTSIZE_OVERHEAD_MASK GENMASK(27, 20) +#define HW_FWD_DESC_NUM_MASK GENMASK(16, 0) + +/* CTRL */ +#define QDMA_DESC_DONE_MASK BIT(31) +#define QDMA_DESC_DROP_MASK BIT(30) /* tx: drop - rx: overflow */ +#define QDMA_DESC_MORE_MASK BIT(29) /* more SG elements */ +#define QDMA_DESC_DEI_MASK BIT(25) +#define QDMA_DESC_NO_DROP_MASK BIT(24) +#define QDMA_DESC_LEN_MASK GENMASK(15, 0) +/* DATA */ +#define QDMA_DESC_NEXT_ID_MASK GENMASK(15, 0) +/* TX MSG0 */ +#define QDMA_ETH_TXMSG_MIC_IDX_MASK BIT(30) +#define QDMA_ETH_TXMSG_SP_TAG_MASK GENMASK(29, 14) +#define QDMA_ETH_TXMSG_ICO_MASK BIT(13) +#define QDMA_ETH_TXMSG_UCO_MASK BIT(12) +#define QDMA_ETH_TXMSG_TCO_MASK BIT(11) +#define QDMA_ETH_TXMSG_TSO_MASK BIT(10) +#define QDMA_ETH_TXMSG_FAST_MASK BIT(9) +#define QDMA_ETH_TXMSG_OAM_MASK BIT(8) +#define QDMA_ETH_TXMSG_CHAN_MASK GENMASK(7, 3) +#define QDMA_ETH_TXMSG_QUEUE_MASK GENMASK(2, 0) +/* TX MSG1 */ +#define QDMA_ETH_TXMSG_NO_DROP BIT(31) +#define QDMA_ETH_TXMSG_METER_MASK GENMASK(30, 24) /* 0x7f no meters */ +#define QDMA_ETH_TXMSG_FPORT_MASK GENMASK(23, 20) +#define QDMA_ETH_TXMSG_NBOQ_MASK GENMASK(19, 15) +#define QDMA_ETH_TXMSG_HWF_MASK BIT(14) +#define QDMA_ETH_TXMSG_HOP_MASK BIT(13) +#define QDMA_ETH_TXMSG_PTP_MASK BIT(12) +#define QDMA_ETH_TXMSG_ACNT_G1_MASK GENMASK(10, 6) /* 0x1f do not count */ +#define QDMA_ETH_TXMSG_ACNT_G0_MASK GENMASK(5, 0) /* 0x3f do not count */ + +/* RX MSG1 */ +#define QDMA_ETH_RXMSG_DEI_MASK BIT(31) +#define QDMA_ETH_RXMSG_IP6_MASK BIT(30) +#define QDMA_ETH_RXMSG_IP4_MASK BIT(29) +#define QDMA_ETH_RXMSG_IP4F_MASK BIT(28) +#define QDMA_ETH_RXMSG_L4_VALID_MASK BIT(27) +#define QDMA_ETH_RXMSG_L4F_MASK BIT(26) +#define QDMA_ETH_RXMSG_SPORT_MASK GENMASK(25, 21) +#define QDMA_ETH_RXMSG_CRSN_MASK GENMASK(20, 16) +#define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0) + +struct airoha_qdma_desc { + __le32 rsv; + __le32 ctrl; + __le32 addr; + __le32 data; + __le32 msg0; + __le32 msg1; + __le32 msg2; + __le32 msg3; +}; + +struct airoha_qdma_fwd_desc { + __le32 addr; + __le32 ctrl0; + __le32 ctrl1; + __le32 ctrl2; + __le32 msg0; + __le32 msg1; + __le32 rsv0; + __le32 rsv1; +}; + +struct airoha_queue { + struct airoha_qdma_desc *desc; + u16 head; + + int ndesc; +}; + +struct airoha_tx_irq_queue { + struct airoha_qdma *qdma; + + int size; + u32 *q; +}; + +struct airoha_qdma { + struct airoha_eth *eth; + void __iomem *regs; + + struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; + + struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; + struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; + + /* descriptor and packet buffers for qdma hw forward */ + struct { + void *desc; + void *q; + } hfwd; +}; + +struct airoha_gdm_port { + struct airoha_qdma *qdma; + int id; +}; + +struct airoha_eth { + void __iomem *fe_regs; + void __iomem *switch_regs; + + struct reset_ctl_bulk rsts; + struct reset_ctl_bulk xsi_rsts; + + struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA]; + struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS]; +}; + +static u32 airoha_rr(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +static void airoha_wr(void __iomem *base, u32 offset, u32 val) +{ + writel(val, base + offset); +} + +static u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val) +{ + val |= (airoha_rr(base, offset) & ~mask); + airoha_wr(base, offset, val); + + return val; +} + +#define airoha_fe_rr(eth, offset) \ + airoha_rr((eth)->fe_regs, (offset)) +#define airoha_fe_wr(eth, offset, val) \ + airoha_wr((eth)->fe_regs, (offset), (val)) +#define airoha_fe_rmw(eth, offset, mask, val) \ + airoha_rmw((eth)->fe_regs, (offset), (mask), (val)) +#define airoha_fe_set(eth, offset, val) \ + airoha_rmw((eth)->fe_regs, (offset), 0, (val)) +#define airoha_fe_clear(eth, offset, val) \ + airoha_rmw((eth)->fe_regs, (offset), (val), 0) + +#define airoha_qdma_rr(qdma, offset) \ + airoha_rr((qdma)->regs, (offset)) +#define airoha_qdma_wr(qdma, offset, val) \ + airoha_wr((qdma)->regs, (offset), (val)) +#define airoha_qdma_rmw(qdma, offset, mask, val) \ + airoha_rmw((qdma)->regs, (offset), (mask), (val)) +#define airoha_qdma_set(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), 0, (val)) +#define airoha_qdma_clear(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), (val), 0) + +#define airoha_switch_wr(eth, offset, val) \ + airoha_wr((eth)->switch_regs, (offset), (val)) + +static void airoha_fe_maccr_init(struct airoha_eth *eth) +{ + int p; + + for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) { + /* Disable any kind of CRC drop or offload */ + airoha_fe_wr(eth, REG_GDM_FWD_CFG(p), 0); + } +} + +static int airoha_fe_init(struct airoha_eth *eth) +{ + airoha_fe_maccr_init(eth); + + return 0; +} + +static void airoha_qdma_reset_rx_desc(struct airoha_queue *q, int index, + uchar *rx_packet) +{ + struct airoha_qdma_desc *desc; + u32 val; + + desc = &q->desc[index]; + index = (index + 1) % q->ndesc; + + dma_map_single(rx_packet, PKTSIZE_ALIGN, DMA_TO_DEVICE); + + WRITE_ONCE(desc->msg0, cpu_to_le32(0)); + WRITE_ONCE(desc->msg1, cpu_to_le32(0)); + WRITE_ONCE(desc->msg2, cpu_to_le32(0)); + WRITE_ONCE(desc->msg3, cpu_to_le32(0)); + WRITE_ONCE(desc->addr, cpu_to_le32(virt_to_phys(rx_packet))); + WRITE_ONCE(desc->data, cpu_to_le32(index)); + val = FIELD_PREP(QDMA_DESC_LEN_MASK, PKTSIZE_ALIGN); + WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); + + dma_map_single(desc, sizeof(*desc), DMA_TO_DEVICE); +} + +static void airoha_qdma_init_rx_desc(struct airoha_queue *q) +{ + int i; + + for (i = 0; i < q->ndesc; i++) + airoha_qdma_reset_rx_desc(q, i, net_rx_packets[i]); +} + +static int airoha_qdma_init_rx_queue(struct airoha_queue *q, + struct airoha_qdma *qdma, int ndesc) +{ + int qid = q - &qdma->q_rx[0]; + unsigned long dma_addr; + + q->ndesc = ndesc; + q->head = 0; + + q->desc = dma_alloc_coherent(q->ndesc * sizeof(*q->desc), &dma_addr); + if (!q->desc) + return -ENOMEM; + + memset(q->desc, 0, q->ndesc * sizeof(*q->desc)); + dma_map_single(q->desc, q->ndesc * sizeof(*q->desc), DMA_TO_DEVICE); + + airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr); + airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), + RX_RING_SIZE_MASK, + FIELD_PREP(RX_RING_SIZE_MASK, ndesc)); + + airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), RX_RING_THR_MASK, + FIELD_PREP(RX_RING_THR_MASK, 0)); + airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK, + FIELD_PREP(RX_RING_CPU_IDX_MASK, q->ndesc - 1)); + airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, + FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head)); + + return 0; +} + +static int airoha_qdma_init_rx(struct airoha_qdma *qdma) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { + int err; + + err = airoha_qdma_init_rx_queue(&qdma->q_rx[i], qdma, + RX_DSCP_NUM); + if (err) + return err; + } + + return 0; +} + +static int airoha_qdma_init_tx_queue(struct airoha_queue *q, + struct airoha_qdma *qdma, int size) +{ + int qid = q - &qdma->q_tx[0]; + unsigned long dma_addr; + + q->ndesc = size; + q->head = 0; + + q->desc = dma_alloc_coherent(q->ndesc * sizeof(*q->desc), &dma_addr); + if (!q->desc) + return -ENOMEM; + + memset(q->desc, 0, q->ndesc * sizeof(*q->desc)); + dma_map_single(q->desc, q->ndesc * sizeof(*q->desc), DMA_TO_DEVICE); + + airoha_qdma_wr(qdma, REG_TX_RING_BASE(qid), dma_addr); + airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, + FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); + airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK, + FIELD_PREP(TX_RING_DMA_IDX_MASK, q->head)); + + return 0; +} + +static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q, + struct airoha_qdma *qdma, int size) +{ + int id = irq_q - &qdma->q_tx_irq[0]; + unsigned long dma_addr; + + irq_q->q = dma_alloc_coherent(size * sizeof(u32), &dma_addr); + if (!irq_q->q) + return -ENOMEM; + + memset(irq_q->q, 0xffffffff, size * sizeof(u32)); + irq_q->size = size; + irq_q->qdma = qdma; + + dma_map_single(irq_q->q, size * sizeof(u32), DMA_TO_DEVICE); + + airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr); + airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK, + FIELD_PREP(TX_IRQ_DEPTH_MASK, size)); + + return 0; +} + +static int airoha_qdma_init_tx(struct airoha_qdma *qdma) +{ + int i, err; + + for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { + err = airoha_qdma_tx_irq_init(&qdma->q_tx_irq[i], qdma, + IRQ_QUEUE_LEN); + if (err) + return err; + } + + for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { + err = airoha_qdma_init_tx_queue(&qdma->q_tx[i], qdma, + TX_DSCP_NUM); + if (err) + return err; + } + + return 0; +} + +static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) +{ + unsigned long dma_addr; + u32 status; + int size; + + size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); + qdma->hfwd.desc = dma_alloc_coherent(size, &dma_addr); + if (!qdma->hfwd.desc) + return -ENOMEM; + + memset(qdma->hfwd.desc, 0, size); + dma_map_single(qdma->hfwd.desc, size, DMA_TO_DEVICE); + + airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); + + size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; + qdma->hfwd.q = dma_alloc_coherent(size, &dma_addr); + if (!qdma->hfwd.q) + return -ENOMEM; + + memset(qdma->hfwd.q, 0, size); + dma_map_single(qdma->hfwd.q, size, DMA_TO_DEVICE); + + airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); + + airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, + HW_FWD_DSCP_PAYLOAD_SIZE_MASK | + HW_FWD_DSCP_MIN_SCATTER_LEN_MASK, + FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0) | + FIELD_PREP(HW_FWD_DSCP_MIN_SCATTER_LEN_MASK, 1)); + airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG, + LMGR_INIT_START | LMGR_SRAM_MODE_MASK | + HW_FWD_DESC_NUM_MASK, + FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) | + LMGR_INIT_START); + + udelay(1000); + return read_poll_timeout(airoha_qdma_rr, status, + !(status & LMGR_INIT_START), USEC_PER_MSEC, + 30 * USEC_PER_MSEC, qdma, + REG_LMGR_INIT_CFG); +} + +static int airoha_qdma_hw_init(struct airoha_qdma *qdma) +{ + int i; + + /* clear pending irqs */ + for (i = 0; i < 2; i++) + airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff); + + airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_CPU_TXR_RR_MASK | + GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK | + GLOBAL_CFG_IRQ0_EN_MASK | + GLOBAL_CFG_TX_WB_DONE_MASK | + FIELD_PREP(GLOBAL_CFG_MAX_ISSUE_NUM_MASK, 3)); + + /* disable qdma rx delay interrupt */ + for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { + if (!qdma->q_rx[i].ndesc) + continue; + + airoha_qdma_clear(qdma, REG_RX_DELAY_INT_IDX(i), + RX_DELAY_INT_MASK); + } + + return 0; +} + +static int airoha_qdma_init(struct udevice *dev, + struct airoha_eth *eth, + struct airoha_qdma *qdma) +{ + int err; + + qdma->eth = eth; + qdma->regs = dev_remap_addr_name(dev, "qdma0"); + if (IS_ERR(qdma->regs)) + return PTR_ERR(qdma->regs); + + err = airoha_qdma_init_rx(qdma); + if (err) + return err; + + err = airoha_qdma_init_tx(qdma); + if (err) + return err; + + err = airoha_qdma_init_hfwd_queues(qdma); + if (err) + return err; + + return airoha_qdma_hw_init(qdma); +} + +static int airoha_hw_init(struct udevice *dev, + struct airoha_eth *eth) +{ + int ret, i; + + /* disable xsi */ + ret = reset_assert_bulk(ð->xsi_rsts); + if (ret) + return ret; + + ret = reset_assert_bulk(ð->rsts); + if (ret) + return ret; + + mdelay(20); + + ret = reset_deassert_bulk(ð->rsts); + if (ret) + return ret; + + mdelay(20); + + ret = airoha_fe_init(eth); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) { + ret = airoha_qdma_init(dev, eth, ð->qdma[i]); + if (ret) + return ret; + } + + return 0; +} + +static int airoha_switch_init(struct udevice *dev, struct airoha_eth *eth) +{ + ofnode switch_node; + fdt_addr_t addr; + + switch_node = ofnode_by_compatible(ofnode_null(), "airoha,en7581-switch"); + if (!ofnode_valid(switch_node)) + return -EINVAL; + + addr = ofnode_get_addr(switch_node); + if (addr == FDT_ADDR_T_NONE) + return -ENOMEM; + + /* Switch doesn't have a DEV, gets address and setup Flood and CPU port */ + eth->switch_regs = map_sysmem(addr, 0); + + /* Set FLOOD, no CPU switch register */ + airoha_switch_wr(eth, SWITCH_MFC, SWITCH_BC_FFP | SWITCH_UNM_FFP | + SWITCH_UNU_FFP); + + /* Set CPU 6 PMCR */ + airoha_switch_wr(eth, SWITCH_PMCR(6), + SWITCH_IPG_CFG_SHORT | SWITCH_MAC_MODE | + SWITCH_FORCE_MODE | SWITCH_MAC_TX_EN | + SWITCH_MAC_RX_EN | SWITCH_BKOFF_EN | SWITCH_BKPR_EN | + SWITCH_FORCE_RX_FC | SWITCH_FORCE_TX_FC | + SWITCH_FORCE_SPD_1000 | SWITCH_FORCE_DPX | + SWITCH_FORCE_LNK); + + /* Sideband signal error for Port 3, which need the auto polling */ + airoha_switch_wr(eth, SWITCH_PHY_POLL, + FIELD_PREP(SWITCH_PHY_AP_EN, 0x7f) | + FIELD_PREP(SWITCH_EEE_POLL_EN, 0x7f) | + SWITCH_PHY_PRE_EN | + FIELD_PREP(SWITCH_PHY_END_ADDR, 0xc) | + FIELD_PREP(SWITCH_PHY_ST_ADDR, 0x8)); + + return 0; +} + +static int airoha_eth_probe(struct udevice *dev) +{ + struct airoha_eth *eth = dev_get_priv(dev); + struct regmap *scu_regmap; + ofnode scu_node; + int ret; + + scu_node = ofnode_by_compatible(ofnode_null(), "airoha,en7581-scu"); + if (!ofnode_valid(scu_node)) + return -EINVAL; + + scu_regmap = syscon_node_to_regmap(scu_node); + if (IS_ERR(scu_regmap)) + return PTR_ERR(scu_regmap); + + /* It seems by default the FEMEM_SEL is set to Memory (0x1) + * preventing any access to any QDMA and FrameEngine register + * reporting all 0xdeadbeef (poor cow :( ) + */ + regmap_write(scu_regmap, SCU_SHARE_FEMEM_SEL, 0x0); + + eth->fe_regs = dev_remap_addr_name(dev, "fe"); + if (!eth->fe_regs) + return -ENOMEM; + + eth->rsts.resets = devm_kcalloc(dev, AIROHA_MAX_NUM_RSTS, + sizeof(struct reset_ctl), GFP_KERNEL); + if (!eth->rsts.resets) + return -ENOMEM; + eth->rsts.count = AIROHA_MAX_NUM_RSTS; + + eth->xsi_rsts.resets = devm_kcalloc(dev, AIROHA_MAX_NUM_XSI_RSTS, + sizeof(struct reset_ctl), GFP_KERNEL); + if (!eth->xsi_rsts.resets) + return -ENOMEM; + eth->xsi_rsts.count = AIROHA_MAX_NUM_XSI_RSTS; + + ret = reset_get_by_name(dev, "fe", ð->rsts.resets[0]); + if (ret) + return ret; + + ret = reset_get_by_name(dev, "pdma", ð->rsts.resets[1]); + if (ret) + return ret; + + ret = reset_get_by_name(dev, "qdma", ð->rsts.resets[2]); + if (ret) + return ret; + + ret = reset_get_by_name(dev, "hsi0-mac", ð->xsi_rsts.resets[0]); + if (ret) + return ret; + + ret = reset_get_by_name(dev, "hsi1-mac", ð->xsi_rsts.resets[1]); + if (ret) + return ret; + + ret = reset_get_by_name(dev, "hsi-mac", ð->xsi_rsts.resets[2]); + if (ret) + return ret; + + ret = reset_get_by_name(dev, "xfp-mac", ð->xsi_rsts.resets[3]); + if (ret) + return ret; + + ret = airoha_hw_init(dev, eth); + if (ret) + return ret; + + return airoha_switch_init(dev, eth); +} + +static int airoha_eth_init(struct udevice *dev) +{ + struct airoha_eth *eth = dev_get_priv(dev); + struct airoha_qdma *qdma = ð->qdma[0]; + struct airoha_queue *q; + int qid; + + qid = 0; + q = &qdma->q_rx[qid]; + + airoha_qdma_init_rx_desc(q); + + airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | + GLOBAL_CFG_RX_DMA_EN_MASK); + + return 0; +} + +static void airoha_eth_stop(struct udevice *dev) +{ + struct airoha_eth *eth = dev_get_priv(dev); + struct airoha_qdma *qdma = ð->qdma[0]; + + airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | + GLOBAL_CFG_RX_DMA_EN_MASK); +} + +static int airoha_eth_send(struct udevice *dev, void *packet, int length) +{ + struct airoha_eth *eth = dev_get_priv(dev); + struct airoha_qdma *qdma = ð->qdma[0]; + struct airoha_qdma_desc *desc; + struct airoha_queue *q; + dma_addr_t dma_addr; + u32 msg0, msg1; + int qid, index; + u8 fport; + u32 val; + int i; + + dma_addr = dma_map_single(packet, length, DMA_TO_DEVICE); + + qid = 0; + q = &qdma->q_tx[qid]; + desc = &q->desc[q->head]; + index = (q->head + 1) % q->ndesc; + + fport = 1; + + msg0 = 0; + msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | + FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); + + val = FIELD_PREP(QDMA_DESC_LEN_MASK, length); + WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); + WRITE_ONCE(desc->addr, cpu_to_le32(dma_addr)); + val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, index); + WRITE_ONCE(desc->data, cpu_to_le32(val)); + WRITE_ONCE(desc->msg0, cpu_to_le32(msg0)); + WRITE_ONCE(desc->msg1, cpu_to_le32(msg1)); + WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff)); + + dma_map_single(desc, sizeof(*desc), DMA_TO_DEVICE); + + airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, + FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); + + for (i = 0; i < 100; i++) { + dma_unmap_single(virt_to_phys(desc), sizeof(*desc), + DMA_FROM_DEVICE); + if (desc->ctrl & QDMA_DESC_DONE_MASK) + break; + + udelay(1); + } + + /* Return error if for some reason the descriptor never ACK */ + if (!(desc->ctrl & QDMA_DESC_DONE_MASK)) + return -EAGAIN; + + q->head = index; + airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(0), + IRQ_CLEAR_LEN_MASK, 1); + + return 0; +} + +static int airoha_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct airoha_eth *eth = dev_get_priv(dev); + struct airoha_qdma *qdma = ð->qdma[0]; + struct airoha_qdma_desc *desc; + struct airoha_queue *q; + u16 length; + int qid; + + qid = 0; + q = &qdma->q_rx[qid]; + desc = &q->desc[q->head]; + + dma_unmap_single(virt_to_phys(desc), sizeof(*desc), + DMA_FROM_DEVICE); + + if (!(desc->ctrl & QDMA_DESC_DONE_MASK)) + return -EAGAIN; + + length = FIELD_GET(QDMA_DESC_LEN_MASK, desc->ctrl); + dma_unmap_single(desc->addr, length, + DMA_FROM_DEVICE); + + *packetp = phys_to_virt(desc->addr); + + return length; +} + +static int arht_eth_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct airoha_eth *eth = dev_get_priv(dev); + struct airoha_qdma *qdma = ð->qdma[0]; + struct airoha_queue *q; + int qid; + + if (!packet) + return 0; + + qid = 0; + q = &qdma->q_rx[qid]; + + dma_map_single(packet, length, DMA_TO_DEVICE); + + airoha_qdma_reset_rx_desc(q, q->head, packet); + + airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK, + FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head)); + q->head = (q->head + 1) % q->ndesc; + + return 0; +} + +static int arht_eth_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct airoha_eth *eth = dev_get_priv(dev); + unsigned char *mac = pdata->enetaddr; + u32 macaddr_lsb, macaddr_msb; + + macaddr_lsb = FIELD_PREP(SMACCR0_MAC2, mac[2]) | + FIELD_PREP(SMACCR0_MAC3, mac[3]) | + FIELD_PREP(SMACCR0_MAC4, mac[4]) | + FIELD_PREP(SMACCR0_MAC5, mac[5]); + macaddr_msb = FIELD_PREP(SMACCR1_MAC1, mac[1]) | + FIELD_PREP(SMACCR1_MAC0, mac[0]); + + /* Set MAC for Switch */ + airoha_switch_wr(eth, SWITCH_SMACCR0, macaddr_lsb); + airoha_switch_wr(eth, SWITCH_SMACCR1, macaddr_msb); + + return 0; +} + +static const struct udevice_id airoha_eth_ids[] = { + { .compatible = "airoha,en7581-eth" }, +}; + +static const struct eth_ops airoha_eth_ops = { + .start = airoha_eth_init, + .stop = airoha_eth_stop, + .send = airoha_eth_send, + .recv = airoha_eth_recv, + .free_pkt = arht_eth_free_pkt, + .write_hwaddr = arht_eth_write_hwaddr, +}; + +U_BOOT_DRIVER(airoha_eth) = { + .name = "airoha-eth", + .id = UCLASS_ETH, + .of_match = airoha_eth_ids, + .probe = airoha_eth_probe, + .ops = &airoha_eth_ops, + .priv_auto = sizeof(struct airoha_eth), + .plat_auto = sizeof(struct eth_pdata), +}; diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 0a1fff38727..0f93c25e3fe 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -33,6 +33,9 @@ #include <linux/printk.h> #include <power/regulator.h> #include "designware.h" +#if IS_ENABLED(CONFIG_ARCH_NPCM8XX) +#include <asm/arch/gmac.h> +#endif static int dw_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) { @@ -223,6 +226,136 @@ static int dw_dm_mdio_init(const char *name, void *priv) } #endif +#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO) +static int dw_eth_bb_mdio_active(struct mii_dev *miidev) +{ + struct dw_eth_dev *priv = miidev->priv; + struct gpio_desc *desc = &priv->mdio_gpio; + + desc->flags = 0; + dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + + return 0; +} + +static int dw_eth_bb_mdio_tristate(struct mii_dev *miidev) +{ + struct dw_eth_dev *priv = miidev->priv; + struct gpio_desc *desc = &priv->mdio_gpio; + + desc->flags = 0; + dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_IN); + + return 0; +} + +static int dw_eth_bb_set_mdio(struct mii_dev *miidev, int v) +{ + struct dw_eth_dev *priv = miidev->priv; + + if (v) + dm_gpio_set_value(&priv->mdio_gpio, 1); + else + dm_gpio_set_value(&priv->mdio_gpio, 0); + + return 0; +} + +static int dw_eth_bb_get_mdio(struct mii_dev *miidev, int *v) +{ + struct dw_eth_dev *priv = miidev->priv; + + *v = dm_gpio_get_value(&priv->mdio_gpio); + + return 0; +} + +static int dw_eth_bb_set_mdc(struct mii_dev *miidev, int v) +{ + struct dw_eth_dev *priv = miidev->priv; + + if (v) + dm_gpio_set_value(&priv->mdc_gpio, 1); + else + dm_gpio_set_value(&priv->mdc_gpio, 0); + + return 0; +} + +static int dw_eth_bb_delay(struct mii_dev *miidev) +{ + struct dw_eth_dev *priv = miidev->priv; + + udelay(priv->bb_delay); + return 0; +} + +static const struct bb_miiphy_bus_ops dw_eth_bb_miiphy_bus_ops = { + .mdio_active = dw_eth_bb_mdio_active, + .mdio_tristate = dw_eth_bb_mdio_tristate, + .set_mdio = dw_eth_bb_set_mdio, + .get_mdio = dw_eth_bb_get_mdio, + .set_mdc = dw_eth_bb_set_mdc, + .delay = dw_eth_bb_delay, +}; + +static int dw_bb_miiphy_read(struct mii_dev *miidev, int addr, + int devad, int reg) +{ + return bb_miiphy_read(miidev, &dw_eth_bb_miiphy_bus_ops, + addr, devad, reg); +} + +static int dw_bb_miiphy_write(struct mii_dev *miidev, int addr, + int devad, int reg, u16 value) +{ + return bb_miiphy_write(miidev, &dw_eth_bb_miiphy_bus_ops, + addr, devad, reg, value); +} + +static int dw_bb_mdio_init(const char *name, struct udevice *dev) +{ + struct dw_eth_dev *dwpriv = dev_get_priv(dev); + struct mii_dev *bus = mdio_alloc(); + int ret; + + if (!bus) { + printf("Failed to allocate MDIO bus\n"); + return -ENOMEM; + } + + debug("\n%s: use bitbang mii..\n", dev->name); + ret = gpio_request_by_name(dev, "snps,mdc-gpio", 0, + &dwpriv->mdc_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret) { + debug("no mdc-gpio\n"); + return ret; + } + ret = gpio_request_by_name(dev, "snps,mdio-gpio", 0, + &dwpriv->mdio_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret) { + debug("no mdio-gpio\n"); + return ret; + } + dwpriv->bb_delay = dev_read_u32_default(dev, "snps,bitbang-delay", 1); + + dwpriv->bus = bus; + dwpriv->dev = dev; + + snprintf(bus->name, sizeof(bus->name), "%s", name); + bus->read = dw_bb_miiphy_read; + bus->write = dw_bb_miiphy_write; +#if CONFIG_IS_ENABLED(DM_GPIO) + bus->reset = dw_mdio_reset; +#endif + bus->priv = dwpriv; + + return mdio_register(bus); +} +#endif + static void tx_descs_init(struct dw_eth_dev *priv) { struct eth_dma_regs *dma_p = priv->dma_regs_p; @@ -352,10 +485,35 @@ static int dw_adjust_link(struct dw_eth_dev *priv, struct eth_mac_regs *mac_p, (phydev->port == PORT_FIBRE) ? ", fiber mode" : ""); #ifdef CONFIG_ARCH_NPCM8XX + if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { + unsigned int start; + + /* Indirect access to VR_MII_MMD registers */ + writew((VR_MII_MMD >> 9), PCS_BA + PCS_IND_AC); + /* Set PCS_Mode to SGMII */ + clrsetbits_le16(PCS_BA + VR_MII_MMD_AN_CTRL, BIT(1), BIT(2)); + /* Set Auto Speed Mode Change */ + setbits_le16(PCS_BA + VR_MII_MMD_CTRL1, BIT(9)); + /* Indirect access to SR_MII_MMD registers */ + writew((SR_MII_MMD >> 9), PCS_BA + PCS_IND_AC); + /* Restart Auto-Negotiation */ + setbits_le16(PCS_BA + SR_MII_MMD_CTRL, BIT(9) | BIT(12)); + + printf("SGMII PHY Wait for link up \n"); + /* SGMII PHY Wait for link up */ + start = get_timer(0); + while (!(readw(PCS_BA + SR_MII_MMD_STS) & BIT(2))) { + if (get_timer(start) >= LINK_UP_TIMEOUT) { + printf("PHY link up timeout\n"); + return -ETIMEDOUT; + } + mdelay(1); + }; + } /* Pass all Multicast Frames */ setbits_le32(&mac_p->framefilt, BIT(4)); - #endif + return 0; } @@ -784,51 +942,29 @@ int designware_eth_probe(struct udevice *dev) priv->interface = pdata->phy_interface; priv->max_speed = pdata->max_speed; -#if IS_ENABLED(CONFIG_DM_MDIO) - ret = dw_dm_mdio_init(dev->name, dev); -#else - ret = dw_mdio_init(dev->name, dev); -#endif - if (ret) { - err = ret; - goto mdio_err; - } - priv->bus = miiphy_get_dev_by_name(dev->name); - priv->dev = dev; - #if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO) if (dev_read_bool(dev, "snps,bitbang-mii")) { - int bus_idx; - - debug("\n%s: use bitbang mii..\n", dev->name); - ret = gpio_request_by_name(dev, "snps,mdc-gpio", 0, - &priv->mdc_gpio, GPIOD_IS_OUT - | GPIOD_IS_OUT_ACTIVE); + ret = dw_bb_mdio_init(dev->name, dev); if (ret) { - debug("no mdc-gpio\n"); - return ret; + err = ret; + goto mdio_err; } - ret = gpio_request_by_name(dev, "snps,mdio-gpio", 0, - &priv->mdio_gpio, GPIOD_IS_OUT - | GPIOD_IS_OUT_ACTIVE); + } else +#endif + { +#if IS_ENABLED(CONFIG_DM_MDIO) + ret = dw_dm_mdio_init(dev->name, dev); +#else + ret = dw_mdio_init(dev->name, dev); +#endif if (ret) { - debug("no mdio-gpio\n"); - return ret; - } - priv->bb_delay = dev_read_u32_default(dev, "snps,bitbang-delay", 1); - - for (bus_idx = 0; bus_idx < bb_miiphy_buses_num; bus_idx++) { - if (!bb_miiphy_buses[bus_idx].priv) { - bb_miiphy_buses[bus_idx].priv = priv; - strlcpy(bb_miiphy_buses[bus_idx].name, priv->bus->name, - MDIO_NAME_LEN); - priv->bus->read = bb_miiphy_read; - priv->bus->write = bb_miiphy_write; - break; - } + err = ret; + goto mdio_err; } + priv->bus = miiphy_get_dev_by_name(dev->name); + priv->dev = dev; } -#endif + ret = dw_phy_init(priv, dev); debug("%s, ret=%d\n", __func__, ret); if (!ret) @@ -939,83 +1075,3 @@ static struct pci_device_id supported[] = { }; U_BOOT_PCI_DEVICE(eth_designware, supported); - -#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO) -static int dw_eth_bb_mdio_active(struct bb_miiphy_bus *bus) -{ - struct dw_eth_dev *priv = bus->priv; - struct gpio_desc *desc = &priv->mdio_gpio; - - desc->flags = 0; - dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); - - return 0; -} - -static int dw_eth_bb_mdio_tristate(struct bb_miiphy_bus *bus) -{ - struct dw_eth_dev *priv = bus->priv; - struct gpio_desc *desc = &priv->mdio_gpio; - - desc->flags = 0; - dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_IN); - - return 0; -} - -static int dw_eth_bb_set_mdio(struct bb_miiphy_bus *bus, int v) -{ - struct dw_eth_dev *priv = bus->priv; - - if (v) - dm_gpio_set_value(&priv->mdio_gpio, 1); - else - dm_gpio_set_value(&priv->mdio_gpio, 0); - - return 0; -} - -static int dw_eth_bb_get_mdio(struct bb_miiphy_bus *bus, int *v) -{ - struct dw_eth_dev *priv = bus->priv; - - *v = dm_gpio_get_value(&priv->mdio_gpio); - - return 0; -} - -static int dw_eth_bb_set_mdc(struct bb_miiphy_bus *bus, int v) -{ - struct dw_eth_dev *priv = bus->priv; - - if (v) - dm_gpio_set_value(&priv->mdc_gpio, 1); - else - dm_gpio_set_value(&priv->mdc_gpio, 0); - - return 0; -} - -static int dw_eth_bb_delay(struct bb_miiphy_bus *bus) -{ - struct dw_eth_dev *priv = bus->priv; - - udelay(priv->bb_delay); - return 0; -} - -struct bb_miiphy_bus bb_miiphy_buses[] = { - { - .name = BB_MII_DEVNAME, - .mdio_active = dw_eth_bb_mdio_active, - .mdio_tristate = dw_eth_bb_mdio_tristate, - .set_mdio = dw_eth_bb_set_mdio, - .get_mdio = dw_eth_bb_get_mdio, - .set_mdc = dw_eth_bb_set_mdc, - .delay = dw_eth_bb_delay, - .priv = NULL, - } -}; - -int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses); -#endif diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 2279481d935..0cfe09333f7 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -1173,7 +1173,7 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length) eqos->config->ops->eqos_inval_buffer(packet, length); - if ((eqos->rx_desc_idx & idx_mask) == idx_mask) { + if (eqos->started && (eqos->rx_desc_idx & idx_mask) == idx_mask) { for (idx = eqos->rx_desc_idx - idx_mask; idx <= eqos->rx_desc_idx; idx++) { @@ -1612,10 +1612,18 @@ static const struct udevice_id eqos_ids[] = { #endif #if IS_ENABLED(CONFIG_DWC_ETH_QOS_ROCKCHIP) { + .compatible = "rockchip,rk3528-gmac", + .data = (ulong)&eqos_rockchip_config + }, + { .compatible = "rockchip,rk3568-gmac", .data = (ulong)&eqos_rockchip_config }, { + .compatible = "rockchip,rk3576-gmac", + .data = (ulong)&eqos_rockchip_config + }, + { .compatible = "rockchip,rk3588-gmac", .data = (ulong)&eqos_rockchip_config }, @@ -1632,6 +1640,12 @@ static const struct udevice_id eqos_ids[] = { .data = (ulong)&eqos_jh7110_config }, #endif +#if IS_ENABLED(CONFIG_DWC_ETH_QOS_ADI) + { + .compatible = "adi,sc59x-dwmac-eqos", + .data = (ulong)&eqos_adi_config + }, +#endif { } }; diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h index 123f98d5d53..403e8203974 100644 --- a/drivers/net/dwc_eth_qos.h +++ b/drivers/net/dwc_eth_qos.h @@ -87,6 +87,7 @@ struct eqos_mac_regs { #define EQOS_MAC_MDIO_ADDRESS_CR_MASK GENMASK(11, 8) #define EQOS_MAC_MDIO_ADDRESS_CR_100_150 1 #define EQOS_MAC_MDIO_ADDRESS_CR_20_35 2 +#define EQOS_MAC_MDIO_ADDRESS_CR_150_250 4 #define EQOS_MAC_MDIO_ADDRESS_CR_250_300 5 #define EQOS_MAC_MDIO_ADDRESS_SKAP BIT(4) #define EQOS_MAC_MDIO_ADDRESS_GOC_MASK GENMASK(3, 2) @@ -301,3 +302,4 @@ extern struct eqos_config eqos_qcom_config; extern struct eqos_config eqos_stm32mp13_config; extern struct eqos_config eqos_stm32mp15_config; extern struct eqos_config eqos_jh7110_config; +extern struct eqos_config eqos_adi_config; diff --git a/drivers/net/dwc_eth_qos_adi.c b/drivers/net/dwc_eth_qos_adi.c new file mode 100644 index 00000000000..0e6a901e303 --- /dev/null +++ b/drivers/net/dwc_eth_qos_adi.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * (C) Copyright 2024 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa <greg.malysa@timesys.com> + * Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + */ + +#include <clk.h> +#include <dm.h> +#include <net.h> +#include <phy.h> +#include <reset.h> +#include <linux/io.h> + +#include <asm/arch-adi/sc5xx/sc5xx.h> + +#include "dwc_eth_qos.h" + +static int eqos_start_resets_adi(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + /* + * Settings need to latch with the DMA reset below. Currently only + * rgmii is supported but other phy interfaces may be supported in + * the future + */ + sc5xx_enable_rgmii(); + setbits_32(&eqos->dma_regs->mode, EQOS_DMA_MODE_SWR); + + return 0; +} + +static int eqos_probe_resources_adi(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + phy_interface_t interface; + int ret; + + ret = eqos_get_base_addr_dt(dev); + if (ret) { + pr_err("eqos_get_base_addr_dt failed: %d\n", ret); + return ret; + } + + interface = eqos->config->interface(dev); + if (interface == PHY_INTERFACE_MODE_NA) { + pr_err("Invalid PHY interface\n"); + return -EINVAL; + } + + return 0; +} + +/** + * rgmii tx clock rate is set to 125 MHz regardless of phy mode, and + * by default the internal clock is always connected to 125 MHz. According + * to the HRM it is invalid for this clock to have any other speed, so + * the hardware won't work anyway if this is wrong. + */ +static ulong eqos_get_tick_clk_rate_adi(struct udevice *dev) +{ + return 125 * 1000000; +} + +static int eqos_get_enetaddr_adi(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + + return eth_env_get_enetaddr("ethaddr", pdata->enetaddr); +} + +static struct eqos_ops eqos_adi_ops = { + .eqos_inval_desc = eqos_inval_desc_generic, + .eqos_flush_desc = eqos_flush_desc_generic, + .eqos_inval_buffer = eqos_inval_buffer_generic, + .eqos_flush_buffer = eqos_flush_buffer_generic, + .eqos_probe_resources = eqos_probe_resources_adi, + .eqos_remove_resources = eqos_null_ops, + .eqos_start_resets = eqos_start_resets_adi, + .eqos_stop_resets = eqos_null_ops, + .eqos_start_clks = eqos_null_ops, + .eqos_stop_clks = eqos_null_ops, + .eqos_calibrate_pads = eqos_null_ops, + .eqos_disable_calibration = eqos_null_ops, + .eqos_set_tx_clk_speed = eqos_null_ops, + .eqos_get_enetaddr = eqos_get_enetaddr_adi, + .eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_adi, +}; + +struct eqos_config __maybe_unused eqos_adi_config = { + .reg_access_always_ok = true, + .mdio_wait = 20, + .swr_wait = 50, + .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB, + .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_150_250, + .axi_bus_width = EQOS_AXI_WIDTH_32, + .interface = dev_read_phy_mode, + .ops = &eqos_adi_ops, +}; diff --git a/drivers/net/dwc_eth_qos_rockchip.c b/drivers/net/dwc_eth_qos_rockchip.c index f3a0f63003e..d646d3ebac8 100644 --- a/drivers/net/dwc_eth_qos_rockchip.c +++ b/drivers/net/dwc_eth_qos_rockchip.c @@ -50,6 +50,132 @@ struct rockchip_platform_data { (((tx) ? soc##_GMAC_TXCLK_DLY_ENABLE : soc##_GMAC_TXCLK_DLY_DISABLE) | \ ((rx) ? soc##_GMAC_RXCLK_DLY_ENABLE : soc##_GMAC_RXCLK_DLY_DISABLE)) +#define RK3528_VO_GRF_GMAC_CON 0x0018 +#define RK3528_VPU_GRF_GMAC_CON5 0x0018 +#define RK3528_VPU_GRF_GMAC_CON6 0x001c + +#define RK3528_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15) +#define RK3528_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15) +#define RK3528_GMAC_TXCLK_DLY_ENABLE GRF_BIT(14) +#define RK3528_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(14) + +#define RK3528_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0xFF, 8) +#define RK3528_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0xFF, 0) + +#define RK3528_GMAC0_PHY_INTF_SEL_RMII GRF_BIT(1) +#define RK3528_GMAC1_PHY_INTF_SEL_RGMII GRF_CLR_BIT(8) +#define RK3528_GMAC1_PHY_INTF_SEL_RMII GRF_BIT(8) + +#define RK3528_GMAC1_CLK_SELECT_CRU GRF_CLR_BIT(12) +#define RK3528_GMAC1_CLK_SELECT_IO GRF_BIT(12) + +#define RK3528_GMAC0_CLK_RMII_DIV2 GRF_BIT(3) +#define RK3528_GMAC0_CLK_RMII_DIV20 GRF_CLR_BIT(3) +#define RK3528_GMAC1_CLK_RMII_DIV2 GRF_BIT(10) +#define RK3528_GMAC1_CLK_RMII_DIV20 GRF_CLR_BIT(10) + +#define RK3528_GMAC1_CLK_RGMII_DIV1 (GRF_CLR_BIT(11) | GRF_CLR_BIT(10)) +#define RK3528_GMAC1_CLK_RGMII_DIV5 (GRF_BIT(11) | GRF_BIT(10)) +#define RK3528_GMAC1_CLK_RGMII_DIV50 (GRF_BIT(11) | GRF_CLR_BIT(10)) + +#define RK3528_GMAC0_CLK_RMII_GATE GRF_BIT(2) +#define RK3528_GMAC0_CLK_RMII_NOGATE GRF_CLR_BIT(2) +#define RK3528_GMAC1_CLK_RMII_GATE GRF_BIT(9) +#define RK3528_GMAC1_CLK_RMII_NOGATE GRF_CLR_BIT(9) + +static int rk3528_set_to_rgmii(struct udevice *dev, + int tx_delay, int rx_delay) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + + regmap_write(data->grf, RK3528_VPU_GRF_GMAC_CON5, + RK3528_GMAC1_PHY_INTF_SEL_RGMII); + + regmap_write(data->grf, RK3528_VPU_GRF_GMAC_CON5, + DELAY_ENABLE(RK3528, tx_delay, rx_delay)); + + regmap_write(data->grf, RK3528_VPU_GRF_GMAC_CON6, + RK3528_GMAC_CLK_RX_DL_CFG(rx_delay) | + RK3528_GMAC_CLK_TX_DL_CFG(tx_delay)); + + return 0; +} + +static int rk3528_set_to_rmii(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + + if (data->id == 1) + regmap_write(data->grf, RK3528_VPU_GRF_GMAC_CON5, + RK3528_GMAC1_PHY_INTF_SEL_RMII); + else + regmap_write(data->grf, RK3528_VO_GRF_GMAC_CON, + RK3528_GMAC0_PHY_INTF_SEL_RMII | + RK3528_GMAC0_CLK_RMII_DIV2); + + return 0; +} + +static int rk3528_set_gmac_speed(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + u32 val, reg; + + switch (eqos->phy->speed) { + case SPEED_10: + if (pdata->phy_interface == PHY_INTERFACE_MODE_RMII) + val = data->id == 1 ? RK3528_GMAC1_CLK_RMII_DIV20 : + RK3528_GMAC0_CLK_RMII_DIV20; + else + val = RK3528_GMAC1_CLK_RGMII_DIV50; + break; + case SPEED_100: + if (pdata->phy_interface == PHY_INTERFACE_MODE_RMII) + val = data->id == 1 ? RK3528_GMAC1_CLK_RMII_DIV2 : + RK3528_GMAC0_CLK_RMII_DIV2; + else + val = RK3528_GMAC1_CLK_RGMII_DIV5; + break; + case SPEED_1000: + if (pdata->phy_interface != PHY_INTERFACE_MODE_RMII) + val = RK3528_GMAC1_CLK_RGMII_DIV1; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + reg = data->id == 1 ? RK3528_VPU_GRF_GMAC_CON5 : + RK3528_VO_GRF_GMAC_CON; + regmap_write(data->grf, reg, val); + + return 0; +} + +static void rk3528_set_clock_selection(struct udevice *dev, bool enable) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + u32 val; + + if (data->id == 1) { + val = data->clock_input ? RK3528_GMAC1_CLK_SELECT_IO : + RK3528_GMAC1_CLK_SELECT_CRU; + val |= enable ? RK3528_GMAC1_CLK_RMII_NOGATE : + RK3528_GMAC1_CLK_RMII_GATE; + regmap_write(data->grf, RK3528_VPU_GRF_GMAC_CON5, val); + } else { + val = enable ? RK3528_GMAC0_CLK_RMII_NOGATE : + RK3528_GMAC0_CLK_RMII_GATE; + regmap_write(data->grf, RK3528_VO_GRF_GMAC_CON, val); + } +} + #define RK3568_GRF_GMAC0_CON0 0x0380 #define RK3568_GRF_GMAC0_CON1 0x0384 #define RK3568_GRF_GMAC1_CON0 0x0388 @@ -134,6 +260,145 @@ static int rk3568_set_gmac_speed(struct udevice *dev) return 0; } +/* VCCIO0_1_3_IOC */ +#define RK3576_VCCIO0_1_3_IOC_CON2 0x6408 +#define RK3576_VCCIO0_1_3_IOC_CON3 0x640c +#define RK3576_VCCIO0_1_3_IOC_CON4 0x6410 +#define RK3576_VCCIO0_1_3_IOC_CON5 0x6414 + +#define RK3576_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15) +#define RK3576_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15) +#define RK3576_GMAC_TXCLK_DLY_ENABLE GRF_BIT(7) +#define RK3576_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(7) + +#define RK3576_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8) +#define RK3576_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) + +/* SDGMAC_GRF */ +#define RK3576_GRF_GMAC_CON0 0x0020 +#define RK3576_GRF_GMAC_CON1 0x0024 + +#define RK3576_GMAC_RMII_MODE GRF_BIT(3) +#define RK3576_GMAC_RGMII_MODE GRF_CLR_BIT(3) + +#define RK3576_GMAC_CLK_SELECT_IO GRF_BIT(7) +#define RK3576_GMAC_CLK_SELECT_CRU GRF_CLR_BIT(7) + +#define RK3576_GMAC_CLK_RMII_DIV2 GRF_BIT(5) +#define RK3576_GMAC_CLK_RMII_DIV20 GRF_CLR_BIT(5) + +#define RK3576_GMAC_CLK_RGMII_DIV1 \ + (GRF_CLR_BIT(6) | GRF_CLR_BIT(5)) +#define RK3576_GMAC_CLK_RGMII_DIV5 \ + (GRF_BIT(6) | GRF_BIT(5)) +#define RK3576_GMAC_CLK_RGMII_DIV50 \ + (GRF_BIT(6) | GRF_CLR_BIT(5)) + +#define RK3576_GMAC_CLK_RMII_GATE GRF_BIT(4) +#define RK3576_GMAC_CLK_RMII_NOGATE GRF_CLR_BIT(4) + +static int rk3576_set_to_rgmii(struct udevice *dev, + int tx_delay, int rx_delay) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + u32 offset_con; + + offset_con = data->id == 1 ? RK3576_GRF_GMAC_CON1 : + RK3576_GRF_GMAC_CON0; + + regmap_write(data->grf, offset_con, RK3576_GMAC_RGMII_MODE); + + offset_con = data->id == 1 ? RK3576_VCCIO0_1_3_IOC_CON4 : + RK3576_VCCIO0_1_3_IOC_CON2; + + /* m0 && m1 delay enabled */ + regmap_write(data->php_grf, offset_con, + DELAY_ENABLE(RK3576, tx_delay, rx_delay)); + regmap_write(data->php_grf, offset_con + 0x4, + DELAY_ENABLE(RK3576, tx_delay, rx_delay)); + + /* m0 && m1 delay value */ + regmap_write(data->php_grf, offset_con, + RK3576_GMAC_CLK_TX_DL_CFG(tx_delay) | + RK3576_GMAC_CLK_RX_DL_CFG(rx_delay)); + regmap_write(data->php_grf, offset_con + 0x4, + RK3576_GMAC_CLK_TX_DL_CFG(tx_delay) | + RK3576_GMAC_CLK_RX_DL_CFG(rx_delay)); + + return 0; +} + +static int rk3576_set_to_rmii(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + u32 offset_con; + + offset_con = data->id == 1 ? RK3576_GRF_GMAC_CON1 : + RK3576_GRF_GMAC_CON0; + + regmap_write(data->grf, offset_con, RK3576_GMAC_RMII_MODE); + + return 0; +} + +static int rk3576_set_gmac_speed(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + u32 val = 0, offset_con; + + switch (eqos->phy->speed) { + case SPEED_10: + if (pdata->phy_interface == PHY_INTERFACE_MODE_RMII) + val = RK3576_GMAC_CLK_RMII_DIV20; + else + val = RK3576_GMAC_CLK_RGMII_DIV50; + break; + case SPEED_100: + if (pdata->phy_interface == PHY_INTERFACE_MODE_RMII) + val = RK3576_GMAC_CLK_RMII_DIV2; + else + val = RK3576_GMAC_CLK_RGMII_DIV5; + break; + case SPEED_1000: + if (pdata->phy_interface != PHY_INTERFACE_MODE_RMII) + val = RK3576_GMAC_CLK_RGMII_DIV1; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + offset_con = data->id == 1 ? RK3576_GRF_GMAC_CON1 : + RK3576_GRF_GMAC_CON0; + + regmap_write(data->grf, offset_con, val); + + return 0; +} + +static void rk3576_set_clock_selection(struct udevice *dev, bool enable) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + + u32 val = data->clock_input ? RK3576_GMAC_CLK_SELECT_IO : + RK3576_GMAC_CLK_SELECT_CRU; + u32 offset_con; + + val |= enable ? RK3576_GMAC_CLK_RMII_NOGATE : + RK3576_GMAC_CLK_RMII_GATE; + + offset_con = data->id == 1 ? RK3576_GRF_GMAC_CON1 : + RK3576_GRF_GMAC_CON0; + + regmap_write(data->grf, offset_con, val); +} + #define RK3588_DELAY_ENABLE(id, tx, rx) \ (((tx) ? RK3588_GMAC_TXCLK_DLY_ENABLE(id) : RK3588_GMAC_TXCLK_DLY_DISABLE(id)) | \ ((rx) ? RK3588_GMAC_RXCLK_DLY_ENABLE(id) : RK3588_GMAC_RXCLK_DLY_DISABLE(id))) @@ -270,6 +535,18 @@ static void rk3588_set_clock_selection(struct udevice *dev, bool enable) static const struct rk_gmac_ops rk_gmac_ops[] = { { + .compatible = "rockchip,rk3528-gmac", + .set_to_rgmii = rk3528_set_to_rgmii, + .set_to_rmii = rk3528_set_to_rmii, + .set_gmac_speed = rk3528_set_gmac_speed, + .set_clock_selection = rk3528_set_clock_selection, + .regs = { + 0xffbd0000, /* gmac0 */ + 0xffbe0000, /* gmac1 */ + 0x0, /* sentinel */ + }, + }, + { .compatible = "rockchip,rk3568-gmac", .set_to_rgmii = rk3568_set_to_rgmii, .set_to_rmii = rk3568_set_to_rmii, @@ -281,6 +558,18 @@ static const struct rk_gmac_ops rk_gmac_ops[] = { }, }, { + .compatible = "rockchip,rk3576-gmac", + .set_to_rgmii = rk3576_set_to_rgmii, + .set_to_rmii = rk3576_set_to_rmii, + .set_gmac_speed = rk3576_set_gmac_speed, + .set_clock_selection = rk3576_set_clock_selection, + .regs = { + 0x2a220000, /* gmac0 */ + 0x2a230000, /* gmac1 */ + 0x0, /* sentinel */ + }, + }, + { .compatible = "rockchip,rk3588-gmac", .set_to_rgmii = rk3588_set_to_rgmii, .set_to_rmii = rk3588_set_to_rmii, @@ -357,7 +646,8 @@ static int eqos_probe_resources_rk(struct udevice *dev) goto err_free; } - if (device_is_compatible(dev, "rockchip,rk3588-gmac")) { + if (device_is_compatible(dev, "rockchip,rk3588-gmac") || + device_is_compatible(dev, "rockchip,rk3576-gmac")) { data->php_grf = syscon_regmap_lookup_by_phandle(dev, "rockchip,php-grf"); if (IS_ERR(data->php_grf)) { diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index 8f432b8637b..b77298070f8 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -4830,6 +4830,7 @@ static int e1000_set_phy_type (struct e1000_hw *hw) hw->phy_type = e1000_phy_igp; break; } + fallthrough; case IGP03E1000_E_PHY_ID: hw->phy_type = e1000_phy_igp_3; break; @@ -4843,6 +4844,7 @@ static int e1000_set_phy_type (struct e1000_hw *hw) hw->phy_type = e1000_phy_gg82563; break; } + fallthrough; case BME1000_E_PHY_ID: hw->phy_type = e1000_phy_bm; break; diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c index c48ae6e88f3..ee8eae1efd9 100644 --- a/drivers/net/phy/micrel_ksz90x1.c +++ b/drivers/net/phy/micrel_ksz90x1.c @@ -389,10 +389,126 @@ U_BOOT_PHY_DRIVER(ksz9031) = { #define KSZ9131RN_DLL_ENABLE_DELAY 0 #define KSZ9131RN_DLL_DISABLE_DELAY BIT(12) +#define KSZ9131RN_COMMON_CTRL 0 +#define KSZ9131RN_COMMON_CTRL_INDIVIDUAL_LED_MODE BIT(4) + +#define KSZ9131RN_LED_ERRATA_REG 0x1e +#define KSZ9131RN_LED_ERRATA_BIT BIT(9) + +#define KSZ9131RN_CONTROL_PAD_SKEW 4 +#define KSZ9131RN_RX_DATA_PAD_SKEW 5 +#define KSZ9131RN_TX_DATA_PAD_SKEW 6 +#define KSZ9131RN_CLK_PAD_SKEW 8 + +#define KSZ9131RN_SKEW_5BIT_MAX 2400 +#define KSZ9131RN_SKEW_4BIT_MAX 800 +#define KSZ9131RN_OFFSET 700 +#define KSZ9131RN_STEP 100 + +static int ksz9131_of_load_skew_values(struct phy_device *phydev, + ofnode of_node, + u16 reg, size_t field_sz, + const char *field[], u8 numfields) +{ + int val[4] = {-(1 + KSZ9131RN_OFFSET), -(2 + KSZ9131RN_OFFSET), + -(3 + KSZ9131RN_OFFSET), -(4 + KSZ9131RN_OFFSET)}; + int skewval, skewmax = 0; + int matches = 0; + u16 maxval; + u16 newval; + u16 mask; + int i; + + /* psec properties in dts should mean x pico seconds */ + if (field_sz == 5) + skewmax = KSZ9131RN_SKEW_5BIT_MAX; + else + skewmax = KSZ9131RN_SKEW_4BIT_MAX; + + for (i = 0; i < numfields; i++) + if (!ofnode_read_s32(of_node, field[i], &skewval)) { + if (skewval < -KSZ9131RN_OFFSET) + skewval = -KSZ9131RN_OFFSET; + else if (skewval > skewmax) + skewval = skewmax; + + val[i] = skewval + KSZ9131RN_OFFSET; + matches++; + } + + if (!matches) + return 0; + + if (matches < numfields) + newval = phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, reg); + else + newval = 0; + + maxval = (field_sz == 4) ? 0xf : 0x1f; + for (i = 0; i < numfields; i++) + if (val[i] != -(i + 1 + KSZ9131RN_OFFSET)) { + mask = 0xffff; + mask ^= maxval << (field_sz * i); + newval = (newval & mask) | + (((val[i] / KSZ9131RN_STEP) & maxval) + << (field_sz * i)); + } + + return phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, reg, newval); +} + +static int ksz9131_of_load_all_skew_values(struct phy_device *phydev) +{ + const char *control_skews[2] = { "txen-skew-psec", "rxdv-skew-psec" }; + const char *clk_skews[2] = { "rxc-skew-psec", "txc-skew-psec" }; + const char *rx_data_skews[4] = { + "rxd0-skew-psec", "rxd1-skew-psec", + "rxd2-skew-psec", "rxd3-skew-psec" + }; + const char *tx_data_skews[4] = { + "txd0-skew-psec", "txd1-skew-psec", + "txd2-skew-psec", "txd3-skew-psec" + }; + struct ofnode_phandle_args phandle_args; + int ret; + + /* + * Silently ignore failure here as the device tree is not required to + * contain a phy node. + */ + if (dev_read_phandle_with_args(phydev->dev, "phy-handle", NULL, 0, 0, + &phandle_args)) + return 0; + + if (!ofnode_valid(phandle_args.node)) + return 0; + + ret = ksz9131_of_load_skew_values(phydev, phandle_args.node, + KSZ9131RN_CLK_PAD_SKEW, 5, + clk_skews, 2); + if (ret < 0) + return ret; + + ret = ksz9131_of_load_skew_values(phydev, phandle_args.node, + KSZ9131RN_CONTROL_PAD_SKEW, 4, + control_skews, 2); + if (ret < 0) + return ret; + + ret = ksz9131_of_load_skew_values(phydev, phandle_args.node, + KSZ9131RN_RX_DATA_PAD_SKEW, 4, + rx_data_skews, 4); + if (ret < 0) + return ret; + + return ksz9131_of_load_skew_values(phydev, phandle_args.node, + KSZ9131RN_TX_DATA_PAD_SKEW, 4, + tx_data_skews, 4); +} + static int ksz9131_config_rgmii_delay(struct phy_device *phydev) { - struct phy_driver *drv = phydev->drv; - u16 rxcdll_val, txcdll_val, val; + u16 rxcdll_val, txcdll_val; int ret; switch (phydev->interface) { @@ -416,24 +532,37 @@ static int ksz9131_config_rgmii_delay(struct phy_device *phydev) return 0; } - val = drv->readext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG, - KSZ9131RN_RXC_DLL_CTRL); - val &= ~KSZ9131RN_DLL_CTRL_BYPASS; - val |= rxcdll_val; - ret = drv->writeext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG, - KSZ9131RN_RXC_DLL_CTRL, val); - if (ret) + ret = phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_RXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, + rxcdll_val); + if (ret < 0) return ret; - val = drv->readext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG, - KSZ9131RN_TXC_DLL_CTRL); + return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_TXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, + txcdll_val); +} + +/* Silicon Errata DS80000693B + * + * When LEDs are configured in Individual Mode, LED1 is ON in a no-link + * condition. Workaround is to set register 0x1e, bit 9, this way LED1 behaves + * according to the datasheet (off if there is no link). + */ +static int ksz9131_led_errata(struct phy_device *phydev) +{ + int reg; - val &= ~KSZ9131RN_DLL_CTRL_BYPASS; - val |= txcdll_val; - ret = drv->writeext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG, - KSZ9131RN_TXC_DLL_CTRL, val); + reg = phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_COMMON_CTRL); + if (reg < 0) + return reg; - return ret; + if (!(reg & KSZ9131RN_COMMON_CTRL_INDIVIDUAL_LED_MODE)) + return 0; + + return phy_set_bits(phydev, MDIO_DEVAD_NONE, KSZ9131RN_LED_ERRATA_REG, + KSZ9131RN_LED_ERRATA_BIT); } static int ksz9131_config(struct phy_device *phydev) @@ -446,6 +575,14 @@ static int ksz9131_config(struct phy_device *phydev) return ret; } + ret = ksz9131_of_load_all_skew_values(phydev); + if (ret < 0) + return ret; + + ret = ksz9131_led_errata(phydev); + if (ret < 0) + return ret; + /* add an option to disable the gigabit feature of this PHY */ if (env_get("disable_giga")) { unsigned features; diff --git a/drivers/net/phy/miiphybb.c b/drivers/net/phy/miiphybb.c index 9f5f9b12c9f..76463da7299 100644 --- a/drivers/net/phy/miiphybb.c +++ b/drivers/net/phy/miiphybb.c @@ -17,37 +17,13 @@ #include <miiphy.h> #include <asm/global_data.h> -int bb_miiphy_init(void) -{ - int i; - - for (i = 0; i < bb_miiphy_buses_num; i++) - if (bb_miiphy_buses[i].init != NULL) - bb_miiphy_buses[i].init(&bb_miiphy_buses[i]); - - return 0; -} - -static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname) -{ - int i; - - /* Search the correct bus */ - for (i = 0; i < bb_miiphy_buses_num; i++) { - if (!strcmp(bb_miiphy_buses[i].name, devname)) { - return &bb_miiphy_buses[i]; - } - } - return NULL; -} - /***************************************************************************** * * Utility to send the preamble, address, and register (common to read * and write). */ -static void miiphy_pre(struct bb_miiphy_bus *bus, char read, - unsigned char addr, unsigned char reg) +static void miiphy_pre(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops, + char read, unsigned char addr, unsigned char reg) { int j; @@ -59,62 +35,62 @@ static void miiphy_pre(struct bb_miiphy_bus *bus, char read, * but it is safer and will be much more robust. */ - bus->mdio_active(bus); - bus->set_mdio(bus, 1); + ops->mdio_active(miidev); + ops->set_mdio(miidev, 1); for (j = 0; j < 32; j++) { - bus->set_mdc(bus, 0); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); } /* send the start bit (01) and the read opcode (10) or write (10) */ - bus->set_mdc(bus, 0); - bus->set_mdio(bus, 0); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 0); - bus->set_mdio(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 0); - bus->set_mdio(bus, read); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 0); - bus->set_mdio(bus, !read); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->set_mdio(miidev, 0); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 0); + ops->set_mdio(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 0); + ops->set_mdio(miidev, read); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 0); + ops->set_mdio(miidev, !read); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); /* send the PHY address */ for (j = 0; j < 5; j++) { - bus->set_mdc(bus, 0); + ops->set_mdc(miidev, 0); if ((addr & 0x10) == 0) { - bus->set_mdio(bus, 0); + ops->set_mdio(miidev, 0); } else { - bus->set_mdio(bus, 1); + ops->set_mdio(miidev, 1); } - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); addr <<= 1; } /* send the register address */ for (j = 0; j < 5; j++) { - bus->set_mdc(bus, 0); + ops->set_mdc(miidev, 0); if ((reg & 0x10) == 0) { - bus->set_mdio(bus, 0); + ops->set_mdio(miidev, 0); } else { - bus->set_mdio(bus, 1); + ops->set_mdio(miidev, 1); } - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); reg <<= 1; } } @@ -126,62 +102,57 @@ static void miiphy_pre(struct bb_miiphy_bus *bus, char read, * Returns: * 0 on success */ -int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg) +int bb_miiphy_read(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops, + int addr, int devad, int reg) { unsigned short rdreg; /* register working value */ int v; int j; /* counter */ - struct bb_miiphy_bus *bus; - bus = bb_miiphy_getbus(miidev->name); - if (bus == NULL) { - return -1; - } - - miiphy_pre (bus, 1, addr, reg); + miiphy_pre(miidev, ops, 1, addr, reg); /* tri-state our MDIO I/O pin so we can read */ - bus->set_mdc(bus, 0); - bus->mdio_tristate(bus); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->mdio_tristate(miidev); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); /* check the turnaround bit: the PHY should be driving it to zero */ - bus->get_mdio(bus, &v); + ops->get_mdio(miidev, &v); if (v != 0) { /* puts ("PHY didn't drive TA low\n"); */ for (j = 0; j < 32; j++) { - bus->set_mdc(bus, 0); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); } /* There is no PHY, return */ return -1; } - bus->set_mdc(bus, 0); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->delay(miidev); /* read 16 bits of register data, MSB first */ rdreg = 0; for (j = 0; j < 16; j++) { - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 1); + ops->delay(miidev); rdreg <<= 1; - bus->get_mdio(bus, &v); + ops->get_mdio(miidev, &v); rdreg |= (v & 0x1); - bus->set_mdc(bus, 0); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->delay(miidev); } - bus->set_mdc(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 0); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 0); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); debug("%s[%s](0x%x) @ 0x%x = 0x%04x\n", __func__, miidev->name, reg, addr, rdreg); @@ -195,54 +166,47 @@ int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg) * Returns: * 0 on success */ -int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg, - u16 value) +int bb_miiphy_write(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops, + int addr, int devad, int reg, u16 value) { - struct bb_miiphy_bus *bus; int j; /* counter */ - bus = bb_miiphy_getbus(miidev->name); - if (bus == NULL) { - /* Bus not found! */ - return -1; - } - - miiphy_pre (bus, 0, addr, reg); + miiphy_pre(miidev, ops, 0, addr, reg); /* send the turnaround (10) */ - bus->set_mdc(bus, 0); - bus->set_mdio(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 0); - bus->set_mdio(bus, 0); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->set_mdio(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 0); + ops->set_mdio(miidev, 0); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); /* write 16 bits of register data, MSB first */ for (j = 0; j < 16; j++) { - bus->set_mdc(bus, 0); + ops->set_mdc(miidev, 0); if ((value & 0x00008000) == 0) { - bus->set_mdio(bus, 0); + ops->set_mdio(miidev, 0); } else { - bus->set_mdio(bus, 1); + ops->set_mdio(miidev, 1); } - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); value <<= 1; } /* * Tri-state the MDIO line. */ - bus->mdio_tristate(bus); - bus->set_mdc(bus, 0); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->mdio_tristate(miidev); + ops->set_mdc(miidev, 0); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); return 0; } diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c index 7286ad19598..539fd37ee59 100644 --- a/drivers/net/ravb.c +++ b/drivers/net/ravb.c @@ -354,8 +354,15 @@ static int ravb_mac_init(struct ravb_priv *eth) /* Disable MAC Interrupt */ writel(0, eth->iobase + RAVB_REG_ECSIPR); - /* Recv frame limit set register */ - writel(RFLR_RFL_MIN, eth->iobase + RAVB_REG_RFLR); + /* + * Set receive frame length + * + * The length set here describes the frame from the destination address + * up to and including the CRC data. However only the frame data, + * excluding the CRC, are transferred to memory. To allow for the + * largest frames add the CRC length to the maximum Rx descriptor size. + */ + writel(RFLR_RFL_MIN + ETH_FCS_LEN, eth->iobase + RAVB_REG_RFLR); return 0; } @@ -490,6 +497,88 @@ static void ravb_stop(struct udevice *dev) ravb_reset(dev); } +/* Bitbang MDIO access */ +static int ravb_bb_mdio_active(struct mii_dev *miidev) +{ + struct ravb_priv *eth = miidev->priv; + + setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MMD); + + return 0; +} + +static int ravb_bb_mdio_tristate(struct mii_dev *miidev) +{ + struct ravb_priv *eth = miidev->priv; + + clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MMD); + + return 0; +} + +static int ravb_bb_set_mdio(struct mii_dev *miidev, int v) +{ + struct ravb_priv *eth = miidev->priv; + + if (v) + setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDO); + else + clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDO); + + return 0; +} + +static int ravb_bb_get_mdio(struct mii_dev *miidev, int *v) +{ + struct ravb_priv *eth = miidev->priv; + + *v = (readl(eth->iobase + RAVB_REG_PIR) & PIR_MDI) >> 3; + + return 0; +} + +static int ravb_bb_set_mdc(struct mii_dev *miidev, int v) +{ + struct ravb_priv *eth = miidev->priv; + + if (v) + setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDC); + else + clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDC); + + return 0; +} + +static int ravb_bb_delay(struct mii_dev *miidev) +{ + udelay(10); + + return 0; +} + +static const struct bb_miiphy_bus_ops ravb_bb_miiphy_bus_ops = { + .mdio_active = ravb_bb_mdio_active, + .mdio_tristate = ravb_bb_mdio_tristate, + .set_mdio = ravb_bb_set_mdio, + .get_mdio = ravb_bb_get_mdio, + .set_mdc = ravb_bb_set_mdc, + .delay = ravb_bb_delay, +}; + +static int ravb_bb_miiphy_read(struct mii_dev *miidev, int addr, + int devad, int reg) +{ + return bb_miiphy_read(miidev, &ravb_bb_miiphy_bus_ops, + addr, devad, reg); +} + +static int ravb_bb_miiphy_write(struct mii_dev *miidev, int addr, + int devad, int reg, u16 value) +{ + return bb_miiphy_write(miidev, &ravb_bb_miiphy_bus_ops, + addr, devad, reg, value); +} + static int ravb_probe(struct udevice *dev) { struct eth_pdata *pdata = dev_get_plat(dev); @@ -503,7 +592,7 @@ static int ravb_probe(struct udevice *dev) ret = clk_get_bulk(dev, ð->clks); if (ret < 0) - goto err_mdio_alloc; + goto err_clk_get; mdiodev = mdio_alloc(); if (!mdiodev) { @@ -511,37 +600,39 @@ static int ravb_probe(struct udevice *dev) goto err_mdio_alloc; } - mdiodev->read = bb_miiphy_read; - mdiodev->write = bb_miiphy_write; - bb_miiphy_buses[0].priv = eth; + mdiodev->read = ravb_bb_miiphy_read; + mdiodev->write = ravb_bb_miiphy_write; + mdiodev->priv = eth; snprintf(mdiodev->name, sizeof(mdiodev->name), dev->name); ret = mdio_register(mdiodev); if (ret < 0) goto err_mdio_register; - eth->bus = miiphy_get_dev_by_name(dev->name); + eth->bus = mdiodev; /* Bring up PHY */ ret = clk_enable_bulk(ð->clks); if (ret) - goto err_mdio_register; + goto err_clk_enable; ret = ravb_reset(dev); if (ret) - goto err_mdio_reset; + goto err_clk_enable; ret = ravb_phy_config(dev); if (ret) - goto err_mdio_reset; + goto err_clk_enable; return 0; -err_mdio_reset: - clk_release_bulk(ð->clks); +err_clk_enable: + mdio_unregister(mdiodev); err_mdio_register: mdio_free(mdiodev); err_mdio_alloc: + clk_release_bulk(ð->clks); +err_clk_get: unmap_physmem(eth->iobase, MAP_NOCACHE); return ret; } @@ -560,83 +651,6 @@ static int ravb_remove(struct udevice *dev) return 0; } -static int ravb_bb_init(struct bb_miiphy_bus *bus) -{ - return 0; -} - -static int ravb_bb_mdio_active(struct bb_miiphy_bus *bus) -{ - struct ravb_priv *eth = bus->priv; - - setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MMD); - - return 0; -} - -static int ravb_bb_mdio_tristate(struct bb_miiphy_bus *bus) -{ - struct ravb_priv *eth = bus->priv; - - clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MMD); - - return 0; -} - -static int ravb_bb_set_mdio(struct bb_miiphy_bus *bus, int v) -{ - struct ravb_priv *eth = bus->priv; - - if (v) - setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDO); - else - clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDO); - - return 0; -} - -static int ravb_bb_get_mdio(struct bb_miiphy_bus *bus, int *v) -{ - struct ravb_priv *eth = bus->priv; - - *v = (readl(eth->iobase + RAVB_REG_PIR) & PIR_MDI) >> 3; - - return 0; -} - -static int ravb_bb_set_mdc(struct bb_miiphy_bus *bus, int v) -{ - struct ravb_priv *eth = bus->priv; - - if (v) - setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDC); - else - clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDC); - - return 0; -} - -static int ravb_bb_delay(struct bb_miiphy_bus *bus) -{ - udelay(10); - - return 0; -} - -struct bb_miiphy_bus bb_miiphy_buses[] = { - { - .name = "ravb", - .init = ravb_bb_init, - .mdio_active = ravb_bb_mdio_active, - .mdio_tristate = ravb_bb_mdio_tristate, - .set_mdio = ravb_bb_set_mdio, - .get_mdio = ravb_bb_get_mdio, - .set_mdc = ravb_bb_set_mdc, - .delay = ravb_bb_delay, - }, -}; -int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses); - static const struct eth_ops ravb_ops = { .start = ravb_start, .send = ravb_send, @@ -658,8 +672,6 @@ int ravb_of_to_plat(struct udevice *dev) pdata->max_speed = dev_read_u32_default(dev, "max-speed", 1000); - sprintf(bb_miiphy_buses[0].name, dev->name); - return 0; } diff --git a/drivers/net/sandbox-lwip.c b/drivers/net/sandbox-lwip.c deleted file mode 100644 index 3721033c310..00000000000 --- a/drivers/net/sandbox-lwip.c +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2015 National Instruments - * - * (C) Copyright 2015 - * Joe Hershberger <joe.hershberger@ni.com> - */ - -#include <dm.h> -#include <log.h> -#include <malloc.h> -#include <net.h> -#include <asm/eth.h> -#include <asm/global_data.h> -#include <asm/test.h> - -DECLARE_GLOBAL_DATA_PTR; - -static int sb_lwip_eth_start(struct udevice *dev) -{ - debug("eth_sandbox_lwip: Start\n"); - - return 0; -} - -static int sb_lwip_eth_send(struct udevice *dev, void *packet, int length) -{ - debug("eth_sandbox_lwip: Send packet %d\n", length); - - return -ENOTSUPP; -} - -static int sb_lwip_eth_recv(struct udevice *dev, int flags, uchar **packetp) -{ - return -EAGAIN; -} - -static int sb_lwip_eth_free_pkt(struct udevice *dev, uchar *packet, int length) -{ - return 0; -} - -static void sb_lwip_eth_stop(struct udevice *dev) -{ -} - -static int sb_lwip_eth_write_hwaddr(struct udevice *dev) -{ - return 0; -} - -static const struct eth_ops sb_eth_ops = { - .start = sb_lwip_eth_start, - .send = sb_lwip_eth_send, - .recv = sb_lwip_eth_recv, - .free_pkt = sb_lwip_eth_free_pkt, - .stop = sb_lwip_eth_stop, - .write_hwaddr = sb_lwip_eth_write_hwaddr, -}; - -static int sb_lwip_eth_remove(struct udevice *dev) -{ - return 0; -} - -static int sb_lwip_eth_of_to_plat(struct udevice *dev) -{ - return 0; -} - -static const struct udevice_id sb_eth_ids[] = { - { .compatible = "sandbox,eth" }, - { } -}; - -U_BOOT_DRIVER(eth_sandbox) = { - .name = "eth_lwip_sandbox", - .id = UCLASS_ETH, - .of_match = sb_eth_ids, - .of_to_plat = sb_lwip_eth_of_to_plat, - .remove = sb_lwip_eth_remove, - .ops = &sb_eth_ops, - .priv_auto = 0, - .plat_auto = sizeof(struct eth_pdata), -}; diff --git a/drivers/net/sandbox.c b/drivers/net/sandbox.c index fe3627db6e3..2011fd31f41 100644 --- a/drivers/net/sandbox.c +++ b/drivers/net/sandbox.c @@ -9,13 +9,84 @@ #include <dm.h> #include <log.h> #include <malloc.h> -#include <net.h> #include <asm/eth.h> #include <asm/global_data.h> #include <asm/test.h> +#include <asm/types.h> + +/* + * Structure definitions for network protocols. Since this file is used for + * both NET and NET_LWIP, and given that the two network stacks do have + * conflicting types (for instance struct icmp_hdr), it is on purpose that the + * structures are defined locally with minimal dependencies -- <asm/types.h> is + * included for the bit types and that's it. + */ + +#define ETHADDR_LEN 6 +#define IP4_LEN 4 + +struct ethhdr { + u8 dst[ETHADDR_LEN]; + u8 src[ETHADDR_LEN]; + u16 protlen; +} __attribute__((packed)); + +#define ETHHDR_SIZE (sizeof(struct ethhdr)) + +struct arphdr { + u16 htype; + u16 ptype; + u8 hlen; + u8 plen; + u16 op; +} __attribute__((packed)); + +#define ARPHDR_SIZE (sizeof(struct arphdr)) + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +struct arpdata { + u8 sha[ETHADDR_LEN]; + u32 spa; + u8 tha[ETHADDR_LEN]; + u32 tpa; +} __attribute__((packed)); + +#define ARPDATA_SIZE (sizeof(struct arpdata)) + +struct iphdr { + u8 hl_v; + u8 tos; + u16 len; + u16 id; + u16 off; + u8 ttl; + u8 prot; + u16 sum; + u32 src; + u32 dst; +} __attribute__((packed)); + +#define IPHDR_SIZE (sizeof(struct iphdr)) + +struct icmphdr { + u8 type; + u8 code; + u16 checksum; + u16 id; + u16 sequence; +} __attribute__((packed)); + +#define ICMPHDR_SIZE (sizeof(struct icmphdr)) + +#define ICMP_ECHO_REQUEST 8 +#define ICMP_ECHO_REPLY 0 +#define IPPROTO_ICMP 1 DECLARE_GLOBAL_DATA_PTR; +static const u8 null_ethaddr[6]; static bool skip_timeout; /* @@ -59,17 +130,19 @@ int sandbox_eth_arp_req_to_reply(struct udevice *dev, void *packet, unsigned int len) { struct eth_sandbox_priv *priv = dev_get_priv(dev); - struct ethernet_hdr *eth = packet; - struct arp_hdr *arp; - struct ethernet_hdr *eth_recv; - struct arp_hdr *arp_recv; - - if (ntohs(eth->et_protlen) != PROT_ARP) + struct ethhdr *eth = packet; + struct arphdr *arp; + struct arpdata *arpd; + struct ethhdr *eth_recv; + struct arphdr *arp_recv; + struct arpdata *arp_recvd; + + if (ntohs(eth->protlen) != PROT_ARP) return -EAGAIN; - arp = packet + ETHER_HDR_SIZE; + arp = packet + ETHHDR_SIZE; - if (ntohs(arp->ar_op) != ARPOP_REQUEST) + if (ntohs(arp->op) != ARP_REQUEST) return -EAGAIN; /* Don't allow the buffer to overrun */ @@ -77,27 +150,29 @@ int sandbox_eth_arp_req_to_reply(struct udevice *dev, void *packet, return 0; /* store this as the assumed IP of the fake host */ - priv->fake_host_ipaddr = net_read_ip(&arp->ar_tpa); + arpd = (struct arpdata *)(arp + 1); + priv->fake_host_ipaddr.s_addr = arpd->tpa; /* Formulate a fake response */ eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets]; - memcpy(eth_recv->et_dest, eth->et_src, ARP_HLEN); - memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN); - eth_recv->et_protlen = htons(PROT_ARP); - - arp_recv = (void *)eth_recv + ETHER_HDR_SIZE; - arp_recv->ar_hrd = htons(ARP_ETHER); - arp_recv->ar_pro = htons(PROT_IP); - arp_recv->ar_hln = ARP_HLEN; - arp_recv->ar_pln = ARP_PLEN; - arp_recv->ar_op = htons(ARPOP_REPLY); - memcpy(&arp_recv->ar_sha, priv->fake_host_hwaddr, ARP_HLEN); - net_write_ip(&arp_recv->ar_spa, priv->fake_host_ipaddr); - memcpy(&arp_recv->ar_tha, &arp->ar_sha, ARP_HLEN); - net_copy_ip(&arp_recv->ar_tpa, &arp->ar_spa); - - priv->recv_packet_length[priv->recv_packets] = - ETHER_HDR_SIZE + ARP_HDR_SIZE; + memcpy(eth_recv->dst, eth->src, ETHADDR_LEN); + memcpy(eth_recv->src, priv->fake_host_hwaddr, ETHADDR_LEN); + eth_recv->protlen = htons(PROT_ARP); + + arp_recv = (void *)eth_recv + ETHHDR_SIZE; + arp_recv->htype = htons(ARP_ETHER); + arp_recv->ptype = htons(PROT_IP); + arp_recv->hlen = ETHADDR_LEN; + arp_recv->plen = IP4_LEN; + arp_recv->op = htons(ARP_REPLY); + arp_recvd = (struct arpdata *)(arp_recv + 1); + memcpy(&arp_recvd->sha, priv->fake_host_hwaddr, ETHADDR_LEN); + arp_recvd->spa = priv->fake_host_ipaddr.s_addr; + memcpy(&arp_recvd->tha, &arpd->sha, ETHADDR_LEN); + arp_recvd->tpa = arpd->spa; + + priv->recv_packet_length[priv->recv_packets] = ETHHDR_SIZE + + ARPHDR_SIZE + ARPDATA_SIZE; ++priv->recv_packets; return 0; @@ -114,22 +189,22 @@ int sandbox_eth_ping_req_to_reply(struct udevice *dev, void *packet, unsigned int len) { struct eth_sandbox_priv *priv = dev_get_priv(dev); - struct ethernet_hdr *eth = packet; - struct ip_udp_hdr *ip; - struct icmp_hdr *icmp; - struct ethernet_hdr *eth_recv; - struct ip_udp_hdr *ipr; - struct icmp_hdr *icmpr; - - if (ntohs(eth->et_protlen) != PROT_IP) + struct ethhdr *eth = packet; + struct iphdr *ip; + struct icmphdr *icmp; + struct ethhdr *eth_recv; + struct iphdr *ipr; + struct icmphdr *icmpr; + + if (ntohs(eth->protlen) != PROT_IP) return -EAGAIN; - ip = packet + ETHER_HDR_SIZE; + ip = packet + ETHHDR_SIZE; - if (ip->ip_p != IPPROTO_ICMP) + if (ip->prot != IPPROTO_ICMP) return -EAGAIN; - icmp = (struct icmp_hdr *)&ip->udp_src; + icmp = (struct icmphdr *)(ip + 1); if (icmp->type != ICMP_ECHO_REQUEST) return -EAGAIN; @@ -141,19 +216,19 @@ int sandbox_eth_ping_req_to_reply(struct udevice *dev, void *packet, /* reply to the ping */ eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets]; memcpy(eth_recv, packet, len); - ipr = (void *)eth_recv + ETHER_HDR_SIZE; - icmpr = (struct icmp_hdr *)&ipr->udp_src; - memcpy(eth_recv->et_dest, eth->et_src, ARP_HLEN); - memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN); - ipr->ip_sum = 0; - ipr->ip_off = 0; - net_copy_ip((void *)&ipr->ip_dst, &ip->ip_src); - net_write_ip((void *)&ipr->ip_src, priv->fake_host_ipaddr); - ipr->ip_sum = compute_ip_checksum(ipr, IP_HDR_SIZE); + ipr = (void *)eth_recv + ETHHDR_SIZE; + icmpr = (struct icmphdr *)(ipr + 1); + memcpy(eth_recv->dst, eth->src, ETHADDR_LEN); + memcpy(eth_recv->src, priv->fake_host_hwaddr, ETHADDR_LEN); + ipr->sum = 0; + ipr->off = 0; + ipr->dst = ip->src; + ipr->src = priv->fake_host_ipaddr.s_addr; + ipr->sum = compute_ip_checksum(ipr, IPHDR_SIZE); icmpr->type = ICMP_ECHO_REPLY; icmpr->checksum = 0; - icmpr->checksum = compute_ip_checksum(icmpr, ICMP_HDR_SIZE); + icmpr->checksum = compute_ip_checksum(icmpr, ICMPHDR_SIZE); priv->recv_packet_length[priv->recv_packets] = len; ++priv->recv_packets; @@ -171,8 +246,9 @@ int sandbox_eth_ping_req_to_reply(struct udevice *dev, void *packet, int sandbox_eth_recv_arp_req(struct udevice *dev) { struct eth_sandbox_priv *priv = dev_get_priv(dev); - struct ethernet_hdr *eth_recv; - struct arp_hdr *arp_recv; + struct ethhdr *eth_recv; + struct arphdr *arp_recv; + struct arpdata *arp_recvd; /* Don't allow the buffer to overrun */ if (priv->recv_packets >= PKTBUFSRX) @@ -180,23 +256,24 @@ int sandbox_eth_recv_arp_req(struct udevice *dev) /* Formulate a fake request */ eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets]; - memcpy(eth_recv->et_dest, net_bcast_ethaddr, ARP_HLEN); - memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN); - eth_recv->et_protlen = htons(PROT_ARP); - - arp_recv = (void *)eth_recv + ETHER_HDR_SIZE; - arp_recv->ar_hrd = htons(ARP_ETHER); - arp_recv->ar_pro = htons(PROT_IP); - arp_recv->ar_hln = ARP_HLEN; - arp_recv->ar_pln = ARP_PLEN; - arp_recv->ar_op = htons(ARPOP_REQUEST); - memcpy(&arp_recv->ar_sha, priv->fake_host_hwaddr, ARP_HLEN); - net_write_ip(&arp_recv->ar_spa, priv->fake_host_ipaddr); - memcpy(&arp_recv->ar_tha, net_null_ethaddr, ARP_HLEN); - net_write_ip(&arp_recv->ar_tpa, net_ip); + memcpy(eth_recv->dst, net_bcast_ethaddr, ETHADDR_LEN); + memcpy(eth_recv->src, priv->fake_host_hwaddr, ETHADDR_LEN); + eth_recv->protlen = htons(PROT_ARP); + + arp_recv = (void *)eth_recv + ETHHDR_SIZE; + arp_recv->htype = htons(ARP_ETHER); + arp_recv->ptype = htons(PROT_IP); + arp_recv->hlen = ETHADDR_LEN; + arp_recv->plen = IP4_LEN; + arp_recv->op = htons(ARP_REQUEST); + arp_recvd = (struct arpdata *)(arp_recv + 1); + memcpy(&arp_recvd->sha, priv->fake_host_hwaddr, ETHADDR_LEN); + arp_recvd->spa = priv->fake_host_ipaddr.s_addr; + memcpy(&arp_recvd->tha, null_ethaddr, ETHADDR_LEN); + arp_recvd->tpa = net_ip.s_addr; priv->recv_packet_length[priv->recv_packets] = - ETHER_HDR_SIZE + ARP_HDR_SIZE; + ETHHDR_SIZE + ARPHDR_SIZE + ARPDATA_SIZE; ++priv->recv_packets; return 0; @@ -212,9 +289,10 @@ int sandbox_eth_recv_arp_req(struct udevice *dev) int sandbox_eth_recv_ping_req(struct udevice *dev) { struct eth_sandbox_priv *priv = dev_get_priv(dev); - struct ethernet_hdr *eth_recv; - struct ip_udp_hdr *ipr; - struct icmp_hdr *icmpr; + struct eth_pdata *pdata = dev_get_plat(dev); + struct ethhdr *eth_recv; + struct iphdr *ipr; + struct icmphdr *icmpr; /* Don't allow the buffer to overrun */ if (priv->recv_packets >= PKTBUFSRX) @@ -223,31 +301,31 @@ int sandbox_eth_recv_ping_req(struct udevice *dev) /* Formulate a fake ping */ eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets]; - memcpy(eth_recv->et_dest, net_ethaddr, ARP_HLEN); - memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN); - eth_recv->et_protlen = htons(PROT_IP); + memcpy(eth_recv->dst, pdata->enetaddr, ETHADDR_LEN); + memcpy(eth_recv->src, priv->fake_host_hwaddr, ETHADDR_LEN); + eth_recv->protlen = htons(PROT_IP); - ipr = (void *)eth_recv + ETHER_HDR_SIZE; - ipr->ip_hl_v = 0x45; - ipr->ip_len = htons(IP_ICMP_HDR_SIZE); - ipr->ip_off = htons(IP_FLAGS_DFRAG); - ipr->ip_p = IPPROTO_ICMP; - ipr->ip_sum = 0; - net_write_ip(&ipr->ip_src, priv->fake_host_ipaddr); - net_write_ip(&ipr->ip_dst, net_ip); - ipr->ip_sum = compute_ip_checksum(ipr, IP_HDR_SIZE); + ipr = (void *)eth_recv + ETHHDR_SIZE; + ipr->hl_v = 0x45; + ipr->len = htons(IPHDR_SIZE + ICMPHDR_SIZE); + ipr->off = htons(IP_FLAGS_DFRAG); + ipr->prot = IPPROTO_ICMP; + ipr->sum = 0; + ipr->src = priv->fake_host_ipaddr.s_addr; + ipr->dst = net_ip.s_addr; + ipr->sum = compute_ip_checksum(ipr, IPHDR_SIZE); - icmpr = (struct icmp_hdr *)&ipr->udp_src; + icmpr = (struct icmphdr *)(ipr + 1); icmpr->type = ICMP_ECHO_REQUEST; icmpr->code = 0; icmpr->checksum = 0; - icmpr->un.echo.id = 0; - icmpr->un.echo.sequence = htons(1); - icmpr->checksum = compute_ip_checksum(icmpr, ICMP_HDR_SIZE); + icmpr->id = 0; + icmpr->sequence = htons(1); + icmpr->checksum = compute_ip_checksum(icmpr, ICMPHDR_SIZE); priv->recv_packet_length[priv->recv_packets] = - ETHER_HDR_SIZE + IP_ICMP_HDR_SIZE; + ETHHDR_SIZE + IPHDR_SIZE + ICMPHDR_SIZE; ++priv->recv_packets; return 0; @@ -398,7 +476,7 @@ static int sb_eth_write_hwaddr(struct udevice *dev) debug("eth_sandbox %s: Write HW ADDR - %pM\n", dev->name, pdata->enetaddr); - memcpy(priv->fake_host_hwaddr, pdata->enetaddr, ARP_HLEN); + memcpy(priv->fake_host_hwaddr, pdata->enetaddr, ETHADDR_LEN); return 0; } diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index f1ce994cfd5..f695a3a41d2 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -643,6 +643,97 @@ static void sh_ether_stop(struct udevice *dev) sh_eth_stop(&priv->shdev); } +/******* for bb_miiphy *******/ +static int sh_eth_bb_mdio_active(struct mii_dev *miidev) +{ + struct sh_eth_dev *eth = miidev->priv; + struct sh_eth_info *port_info = ð->port_info[eth->port]; + + sh_eth_write(port_info, sh_eth_read(port_info, PIR) | PIR_MMD, PIR); + + return 0; +} + +static int sh_eth_bb_mdio_tristate(struct mii_dev *miidev) +{ + struct sh_eth_dev *eth = miidev->priv; + struct sh_eth_info *port_info = ð->port_info[eth->port]; + + sh_eth_write(port_info, sh_eth_read(port_info, PIR) & ~PIR_MMD, PIR); + + return 0; +} + +static int sh_eth_bb_set_mdio(struct mii_dev *miidev, int v) +{ + struct sh_eth_dev *eth = miidev->priv; + struct sh_eth_info *port_info = ð->port_info[eth->port]; + + if (v) + sh_eth_write(port_info, + sh_eth_read(port_info, PIR) | PIR_MDO, PIR); + else + sh_eth_write(port_info, + sh_eth_read(port_info, PIR) & ~PIR_MDO, PIR); + + return 0; +} + +static int sh_eth_bb_get_mdio(struct mii_dev *miidev, int *v) +{ + struct sh_eth_dev *eth = miidev->priv; + struct sh_eth_info *port_info = ð->port_info[eth->port]; + + *v = (sh_eth_read(port_info, PIR) & PIR_MDI) >> 3; + + return 0; +} + +static int sh_eth_bb_set_mdc(struct mii_dev *miidev, int v) +{ + struct sh_eth_dev *eth = miidev->priv; + struct sh_eth_info *port_info = ð->port_info[eth->port]; + + if (v) + sh_eth_write(port_info, + sh_eth_read(port_info, PIR) | PIR_MDC, PIR); + else + sh_eth_write(port_info, + sh_eth_read(port_info, PIR) & ~PIR_MDC, PIR); + + return 0; +} + +static int sh_eth_bb_delay(struct mii_dev *miidev) +{ + udelay(10); + + return 0; +} + +static const struct bb_miiphy_bus_ops sh_ether_bb_miiphy_bus_ops = { + .mdio_active = sh_eth_bb_mdio_active, + .mdio_tristate = sh_eth_bb_mdio_tristate, + .set_mdio = sh_eth_bb_set_mdio, + .get_mdio = sh_eth_bb_get_mdio, + .set_mdc = sh_eth_bb_set_mdc, + .delay = sh_eth_bb_delay, +}; + +static int sh_eth_bb_miiphy_read(struct mii_dev *miidev, int addr, + int devad, int reg) +{ + return bb_miiphy_read(miidev, &sh_ether_bb_miiphy_bus_ops, + addr, devad, reg); +} + +static int sh_eth_bb_miiphy_write(struct mii_dev *miidev, int addr, + int devad, int reg, u16 value) +{ + return bb_miiphy_write(miidev, &sh_ether_bb_miiphy_bus_ops, + addr, devad, reg, value); +} + static int sh_ether_probe(struct udevice *udev) { struct eth_pdata *pdata = dev_get_plat(udev); @@ -664,16 +755,16 @@ static int sh_ether_probe(struct udevice *udev) return ret; } - mdiodev->read = bb_miiphy_read; - mdiodev->write = bb_miiphy_write; - bb_miiphy_buses[0].priv = eth; + mdiodev->read = sh_eth_bb_miiphy_read; + mdiodev->write = sh_eth_bb_miiphy_write; + mdiodev->priv = eth; snprintf(mdiodev->name, sizeof(mdiodev->name), udev->name); ret = mdio_register(mdiodev); if (ret < 0) goto err_mdio_register; - priv->bus = miiphy_get_dev_by_name(udev->name); + priv->bus = mdiodev; eth->port = CFG_SH_ETHER_USE_PORT; eth->port_info[eth->port].phy_addr = CFG_SH_ETHER_PHY_ADDR; @@ -748,8 +839,6 @@ int sh_ether_of_to_plat(struct udevice *dev) if (cell) pdata->max_speed = fdt32_to_cpu(*cell); - sprintf(bb_miiphy_buses[0].name, dev->name); - return 0; } @@ -775,91 +864,3 @@ U_BOOT_DRIVER(eth_sh_ether) = { .plat_auto = sizeof(struct eth_pdata), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; - -/******* for bb_miiphy *******/ -static int sh_eth_bb_init(struct bb_miiphy_bus *bus) -{ - return 0; -} - -static int sh_eth_bb_mdio_active(struct bb_miiphy_bus *bus) -{ - struct sh_eth_dev *eth = bus->priv; - struct sh_eth_info *port_info = ð->port_info[eth->port]; - - sh_eth_write(port_info, sh_eth_read(port_info, PIR) | PIR_MMD, PIR); - - return 0; -} - -static int sh_eth_bb_mdio_tristate(struct bb_miiphy_bus *bus) -{ - struct sh_eth_dev *eth = bus->priv; - struct sh_eth_info *port_info = ð->port_info[eth->port]; - - sh_eth_write(port_info, sh_eth_read(port_info, PIR) & ~PIR_MMD, PIR); - - return 0; -} - -static int sh_eth_bb_set_mdio(struct bb_miiphy_bus *bus, int v) -{ - struct sh_eth_dev *eth = bus->priv; - struct sh_eth_info *port_info = ð->port_info[eth->port]; - - if (v) - sh_eth_write(port_info, - sh_eth_read(port_info, PIR) | PIR_MDO, PIR); - else - sh_eth_write(port_info, - sh_eth_read(port_info, PIR) & ~PIR_MDO, PIR); - - return 0; -} - -static int sh_eth_bb_get_mdio(struct bb_miiphy_bus *bus, int *v) -{ - struct sh_eth_dev *eth = bus->priv; - struct sh_eth_info *port_info = ð->port_info[eth->port]; - - *v = (sh_eth_read(port_info, PIR) & PIR_MDI) >> 3; - - return 0; -} - -static int sh_eth_bb_set_mdc(struct bb_miiphy_bus *bus, int v) -{ - struct sh_eth_dev *eth = bus->priv; - struct sh_eth_info *port_info = ð->port_info[eth->port]; - - if (v) - sh_eth_write(port_info, - sh_eth_read(port_info, PIR) | PIR_MDC, PIR); - else - sh_eth_write(port_info, - sh_eth_read(port_info, PIR) & ~PIR_MDC, PIR); - - return 0; -} - -static int sh_eth_bb_delay(struct bb_miiphy_bus *bus) -{ - udelay(10); - - return 0; -} - -struct bb_miiphy_bus bb_miiphy_buses[] = { - { - .name = "sh_eth", - .init = sh_eth_bb_init, - .mdio_active = sh_eth_bb_mdio_active, - .mdio_tristate = sh_eth_bb_mdio_tristate, - .set_mdio = sh_eth_bb_set_mdio, - .get_mdio = sh_eth_bb_get_mdio, - .set_mdc = sh_eth_bb_set_mdc, - .delay = sh_eth_bb_delay, - } -}; - -int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses); diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 0da182d9f4c..8433e7db265 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -335,6 +335,7 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata, reg |= SC_RMII_EN | SC_ETCS_EXT_GMII; break; } + fallthrough; default: debug("%s: Invalid PHY interface\n", __func__); return -EINVAL; diff --git a/drivers/net/ti/am65-cpsw-nuss.c b/drivers/net/ti/am65-cpsw-nuss.c index c70b42f6bcc..3c62fc0b428 100644 --- a/drivers/net/ti/am65-cpsw-nuss.c +++ b/drivers/net/ti/am65-cpsw-nuss.c @@ -661,7 +661,7 @@ static int am65_cpsw_ofdata_parse_phy(struct udevice *dev) dev_read_u32(dev, "max-speed", (u32 *)&pdata->max_speed); if (pdata->max_speed) - dev_err(dev, "Port %u speed froced to %uMbit\n", + dev_err(dev, "Port %u speed forced to %uMbit\n", priv->port_id, pdata->max_speed); return 0; diff --git a/drivers/nvme/Makefile b/drivers/nvme/Makefile index 8c32cfbfc0f..5f8ed79077d 100644 --- a/drivers/nvme/Makefile +++ b/drivers/nvme/Makefile @@ -4,4 +4,4 @@ obj-y += nvme-uclass.o nvme.o nvme_show.o obj-$(CONFIG_NVME_APPLE) += nvme_apple.o -obj-$(CONFIG_$(XPL_)NVME_PCI) += nvme_pci.o +obj-$(CONFIG_$(PHASE_)NVME_PCI) += nvme_pci.o diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 4f876d39875..409049137cc 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -67,6 +67,7 @@ config PCI_CONFIG_HOST_BRIDGE config PCI_MAP_SYSTEM_MEMORY bool "Map local system memory from a virtual base address" depends on MIPS + default y if !ARCH_MAP_SYSMEM help Say Y if base address of system memory is being used as a virtual address instead of a physical address (e.g. on MIPS). The PCI core will then remap @@ -75,6 +76,15 @@ config PCI_MAP_SYSTEM_MEMORY This should only be required on MIPS where CFG_SYS_SDRAM_BASE is still being used as virtual address. +config PCI_BRIDGE_MEM_ALIGNMENT + hex "Alignment boundary of PCI memory resource allocation" + default 0x10000 if TARGET_BOSTON + default 0x100000 + help + Specify a boundary for alignment of PCI memory resource allocation, + this is normally 0x100000 (1MB) but can be reduced to accommodate + hardware with tight bridge range if hardware allows. + config PCI_SRIOV bool "Enable Single Root I/O Virtualization support for PCI" help diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index 90f81886445..4a1c782be36 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -107,7 +107,8 @@ static void dm_pciauto_setup_device(struct udevice *dev, } if (prefetch && - (bar_response & PCI_BASE_ADDRESS_MEM_PREFETCH)) + (bar_response & PCI_BASE_ADDRESS_MEM_PREFETCH) && + (found_mem64 || prefetch->bus_lower < 0x100000000ULL)) bar_res = prefetch; else bar_res = mem; @@ -372,8 +373,8 @@ void dm_pciauto_prescan_setup_bridge(struct udevice *dev, int sub_bus) dm_pci_write_config8(dev, PCI_SUBORDINATE_BUS, 0xff); if (pci_mem) { - /* Round memory allocator to 1MB boundary */ - pciauto_region_align(pci_mem, 0x100000); + /* Round memory allocator */ + pciauto_region_align(pci_mem, CONFIG_PCI_BRIDGE_MEM_ALIGNMENT); /* * Set up memory and I/O filter limits, assume 32-bit @@ -387,8 +388,8 @@ void dm_pciauto_prescan_setup_bridge(struct udevice *dev, int sub_bus) } if (pci_prefetch) { - /* Round memory allocator to 1MB boundary */ - pciauto_region_align(pci_prefetch, 0x100000); + /* Round memory allocator */ + pciauto_region_align(pci_prefetch, CONFIG_PCI_BRIDGE_MEM_ALIGNMENT); /* * Set up memory and I/O filter limits, assume 32-bit @@ -465,8 +466,8 @@ void dm_pciauto_postscan_setup_bridge(struct udevice *dev, int sub_bus) dm_pci_write_config8(dev, PCI_SUBORDINATE_BUS, sub_bus - dev_seq(ctlr)); if (pci_mem) { - /* Round memory allocator to 1MB boundary */ - pciauto_region_align(pci_mem, 0x100000); + /* Round memory allocator */ + pciauto_region_align(pci_mem, CONFIG_PCI_BRIDGE_MEM_ALIGNMENT); dm_pci_write_config16(dev, PCI_MEMORY_LIMIT, ((pci_mem->bus_lower - 1) >> 16) & @@ -480,8 +481,8 @@ void dm_pciauto_postscan_setup_bridge(struct udevice *dev, int sub_bus) &prefechable_64); prefechable_64 &= PCI_PREF_RANGE_TYPE_MASK; - /* Round memory allocator to 1MB boundary */ - pciauto_region_align(pci_prefetch, 0x100000); + /* Round memory allocator */ + pciauto_region_align(pci_prefetch, CONFIG_PCI_BRIDGE_MEM_ALIGNMENT); dm_pci_write_config16(dev, PCI_PREF_MEMORY_LIMIT, (((pci_prefetch->bus_lower - 1) >> 16) & diff --git a/drivers/pci/pcie_cdns_ti.c b/drivers/pci/pcie_cdns_ti.c index 41469a186a3..9d1d123a18c 100644 --- a/drivers/pci/pcie_cdns_ti.c +++ b/drivers/pci/pcie_cdns_ti.c @@ -19,6 +19,7 @@ #include <linux/io.h> #include <linux/ioport.h> #include <linux/log2.h> +#include <linux/sizes.h> #include <power-domain.h> #include <regmap.h> #include <syscon.h> @@ -834,11 +835,21 @@ static const struct pcie_cdns_ti_data j7200_pcie_rc_data = { .max_lanes = 2, }; +static const struct pcie_cdns_ti_data am64_pcie_rc_data = { + .mode = PCIE_MODE_RC, + .quirk_detect_quiet_flag = true, + .max_lanes = 1, +}; + static const struct udevice_id pcie_cdns_ti_ids[] = { { .compatible = "ti,j7200-pcie-host", .data = (ulong)&j7200_pcie_rc_data, }, + { + .compatible = "ti,am64-pcie-host", + .data = (ulong)&am64_pcie_rc_data, + }, {}, }; diff --git a/drivers/pci/pcie_xilinx.c b/drivers/pci/pcie_xilinx.c index a674ab04bee..63058e8e7c5 100644 --- a/drivers/pci/pcie_xilinx.c +++ b/drivers/pci/pcie_xilinx.c @@ -18,14 +18,19 @@ */ struct xilinx_pcie { void *cfg_base; + pci_size_t size; + int first_busno; }; /* Register definitions */ -#define XILINX_PCIE_REG_PSCR 0x144 -#define XILINX_PCIE_REG_PSCR_LNKUP BIT(11) -#define XILINX_PCIE_REG_RPSC 0x148 -#define XILINX_PCIE_REG_RPSC_BEN BIT(0) - +#define XILINX_PCIE_REG_BRIDGE_INFO 0x130 +#define XILINX_PCIE_REG_BRIDGE_INFO_ECAMSZ_SHIFT 16 +#define XILINX_PCIE_REG_BRIDGE_INFO_ECAMSZ_MASK (0x7 << 16) +#define XILINX_PCIE_REG_INT_MASK 0x13c +#define XILINX_PCIE_REG_PSCR 0x144 +#define XILINX_PCIE_REG_PSCR_LNKUP BIT(11) +#define XILINX_PCIE_REG_RPSC 0x148 +#define XILINX_PCIE_REG_RPSC_BEN BIT(0) /** * pcie_xilinx_link_up() - Check whether the PCIe link is up * @pcie: Pointer to the PCI controller state @@ -61,14 +66,18 @@ static int pcie_xilinx_config_address(const struct udevice *udev, pci_dev_t bdf, uint offset, void **paddress) { struct xilinx_pcie *pcie = dev_get_priv(udev); - unsigned int bus = PCI_BUS(bdf); + unsigned int bus = PCI_BUS(bdf) - pcie->first_busno; unsigned int dev = PCI_DEV(bdf); unsigned int func = PCI_FUNC(bdf); + int num_buses = DIV_ROUND_UP(pcie->size, 1 << 16); void *addr; if ((bus > 0) && !pcie_xilinx_link_up(pcie)) return -ENODEV; + if (bus > num_buses) + return -ENODEV; + /* * Busses 0 (host-PCIe bridge) & 1 (its immediate child) are * limited to a single device each. @@ -142,20 +151,37 @@ static int pcie_xilinx_of_to_plat(struct udevice *dev) struct xilinx_pcie *pcie = dev_get_priv(dev); fdt_addr_t addr; fdt_size_t size; - u32 rpsc; addr = dev_read_addr_size(dev, &size); if (addr == FDT_ADDR_T_NONE) return -EINVAL; - pcie->cfg_base = devm_ioremap(dev, addr, size); - if (IS_ERR(pcie->cfg_base)) - return PTR_ERR(pcie->cfg_base); + pcie->cfg_base = map_physmem(addr, size, MAP_NOCACHE); + if (!pcie->cfg_base) + return -ENOMEM; + pcie->size = size; + return 0; +} - /* Enable the Bridge enable bit */ - rpsc = __raw_readl(pcie->cfg_base + XILINX_PCIE_REG_RPSC); +static int pci_xilinx_probe(struct udevice *dev) +{ + struct xilinx_pcie *pcie = dev_get_priv(dev); + u32 rpsc; + int num_buses = DIV_ROUND_UP(pcie->size, 1 << 16); + + pcie->first_busno = dev_seq(dev); + + /* Disable all interrupts */ + writel(0, pcie->cfg_base + XILINX_PCIE_REG_INT_MASK); + + /* Enable the bridge */ + rpsc = readl(pcie->cfg_base + XILINX_PCIE_REG_RPSC); rpsc |= XILINX_PCIE_REG_RPSC_BEN; - __raw_writel(rpsc, pcie->cfg_base + XILINX_PCIE_REG_RPSC); + writel(rpsc, pcie->cfg_base + XILINX_PCIE_REG_RPSC); + + /* Enable access to all possible subordinate buses */ + writel((0 << 0) | (1 << 8) | (num_buses << 16), + pcie->cfg_base + PCI_PRIMARY_BUS); return 0; } @@ -176,5 +202,6 @@ U_BOOT_DRIVER(pcie_xilinx) = { .of_match = pcie_xilinx_ids, .ops = &pcie_xilinx_ops, .of_to_plat = pcie_xilinx_of_to_plat, + .probe = pci_xilinx_probe, .priv_auto = sizeof(struct xilinx_pcie), }; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index e12347e8a03..d3fe90d939e 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -163,8 +163,8 @@ config PHY_RCAR_GEN2 config PHY_RCAR_GEN3 tristate "Renesas R-Car Gen3 USB PHY" - depends on PHY && RCAR_GEN3 && CLK && DM_REGULATOR - default y if RCAR_GEN3 + depends on PHY && CLK && DM_REGULATOR && (RCAR_GEN3 || RZG2L) + default y if (RCAR_GEN3 || RZG2L) help Support for the Renesas R-Car Gen3 USB PHY. This driver operates the PHY connected to EHCI USB module and controls USB OTG operation. @@ -309,5 +309,6 @@ source "drivers/phy/cadence/Kconfig" source "drivers/phy/ti/Kconfig" source "drivers/phy/qcom/Kconfig" source "drivers/phy/renesas/Kconfig" +source "drivers/phy/starfive/Kconfig" endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c35f9294dd9..b4d01fc700d 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -8,8 +8,8 @@ obj-y += marvell/ obj-y += rockchip/ obj-y += socionext/ -obj-$(CONFIG_$(XPL_)PHY) += phy-uclass.o -obj-$(CONFIG_$(XPL_)NOP_PHY) += nop-phy.o +obj-$(CONFIG_$(PHASE_)PHY) += phy-uclass.o +obj-$(CONFIG_$(PHASE_)NOP_PHY) += nop-phy.o obj-$(CONFIG_MIPI_DPHY_HELPERS) += phy-core-mipi-dphy.o obj-$(CONFIG_AB8500_USB_PHY) += phy-ab8500-usb.o obj-$(CONFIG_APPLE_ATCPHY) += phy-apple-atc.o @@ -19,7 +19,7 @@ obj-$(CONFIG_BCM6358_USBH_PHY) += bcm6358-usbh-phy.o obj-$(CONFIG_BCM6368_USBH_PHY) += bcm6368-usbh-phy.o obj-$(CONFIG_BCM_SR_PCIE_PHY) += phy-bcm-sr-pcie.o obj-$(CONFIG_PHY_SANDBOX) += sandbox-phy.o -obj-$(CONFIG_$(XPL_)PIPE3_PHY) += ti-pipe3-phy.o +obj-$(CONFIG_$(PHASE_)PIPE3_PHY) += ti-pipe3-phy.o obj-$(CONFIG_AM654_PHY) += phy-ti-am654.o obj-$(CONFIG_STI_USB_PHY) += sti_usb_phy.o obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o @@ -44,3 +44,4 @@ obj-y += cadence/ obj-y += ti/ obj-y += qcom/ obj-y += renesas/ +obj-y += starfive/ diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index b9306c9a827..3acffb40b0b 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -85,41 +85,14 @@ struct sun4i_usb_phy_cfg { int missing_phys; }; -struct sun4i_usb_phy_info { - const char *gpio_vbus; - const char *gpio_vbus_det; - const char *gpio_id_det; -} phy_info[] = { - { - .gpio_vbus = CONFIG_USB0_VBUS_PIN, - .gpio_vbus_det = CONFIG_USB0_VBUS_DET, - .gpio_id_det = CONFIG_USB0_ID_DET, - }, - { - .gpio_vbus = CONFIG_USB1_VBUS_PIN, - .gpio_vbus_det = NULL, - .gpio_id_det = NULL, - }, - { - .gpio_vbus = CONFIG_USB2_VBUS_PIN, - .gpio_vbus_det = NULL, - .gpio_id_det = NULL, - }, - { - .gpio_vbus = CONFIG_USB3_VBUS_PIN, - .gpio_vbus_det = NULL, - .gpio_id_det = NULL, - }, -}; - struct sun4i_usb_phy_plat { void __iomem *pmu; - struct gpio_desc gpio_vbus; struct gpio_desc gpio_vbus_det; struct gpio_desc gpio_id_det; struct clk clocks; struct clk clk2; struct reset_ctl resets; + struct udevice *vbus; int id; }; @@ -208,6 +181,7 @@ static int sun4i_usb_phy_power_on(struct phy *phy) { struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; + int ret; if (initial_usb_scan_delay) { mdelay(initial_usb_scan_delay); @@ -220,8 +194,9 @@ static int sun4i_usb_phy_power_on(struct phy *phy) return 0; } - if (dm_gpio_is_valid(&usb_phy->gpio_vbus)) - dm_gpio_set_value(&usb_phy->gpio_vbus, 1); + ret = regulator_set_enable_if_allowed(usb_phy->vbus, true); + if (ret) + return ret; return 0; } @@ -230,9 +205,11 @@ static int sun4i_usb_phy_power_off(struct phy *phy) { struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; + int ret; - if (dm_gpio_is_valid(&usb_phy->gpio_vbus)) - dm_gpio_set_value(&usb_phy->gpio_vbus, 0); + ret = regulator_set_enable_if_allowed(usb_phy->vbus, false); + if (ret) + return ret; return 0; } @@ -481,48 +458,39 @@ static int sun4i_usb_phy_probe(struct udevice *dev) data->usb_phy = plat; for (i = 0; i < data->cfg->num_phys; i++) { struct sun4i_usb_phy_plat *phy = &plat[i]; - struct sun4i_usb_phy_info *info = &phy_info[i]; - char name[16]; + char name[32]; if (data->cfg->missing_phys & BIT(i)) continue; - ret = dm_gpio_lookup_name(info->gpio_vbus, &phy->gpio_vbus); - if (ret == 0) { - ret = dm_gpio_request(&phy->gpio_vbus, "usb_vbus"); - if (ret) - return ret; - ret = dm_gpio_set_dir_flags(&phy->gpio_vbus, - GPIOD_IS_OUT); - if (ret) - return ret; - ret = dm_gpio_set_value(&phy->gpio_vbus, 0); - if (ret) - return ret; - } - - ret = dm_gpio_lookup_name(info->gpio_vbus_det, - &phy->gpio_vbus_det); - if (ret == 0) { - ret = dm_gpio_request(&phy->gpio_vbus_det, - "usb_vbus_det"); - if (ret) - return ret; - ret = dm_gpio_set_dir_flags(&phy->gpio_vbus_det, - GPIOD_IS_IN); + snprintf(name, sizeof(name), "usb%d_vbus-supply", i); + ret = device_get_supply_regulator(dev, name, &phy->vbus); + if (phy->vbus) { + ret = regulator_set_enable_if_allowed(phy->vbus, false); if (ret) return ret; } - ret = dm_gpio_lookup_name(info->gpio_id_det, &phy->gpio_id_det); - if (ret == 0) { - ret = dm_gpio_request(&phy->gpio_id_det, "usb_id_det"); - if (ret) + if (i == 0) { + ret = gpio_request_by_name(dev, "usb0_vbus_det-gpios", + 0, &phy->gpio_vbus_det, + GPIOD_IS_IN); + if (ret && ret != -ENOENT) { + dev_err(dev, + "failed to get VBUS detect GPIO: %d\n", + ret); return ret; - ret = dm_gpio_set_dir_flags(&phy->gpio_id_det, - GPIOD_IS_IN | GPIOD_PULL_UP); - if (ret) + } + + ret = gpio_request_by_name(dev, "usb0_id_det-gpios", 0, + &phy->gpio_id_det, + GPIOD_IS_IN | GPIOD_PULL_UP); + if (ret && ret != -ENOENT) { + dev_err(dev, + "failed to get ID detect GPIO: %d\n", + ret); return ret; + } } if (data->cfg->dedicated_clocks) diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile index e0da41cd7eb..2b1e51768da 100644 --- a/drivers/phy/cadence/Makefile +++ b/drivers/phy/cadence/Makefile @@ -1,2 +1,2 @@ -obj-$(CONFIG_$(XPL_)PHY_CADENCE_SIERRA) += phy-cadence-sierra.o -obj-$(CONFIG_$(XPL_)PHY_CADENCE_TORRENT) += phy-cadence-torrent.o +obj-$(CONFIG_$(PHASE_)PHY_CADENCE_SIERRA) += phy-cadence-sierra.o +obj-$(CONFIG_$(PHASE_)PHY_CADENCE_TORRENT) += phy-cadence-torrent.o diff --git a/drivers/phy/phy-rcar-gen3.c b/drivers/phy/phy-rcar-gen3.c index 8c004eaf4c6..d06861c4f79 100644 --- a/drivers/phy/phy-rcar-gen3.c +++ b/drivers/phy/phy-rcar-gen3.c @@ -55,6 +55,7 @@ /* VBCTRL */ #define USB2_VBCTRL_DRVVBUSSEL BIT(8) +#define USB2_VBCTRL_VBOUT BIT(0) /* LINECTRL1 */ #define USB2_LINECTRL1_DPRPD_EN BIT(19) @@ -68,6 +69,13 @@ #define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ #define USB2_ADPCTRL_DRVVBUS BIT(4) +/* RZ/G2L specific */ +#define USB2_OBINT_IDCHG_EN BIT(0) +#define USB2_LINECTRL1_USB2_IDMON BIT(0) + +/* Device flags */ +#define RCAR_GEN3_PHY_NO_ADPCTRL BIT(0) + struct rcar_gen3_phy { fdt_addr_t regs; struct clk clk; @@ -122,15 +130,50 @@ static int rcar_gen3_phy_phy_power_off(struct phy *phy) return regulator_set_enable(priv->vbus_supply, false); } +static bool rcar_gen3_phy_check_id(struct phy *phy) +{ + const u32 adpdevmask = USB2_ADPCTRL_IDDIG | USB2_ADPCTRL_OTGSESSVLD; + struct rcar_gen3_phy *priv = dev_get_priv(phy->dev); + ulong flags = dev_get_driver_data(phy->dev); + u32 val; + + if (flags & RCAR_GEN3_PHY_NO_ADPCTRL) { + val = readl(priv->regs + USB2_LINECTRL1); + return !!(val & USB2_LINECTRL1_USB2_IDMON); + } + + val = readl(priv->regs + USB2_ADPCTRL); + return (val & adpdevmask) == adpdevmask; +} + +static void rcar_gen3_phy_set_vbus(struct phy *phy, bool enable) +{ + struct rcar_gen3_phy *priv = dev_get_priv(phy->dev); + ulong flags = dev_get_driver_data(phy->dev); + u32 bits = USB2_ADPCTRL_DRVVBUS; + u64 reg = USB2_ADPCTRL; + + if (flags & RCAR_GEN3_PHY_NO_ADPCTRL) { + bits = USB2_VBCTRL_VBOUT; + reg = USB2_VBCTRL; + } + + if (enable) + setbits_le32(priv->regs + reg, bits); + else + clrbits_le32(priv->regs + reg, bits); +} + static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) { - const u32 adpdevmask = USB2_ADPCTRL_IDDIG | USB2_ADPCTRL_OTGSESSVLD; struct rcar_gen3_phy *priv = dev_get_priv(phy->dev); - u32 adpctrl; + ulong flags = dev_get_driver_data(phy->dev); if (mode == PHY_MODE_USB_OTG) { if (submode) { + u32 obint_enable_bits; + /* OTG submode is used as initialization indicator */ writel(USB2_INT_ENABLE_UCOM_INTEN | USB2_INT_ENABLE_USBH_INTB_EN | @@ -138,13 +181,16 @@ static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, priv->regs + USB2_INT_ENABLE); setbits_le32(priv->regs + USB2_VBCTRL, USB2_VBCTRL_DRVVBUSSEL); - writel(USB2_OBINT_SESSVLDCHG | USB2_OBINT_IDDIGCHG, - priv->regs + USB2_OBINTSTA); - setbits_le32(priv->regs + USB2_OBINTEN, - USB2_OBINT_SESSVLDCHG | - USB2_OBINT_IDDIGCHG); - setbits_le32(priv->regs + USB2_ADPCTRL, - USB2_ADPCTRL_IDPULLUP); + if (flags & RCAR_GEN3_PHY_NO_ADPCTRL) { + obint_enable_bits = USB2_OBINT_IDCHG_EN; + } else { + obint_enable_bits = USB2_OBINT_SESSVLDCHG | + USB2_OBINT_IDDIGCHG; + setbits_le32(priv->regs + USB2_ADPCTRL, + USB2_ADPCTRL_IDPULLUP); + } + writel(obint_enable_bits, priv->regs + USB2_OBINTSTA); + setbits_le32(priv->regs + USB2_OBINTEN, obint_enable_bits); clrsetbits_le32(priv->regs + USB2_LINECTRL1, USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD | @@ -154,8 +200,7 @@ static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, USB2_LINECTRL1_DMRPD_EN); } - adpctrl = readl(priv->regs + USB2_ADPCTRL); - if ((adpctrl & adpdevmask) == adpdevmask) + if (rcar_gen3_phy_check_id(phy)) mode = PHY_MODE_USB_DEVICE; else mode = PHY_MODE_USB_HOST; @@ -165,13 +210,13 @@ static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, clrbits_le32(priv->regs + USB2_COMMCTRL, USB2_COMMCTRL_OTG_PERI); setbits_le32(priv->regs + USB2_LINECTRL1, USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD); - setbits_le32(priv->regs + USB2_ADPCTRL, USB2_ADPCTRL_DRVVBUS); + rcar_gen3_phy_set_vbus(phy, true); } else if (mode == PHY_MODE_USB_DEVICE) { setbits_le32(priv->regs + USB2_COMMCTRL, USB2_COMMCTRL_OTG_PERI); clrsetbits_le32(priv->regs + USB2_LINECTRL1, USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD, USB2_LINECTRL1_DM_RPD); - clrbits_le32(priv->regs + USB2_ADPCTRL, USB2_ADPCTRL_DRVVBUS); + rcar_gen3_phy_set_vbus(phy, false); } else { dev_err(phy->dev, "Unknown mode %d\n", mode); return -EINVAL; @@ -226,7 +271,13 @@ static int rcar_gen3_phy_remove(struct udevice *dev) } static const struct udevice_id rcar_gen3_phy_of_match[] = { - { .compatible = "renesas,rcar-gen3-usb2-phy", }, + { + .compatible = "renesas,rcar-gen3-usb2-phy", + }, + { + .compatible = "renesas,rzg2l-usb2-phy", + .data = RCAR_GEN3_PHY_NO_ADPCTRL, + }, { }, }; diff --git a/drivers/phy/qcom/phy-qcom-qusb2.c b/drivers/phy/qcom/phy-qcom-qusb2.c index c91ba18c4ab..d98f6108e69 100644 --- a/drivers/phy/qcom/phy-qcom-qusb2.c +++ b/drivers/phy/qcom/phy-qcom-qusb2.c @@ -122,6 +122,9 @@ struct qusb2_phy_cfg { /* true if PHY has PLL_CORE_INPUT_OVERRIDE register to reset PLL */ bool has_pll_override; + + /* true if PHY default clk scheme is single-ended */ + bool se_clk_scheme_default; }; /* set of registers with offsets different per-PHY */ @@ -173,6 +176,19 @@ static const unsigned int sm6115_regs_layout[] = { [QUSB2PHY_PORT_POWERDOWN] = 0xb4, [QUSB2PHY_INTR_CTRL] = 0xbc, }; +static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = { + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0xb3), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x83), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0xc0), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), +}; + static const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = { QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x03), QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c), @@ -214,6 +230,19 @@ static const struct qusb2_phy_cfg sm6115_phy_cfg = { .regs = sm6115_regs_layout, .has_pll_test = true, + .se_clk_scheme_default = true, + .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN), + .mask_core_ready = PLL_LOCKED, + .autoresume_en = BIT(3), +}; + +static const struct qusb2_phy_cfg sdm660_phy_cfg = { + .tbl = msm8996_init_tbl, + .tbl_num = ARRAY_SIZE(msm8996_init_tbl), + .regs = sm6115_regs_layout, + + .has_pll_test = true, + .se_clk_scheme_default = false, .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN), .mask_core_ready = PLL_LOCKED, .autoresume_en = BIT(3), @@ -228,6 +257,7 @@ static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = { POWER_DOWN), .mask_core_ready = CORE_READY_STATUS, .has_pll_override = true, + .se_clk_scheme_default = true, .autoresume_en = BIT(0), .update_tune1_with_efuse = true, }; @@ -316,8 +346,18 @@ static int qusb2phy_power_on(struct phy *phy) /* Required to get phy pll lock successfully */ udelay(150); + /* + * Not all the SoCs have got a readable TCSR_PHY_CLK_SCHEME + * register in the TCSR so, if there's none, use the default + * value hardcoded in the configuration. + */ + qphy->has_se_clk_scheme = cfg->se_clk_scheme_default; + if (cfg->has_pll_test) { - val |= CLK_REF_SEL; + if (!qphy->has_se_clk_scheme) + val &= ~CLK_REF_SEL; + else + val |= CLK_REF_SEL; writel(val, qphy->base + QUSB2PHY_PLL_TEST); @@ -413,6 +453,8 @@ static const struct udevice_id qusb2phy_ids[] = { { .compatible = "qcom,qusb2-phy" }, { .compatible = "qcom,qcm2290-qusb2-phy", .data = (ulong)&sm6115_phy_cfg }, + { .compatible = "qcom,sdm660-qusb2-phy", + .data = (ulong)&sdm660_phy_cfg }, { .compatible = "qcom,sm6115-qusb2-phy", .data = (ulong)&sm6115_phy_cfg }, { .compatible = "qcom,qusb2-v2-phy", .data = (ulong)&qusb2_v2_phy_cfg }, diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 43f6e020a6a..88b33de1b2a 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -40,11 +40,13 @@ struct rockchip_usb2phy_port_cfg { struct rockchip_usb2phy_cfg { unsigned int reg; struct usb2phy_reg clkout_ctl; + struct usb2phy_reg clkout_ctl_phy; const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS]; }; struct rockchip_usb2phy { struct regmap *reg_base; + struct regmap *phy_base; struct clk phyclk; const struct rockchip_usb2phy_cfg *phy_cfg; }; @@ -165,6 +167,22 @@ static struct phy_ops rockchip_usb2phy_ops = { .of_xlate = rockchip_usb2phy_of_xlate, }; +static void rockchip_usb2phy_clkout_ctl(struct clk *clk, struct regmap **base, + const struct usb2phy_reg **clkout_ctl) +{ + struct udevice *parent = dev_get_parent(clk->dev); + struct rockchip_usb2phy *priv = dev_get_priv(parent); + const struct rockchip_usb2phy_cfg *phy_cfg = priv->phy_cfg; + + if (priv->phy_cfg->clkout_ctl_phy.enable) { + *base = priv->phy_base; + *clkout_ctl = &phy_cfg->clkout_ctl_phy; + } else { + *base = priv->reg_base; + *clkout_ctl = &phy_cfg->clkout_ctl; + } +} + /** * round_rate() - Adjust a rate to the exact rate a clock can provide. * @clk: The clock to manipulate. @@ -185,13 +203,14 @@ ulong rockchip_usb2phy_clk_round_rate(struct clk *clk, ulong rate) */ int rockchip_usb2phy_clk_enable(struct clk *clk) { - struct udevice *parent = dev_get_parent(clk->dev); - struct rockchip_usb2phy *priv = dev_get_priv(parent); - const struct rockchip_usb2phy_cfg *phy_cfg = priv->phy_cfg; + const struct usb2phy_reg *clkout_ctl; + struct regmap *base; + + rockchip_usb2phy_clkout_ctl(clk, &base, &clkout_ctl); /* turn on 480m clk output if it is off */ - if (!property_enabled(priv->reg_base, &phy_cfg->clkout_ctl)) { - property_enable(priv->reg_base, &phy_cfg->clkout_ctl, true); + if (!property_enabled(base, clkout_ctl)) { + property_enable(base, clkout_ctl, true); /* waiting for the clk become stable */ usleep_range(1200, 1300); @@ -208,12 +227,13 @@ int rockchip_usb2phy_clk_enable(struct clk *clk) */ int rockchip_usb2phy_clk_disable(struct clk *clk) { - struct udevice *parent = dev_get_parent(clk->dev); - struct rockchip_usb2phy *priv = dev_get_priv(parent); - const struct rockchip_usb2phy_cfg *phy_cfg = priv->phy_cfg; + const struct usb2phy_reg *clkout_ctl; + struct regmap *base; + + rockchip_usb2phy_clkout_ctl(clk, &base, &clkout_ctl); /* turn off 480m clk output */ - property_enable(priv->reg_base, &phy_cfg->clkout_ctl, false); + property_enable(base, clkout_ctl, false); return 0; } @@ -281,7 +301,10 @@ static int rockchip_usb2phy_probe(struct udevice *dev) return ret; } - return 0; + if (priv->phy_cfg->clkout_ctl_phy.enable) + ret = regmap_init_mem_index(dev_ofnode(dev), &priv->phy_base, 0); + + return ret; } static int rockchip_usb2phy_bind(struct udevice *dev) @@ -389,6 +412,22 @@ static const struct rockchip_usb2phy_cfg rk3399_usb2phy_cfgs[] = { { /* sentinel */ } }; +static const struct rockchip_usb2phy_cfg rk3528_phy_cfgs[] = { + { + .reg = 0xffdf0000, + .clkout_ctl_phy = { 0x041c, 7, 2, 0, 0x27 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x004c, 1, 0, 2, 1 }, + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x005c, 1, 0, 2, 1 }, + } + }, + }, + { /* sentinel */ } +}; + static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = { { .reg = 0xfe8a0000, @@ -471,6 +510,10 @@ static const struct udevice_id rockchip_usb2phy_ids[] = { .data = (ulong)&rk3399_usb2phy_cfgs, }, { + .compatible = "rockchip,rk3528-usb2phy", + .data = (ulong)&rk3528_phy_cfgs, + }, + { .compatible = "rockchip,rk3568-usb2phy", .data = (ulong)&rk3568_phy_cfgs, }, diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig new file mode 100644 index 00000000000..d11338ed484 --- /dev/null +++ b/drivers/phy/starfive/Kconfig @@ -0,0 +1,21 @@ +# +# PHY drivers for Starfive platforms +# + +menu "Starfive PHY driver" + +config PHY_STARFIVE_JH7110_PCIE + bool "Starfive JH7110 PCIe 2.0 PHY driver" + depends on PHY + help + Enable this to support the Starfive JH7110 PCIE 2.0/USB 3.0 PHY. + Generic PHY driver JH7110 USB 3.0/ PCIe 2.0. + +config PHY_STARFIVE_JH7110_USB2 + bool "Starfive JH7110 USB 2.0 PHY driver" + depends on PHY + help + Enable this to support the Starfive JH7110 USB 2.0 PHY. + Generic PHY driver JH7110 USB 2.0. + +endmenu diff --git a/drivers/phy/starfive/Makefile b/drivers/phy/starfive/Makefile new file mode 100644 index 00000000000..82f25aa21b7 --- /dev/null +++ b/drivers/phy/starfive/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2023 Starfive +# + +obj-$(CONFIG_PHY_STARFIVE_JH7110_PCIE) += phy-jh7110-pcie.o +obj-$(CONFIG_PHY_STARFIVE_JH7110_USB2) += phy-jh7110-usb2.o diff --git a/drivers/phy/starfive/phy-jh7110-pcie.c b/drivers/phy/starfive/phy-jh7110-pcie.c new file mode 100644 index 00000000000..a30582821d9 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-pcie.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 PCIe 2.0 PHY driver + * + * Copyright (C) 2024 StarFive Technology Co., Ltd. + * Author: Minda Chen <minda.chen@starfivetech.com> + */ +#include <asm/io.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <errno.h> +#include <generic-phy.h> +#include <regmap.h> +#include <soc.h> +#include <syscon.h> +#include <linux/bitops.h> +#include <linux/err.h> + +#include "phy-jh7110-usb-syscon.h" + +#define PCIE_KVCO_LEVEL_OFF 0x28 +#define PCIE_USB3_PHY_PLL_CTL_OFF 0x7c +#define PCIE_USB3_PHY_SS_MODE BIT(4) +#define PCIE_KVCO_TUNE_SIGNAL_OFF 0x80 +#define PHY_KVCO_FINE_TUNE_LEVEL 0x91 +#define PHY_KVCO_FINE_TUNE_SIGNALS 0xc + +#define PCIE_USB3_PHY_MODE 0x1 +#define PCIE_BUS_WIDTH 0x2 +#define PCIE_USB3_PHY_ENABLE 0x1 +#define PCIE_USB3_PHY_SPLIT 0x1 + +struct jh7110_pcie_phy { + struct phy *phy; + struct regmap *stg_syscon; + struct regmap *sys_syscon; + void __iomem *regs; + struct regmap_field *phy_mode; + struct regmap_field *bus_width; + struct regmap_field *usb3_phy_en; + struct regmap_field *usb_split; + enum phy_mode mode; +}; + +static int phy_pcie_mode_set(struct jh7110_pcie_phy *data, bool usb_mode) +{ + unsigned int phy_mode, width, usb3_phy, ss_mode, split; + + /* default is PCIe mode */ + if (!data->stg_syscon || !data->sys_syscon) { + if (usb_mode) { + dev_err(data->phy->dev, "doesn't support USB3 mode\n"); + return -EINVAL; + } + return 0; + } + + if (usb_mode) { + phy_mode = PCIE_USB3_PHY_MODE; + width = 0; + usb3_phy = PCIE_USB3_PHY_ENABLE; + ss_mode = PCIE_USB3_PHY_SS_MODE; + split = 0; + } else { + phy_mode = 0; + width = PCIE_BUS_WIDTH; + usb3_phy = 0; + ss_mode = 0; + split = PCIE_USB3_PHY_SPLIT; + } + + regmap_field_write(data->phy_mode, phy_mode); + regmap_field_write(data->bus_width, width); + regmap_field_write(data->usb3_phy_en, usb3_phy); + clrsetbits_le32(data->regs + PCIE_USB3_PHY_PLL_CTL_OFF, + PCIE_USB3_PHY_SS_MODE, ss_mode); + regmap_field_write(data->usb_split, split); + + return 0; +} + +static void phy_kvco_gain_set(struct jh7110_pcie_phy *phy) +{ + /* PCIe Multi-PHY PLL KVCO Gain fine tune settings: */ + writel(PHY_KVCO_FINE_TUNE_LEVEL, phy->regs + PCIE_KVCO_LEVEL_OFF); + writel(PHY_KVCO_FINE_TUNE_SIGNALS, phy->regs + PCIE_KVCO_TUNE_SIGNAL_OFF); +} + +static int jh7110_pcie_phy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) +{ + struct udevice *dev = phy->dev; + struct jh7110_pcie_phy *pcie_phy = dev_get_priv(dev); + int ret; + + if (mode == pcie_phy->mode) + return 0; + + switch (mode) { + case PHY_MODE_USB_HOST: + case PHY_MODE_USB_DEVICE: + case PHY_MODE_USB_OTG: + ret = phy_pcie_mode_set(pcie_phy, 1); + if (ret) + return ret; + break; + case PHY_MODE_PCIE: + phy_pcie_mode_set(pcie_phy, 0); + break; + default: + return -EINVAL; + } + + dev_dbg(phy->dev, "Changing PHY mode to %d\n", mode); + pcie_phy->mode = mode; + + return 0; +} + +static const struct phy_ops jh7110_pcie_phy_ops = { + .set_mode = jh7110_pcie_phy_set_mode, +}; + +static int phy_stg_regfield_init(struct udevice *dev, int mode, int usb3) +{ + struct jh7110_pcie_phy *phy = dev_get_priv(dev); + struct reg_field phy_mode = REG_FIELD(mode, 20, 21); + struct reg_field bus_width = REG_FIELD(usb3, 2, 3); + struct reg_field usb3_phy_en = REG_FIELD(usb3, 4, 4); + + phy->phy_mode = devm_regmap_field_alloc(dev, phy->stg_syscon, phy_mode); + if (IS_ERR(phy->phy_mode)) { + dev_err(dev, "PHY mode reg field init failed\n"); + return PTR_ERR(phy->phy_mode); + } + + phy->bus_width = devm_regmap_field_alloc(dev, phy->stg_syscon, bus_width); + if (IS_ERR(phy->bus_width)) { + dev_err(dev, "PHY bus width reg field init failed\n"); + return PTR_ERR(phy->bus_width); + } + + phy->usb3_phy_en = devm_regmap_field_alloc(dev, phy->stg_syscon, usb3_phy_en); + if (IS_ERR(phy->usb3_phy_en)) { + dev_err(dev, "USB3 PHY enable field init failed\n"); + return PTR_ERR(phy->bus_width); + } + + return 0; +} + +static int phy_sys_regfield_init(struct udevice *dev, int split) +{ + struct jh7110_pcie_phy *phy = dev_get_priv(dev); + struct reg_field usb_split = REG_FIELD(split, USB_PDRSTN_SPLIT_BIT, USB_PDRSTN_SPLIT_BIT); + + phy->usb_split = devm_regmap_field_alloc(dev, phy->sys_syscon, usb_split); + if (IS_ERR(phy->usb_split)) { + dev_err(dev, "USB split field init failed\n"); + return PTR_ERR(phy->usb_split); + } + + return 0; +} + +static int starfive_pcie_phy_get_syscon(struct udevice *dev) +{ + struct jh7110_pcie_phy *phy = dev_get_priv(dev); + struct ofnode_phandle_args sys_phandle, stg_phandle; + int ret; + + /* get corresponding syscon phandle */ + ret = dev_read_phandle_with_args(dev, "starfive,sys-syscon", NULL, 1, 0, + &sys_phandle); + + if (ret < 0) { + dev_err(dev, "Can't get sys cfg phandle: %d\n", ret); + return ret; + } + + ret = dev_read_phandle_with_args(dev, "starfive,stg-syscon", NULL, 2, 0, + &stg_phandle); + + if (ret < 0) { + dev_err(dev, "Can't get stg cfg phandle: %d\n", ret); + return ret; + } + + phy->sys_syscon = syscon_node_to_regmap(sys_phandle.node); + /* get syscon register offset */ + if (!IS_ERR(phy->sys_syscon)) { + ret = phy_sys_regfield_init(dev, SYSCON_USB_PDRSTN_REG_OFFSET); + if (ret) + return ret; + } else { + phy->sys_syscon = NULL; + } + + phy->stg_syscon = syscon_node_to_regmap(stg_phandle.node); + if (!IS_ERR(phy->stg_syscon)) + return phy_stg_regfield_init(dev, stg_phandle.args[0], + stg_phandle.args[1]); + else + phy->stg_syscon = NULL; + + return 0; +} + +int jh7110_pcie_phy_probe(struct udevice *dev) +{ + struct jh7110_pcie_phy *phy = dev_get_priv(dev); + int rc; + + phy->regs = dev_read_addr_ptr(dev); + if (!phy->regs) + return -EINVAL; + + rc = starfive_pcie_phy_get_syscon(dev); + if (rc) + return rc; + + phy_kvco_gain_set(phy); + + return 0; +} + +static const struct udevice_id jh7110_pcie_phy[] = { + { .compatible = "starfive,jh7110-pcie-phy"}, + {}, +}; + +U_BOOT_DRIVER(jh7110_pcie_phy) = { + .name = "jh7110_pcie_phy", + .id = UCLASS_PHY, + .of_match = jh7110_pcie_phy, + .probe = jh7110_pcie_phy_probe, + .ops = &jh7110_pcie_phy_ops, + .priv_auto = sizeof(struct jh7110_pcie_phy), +}; diff --git a/drivers/phy/starfive/phy-jh7110-usb-syscon.h b/drivers/phy/starfive/phy-jh7110-usb-syscon.h new file mode 100644 index 00000000000..0eb66f0d859 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-usb-syscon.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef PHY_JH7110_USB_SYSCON_H_ +#define PHY_JH7110_USB_SYSCON_H_ + +#define SYSCON_USB_PDRSTN_REG_OFFSET 0x18 +#define USB_PDRSTN_SPLIT_BIT 17 + +#endif diff --git a/drivers/phy/starfive/phy-jh7110-usb2.c b/drivers/phy/starfive/phy-jh7110-usb2.c new file mode 100644 index 00000000000..1a28381e0df --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-usb2.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 USB 2.0 PHY driver + * + * Copyright (C) 2024 StarFive Technology Co., Ltd. + * Author: Minda Chen <minda.chen@starfivetech.com> + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <errno.h> +#include <generic-phy.h> +#include <regmap.h> +#include <soc.h> +#include <syscon.h> +#include <linux/bitops.h> +#include <linux/err.h> + +#include "phy-jh7110-usb-syscon.h" + +#define USB_LS_KEEPALIVE_OFF 0x4 +#define USB_LS_KEEPALIVE_ENABLE BIT(4) +#define USB_PHY_CLK_RATE 125000000 + +struct jh7110_usb2_phy { + struct phy *phy; + struct regmap *sys_syscon; + void __iomem *regs; + struct clk *usb_125m_clk; + struct clk *app_125m; + struct regmap_field *usb_split; + enum phy_mode mode; +}; + +static void usb2_set_ls_keepalive(struct jh7110_usb2_phy *phy, bool set) +{ + /* Host mode enable the LS speed keep-alive signal */ + clrsetbits_le32(phy->regs + USB_LS_KEEPALIVE_OFF, + USB_LS_KEEPALIVE_ENABLE, + set ? USB_LS_KEEPALIVE_ENABLE : 0); +} + +static int usb2_phy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) +{ + struct udevice *dev = phy->dev; + struct jh7110_usb2_phy *usb2_phy = dev_get_priv(dev); + + if (mode == usb2_phy->mode) + return 0; + + switch (mode) { + case PHY_MODE_USB_HOST: + case PHY_MODE_USB_DEVICE: + case PHY_MODE_USB_OTG: + dev_dbg(dev, "Changing PHY to %d\n", mode); + usb2_phy->mode = mode; + usb2_set_ls_keepalive(usb2_phy, (mode != PHY_MODE_USB_DEVICE)); + break; + default: + return -EINVAL; + } + + /* set default split usb 2.0 only mode */ + regmap_field_write(usb2_phy->usb_split, true); + + return 0; +} + +static int jh7110_usb2_phy_init(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct jh7110_usb2_phy *usb2_phy = dev_get_priv(dev); + int ret; + + ret = clk_set_rate(usb2_phy->usb_125m_clk, USB_PHY_CLK_RATE); + if (ret < 0) { + dev_err(dev, "Failed to set 125m clock\n"); + return ret; + } + + return clk_prepare_enable(usb2_phy->app_125m); +} + +static int jh7110_usb2_phy_exit(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct jh7110_usb2_phy *usb2_phy = dev_get_priv(dev); + + clk_disable_unprepare(usb2_phy->app_125m); + + return 0; +} + +struct phy_ops jh7110_usb2_phy_ops = { + .init = jh7110_usb2_phy_init, + .exit = jh7110_usb2_phy_exit, + .set_mode = usb2_phy_set_mode, +}; + +int jh7110_usb2_phy_probe(struct udevice *dev) +{ + struct jh7110_usb2_phy *phy = dev_get_priv(dev); + ofnode node; + struct reg_field usb_split; + int ret; + + phy->regs = dev_read_addr_ptr(dev); + if (!phy->regs) + return -EINVAL; + + node = ofnode_by_compatible(ofnode_null(), "starfive,jh7110-sys-syscon"); + if (!ofnode_valid(node)) { + dev_err(dev, "Can't get syscon dev node\n"); + return -ENODEV; + } + + phy->sys_syscon = syscon_node_to_regmap(node); + if (IS_ERR(phy->sys_syscon)) { + dev_err(dev, "Can't get syscon regmap: %d\n", ret); + return PTR_ERR(phy->sys_syscon); + } + + usb_split.reg = SYSCON_USB_PDRSTN_REG_OFFSET; + usb_split.lsb = USB_PDRSTN_SPLIT_BIT; + usb_split.msb = USB_PDRSTN_SPLIT_BIT; + phy->usb_split = devm_regmap_field_alloc(dev, phy->sys_syscon, usb_split); + if (IS_ERR(phy->usb_split)) { + dev_err(dev, "USB split field init failed\n"); + return PTR_ERR(phy->usb_split); + } + + phy->usb_125m_clk = devm_clk_get(dev, "125m"); + if (IS_ERR(phy->usb_125m_clk)) { + dev_err(dev, "Failed to get 125m clock\n"); + return PTR_ERR(phy->usb_125m_clk); + } + + phy->app_125m = devm_clk_get(dev, "app_125m"); + if (IS_ERR(phy->app_125m)) { + dev_err(dev, "Failed to get app 125m clock\n"); + return PTR_ERR(phy->app_125m); + } + + return 0; +} + +static const struct udevice_id jh7110_usb2_phy[] = { + { .compatible = "starfive,jh7110-usb-phy"}, + {}, +}; + +U_BOOT_DRIVER(jh7110_usb2_phy) = { + .name = "jh7110_usb2_phy", + .id = UCLASS_PHY, + .of_match = jh7110_usb2_phy, + .probe = jh7110_usb2_phy_probe, + .ops = &jh7110_usb2_phy_ops, + .priv_auto = sizeof(struct jh7110_usb2_phy), +}; diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile index 699901fd15e..169036a08d7 100644 --- a/drivers/phy/ti/Makefile +++ b/drivers/phy/ti/Makefile @@ -1 +1 @@ -obj-$(CONFIG_$(XPL_)PHY_J721E_WIZ) += phy-j721e-wiz.o +obj-$(CONFIG_$(PHASE_)PHY_J721E_WIZ) += phy-j721e-wiz.o diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 6ee7dc1cce8..687fb339ea0 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -178,6 +178,14 @@ config PINCTRL_APPLE both the GPIO definitions and pin control functions for each available multiplex function. +config PINCTRL_ADI + bool "ADI pinctrl driver" + depends on DM && ARCH_SC5XX + help + This driver enables pinctrl support on SC5xx processors. This + driver covers only the pin configuration functionality, and + GPIO functionality is contained in the separate GPIO driver. + config PINCTRL_AR933X bool "QCA/Athores ar933x pin control driver" depends on DM && SOC_AR933X diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 634047a91f4..a8eba656843 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -1,13 +1,14 @@ # SPDX-License-Identifier: GPL-2.0+ obj-y += pinctrl-uclass.o -obj-$(CONFIG_$(XPL_)PINCTRL_GENERIC) += pinctrl-generic.o +obj-$(CONFIG_$(PHASE_)PINCTRL_GENERIC) += pinctrl-generic.o +obj-$(CONFIG_PINCTRL_ADI) += pinctrl-adi-adsp.o obj-$(CONFIG_PINCTRL_APPLE) += pinctrl-apple.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o obj-y += nxp/ -obj-$(CONFIG_$(XPL_)PINCTRL_ROCKCHIP) += rockchip/ +obj-$(CONFIG_$(PHASE_)PINCTRL_ROCKCHIP) += rockchip/ obj-$(CONFIG_ARCH_ASPEED) += aspeed/ obj-$(CONFIG_ARCH_ATH79) += ath79/ obj-$(CONFIG_PINCTRL_INTEL) += intel/ @@ -15,10 +16,9 @@ obj-$(CONFIG_ARCH_MTMIPS) += mtmips/ obj-$(CONFIG_ARCH_NPCM) += nuvoton/ obj-$(CONFIG_PINCTRL_QCOM) += qcom/ obj-$(CONFIG_ARCH_RENESAS) += renesas/ -obj-$(CONFIG_ARCH_RZN1) += renesas/ obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o obj-$(CONFIG_PINCTRL_SUNXI) += sunxi/ -obj-$(CONFIG_$(XPL_)PINCTRL_TEGRA) += tegra/ +obj-$(CONFIG_$(PHASE_)PINCTRL_TEGRA) += tegra/ obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl_pic32.o obj-$(CONFIG_PINCTRL_EXYNOS) += exynos/ @@ -32,7 +32,7 @@ obj-$(CONFIG_PINCTRL_QE) += pinctrl-qe-io.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_STI) += pinctrl-sti.o obj-$(CONFIG_PINCTRL_STM32) += pinctrl_stm32.o -obj-$(CONFIG_$(XPL_)PINCTRL_STMFX) += pinctrl-stmfx.o +obj-$(CONFIG_$(PHASE_)PINCTRL_STMFX) += pinctrl-stmfx.o obj-y += broadcom/ obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o obj-$(CONFIG_PINCTRL_STARFIVE) += starfive/ diff --git a/drivers/pinctrl/intel/Kconfig b/drivers/pinctrl/intel/Kconfig index 316a8fe27fd..0d8bfc53c11 100644 --- a/drivers/pinctrl/intel/Kconfig +++ b/drivers/pinctrl/intel/Kconfig @@ -8,9 +8,6 @@ config INTEL_PINCTRL_DUAL_ROUTE_SUPPORT bool default y -config INTEL_PINCTRL_PADCFG_PADTOL - bool n - config INTEL_PINCTRL_IOSTANDBY bool default y diff --git a/drivers/pinctrl/pinctrl-adi-adsp.c b/drivers/pinctrl/pinctrl-adi-adsp.c new file mode 100644 index 00000000000..debf434212d --- /dev/null +++ b/drivers/pinctrl/pinctrl-adi-adsp.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa <greg.malysa@timesys.com> + * Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * + * dm pinctrl implementation for ADI ADSP SoCs + * + */ + +#include <dm.h> +#include <dm/pinctrl.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/io.h> + +#define ADSP_PORT_MMIO_SIZE 0x80 +#define ADSP_PORT_PIN_SIZE 16 + +#define ADSP_PORT_PORT_MUX_BITS 2 +#define ADSP_PORT_PORT_MUX_MASK 0x03 +#define ADSP_PINCTRL_FUNCTION_COUNT 4 + +#define ADSP_PORT_REG_FER 0x00 +#define ADSP_PORT_REG_FER_SET 0x04 +#define ADSP_PORT_REG_FER_CLEAR 0x08 +#define ADSP_PORT_REG_DATA 0x0c +#define ADSP_PORT_REG_DATA_SET 0x10 +#define ADSP_PORT_REG_DATA_CLEAR 0x14 +#define ADSP_PORT_REG_DIR 0x18 +#define ADSP_PORT_REG_DIR_SET 0x1c +#define ADSP_PORT_REG_DIR_CLEAR 0x20 +#define ADSP_PORT_REG_INEN 0x24 +#define ADSP_PORT_REG_INEN_SET 0x28 +#define ADSP_PORT_REG_INEN_CLEAR 0x2c +#define ADSP_PORT_REG_PORT_MUX 0x30 +#define ADSP_PORT_REG_DATA_TGL 0x34 +#define ADSP_PORT_REG_POLAR 0x38 +#define ADSP_PORT_REG_POLAR_SET 0x3c +#define ADSP_PORT_REG_POLAR_CLEAR 0x40 +#define ADSP_PORT_REG_LOCK 0x44 +#define ADSP_PORT_REG_TRIG_TGL 0x48 + +struct adsp_pinctrl_priv { + void __iomem *base; + int npins; + char pinbuf[16]; +}; + +static u32 get_port(unsigned int pin) +{ + return pin / ADSP_PORT_PIN_SIZE; +} + +static u32 get_offset(unsigned int pin) +{ + return pin % ADSP_PORT_PIN_SIZE; +} + +static int adsp_pinctrl_pinmux_set(struct udevice *udev, unsigned int pin, unsigned int func) +{ + struct adsp_pinctrl_priv *priv = dev_get_priv(udev); + void __iomem *portbase; + u32 port, offset; + u32 val; + + if (pin >= priv->npins) + return -ENODEV; + + if (func >= ADSP_PINCTRL_FUNCTION_COUNT) + return -EINVAL; + + port = get_port(pin); + offset = get_offset(pin); + portbase = priv->base + port * ADSP_PORT_MMIO_SIZE; + + val = ioread32(portbase + ADSP_PORT_REG_PORT_MUX); + val &= ~(ADSP_PORT_PORT_MUX_MASK << (ADSP_PORT_PORT_MUX_BITS * offset)); + val |= func << (ADSP_PORT_PORT_MUX_BITS * offset); + iowrite32(val, portbase + ADSP_PORT_REG_PORT_MUX); + + iowrite32(BIT(offset), portbase + ADSP_PORT_REG_FER_SET); + return 0; +} + +static int adsp_pinctrl_set_state(struct udevice *udev, struct udevice *config) +{ + const struct fdt_property *pinlist; + int length = 0; + int ret, i; + u32 pin, function; + + pinlist = dev_read_prop(config, "adi,pins", &length); + if (!pinlist) { + dev_err(udev, "missing adi,pins property in pinctrl config node\n"); + return -EINVAL; + } + + if (length % (sizeof(uint32_t) * 2)) { + dev_err(udev, "adi,pins property must be a multiple of two uint32_ts\n"); + return -EINVAL; + } + + for (i = 0; i < length / sizeof(uint32_t); i += 2) { + ret = dev_read_u32_index(config, "adi,pins", i, &pin); + if (ret) + return ret; + + ret = dev_read_u32_index(config, "adi,pins", i + 1, &function); + if (ret) + return ret; + + ret = adsp_pinctrl_pinmux_set(udev, pin, function); + if (ret) + return ret; + } + + return 0; +} + +const struct pinctrl_ops adsp_pinctrl_ops = { + .set_state = adsp_pinctrl_set_state, +}; + +static int adsp_pinctrl_probe(struct udevice *udev) +{ + struct adsp_pinctrl_priv *priv = dev_get_priv(udev); + + priv->base = dev_read_addr_ptr(udev); + priv->npins = dev_read_u32_default(udev, "adi,npins", 0); + + if (!priv->base) { + dev_err(udev, "Missing or invalid pinctrl base address\n"); + return -ENOENT; + } + + if (!priv->npins) { + dev_err(udev, "Missing adi,npins property!\n"); + return -ENOENT; + } + + return 0; +} + +static const struct udevice_id adsp_pinctrl_match[] = { + { .compatible = "adi,adsp-pinctrl" }, + { }, +}; + +U_BOOT_DRIVER(adi_adsp_pinctrl) = { + .name = "adi_adsp_pinctrl", + .id = UCLASS_PINCTRL, + .of_match = adsp_pinctrl_match, + .probe = adsp_pinctrl_probe, + .priv_auto = sizeof(struct adsp_pinctrl_priv), + .ops = &adsp_pinctrl_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 5038cb535e3..2938635ed95 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -9,6 +9,8 @@ #include <dm.h> #include <log.h> #include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/lists.h> #include <dm/pinctrl.h> #include <asm/hardware.h> #include <linux/bitops.h> @@ -492,21 +494,58 @@ const struct pinctrl_ops at91_pinctrl_ops = { .set_state = at91_pinctrl_set_state, }; +/** + * at91_pinctrl_bind() - Iterates through all subnodes of the pinctrl device + * in the DT and binds them to U-Boot's device model. Each subnode + * typically represents a GPIO controller or pin configuration data. + * + * @dev: Pointer to the pinctrl device + * + * Returns 0 on success or negative error on failure + */ +static int at91_pinctrl_bind(struct udevice *dev) +{ + ofnode gpio_node; + struct udevice *gpio; + int ret; + + ofnode_for_each_subnode(gpio_node, dev_ofnode(dev)) { + ret = lists_bind_fdt(dev, gpio_node, &gpio, NULL, false); + if (ret) + return ret; + } + + return 0; +} + static int at91_pinctrl_probe(struct udevice *dev) { struct at91_pinctrl_priv *priv = dev_get_priv(dev); fdt_addr_t addr_base; + struct udevice *gpio_node; int index; - for (index = 0; index < MAX_GPIO_BANKS; index++) { - addr_base = devfdt_get_addr_index(dev, index); - if (addr_base == FDT_ADDR_T_NONE) - break; + if (list_empty(&dev->child_head)) { + for (index = 0; index < MAX_GPIO_BANKS; index++) { + addr_base = devfdt_get_addr_index(dev, index); + if (addr_base == FDT_ADDR_T_NONE) + break; - priv->reg_base[index] = (struct at91_port *)addr_base; + priv->reg_base[index] = (struct at91_port *)addr_base; + } + } else { + index = 0; + list_for_each_entry(gpio_node, &dev->child_head, sibling_node) { + addr_base = dev_read_addr(gpio_node); + if (addr_base == FDT_ADDR_T_NONE) + break; + + priv->reg_base[index] = (struct at91_port *)addr_base; + index++; + } } - priv->nbanks = index; + priv->nbanks = index < MAX_GPIO_BANKS ? index : MAX_GPIO_BANKS; return 0; } @@ -524,6 +563,7 @@ U_BOOT_DRIVER(atmel_sama5d3_pinctrl) = { .id = UCLASS_PINCTRL, .of_match = at91_pinctrl_match, .probe = at91_pinctrl_probe, + .bind = at91_pinctrl_bind, .priv_auto = sizeof(struct at91_pinctrl_priv), .ops = &at91_pinctrl_ops, }; diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index d3eb6998551..e567cb113a3 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -7,84 +7,111 @@ config PINCTRL_QCOM menu "Qualcomm pinctrl drivers" config PINCTRL_QCOM_APQ8016 - bool "Qualcomm APQ8016 GCC" + bool "Qualcomm APQ8016 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the MSM8916 / APQ8016 Snapdragon 410 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_APQ8096 - bool "Qualcomm APQ8096 GCC" + bool "Qualcomm APQ8096 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the MSM8996 / APQ8096 Snapdragon 820 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_IPQ4019 - bool "Qualcomm IPQ4019 GCC" + bool "Qualcomm IPQ4019 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the IPQ4019 SoC, as well as the associated GPIO driver. +config PINCTRL_QCOM_IPQ9574 + bool "Qualcomm IPQ9574 Pinctrl" + select PINCTRL_QCOM + help + Say Y here to enable support for pinctrl on the IPQ9574 SoC, + as well as the associated GPIO driver. + config PINCTRL_QCOM_QCM2290 - bool "Qualcomm QCM2290 GCC" + bool "Qualcomm QCM2290 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon QCM2290 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_QCS404 - bool "Qualcomm QCS404 GCC" + bool "Qualcomm QCS404 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon QCS404 SoC, as well as the associated GPIO driver. +config PINCTRL_QCOM_SA8775P + bool "Qualcomm SA8775P Pinctrl" + select PINCTRL_QCOM + help + Say Y here to enable support for pinctrl on the Snapdragon SA8775P SoC, + as well as the associated GPIO driver. + +config PINCTRL_QCOM_SC7280 + bool "Qualcomm SC7280/QCM6490 Pinctrl" + select PINCTRL_QCOM + help + Say Y here to enable support for pinctrl on the Snapdragon SC7280 SoC, + +config PINCTRL_QCOM_SDM660 + bool "Qualcomm SDM630/660 Pinctrl" + select PINCTRL_QCOM + help + Say Y here to enable support for pinctrl on the Snapdragon 630/636/660 + SoCs, as well as the associated GPIO driver. + config PINCTRL_QCOM_SDM845 - bool "Qualcomm SDM845 GCC" + bool "Qualcomm SDM845 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon 845 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_SM6115 - bool "Qualcomm SM6115 GCC" + bool "Qualcomm SM6115 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon SM6115 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_SM8150 - bool "Qualcomm SM8150 GCC" + bool "Qualcomm SM8150 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon SM8150 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_SM8250 - bool "Qualcomm SM8250 GCC" + bool "Qualcomm SM8250 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon SM8250 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_SM8550 - bool "Qualcomm SM8550 GCC" + bool "Qualcomm SM8550 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon SM8550 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_SM8650 - bool "Qualcomm SM8650 GCC" + bool "Qualcomm SM8650 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon SM8650 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_X1E80100 - bool "Qualcomm X1E80100 GCC" + bool "Qualcomm X1E80100 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon X1E80100 SoC, diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index 06d3c95f93a..6ffc6d48c15 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -5,9 +5,13 @@ obj-$(CONFIG_PINCTRL_QCOM) += pinctrl-qcom.o obj-$(CONFIG_PINCTRL_QCOM_APQ8016) += pinctrl-apq8016.o obj-$(CONFIG_PINCTRL_QCOM_IPQ4019) += pinctrl-ipq4019.o +obj-$(CONFIG_PINCTRL_QCOM_IPQ9574) += pinctrl-ipq9574.o obj-$(CONFIG_PINCTRL_QCOM_APQ8096) += pinctrl-apq8096.o obj-$(CONFIG_PINCTRL_QCOM_QCM2290) += pinctrl-qcm2290.o obj-$(CONFIG_PINCTRL_QCOM_QCS404) += pinctrl-qcs404.o +obj-$(CONFIG_PINCTRL_QCOM_SA8775P) += pinctrl-sa8775p.o +obj-$(CONFIG_PINCTRL_QCOM_SC7280) += pinctrl-sc7280.o +obj-$(CONFIG_PINCTRL_QCOM_SDM660) += pinctrl-sdm660.o obj-$(CONFIG_PINCTRL_QCOM_SDM845) += pinctrl-sdm845.o obj-$(CONFIG_PINCTRL_QCOM_SM6115) += pinctrl-sm6115.o obj-$(CONFIG_PINCTRL_QCOM_SM8150) += pinctrl-sm8150.o diff --git a/drivers/pinctrl/qcom/pinctrl-apq8016.c b/drivers/pinctrl/qcom/pinctrl-apq8016.c index 0c7437822ff..9ae07d43d93 100644 --- a/drivers/pinctrl/qcom/pinctrl-apq8016.c +++ b/drivers/pinctrl/qcom/pinctrl-apq8016.c @@ -50,8 +50,8 @@ static const char *apq8016_get_pin_name(struct udevice *dev, } } -static unsigned int apq8016_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int apq8016_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-apq8096.c b/drivers/pinctrl/qcom/pinctrl-apq8096.c index 132ece868bf..eaa927c6e22 100644 --- a/drivers/pinctrl/qcom/pinctrl-apq8096.c +++ b/drivers/pinctrl/qcom/pinctrl-apq8096.c @@ -43,8 +43,8 @@ static const char *apq8096_get_pin_name(struct udevice *dev, } } -static unsigned int apq8096_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int apq8096_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-ipq4019.c b/drivers/pinctrl/qcom/pinctrl-ipq4019.c index 3215c677b26..dafcd494df4 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq4019.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq4019.c @@ -311,8 +311,7 @@ static const char *ipq4019_get_pin_name(struct udevice *dev, return pin_name; } -static unsigned int ipq4019_get_function_mux(unsigned int pin, - unsigned int selector) +static int ipq4019_get_function_mux(unsigned int pin, unsigned int selector) { unsigned int i; const msm_pin_function *func = ipq4019_pin_functions + pin; diff --git a/drivers/pinctrl/qcom/pinctrl-ipq9574.c b/drivers/pinctrl/qcom/pinctrl-ipq9574.c new file mode 100644 index 00000000000..ce09072ae3c --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-ipq9574.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * pinctrl driver for Qualcomm ipq9574 + * + * (C) Copyright 2025 Linaro Ltd. + */ + +#include <dm.h> + +#include "pinctrl-qcom.h" + +#define MAX_PIN_NAME_LEN 32 +static char pin_name[MAX_PIN_NAME_LEN] __section(".data"); + +enum ipq9574_functions { + msm_mux_blsp0_spi, + msm_mux_blsp0_uart, + msm_mux_blsp1_i2c, + msm_mux_blsp1_spi, + msm_mux_blsp1_uart, + msm_mux_blsp2_i2c, + msm_mux_blsp2_spi, + msm_mux_blsp2_uart, + msm_mux_blsp3_i2c, + msm_mux_blsp3_spi, + msm_mux_blsp3_uart, + msm_mux_blsp4_i2c, + msm_mux_blsp4_spi, + msm_mux_blsp4_uart, + msm_mux_blsp5_i2c, + msm_mux_blsp5_uart, + msm_mux_gpio, + msm_mux_mdc, + msm_mux_mdio, + msm_mux_pcie0_clk, + msm_mux_pcie0_wake, + msm_mux_pcie1_clk, + msm_mux_pcie1_wake, + msm_mux_pcie2_clk, + msm_mux_pcie2_wake, + msm_mux_pcie3_clk, + msm_mux_pcie3_wake, + msm_mux_qspi_data, + msm_mux_qspi_clk, + msm_mux_qspi_cs, + msm_mux_sdc_data, + msm_mux_sdc_clk, + msm_mux_sdc_cmd, + msm_mux_sdc_rclk, + msm_mux_NA, +}; + +#define MSM_PIN_FUNCTION(fname) \ + [msm_mux_##fname] = {#fname, msm_mux_##fname} + +static const struct pinctrl_function msm_pinctrl_functions[] = { + MSM_PIN_FUNCTION(blsp0_spi), + MSM_PIN_FUNCTION(blsp0_uart), + MSM_PIN_FUNCTION(blsp1_i2c), + MSM_PIN_FUNCTION(blsp1_spi), + MSM_PIN_FUNCTION(blsp1_uart), + MSM_PIN_FUNCTION(blsp2_i2c), + MSM_PIN_FUNCTION(blsp2_spi), + MSM_PIN_FUNCTION(blsp2_uart), + MSM_PIN_FUNCTION(blsp3_i2c), + MSM_PIN_FUNCTION(blsp3_spi), + MSM_PIN_FUNCTION(blsp3_uart), + MSM_PIN_FUNCTION(blsp4_i2c), + MSM_PIN_FUNCTION(blsp4_spi), + MSM_PIN_FUNCTION(blsp4_uart), + MSM_PIN_FUNCTION(blsp5_i2c), + MSM_PIN_FUNCTION(blsp5_uart), + MSM_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(mdc), + MSM_PIN_FUNCTION(mdio), + MSM_PIN_FUNCTION(pcie0_clk), + MSM_PIN_FUNCTION(pcie0_wake), + MSM_PIN_FUNCTION(pcie1_clk), + MSM_PIN_FUNCTION(pcie1_wake), + MSM_PIN_FUNCTION(pcie2_clk), + MSM_PIN_FUNCTION(pcie2_wake), + MSM_PIN_FUNCTION(pcie3_clk), + MSM_PIN_FUNCTION(pcie3_wake), + MSM_PIN_FUNCTION(qspi_data), + MSM_PIN_FUNCTION(qspi_clk), + MSM_PIN_FUNCTION(qspi_cs), + MSM_PIN_FUNCTION(sdc_data), + MSM_PIN_FUNCTION(sdc_clk), + MSM_PIN_FUNCTION(sdc_cmd), + MSM_PIN_FUNCTION(sdc_rclk), +}; + +typedef unsigned int msm_pin_function[10]; + +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + [id] = { msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + } + +static const msm_pin_function ipq9574_pin_functions[] = { + PINGROUP(0, sdc_data, qspi_data, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(1, sdc_data, qspi_data, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(2, sdc_data, qspi_data, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(3, sdc_data, qspi_data, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(4, sdc_cmd, qspi_cs, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(5, sdc_clk, qspi_clk, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(6, sdc_data, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(7, sdc_data, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(8, sdc_data, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(9, sdc_data, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(10, sdc_rclk, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(11, blsp0_spi, blsp0_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(12, blsp0_spi, blsp0_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(13, blsp0_spi, blsp0_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(14, blsp0_spi, blsp0_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(15, blsp3_spi, blsp3_i2c, blsp3_uart, NA, NA, NA, NA, NA, NA), + PINGROUP(16, blsp3_spi, blsp3_i2c, blsp3_uart, NA, NA, NA, NA, NA, NA), + PINGROUP(17, blsp3_spi, blsp3_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(18, blsp3_spi, blsp3_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(19, blsp3_spi, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(20, blsp3_spi, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(21, blsp3_spi, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(22, pcie0_clk, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(23, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(24, pcie0_wake, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(25, pcie1_clk, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(26, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(27, pcie1_wake, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(28, pcie2_clk, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(29, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(30, pcie2_wake, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(31, pcie3_clk, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(32, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(33, pcie3_wake, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(34, blsp2_uart, blsp2_i2c, blsp2_spi, blsp1_uart, NA, NA, NA, NA, NA), + PINGROUP(35, blsp2_uart, blsp2_i2c, blsp2_spi, blsp1_uart, NA, NA, NA, NA, NA), + PINGROUP(36, blsp1_uart, blsp1_i2c, blsp2_spi, NA, NA, NA, NA, NA, NA), + PINGROUP(37, blsp1_uart, blsp1_i2c, blsp2_spi, NA, NA, NA, NA, NA, NA), + PINGROUP(38, mdc, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(39, mdio, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(40, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(41, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(42, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(43, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(44, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(45, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(46, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(47, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(48, blsp5_i2c, blsp5_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(49, blsp5_i2c, blsp5_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(50, blsp4_uart, blsp4_i2c, blsp4_spi, NA, NA, NA, NA, NA, NA), + PINGROUP(51, blsp4_uart, blsp4_i2c, blsp4_spi, NA, NA, NA, NA, NA, NA), + PINGROUP(52, blsp4_uart, blsp4_spi, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(53, blsp4_uart, blsp4_spi, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(54, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(55, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(56, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(57, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(58, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(59, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(60, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(61, blsp1_spi, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(62, blsp1_spi, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(63, blsp1_spi, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(64, blsp1_spi, NA, NA, NA, NA, NA, NA, NA, NA), +}; + +static const char *ipq9574_get_function_name(struct udevice *dev, + unsigned int selector) +{ + return msm_pinctrl_functions[selector].name; +} + +static const char *ipq9574_get_pin_name(struct udevice *dev, + unsigned int selector) +{ + snprintf(pin_name, MAX_PIN_NAME_LEN, "gpio%u", selector); + return pin_name; +} + +static int ipq9574_get_function_mux(unsigned int pin, unsigned int selector) +{ + unsigned int i; + const msm_pin_function *func = ipq9574_pin_functions + pin; + + for (i = 0; i < 10; i++) + if ((*func)[i] == selector) + return i; + + debug("Can't find requested function for pin:selector %u:%u\n", + pin, selector); + + return -EINVAL; +} + +static const struct msm_pinctrl_data ipq9574_data = { + .pin_data = { + .pin_count = 65, + }, + .functions_count = ARRAY_SIZE(msm_pinctrl_functions), + .get_function_name = ipq9574_get_function_name, + .get_function_mux = ipq9574_get_function_mux, + .get_pin_name = ipq9574_get_pin_name, +}; + +static const struct udevice_id msm_pinctrl_ids[] = { + { .compatible = "qcom,ipq9574-tlmm", .data = (ulong)&ipq9574_data }, + { /* Sentinal */ } +}; + +U_BOOT_DRIVER(pinctrl_ipq9574) = { + .name = "pinctrl_ipq9574", + .id = UCLASS_NOP, + .of_match = msm_pinctrl_ids, + .ops = &msm_pinctrl_ops, + .bind = msm_pinctrl_bind, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/pinctrl/qcom/pinctrl-qcm2290.c b/drivers/pinctrl/qcom/pinctrl-qcm2290.c index af969e177d7..0c2222ce663 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcm2290.c +++ b/drivers/pinctrl/qcom/pinctrl-qcm2290.c @@ -38,7 +38,7 @@ static const char *qcm2290_get_pin_name(struct udevice *dev, unsigned int select return pin_name; } -static unsigned int qcm2290_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) +static int qcm2290_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-qcom.c b/drivers/pinctrl/qcom/pinctrl-qcom.c index 26a3fba194a..c95db56bc47 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcom.c +++ b/drivers/pinctrl/qcom/pinctrl-qcom.c @@ -15,14 +15,18 @@ #include <asm/gpio.h> #include <dm/pinctrl.h> #include <linux/bitops.h> +#include <linux/bitmap.h> #include <linux/bug.h> #include <mach/gpio.h> #include "pinctrl-qcom.h" +#define MSM_PINCTRL_MAX_PINS 256 + struct msm_pinctrl_priv { phys_addr_t base; struct msm_pinctrl_data *data; + DECLARE_BITMAP(reserved_map, MSM_PINCTRL_MAX_PINS); }; #define GPIO_CONFIG_REG(priv, x) \ @@ -71,13 +75,60 @@ static const char *msm_get_function_name(struct udevice *dev, return priv->data->get_function_name(dev, selector); } +static int msm_pinctrl_parse_ranges(struct udevice *dev) +{ + struct msm_pinctrl_priv *priv = dev_get_priv(dev); + ofnode node = dev_ofnode(dev); + int ret, count, i; + u32 *ranges; + + if (ofnode_read_prop(node, "gpio-reserved-ranges", &count)) { + if (count % 2 == 1) { + dev_err(dev, "gpio-reserved-ranges must be a multiple of 2\n"); + return -EINVAL; + } + + ranges = malloc(count); + if (!ranges) + return -ENOMEM; + + ret = ofnode_read_u32_array(node, "gpio-reserved-ranges", ranges, count / 4); + if (ret) { + dev_err(dev, "failed to read gpio-reserved-ranges array (%d)\n", ret); + return ret; + } + + for (i = 0; i < count / 4; i += 2) { + if (ranges[i] >= MSM_PINCTRL_MAX_PINS || + (ranges[i] + ranges[i + 1]) >= MSM_PINCTRL_MAX_PINS) { + dev_err(dev, "invalid reserved-range (%d;%d)\n", + ranges[i], ranges[i + 1]); + return -EINVAL; + } + + bitmap_set(priv->reserved_map, ranges[i], ranges[i + 1]); + } + + free(ranges); + } + + return 0; +} + static int msm_pinctrl_probe(struct udevice *dev) { struct msm_pinctrl_priv *priv = dev_get_priv(dev); + int ret; priv->base = dev_read_addr(dev); priv->data = (struct msm_pinctrl_data *)dev_get_driver_data(dev); + ret = msm_pinctrl_parse_ranges(dev); + if (ret) { + printf("Couldn't parse reserved GPIO ranges!\n"); + return ret; + } + return priv->base == FDT_ADDR_T_NONE ? -EINVAL : 0; } @@ -92,7 +143,13 @@ static int msm_pinmux_set(struct udevice *dev, unsigned int pin_selector, unsigned int func_selector) { struct msm_pinctrl_priv *priv = dev_get_priv(dev); - u32 func = priv->data->get_function_mux(pin_selector, func_selector); + int func = priv->data->get_function_mux(pin_selector, func_selector); + + if (func < 0) + return func; + + if (msm_pinctrl_is_reserved(dev, pin_selector)) + return -EPERM; /* Always NOP for special pins, assume they're in the correct state */ if (qcom_is_special_pin(&priv->data->pin_data, pin_selector)) @@ -142,6 +199,9 @@ static int msm_pinconf_set(struct udevice *dev, unsigned int pin_selector, { struct msm_pinctrl_priv *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev, pin_selector)) + return -EPERM; + if (qcom_is_special_pin(&priv->data->pin_data, pin_selector)) return msm_pinconf_set_special(priv, pin_selector, param, argument); @@ -238,3 +298,13 @@ U_BOOT_DRIVER(pinctrl_qcom) = { .ops = &msm_pinctrl_ops, .probe = msm_pinctrl_probe, }; + +bool msm_pinctrl_is_reserved(struct udevice *dev, unsigned int pin) +{ + struct msm_pinctrl_priv *priv = dev_get_priv(dev); + + if (pin >= MSM_PINCTRL_MAX_PINS) + return false; + + return test_bit(pin, priv->reserved_map); +} diff --git a/drivers/pinctrl/qcom/pinctrl-qcom.h b/drivers/pinctrl/qcom/pinctrl-qcom.h index 49b7bfbc001..cd167e63868 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcom.h +++ b/drivers/pinctrl/qcom/pinctrl-qcom.h @@ -18,8 +18,7 @@ struct msm_pinctrl_data { int functions_count; const char *(*get_function_name)(struct udevice *dev, unsigned int selector); - unsigned int (*get_function_mux)(unsigned int pin, - unsigned int selector); + int (*get_function_mux)(unsigned int pin, unsigned int selector); const char *(*get_pin_name)(struct udevice *dev, unsigned int selector); }; diff --git a/drivers/pinctrl/qcom/pinctrl-qcs404.c b/drivers/pinctrl/qcom/pinctrl-qcs404.c index fb6defaeddf..c272595aa2a 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs404.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs404.c @@ -93,8 +93,8 @@ static const char *qcs404_get_pin_name(struct udevice *dev, } } -static unsigned int qcs404_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int qcs404_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-sa8775p.c b/drivers/pinctrl/qcom/pinctrl-sa8775p.c new file mode 100644 index 00000000000..cb2496ff1fb --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sa8775p.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, Linaro Limited + */ + +#include <dm.h> + +#include "pinctrl-qcom.h" + +#define MAX_PIN_NAME_LEN 32 +static char pin_name[MAX_PIN_NAME_LEN] __section(".data"); + +typedef unsigned int msm_pin_function[10]; +#define SA8775_PIN_OFFSET 0x100000 + +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9)\ + { \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9 \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .name = pg_name, \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + } + +#define UFS_RESET(pg_name, ctl) \ + { \ + .name = pg_name, \ + .ctl_reg = ctl, \ + .io_reg = ctl + 0x4, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + } + +enum sa8775p_functions { + msm_mux_gpio, + msm_mux_atest_char, + msm_mux_atest_usb2, + msm_mux_audio_ref, + msm_mux_cam_mclk, + msm_mux_cci_async, + msm_mux_cci_i2c, + msm_mux_cci_timer0, + msm_mux_cci_timer1, + msm_mux_cci_timer2, + msm_mux_cci_timer3, + msm_mux_cci_timer4, + msm_mux_cci_timer5, + msm_mux_cci_timer6, + msm_mux_cci_timer7, + msm_mux_cci_timer8, + msm_mux_cci_timer9, + msm_mux_cri_trng, + msm_mux_cri_trng0, + msm_mux_cri_trng1, + msm_mux_dbg_out, + msm_mux_ddr_bist, + msm_mux_ddr_pxi0, + msm_mux_ddr_pxi1, + msm_mux_ddr_pxi2, + msm_mux_ddr_pxi3, + msm_mux_ddr_pxi4, + msm_mux_ddr_pxi5, + msm_mux_edp0_hot, + msm_mux_edp0_lcd, + msm_mux_edp1_hot, + msm_mux_edp1_lcd, + msm_mux_edp2_hot, + msm_mux_edp2_lcd, + msm_mux_edp3_hot, + msm_mux_edp3_lcd, + msm_mux_emac0_mcg0, + msm_mux_emac0_mcg1, + msm_mux_emac0_mcg2, + msm_mux_emac0_mcg3, + msm_mux_emac0_mdc, + msm_mux_emac0_mdio, + msm_mux_emac0_ptp_aux, + msm_mux_emac0_ptp_pps, + msm_mux_emac1_mcg0, + msm_mux_emac1_mcg1, + msm_mux_emac1_mcg2, + msm_mux_emac1_mcg3, + msm_mux_emac1_mdc, + msm_mux_emac1_mdio, + msm_mux_emac1_ptp_aux, + msm_mux_emac1_ptp_pps, + msm_mux_gcc_gp1, + msm_mux_gcc_gp2, + msm_mux_gcc_gp3, + msm_mux_gcc_gp4, + msm_mux_gcc_gp5, + msm_mux_hs0_mi2s, + msm_mux_hs1_mi2s, + msm_mux_hs2_mi2s, + msm_mux_ibi_i3c, + msm_mux_jitter_bist, + msm_mux_mdp0_vsync0, + msm_mux_mdp0_vsync1, + msm_mux_mdp0_vsync2, + msm_mux_mdp0_vsync3, + msm_mux_mdp0_vsync4, + msm_mux_mdp0_vsync5, + msm_mux_mdp0_vsync6, + msm_mux_mdp0_vsync7, + msm_mux_mdp0_vsync8, + msm_mux_mdp1_vsync0, + msm_mux_mdp1_vsync1, + msm_mux_mdp1_vsync2, + msm_mux_mdp1_vsync3, + msm_mux_mdp1_vsync4, + msm_mux_mdp1_vsync5, + msm_mux_mdp1_vsync6, + msm_mux_mdp1_vsync7, + msm_mux_mdp1_vsync8, + msm_mux_mdp_vsync, + msm_mux_mi2s1_data0, + msm_mux_mi2s1_data1, + msm_mux_mi2s1_sck, + msm_mux_mi2s1_ws, + msm_mux_mi2s2_data0, + msm_mux_mi2s2_data1, + msm_mux_mi2s2_sck, + msm_mux_mi2s2_ws, + msm_mux_mi2s_mclk0, + msm_mux_mi2s_mclk1, + msm_mux_pcie0_clkreq, + msm_mux_pcie1_clkreq, + msm_mux_phase_flag, + msm_mux_pll_bist, + msm_mux_pll_clk, + msm_mux_prng_rosc0, + msm_mux_prng_rosc1, + msm_mux_prng_rosc2, + msm_mux_prng_rosc3, + msm_mux_qdss_cti, + msm_mux_qdss_gpio, + msm_mux_qup0_se0, + msm_mux_qup0_se1, + msm_mux_qup0_se2, + msm_mux_qup0_se3, + msm_mux_qup0_se4, + msm_mux_qup0_se5, + msm_mux_qup1_se0, + msm_mux_qup1_se1, + msm_mux_qup1_se2, + msm_mux_qup1_se3, + msm_mux_qup1_se4, + msm_mux_qup1_se5, + msm_mux_qup1_se6, + msm_mux_qup2_se0, + msm_mux_qup2_se1, + msm_mux_qup2_se2, + msm_mux_qup2_se3, + msm_mux_qup2_se4, + msm_mux_qup2_se5, + msm_mux_qup2_se6, + msm_mux_qup3_se0, + msm_mux_sail_top, + msm_mux_sailss_emac0, + msm_mux_sailss_ospi, + msm_mux_sgmii_phy, + msm_mux_tb_trig, + msm_mux_tgu_ch0, + msm_mux_tgu_ch1, + msm_mux_tgu_ch2, + msm_mux_tgu_ch3, + msm_mux_tgu_ch4, + msm_mux_tgu_ch5, + msm_mux_tsense_pwm1, + msm_mux_tsense_pwm2, + msm_mux_tsense_pwm3, + msm_mux_tsense_pwm4, + msm_mux_usb2phy_ac, + msm_mux_vsense_trigger, + msm_mux__, +}; + +#define MSM_PIN_FUNCTION(fname) \ + [msm_mux_##fname] = {#fname, msm_mux_##fname} + +static const struct pinctrl_function msm_pinctrl_functions[] = { + MSM_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_usb2), + MSM_PIN_FUNCTION(audio_ref), + MSM_PIN_FUNCTION(cam_mclk), + MSM_PIN_FUNCTION(cci_async), + MSM_PIN_FUNCTION(cci_i2c), + MSM_PIN_FUNCTION(cci_timer0), + MSM_PIN_FUNCTION(cci_timer1), + MSM_PIN_FUNCTION(cci_timer2), + MSM_PIN_FUNCTION(cci_timer3), + MSM_PIN_FUNCTION(cci_timer4), + MSM_PIN_FUNCTION(cci_timer5), + MSM_PIN_FUNCTION(cci_timer6), + MSM_PIN_FUNCTION(cci_timer7), + MSM_PIN_FUNCTION(cci_timer8), + MSM_PIN_FUNCTION(cci_timer9), + MSM_PIN_FUNCTION(cri_trng), + MSM_PIN_FUNCTION(cri_trng0), + MSM_PIN_FUNCTION(cri_trng1), + MSM_PIN_FUNCTION(dbg_out), + MSM_PIN_FUNCTION(ddr_bist), + MSM_PIN_FUNCTION(ddr_pxi0), + MSM_PIN_FUNCTION(ddr_pxi1), + MSM_PIN_FUNCTION(ddr_pxi2), + MSM_PIN_FUNCTION(ddr_pxi3), + MSM_PIN_FUNCTION(ddr_pxi4), + MSM_PIN_FUNCTION(ddr_pxi5), + MSM_PIN_FUNCTION(edp0_hot), + MSM_PIN_FUNCTION(edp0_lcd), + MSM_PIN_FUNCTION(edp1_hot), + MSM_PIN_FUNCTION(edp1_lcd), + MSM_PIN_FUNCTION(edp2_hot), + MSM_PIN_FUNCTION(edp2_lcd), + MSM_PIN_FUNCTION(edp3_hot), + MSM_PIN_FUNCTION(edp3_lcd), + MSM_PIN_FUNCTION(emac0_mcg0), + MSM_PIN_FUNCTION(emac0_mcg1), + MSM_PIN_FUNCTION(emac0_mcg2), + MSM_PIN_FUNCTION(emac0_mcg3), + MSM_PIN_FUNCTION(emac0_mdc), + MSM_PIN_FUNCTION(emac0_mdio), + MSM_PIN_FUNCTION(emac0_ptp_aux), + MSM_PIN_FUNCTION(emac0_ptp_pps), + MSM_PIN_FUNCTION(emac1_mcg0), + MSM_PIN_FUNCTION(emac1_mcg1), + MSM_PIN_FUNCTION(emac1_mcg2), + MSM_PIN_FUNCTION(emac1_mcg3), + MSM_PIN_FUNCTION(emac1_mdc), + MSM_PIN_FUNCTION(emac1_mdio), + MSM_PIN_FUNCTION(emac1_ptp_aux), + MSM_PIN_FUNCTION(emac1_ptp_pps), + MSM_PIN_FUNCTION(gcc_gp1), + MSM_PIN_FUNCTION(gcc_gp2), + MSM_PIN_FUNCTION(gcc_gp3), + MSM_PIN_FUNCTION(gcc_gp4), + MSM_PIN_FUNCTION(gcc_gp5), + MSM_PIN_FUNCTION(hs0_mi2s), + MSM_PIN_FUNCTION(hs1_mi2s), + MSM_PIN_FUNCTION(hs2_mi2s), + MSM_PIN_FUNCTION(ibi_i3c), + MSM_PIN_FUNCTION(jitter_bist), + MSM_PIN_FUNCTION(mdp0_vsync0), + MSM_PIN_FUNCTION(mdp0_vsync1), + MSM_PIN_FUNCTION(mdp0_vsync2), + MSM_PIN_FUNCTION(mdp0_vsync3), + MSM_PIN_FUNCTION(mdp0_vsync4), + MSM_PIN_FUNCTION(mdp0_vsync5), + MSM_PIN_FUNCTION(mdp0_vsync6), + MSM_PIN_FUNCTION(mdp0_vsync7), + MSM_PIN_FUNCTION(mdp0_vsync8), + MSM_PIN_FUNCTION(mdp1_vsync0), + MSM_PIN_FUNCTION(mdp1_vsync1), + MSM_PIN_FUNCTION(mdp1_vsync2), + MSM_PIN_FUNCTION(mdp1_vsync3), + MSM_PIN_FUNCTION(mdp1_vsync4), + MSM_PIN_FUNCTION(mdp1_vsync5), + MSM_PIN_FUNCTION(mdp1_vsync6), + MSM_PIN_FUNCTION(mdp1_vsync7), + MSM_PIN_FUNCTION(mdp1_vsync8), + MSM_PIN_FUNCTION(mdp_vsync), + MSM_PIN_FUNCTION(mi2s1_data0), + MSM_PIN_FUNCTION(mi2s1_data1), + MSM_PIN_FUNCTION(mi2s1_sck), + MSM_PIN_FUNCTION(mi2s1_ws), + MSM_PIN_FUNCTION(mi2s2_data0), + MSM_PIN_FUNCTION(mi2s2_data1), + MSM_PIN_FUNCTION(mi2s2_sck), + MSM_PIN_FUNCTION(mi2s2_ws), + MSM_PIN_FUNCTION(mi2s_mclk0), + MSM_PIN_FUNCTION(mi2s_mclk1), + MSM_PIN_FUNCTION(pcie0_clkreq), + MSM_PIN_FUNCTION(pcie1_clkreq), + MSM_PIN_FUNCTION(phase_flag), + MSM_PIN_FUNCTION(pll_bist), + MSM_PIN_FUNCTION(pll_clk), + MSM_PIN_FUNCTION(prng_rosc0), + MSM_PIN_FUNCTION(prng_rosc1), + MSM_PIN_FUNCTION(prng_rosc2), + MSM_PIN_FUNCTION(prng_rosc3), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qdss_gpio), + MSM_PIN_FUNCTION(qup0_se0), + MSM_PIN_FUNCTION(qup0_se1), + MSM_PIN_FUNCTION(qup0_se2), + MSM_PIN_FUNCTION(qup0_se3), + MSM_PIN_FUNCTION(qup0_se4), + MSM_PIN_FUNCTION(qup0_se5), + MSM_PIN_FUNCTION(qup1_se0), + MSM_PIN_FUNCTION(qup1_se1), + MSM_PIN_FUNCTION(qup1_se2), + MSM_PIN_FUNCTION(qup1_se3), + MSM_PIN_FUNCTION(qup1_se4), + MSM_PIN_FUNCTION(qup1_se5), + MSM_PIN_FUNCTION(qup1_se6), + MSM_PIN_FUNCTION(qup2_se0), + MSM_PIN_FUNCTION(qup2_se1), + MSM_PIN_FUNCTION(qup2_se2), + MSM_PIN_FUNCTION(qup2_se3), + MSM_PIN_FUNCTION(qup2_se4), + MSM_PIN_FUNCTION(qup2_se5), + MSM_PIN_FUNCTION(qup2_se6), + MSM_PIN_FUNCTION(qup3_se0), + MSM_PIN_FUNCTION(sail_top), + MSM_PIN_FUNCTION(sailss_emac0), + MSM_PIN_FUNCTION(sailss_ospi), + MSM_PIN_FUNCTION(sgmii_phy), + MSM_PIN_FUNCTION(tb_trig), + MSM_PIN_FUNCTION(tgu_ch0), + MSM_PIN_FUNCTION(tgu_ch1), + MSM_PIN_FUNCTION(tgu_ch2), + MSM_PIN_FUNCTION(tgu_ch3), + MSM_PIN_FUNCTION(tgu_ch4), + MSM_PIN_FUNCTION(tgu_ch5), + MSM_PIN_FUNCTION(tsense_pwm1), + MSM_PIN_FUNCTION(tsense_pwm2), + MSM_PIN_FUNCTION(tsense_pwm3), + MSM_PIN_FUNCTION(tsense_pwm4), + MSM_PIN_FUNCTION(usb2phy_ac), + MSM_PIN_FUNCTION(vsense_trigger), +}; + +static const msm_pin_function sa8775p_pin_functions[] = { + [0] = PINGROUP(0, _, _, _, _, _, _, _, _, _), + [1] = PINGROUP(1, pcie0_clkreq, _, _, _, _, _, _, _, _), + [2] = PINGROUP(2, _, _, _, _, _, _, _, _, _), + [3] = PINGROUP(3, pcie1_clkreq, _, _, _, _, _, _, _, _), + [4] = PINGROUP(4, _, _, _, _, _, _, _, _, _), + [5] = PINGROUP(5, _, _, _, _, _, _, _, _, _), + [6] = PINGROUP(6, emac0_ptp_aux, emac0_ptp_pps, emac1_ptp_aux, + emac1_ptp_pps, _, _, _, _, _), + [7] = PINGROUP(7, sgmii_phy, _, _, _, _, _, _, _, _), + [8] = PINGROUP(8, emac0_mdc, _, _, _, _, _, _, _, _), + [9] = PINGROUP(9, emac0_mdio, _, _, _, _, _, _, _, _), + [10] = PINGROUP(10, usb2phy_ac, emac0_ptp_aux, emac0_ptp_pps, + emac1_ptp_aux, emac1_ptp_pps, _, _, _, _), + [11] = PINGROUP(11, usb2phy_ac, emac0_ptp_aux, emac0_ptp_pps, + emac1_ptp_aux, emac1_ptp_pps, _, _, _, _), + [12] = PINGROUP(12, usb2phy_ac, emac0_ptp_aux, emac0_ptp_pps, + emac1_ptp_aux, emac1_ptp_pps, emac0_mcg0, _, _, _), + [13] = PINGROUP(13, qup3_se0, emac0_mcg1, _, _, sail_top, _, _, _, _), + [14] = PINGROUP(14, qup3_se0, emac0_mcg2, _, _, sail_top, _, _, _, _), + [15] = PINGROUP(15, qup3_se0, emac0_mcg3, _, _, sail_top, _, _, _, _), + [16] = PINGROUP(16, qup3_se0, emac1_mcg0, _, _, sail_top, _, _, _, _), + [17] = PINGROUP(17, qup3_se0, tb_trig, tb_trig, emac1_mcg1, _, _, _, _, _), + [18] = PINGROUP(18, qup3_se0, emac1_mcg2, _, _, sailss_ospi, sailss_emac0, _, _, _), + [19] = PINGROUP(19, qup3_se0, emac1_mcg3, _, _, sailss_ospi, sailss_emac0, _, _, _), + [20] = PINGROUP(20, qup0_se0, emac1_mdc, qdss_gpio, _, _, _, _, _, _), + [21] = PINGROUP(21, qup0_se0, emac1_mdio, qdss_gpio, _, _, _, _, _, _), + [22] = PINGROUP(22, qup0_se0, qdss_gpio, _, _, _, _, _, _, _), + [23] = PINGROUP(23, qup0_se0, qdss_gpio, _, _, _, _, _, _, _), + [24] = PINGROUP(24, qup0_se1, qdss_gpio, _, _, _, _, _, _, _), + [25] = PINGROUP(25, qup0_se1, phase_flag, _, qdss_gpio, _, _, _, _, _), + [26] = PINGROUP(26, sgmii_phy, qup0_se1, qdss_cti, phase_flag, _, _, _, _, _), + [27] = PINGROUP(27, qup0_se1, qdss_cti, phase_flag, _, atest_char, _, _, _, _), + [28] = PINGROUP(28, qup0_se3, phase_flag, _, qdss_gpio, _, _, _, _, _), + [29] = PINGROUP(29, qup0_se3, phase_flag, _, qdss_gpio, _, _, _, _, _), + [30] = PINGROUP(30, qup0_se3, phase_flag, _, qdss_gpio, _, _, _, _, _), + [31] = PINGROUP(31, qup0_se3, phase_flag, _, qdss_gpio, _, _, _, _, _), + [32] = PINGROUP(32, qup0_se4, phase_flag, _, _, _, _, _, _, _), + [33] = PINGROUP(33, qup0_se4, gcc_gp4, _, ddr_pxi0, _, _, _, _, _), + [34] = PINGROUP(34, qup0_se4, gcc_gp5, _, ddr_pxi0, _, _, _, _, _), + [35] = PINGROUP(35, qup0_se4, phase_flag, _, _, _, _, _, _, _), + [36] = PINGROUP(36, qup0_se2, qup0_se5, phase_flag, tgu_ch2, _, _, _, _, _), + [37] = PINGROUP(37, qup0_se2, qup0_se5, phase_flag, tgu_ch3, _, _, _, _, _), + [38] = PINGROUP(38, qup0_se5, qup0_se2, qdss_cti, phase_flag, tgu_ch4, _, _, _, _), + [39] = PINGROUP(39, qup0_se5, qup0_se2, qdss_cti, phase_flag, tgu_ch5, _, _, _, _), + [40] = PINGROUP(40, qup1_se0, qup1_se1, ibi_i3c, mdp1_vsync0, _, _, _, _, _), + [41] = PINGROUP(41, qup1_se0, qup1_se1, ibi_i3c, mdp1_vsync1, _, _, _, _, _), + [42] = PINGROUP(42, qup1_se1, qup1_se0, ibi_i3c, mdp1_vsync2, gcc_gp5, _, _, _, _), + [43] = PINGROUP(43, qup1_se1, qup1_se0, ibi_i3c, mdp1_vsync3, _, _, _, _, _), + [44] = PINGROUP(44, qup1_se2, qup1_se3, edp0_lcd, _, _, _, _, _, _), + [45] = PINGROUP(45, qup1_se2, qup1_se3, edp1_lcd, _, _, _, _, _, _), + [46] = PINGROUP(46, qup1_se3, qup1_se2, mdp1_vsync4, tgu_ch0, _, _, _, _, _), + [47] = PINGROUP(47, qup1_se3, qup1_se2, mdp1_vsync5, tgu_ch1, _, _, _, _, _), + [48] = PINGROUP(48, qup1_se4, qdss_cti, edp2_lcd, _, _, _, _, _, _), + [49] = PINGROUP(49, qup1_se4, qdss_cti, edp3_lcd, _, _, _, _, _, _), + [50] = PINGROUP(50, qup1_se4, cci_async, qdss_cti, mdp1_vsync8, _, _, _, _, _), + [51] = PINGROUP(51, qup1_se4, qdss_cti, mdp1_vsync6, gcc_gp1, _, _, _, _, _), + [52] = PINGROUP(52, qup1_se5, cci_timer4, cci_i2c, mdp1_vsync7, gcc_gp2, _, ddr_pxi1, _, _), + [53] = PINGROUP(53, qup1_se5, cci_timer5, cci_i2c, gcc_gp3, _, ddr_pxi1, _, _, _), + [54] = PINGROUP(54, qup1_se5, cci_timer6, cci_i2c, _, _, _, _, _, _), + [55] = PINGROUP(55, qup1_se5, cci_timer7, cci_i2c, gcc_gp4, _, ddr_pxi2, _, _, _), + [56] = PINGROUP(56, qup1_se6, qup1_se6, cci_timer8, cci_i2c, phase_flag, ddr_bist, _, _, _), + [57] = PINGROUP(57, qup1_se6, qup1_se6, cci_timer9, cci_i2c, + mdp0_vsync0, phase_flag, ddr_bist, _, _), + [58] = PINGROUP(58, cci_i2c, mdp0_vsync1, ddr_bist, _, atest_usb2, atest_char, _, _, _), + [59] = PINGROUP(59, cci_i2c, mdp0_vsync2, ddr_bist, _, atest_usb2, atest_char, _, _, _), + [60] = PINGROUP(60, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [61] = PINGROUP(61, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [62] = PINGROUP(62, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [63] = PINGROUP(63, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [64] = PINGROUP(64, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [65] = PINGROUP(65, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [66] = PINGROUP(66, cci_i2c, cci_async, qdss_gpio, _, _, _, _, _, _), + [67] = PINGROUP(67, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [68] = PINGROUP(68, cci_timer0, cci_async, _, _, _, _, _, _, _), + [69] = PINGROUP(69, cci_timer1, cci_async, _, _, _, _, _, _, _), + [70] = PINGROUP(70, cci_timer2, cci_async, _, _, _, _, _, _, _), + [71] = PINGROUP(71, cci_timer3, cci_async, _, _, _, _, _, _, _), + [72] = PINGROUP(72, cam_mclk, _, _, _, _, _, _, _, _), + [73] = PINGROUP(73, cam_mclk, _, _, _, _, _, _, _, _), + [74] = PINGROUP(74, cam_mclk, _, _, _, _, _, _, _, _), + [75] = PINGROUP(75, cam_mclk, _, _, _, _, _, _, _, _), + [76] = PINGROUP(76, _, _, _, _, _, _, _, _, _), + [77] = PINGROUP(77, _, _, _, _, _, _, _, _, _), + [78] = PINGROUP(78, _, _, _, _, _, _, _, _, _), + [79] = PINGROUP(79, _, _, _, _, _, _, _, _, _), + [80] = PINGROUP(80, qup2_se0, ibi_i3c, mdp0_vsync3, _, _, _, _, _, _), + [81] = PINGROUP(81, qup2_se0, ibi_i3c, mdp0_vsync4, _, _, _, _, _, _), + [82] = PINGROUP(82, qup2_se0, mdp_vsync, gcc_gp1, _, _, _, _, _, _), + [83] = PINGROUP(83, qup2_se0, mdp_vsync, gcc_gp2, _, _, _, _, _, _), + [84] = PINGROUP(84, qup2_se1, qup2_se5, ibi_i3c, mdp_vsync, gcc_gp3, _, _, _, _), + [85] = PINGROUP(85, qup2_se1, qup2_se5, ibi_i3c, _, _, _, _, _, _), + [86] = PINGROUP(86, qup2_se2, jitter_bist, atest_usb2, ddr_pxi2, _, _, _, _, _), + [87] = PINGROUP(87, qup2_se2, pll_clk, atest_usb2, ddr_pxi3, _, _, _, _, _), + [88] = PINGROUP(88, qup2_se2, _, atest_usb2, ddr_pxi3, _, _, _, _, _), + [89] = PINGROUP(89, qup2_se2, _, atest_usb2, ddr_pxi4, atest_char, _, _, _, _), + [90] = PINGROUP(90, qup2_se2, _, atest_usb2, ddr_pxi4, atest_char, _, _, _, _), + [91] = PINGROUP(91, qup2_se3, mdp0_vsync5, _, atest_usb2, _, _, _, _, _), + [92] = PINGROUP(92, qup2_se3, mdp0_vsync6, _, atest_usb2, _, _, _, _, _), + [93] = PINGROUP(93, qup2_se3, mdp0_vsync7, _, atest_usb2, _, _, _, _, _), + [94] = PINGROUP(94, qup2_se3, mdp0_vsync8, _, atest_usb2, _, _, _, _, _), + [95] = PINGROUP(95, qup2_se4, qup2_se6, _, atest_usb2, _, _, _, _, _), + [96] = PINGROUP(96, qup2_se4, qup2_se6, _, atest_usb2, _, _, _, _, _), + [97] = PINGROUP(97, qup2_se6, qup2_se4, cri_trng0, _, atest_usb2, _, _, _, _), + [98] = PINGROUP(98, qup2_se6, qup2_se4, phase_flag, cri_trng1, _, _, _, _, _), + [99] = PINGROUP(99, qup2_se5, qup2_se1, phase_flag, cri_trng, _, _, _, _, _), + [100] = PINGROUP(100, qup2_se5, qup2_se1, _, _, _, _, _, _, _), + [101] = PINGROUP(101, edp0_hot, prng_rosc0, tsense_pwm4, _, _, _, _, _, _), + [102] = PINGROUP(102, edp1_hot, prng_rosc1, tsense_pwm3, _, _, _, _, _, _), + [103] = PINGROUP(103, edp3_hot, prng_rosc2, tsense_pwm2, _, _, _, _, _, _), + [104] = PINGROUP(104, edp2_hot, prng_rosc3, tsense_pwm1, _, _, _, _, _, _), + [105] = PINGROUP(105, mi2s_mclk0, _, qdss_gpio, atest_usb2, _, _, _, _, _), + [106] = PINGROUP(106, mi2s1_sck, phase_flag, _, qdss_gpio, _, _, _, _, _), + [107] = PINGROUP(107, mi2s1_ws, phase_flag, _, qdss_gpio, _, _, _, _, _), + [108] = PINGROUP(108, mi2s1_data0, phase_flag, _, qdss_gpio, _, _, _, _, _), + [109] = PINGROUP(109, mi2s1_data1, phase_flag, _, qdss_gpio, _, _, _, _, _), + [110] = PINGROUP(110, mi2s2_sck, phase_flag, _, qdss_gpio, _, _, _, _, _), + [111] = PINGROUP(111, mi2s2_ws, phase_flag, _, qdss_gpio, vsense_trigger, _, _, _, _), + [112] = PINGROUP(112, mi2s2_data0, phase_flag, _, qdss_gpio, _, _, _, _, _), + [113] = PINGROUP(113, mi2s2_data1, audio_ref, phase_flag, _, qdss_gpio, _, _, _, _), + [114] = PINGROUP(114, hs0_mi2s, pll_bist, phase_flag, _, qdss_gpio, _, _, _, _), + [115] = PINGROUP(115, hs0_mi2s, _, qdss_gpio, _, _, _, _, _, _), + [116] = PINGROUP(116, hs0_mi2s, _, qdss_gpio, _, _, _, _, _, _), + [117] = PINGROUP(117, hs0_mi2s, mi2s_mclk1, _, qdss_gpio, _, _, _, _, _), + [118] = PINGROUP(118, hs1_mi2s, _, qdss_gpio, ddr_pxi5, _, _, _, _, _), + [119] = PINGROUP(119, hs1_mi2s, _, qdss_gpio, ddr_pxi5, _, _, _, _, _), + [120] = PINGROUP(120, hs1_mi2s, phase_flag, _, qdss_gpio, _, _, _, _, _), + [121] = PINGROUP(121, hs1_mi2s, phase_flag, _, qdss_gpio, _, _, _, _, _), + [122] = PINGROUP(122, hs2_mi2s, phase_flag, _, qdss_gpio, _, _, _, _, _), + [123] = PINGROUP(123, hs2_mi2s, phase_flag, _, _, _, _, _, _, _), + [124] = PINGROUP(124, hs2_mi2s, phase_flag, _, _, _, _, _, _, _), + [125] = PINGROUP(125, hs2_mi2s, phase_flag, _, _, _, _, _, _, _), + [126] = PINGROUP(126, _, _, _, _, _, _, _, _, _), + [127] = PINGROUP(127, _, _, _, _, _, _, _, _, _), + [128] = PINGROUP(128, _, _, _, _, _, _, _, _, _), + [129] = PINGROUP(129, _, _, _, _, _, _, _, _, _), + [130] = PINGROUP(130, _, _, _, _, _, _, _, _, _), + [131] = PINGROUP(131, _, _, _, _, _, _, _, _, _), + [132] = PINGROUP(132, _, _, _, _, _, _, _, _, _), + [133] = PINGROUP(133, _, _, _, _, _, _, _, _, _), + [134] = PINGROUP(134, _, _, _, _, _, _, _, _, _), + [135] = PINGROUP(135, _, _, _, _, _, _, _, _, _), + [136] = PINGROUP(136, _, _, _, _, _, _, _, _, _), + [137] = PINGROUP(137, _, _, _, _, _, _, _, _, _), + [138] = PINGROUP(138, _, _, _, _, _, _, _, _, _), + [139] = PINGROUP(139, _, _, _, _, _, _, _, _, _), + [140] = PINGROUP(140, _, _, _, _, _, _, _, _, _), + [141] = PINGROUP(141, _, _, _, _, _, _, _, _, _), + [142] = PINGROUP(142, _, _, _, _, _, _, _, _, _), + [143] = PINGROUP(143, _, _, _, _, _, _, _, _, _), + [144] = PINGROUP(144, dbg_out, _, _, _, _, _, _, _, _), + [145] = PINGROUP(145, _, _, _, _, _, _, _, _, _), + [146] = PINGROUP(146, _, _, _, _, _, _, _, _, _), + [147] = PINGROUP(147, _, _, _, _, _, _, _, _, _), + [148] = PINGROUP(148, _, _, _, _, _, _, _, _, _), +}; + +static const struct msm_special_pin_data msm_special_pins_data[] = { + [0] = UFS_RESET("ufs_reset", 0x1a2000), + [1] = SDC_QDSD_PINGROUP("sdc1_rclk", 0x199000, 15, 0), + [2] = SDC_QDSD_PINGROUP("sdc1_clk", 0x199000, 13, 6), + [3] = SDC_QDSD_PINGROUP("sdc1_cmd", 0x199000, 11, 3), + [4] = SDC_QDSD_PINGROUP("sdc1_data", 0x199000, 9, 0), +}; + +static const char *sa8775p_get_function_name(struct udevice *dev, + unsigned int selector) +{ + return msm_pinctrl_functions[selector].name; +} + +static const char *sa8775p_get_pin_name(struct udevice *dev, + unsigned int selector) +{ + if (selector >= 149 && selector <= 155) + snprintf(pin_name, MAX_PIN_NAME_LEN, + msm_special_pins_data[selector - 149].name); + else + snprintf(pin_name, MAX_PIN_NAME_LEN, "gpio%u", selector); + + return pin_name; +} + +static int sa8775p_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) +{ + unsigned int i; + const msm_pin_function *func = sa8775p_pin_functions + pin; + + for (i = 0; i < 10; i++) + if ((*func)[i] == selector) + return i; + + pr_err("Can't find requested function for pin %u pin\n", pin); + + return -EINVAL; +} + +static const unsigned int sa8775p_pin_offsets[] = { + [0] = SA8775_PIN_OFFSET, [1] = SA8775_PIN_OFFSET, [2] = SA8775_PIN_OFFSET, + [3] = SA8775_PIN_OFFSET, [4] = SA8775_PIN_OFFSET, [5] = SA8775_PIN_OFFSET, + [6] = SA8775_PIN_OFFSET, [7] = SA8775_PIN_OFFSET, [8] = SA8775_PIN_OFFSET, + [9] = SA8775_PIN_OFFSET, [10] = SA8775_PIN_OFFSET, [11] = SA8775_PIN_OFFSET, + [12] = SA8775_PIN_OFFSET, [13] = SA8775_PIN_OFFSET, [14] = SA8775_PIN_OFFSET, + [15] = SA8775_PIN_OFFSET, [16] = SA8775_PIN_OFFSET, [17] = SA8775_PIN_OFFSET, + [18] = SA8775_PIN_OFFSET, [19] = SA8775_PIN_OFFSET, [20] = SA8775_PIN_OFFSET, + [21] = SA8775_PIN_OFFSET, [22] = SA8775_PIN_OFFSET, [23] = SA8775_PIN_OFFSET, + [24] = SA8775_PIN_OFFSET, [25] = SA8775_PIN_OFFSET, [26] = SA8775_PIN_OFFSET, + [27] = SA8775_PIN_OFFSET, [28] = SA8775_PIN_OFFSET, [29] = SA8775_PIN_OFFSET, + [30] = SA8775_PIN_OFFSET, [31] = SA8775_PIN_OFFSET, [32] = SA8775_PIN_OFFSET, + [33] = SA8775_PIN_OFFSET, [34] = SA8775_PIN_OFFSET, [35] = SA8775_PIN_OFFSET, + [36] = SA8775_PIN_OFFSET, [37] = SA8775_PIN_OFFSET, [38] = SA8775_PIN_OFFSET, + [39] = SA8775_PIN_OFFSET, [40] = SA8775_PIN_OFFSET, [41] = SA8775_PIN_OFFSET, + [42] = SA8775_PIN_OFFSET, [43] = SA8775_PIN_OFFSET, [44] = SA8775_PIN_OFFSET, + [45] = SA8775_PIN_OFFSET, [46] = SA8775_PIN_OFFSET, [47] = SA8775_PIN_OFFSET, + [48] = SA8775_PIN_OFFSET, [49] = SA8775_PIN_OFFSET, [50] = SA8775_PIN_OFFSET, + [51] = SA8775_PIN_OFFSET, [52] = SA8775_PIN_OFFSET, [53] = SA8775_PIN_OFFSET, + [54] = SA8775_PIN_OFFSET, [55] = SA8775_PIN_OFFSET, [56] = SA8775_PIN_OFFSET, + [57] = SA8775_PIN_OFFSET, [58] = SA8775_PIN_OFFSET, [59] = SA8775_PIN_OFFSET, + [60] = SA8775_PIN_OFFSET, [61] = SA8775_PIN_OFFSET, [62] = SA8775_PIN_OFFSET, + [63] = SA8775_PIN_OFFSET, [64] = SA8775_PIN_OFFSET, [65] = SA8775_PIN_OFFSET, + [66] = SA8775_PIN_OFFSET, [67] = SA8775_PIN_OFFSET, [68] = SA8775_PIN_OFFSET, + [69] = SA8775_PIN_OFFSET, [70] = SA8775_PIN_OFFSET, [71] = SA8775_PIN_OFFSET, + [72] = SA8775_PIN_OFFSET, [73] = SA8775_PIN_OFFSET, [74] = SA8775_PIN_OFFSET, + [75] = SA8775_PIN_OFFSET, [76] = SA8775_PIN_OFFSET, [77] = SA8775_PIN_OFFSET, + [78] = SA8775_PIN_OFFSET, [79] = SA8775_PIN_OFFSET, [80] = SA8775_PIN_OFFSET, + [81] = SA8775_PIN_OFFSET, [82] = SA8775_PIN_OFFSET, [83] = SA8775_PIN_OFFSET, + [84] = SA8775_PIN_OFFSET, [85] = SA8775_PIN_OFFSET, [86] = SA8775_PIN_OFFSET, + [87] = SA8775_PIN_OFFSET, [88] = SA8775_PIN_OFFSET, [89] = SA8775_PIN_OFFSET, + [90] = SA8775_PIN_OFFSET, [91] = SA8775_PIN_OFFSET, [92] = SA8775_PIN_OFFSET, + [93] = SA8775_PIN_OFFSET, [94] = SA8775_PIN_OFFSET, [95] = SA8775_PIN_OFFSET, + [96] = SA8775_PIN_OFFSET, [97] = SA8775_PIN_OFFSET, [98] = SA8775_PIN_OFFSET, + [99] = SA8775_PIN_OFFSET, [100] = SA8775_PIN_OFFSET, [101] = SA8775_PIN_OFFSET, + [102] = SA8775_PIN_OFFSET, [103] = SA8775_PIN_OFFSET, [104] = SA8775_PIN_OFFSET, + [105] = SA8775_PIN_OFFSET, [106] = SA8775_PIN_OFFSET, [107] = SA8775_PIN_OFFSET, + [108] = SA8775_PIN_OFFSET, [109] = SA8775_PIN_OFFSET, [110] = SA8775_PIN_OFFSET, + [111] = SA8775_PIN_OFFSET, [112] = SA8775_PIN_OFFSET, [113] = SA8775_PIN_OFFSET, + [114] = SA8775_PIN_OFFSET, [115] = SA8775_PIN_OFFSET, [116] = SA8775_PIN_OFFSET, + [117] = SA8775_PIN_OFFSET, [118] = SA8775_PIN_OFFSET, [119] = SA8775_PIN_OFFSET, + [120] = SA8775_PIN_OFFSET, [121] = SA8775_PIN_OFFSET, [122] = SA8775_PIN_OFFSET, + [123] = SA8775_PIN_OFFSET, [124] = SA8775_PIN_OFFSET, [125] = SA8775_PIN_OFFSET, + [126] = SA8775_PIN_OFFSET, [127] = SA8775_PIN_OFFSET, [128] = SA8775_PIN_OFFSET, + [129] = SA8775_PIN_OFFSET, [130] = SA8775_PIN_OFFSET, [131] = SA8775_PIN_OFFSET, + [132] = SA8775_PIN_OFFSET, [133] = SA8775_PIN_OFFSET, [134] = SA8775_PIN_OFFSET, + [135] = SA8775_PIN_OFFSET, [136] = SA8775_PIN_OFFSET, [137] = SA8775_PIN_OFFSET, + [138] = SA8775_PIN_OFFSET, [139] = SA8775_PIN_OFFSET, [140] = SA8775_PIN_OFFSET, + [141] = SA8775_PIN_OFFSET, [142] = SA8775_PIN_OFFSET, [143] = SA8775_PIN_OFFSET, + [144] = SA8775_PIN_OFFSET, [145] = SA8775_PIN_OFFSET, [146] = SA8775_PIN_OFFSET, + [147] = SA8775_PIN_OFFSET, [148] = SA8775_PIN_OFFSET, [148] = SA8775_PIN_OFFSET, + [149] = SA8775_PIN_OFFSET, [150] = SA8775_PIN_OFFSET, [151] = SA8775_PIN_OFFSET, + [152] = SA8775_PIN_OFFSET, [153] = SA8775_PIN_OFFSET, [154] = SA8775_PIN_OFFSET, +}; + +static const struct msm_pinctrl_data sa8775p_data = { + .pin_data = { + .pin_count = 155, + .special_pins_start = 149, + .special_pins_data = msm_special_pins_data, + .pin_offsets = sa8775p_pin_offsets, + }, + .functions_count = ARRAY_SIZE(msm_pinctrl_functions), + .get_function_name = sa8775p_get_function_name, + .get_function_mux = sa8775p_get_function_mux, + .get_pin_name = sa8775p_get_pin_name, +}; + +static const struct udevice_id msm_pinctrl_ids[] = { + { .compatible = "qcom,sa8775p-tlmm", .data = (ulong)&sa8775p_data }, + { /* Sentinal */ } +}; + +U_BOOT_DRIVER(pinctrl_sa8775p) = { + .name = "pinctrl_sa8775p", + .id = UCLASS_NOP, + .of_match = msm_pinctrl_ids, + .ops = &msm_pinctrl_ops, + .bind = msm_pinctrl_bind, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/pinctrl/qcom/pinctrl-sc7280.c b/drivers/pinctrl/qcom/pinctrl-sc7280.c new file mode 100644 index 00000000000..fe87947fbbf --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sc7280.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Qualcomm sc7280 pinctrl + * + * (C) Copyright 2024 Linaro Ltd. + * + */ + +#include <dm.h> + +#include "pinctrl-qcom.h" + +#define WEST 0x00000000 +#define SOUTH 0x00400000 +#define NORTH 0x00800000 + +#define MAX_PIN_NAME_LEN 32 +static char pin_name[MAX_PIN_NAME_LEN] __section(".data"); + +static const struct pinctrl_function msm_pinctrl_functions[] = { + { "qup05", 1 }, + { "gpio", 0 }, + { "pcie1_clkreqn", 3}, +}; +#define SDC_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .name = pg_name, \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + } + +#define UFS_RESET(pg_name, offset) \ + { \ + .name = pg_name, \ + .ctl_reg = offset, \ + .io_reg = offset + 0x4, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + } + +static const struct msm_special_pin_data sc7280_special_pins_data[] = { + [0] = UFS_RESET("ufs_reset", SOUTH + 0xbe000), + [1] = SDC_PINGROUP("sdc1_rclk", 0xb3004, 0, 6), + [2] = SDC_PINGROUP("sdc1_clk", 0xb3000, 13, 6), + [3] = SDC_PINGROUP("sdc1_cmd", 0xb3000, 11, 3), + [4] = SDC_PINGROUP("sdc1_data", 0xb3000, 9, 0), + [5] = SDC_PINGROUP("sdc2_clk", 0xb4000, 14, 6), + [6] = SDC_PINGROUP("sdc2_cmd", 0xb4000, 11, 3), + [7] = SDC_PINGROUP("sdc2_data", 0xb4000, 9, 0), +}; + +static const char *sc7280_get_function_name(struct udevice *dev, unsigned int selector) +{ + return msm_pinctrl_functions[selector].name; +} + +static const char *sc7280_get_pin_name(struct udevice *dev, unsigned int selector) +{ + if (selector >= 175 && selector <= 182) + snprintf(pin_name, MAX_PIN_NAME_LEN, + sc7280_special_pins_data[selector - 175].name); + else + snprintf(pin_name, MAX_PIN_NAME_LEN, "gpio%u", selector); + + return pin_name; +} + +static int sc7280_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) +{ + return msm_pinctrl_functions[selector].val; +} + +static struct msm_pinctrl_data sc7280_data = { + .pin_data = { + .pin_count = 183, + .special_pins_start = 175, + .special_pins_data = sc7280_special_pins_data, + }, + .functions_count = ARRAY_SIZE(msm_pinctrl_functions), + .get_function_name = sc7280_get_function_name, + .get_function_mux = sc7280_get_function_mux, + .get_pin_name = sc7280_get_pin_name, +}; + +static const struct udevice_id msm_pinctrl_ids[] = { + { + .compatible = "qcom,sc7280-pinctrl", + .data = (ulong)&sc7280_data + }, + { /* Sentinel */ } }; + +U_BOOT_DRIVER(pinctrl_sc7280) = { + .name = "pinctrl_sc7280", + .id = UCLASS_NOP, + .of_match = msm_pinctrl_ids, + .ops = &msm_pinctrl_ops, + .bind = msm_pinctrl_bind, +}; diff --git a/drivers/pinctrl/qcom/pinctrl-sdm660.c b/drivers/pinctrl/qcom/pinctrl-sdm660.c new file mode 100644 index 00000000000..646d848ffa4 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sdm660.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Qualcomm SDM630/660 TLMM pinctrl + * + */ + +#include <dm.h> +#include "pinctrl-qcom.h" + +#define TLMM_BASE 0x03100000 +#define SOUTH (0x03100000 - TLMM_BASE) /* 0x0 */ +#define CENTER (0x03500000 - TLMM_BASE) /* 0x400000 */ +#define NORTH (0x03900000 - TLMM_BASE) /* 0x800000 */ + +#define MAX_PIN_NAME_LEN 32 +static char pin_name[MAX_PIN_NAME_LEN] __section(".data"); + +static const struct pinctrl_function sdm660_pinctrl_functions[] = { + { "gpio", 0 }, + { "blsp_uart2", 3 }, /* gpio 4 and 5, used for debug uart */ +}; + +static const unsigned int sdm660_pin_offsets[] = { + [0] = SOUTH, + [1] = SOUTH, + [2] = SOUTH, + [3] = SOUTH, + [4] = NORTH, + [5] = SOUTH, + [6] = SOUTH, + [7] = SOUTH, + [8] = NORTH, + [9] = NORTH, + [10] = NORTH, + [11] = NORTH, + [12] = NORTH, + [13] = NORTH, + [14] = NORTH, + [15] = NORTH, + [16] = CENTER, + [17] = CENTER, + [18] = CENTER, + [19] = CENTER, + [20] = SOUTH, + [21] = SOUTH, + [22] = CENTER, + [23] = CENTER, + [24] = NORTH, + [25] = NORTH, + [26] = NORTH, + [27] = NORTH, + [28] = CENTER, + [29] = CENTER, + [30] = CENTER, + [31] = CENTER, + [32] = SOUTH, + [33] = SOUTH, + [34] = SOUTH, + [35] = SOUTH, + [36] = SOUTH, + [37] = SOUTH, + [38] = SOUTH, + [39] = SOUTH, + [40] = SOUTH, + [41] = SOUTH, + [42] = SOUTH, + [43] = SOUTH, + [44] = SOUTH, + [45] = SOUTH, + [46] = SOUTH, + [47] = SOUTH, + [48] = SOUTH, + [49] = SOUTH, + [50] = SOUTH, + [51] = SOUTH, + [52] = SOUTH, + [53] = NORTH, + [54] = NORTH, + [55] = SOUTH, + [56] = SOUTH, + [57] = SOUTH, + [58] = SOUTH, + [59] = NORTH, + [60] = NORTH, + [61] = NORTH, + [62] = NORTH, + [63] = NORTH, + [64] = SOUTH, + [65] = SOUTH, + [66] = NORTH, + [67] = NORTH, + [68] = NORTH, + [69] = NORTH, + [70] = NORTH, + [71] = NORTH, + [72] = NORTH, + [73] = NORTH, + [74] = NORTH, + [75] = NORTH, + [76] = NORTH, + [77] = NORTH, + [78] = NORTH, + [79] = SOUTH, + [80] = SOUTH, + [81] = CENTER, + [82] = CENTER, + [83] = SOUTH, + [84] = SOUTH, + [85] = SOUTH, + [86] = SOUTH, + [87] = SOUTH, + [88] = SOUTH, + [89] = SOUTH, + [90] = SOUTH, + [91] = SOUTH, + [92] = SOUTH, + [93] = SOUTH, + [94] = SOUTH, + [95] = SOUTH, + [96] = SOUTH, + [97] = SOUTH, + [98] = SOUTH, + [99] = SOUTH, + [100] = SOUTH, + [101] = SOUTH, + [102] = SOUTH, + [103] = SOUTH, + [104] = SOUTH, + [105] = SOUTH, + [106] = SOUTH, + [107] = SOUTH, + [108] = SOUTH, + [109] = SOUTH, + [110] = SOUTH, + [111] = SOUTH, + [112] = SOUTH, + [113] = SOUTH, +}; + +/* + * Special pins - eMMC/SD related: [114..120], in total 7 special pins + */ + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .name = pg_name, \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + } + +/* All SDC pins are in the NORTH tile */ +static const struct msm_special_pin_data sdm660_special_pins_data[] = { + SDC_QDSD_PINGROUP("sdc1_clk", NORTH + 0x9a000, 13, 6), + SDC_QDSD_PINGROUP("sdc1_cmd", NORTH + 0x9a000, 11, 3), + SDC_QDSD_PINGROUP("sdc1_data", NORTH + 0x9a000, 9, 0), + SDC_QDSD_PINGROUP("sdc2_clk", NORTH + 0x9b000, 14, 6), + SDC_QDSD_PINGROUP("sdc2_cmd", NORTH + 0x9b000, 11, 3), + SDC_QDSD_PINGROUP("sdc2_data", NORTH + 0x9b000, 9, 0), + SDC_QDSD_PINGROUP("sdc1_rclk", NORTH + 0x9a000, 15, 0), +}; + +static const char *sdm660_get_function_name(struct udevice *dev, unsigned int selector) +{ + return sdm660_pinctrl_functions[selector].name; +} + +static const char *sdm660_get_pin_name(struct udevice *dev, unsigned int selector) +{ + static const char * const special_pins_names[] = { + "sdc1_clk", "sdc1_cmd", "sdc1_data", + "sdc2_clk", "sdc2_cmd", "sdc2_data", + "sdc1_rclk" + }; + + if (selector >= 114 && selector <= 120) + snprintf(pin_name, MAX_PIN_NAME_LEN, special_pins_names[selector - 114]); + else + snprintf(pin_name, MAX_PIN_NAME_LEN, "gpio%u", selector); + + return pin_name; +} + +static int sdm660_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) +{ + if (selector >= 0 && selector < ARRAY_SIZE(sdm660_pinctrl_functions)) + return sdm660_pinctrl_functions[selector].val; + return -EINVAL; +} + +struct msm_pinctrl_data sdm660_data = { + .pin_data = { + .pin_offsets = sdm660_pin_offsets, + .pin_count = ARRAY_SIZE(sdm660_pin_offsets) + ARRAY_SIZE(sdm660_special_pins_data), + .special_pins_start = 114, + .special_pins_data = sdm660_special_pins_data, + }, + .functions_count = ARRAY_SIZE(sdm660_pinctrl_functions), + .get_function_name = sdm660_get_function_name, + .get_function_mux = sdm660_get_function_mux, + .get_pin_name = sdm660_get_pin_name, +}; + +static const struct udevice_id msm_pinctrl_ids[] = { + { + .compatible = "qcom,sdm630-pinctrl", + .data = (ulong)&sdm660_data + }, + { + .compatible = "qcom,sdm660-pinctrl", + .data = (ulong)&sdm660_data + }, + { /* Sentinel */ } +}; + +U_BOOT_DRIVER(pinctrl_ssdm660) = { + .name = "pinctrl_sdm660", + .id = UCLASS_NOP, + .of_match = msm_pinctrl_ids, + .ops = &msm_pinctrl_ops, + .bind = msm_pinctrl_bind, +}; diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c index f1a23f51099..3f55fc81c8e 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm845.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c @@ -80,8 +80,8 @@ static const char *sdm845_get_pin_name(struct udevice *dev, return pin_name; } -static unsigned int sdm845_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int sdm845_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-sm6115.c b/drivers/pinctrl/qcom/pinctrl-sm6115.c index f07f39f4ac3..7e80ea39a12 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6115.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6115.c @@ -167,7 +167,7 @@ static const char *sm6115_get_pin_name(struct udevice *dev, unsigned int selecto return pin_name; } -static unsigned int sm6115_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) +static int sm6115_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c index 1fb2ffb9597..fe789e8639b 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8150.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c @@ -123,8 +123,8 @@ static const char *sm8150_get_pin_name(struct udevice *dev, return pin_name; } -static unsigned int sm8150_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int sm8150_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-sm8250.c b/drivers/pinctrl/qcom/pinctrl-sm8250.c index b21cdc4d24b..d5447651695 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8250.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8250.c @@ -99,7 +99,7 @@ static const char *sm8250_get_pin_name(struct udevice *dev, unsigned int selecto return pin_name; } -static unsigned int sm8250_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) +static int sm8250_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-sm8550.c b/drivers/pinctrl/qcom/pinctrl-sm8550.c index 25b972a6d82..f7fcad0e4aa 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8550.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8550.c @@ -68,8 +68,8 @@ static const char *sm8550_get_pin_name(struct udevice *dev, return pin_name; } -static unsigned int sm8550_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int sm8550_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-sm8650.c b/drivers/pinctrl/qcom/pinctrl-sm8650.c index 9146d6abd9a..6b9d56b1058 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8650.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8650.c @@ -69,8 +69,8 @@ static const char *sm8650_get_pin_name(struct udevice *dev, return pin_name; } -static unsigned int sm8650_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int sm8650_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-x1e80100.c b/drivers/pinctrl/qcom/pinctrl-x1e80100.c index f39dc426d68..319667bfc16 100644 --- a/drivers/pinctrl/qcom/pinctrl-x1e80100.c +++ b/drivers/pinctrl/qcom/pinctrl-x1e80100.c @@ -72,8 +72,8 @@ static const char *x1e80100_get_pin_name(struct udevice *dev, return pin_name; } -static unsigned int x1e80100_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int x1e80100_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/rockchip/Makefile b/drivers/pinctrl/rockchip/Makefile index c91f650b043..e17415e1ca6 100644 --- a/drivers/pinctrl/rockchip/Makefile +++ b/drivers/pinctrl/rockchip/Makefile @@ -14,7 +14,9 @@ obj-$(CONFIG_ROCKCHIP_RK3308) += pinctrl-rk3308.o obj-$(CONFIG_ROCKCHIP_RK3328) += pinctrl-rk3328.o obj-$(CONFIG_ROCKCHIP_RK3368) += pinctrl-rk3368.o obj-$(CONFIG_ROCKCHIP_RK3399) += pinctrl-rk3399.o +obj-$(CONFIG_ROCKCHIP_RK3528) += pinctrl-rk3528.o obj-$(CONFIG_ROCKCHIP_RK3568) += pinctrl-rk3568.o +obj-$(CONFIG_ROCKCHIP_RK3576) += pinctrl-rk3576.o obj-$(CONFIG_ROCKCHIP_RK3588) += pinctrl-rk3588.o obj-$(CONFIG_ROCKCHIP_RV1108) += pinctrl-rv1108.o obj-$(CONFIG_ROCKCHIP_RV1126) += pinctrl-rv1126.o diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3528.c b/drivers/pinctrl/rockchip/pinctrl-rk3528.c new file mode 100644 index 00000000000..a3e1f0b2c9d --- /dev/null +++ b/drivers/pinctrl/rockchip/pinctrl-rk3528.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + */ + +#include <dm.h> +#include <dm/pinctrl.h> +#include <regmap.h> +#include <syscon.h> + +#include "pinctrl-rockchip.h" +#include <dt-bindings/pinctrl/rockchip.h> + +static int rk3528_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + int iomux_num = (pin / 8); + struct regmap *regmap; + int reg, mask; + u8 bit; + u32 data, rmask; + + regmap = priv->regmap_base; + reg = bank->iomux[iomux_num].offset; + if ((pin % 8) >= 4) + reg += 0x4; + bit = (pin % 4) * 4; + mask = 0xf; + + data = (mask << (bit + 16)); + rmask = data | (data >> 16); + data |= (mux & mask) << bit; + + return regmap_update_bits(regmap, reg, rmask, data); +} + +#define RK3528_DRV_BITS_PER_PIN 8 +#define RK3528_DRV_PINS_PER_REG 2 +#define RK3528_DRV_GPIO0_OFFSET 0x100 +#define RK3528_DRV_GPIO1_OFFSET 0x20120 +#define RK3528_DRV_GPIO2_OFFSET 0x30160 +#define RK3528_DRV_GPIO3_OFFSET 0x20190 +#define RK3528_DRV_GPIO4_OFFSET 0x101C0 + +static void rk3528_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + + *regmap = priv->regmap_base; + + if (bank->bank_num == 0) { + *reg = RK3528_DRV_GPIO0_OFFSET; + } else if (bank->bank_num == 1) { + *reg = RK3528_DRV_GPIO1_OFFSET; + } else if (bank->bank_num == 2) { + *reg = RK3528_DRV_GPIO2_OFFSET; + } else if (bank->bank_num == 3) { + *reg = RK3528_DRV_GPIO3_OFFSET; + } else if (bank->bank_num == 4) { + *reg = RK3528_DRV_GPIO4_OFFSET; + } else { + *reg = 0; + debug("unsupported bank_num %d\n", bank->bank_num); + } + + *reg += ((pin_num / RK3528_DRV_PINS_PER_REG) * 4); + *bit = pin_num % RK3528_DRV_PINS_PER_REG; + *bit *= RK3528_DRV_BITS_PER_PIN; +} + +static int rk3528_set_drive(struct rockchip_pin_bank *bank, + int pin_num, int strength) +{ + struct regmap *regmap; + int reg; + u32 data, rmask; + u8 bit; + int drv = (1 << (strength + 1)) - 1; + + rk3528_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3528_DRV_BITS_PER_PIN) - 1) << (bit + 16); + rmask = data | (data >> 16); + data |= (drv << bit); + + return regmap_update_bits(regmap, reg, rmask, data); +} + +#define RK3528_PULL_BITS_PER_PIN 2 +#define RK3528_PULL_PINS_PER_REG 8 +#define RK3528_PULL_GPIO0_OFFSET 0x200 +#define RK3528_PULL_GPIO1_OFFSET 0x20210 +#define RK3528_PULL_GPIO2_OFFSET 0x30220 +#define RK3528_PULL_GPIO3_OFFSET 0x20230 +#define RK3528_PULL_GPIO4_OFFSET 0x10240 + +static void rk3528_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + + *regmap = priv->regmap_base; + + if (bank->bank_num == 0) { + *reg = RK3528_PULL_GPIO0_OFFSET; + } else if (bank->bank_num == 1) { + *reg = RK3528_PULL_GPIO1_OFFSET; + } else if (bank->bank_num == 2) { + *reg = RK3528_PULL_GPIO2_OFFSET; + } else if (bank->bank_num == 3) { + *reg = RK3528_PULL_GPIO3_OFFSET; + } else if (bank->bank_num == 4) { + *reg = RK3528_PULL_GPIO4_OFFSET; + } else { + *reg = 0; + debug("unsupported bank_num %d\n", bank->bank_num); + } + + *reg += ((pin_num / RK3528_PULL_PINS_PER_REG) * 4); + *bit = pin_num % RK3528_PULL_PINS_PER_REG; + *bit *= RK3528_PULL_BITS_PER_PIN; +} + +static int rk3528_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct regmap *regmap; + int reg, ret; + u8 bit, type; + u32 data, rmask; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + return -EOPNOTSUPP; + + rk3528_calc_pull_reg_and_bit(bank, pin_num, ®map, ®, &bit); + type = bank->pull_type[pin_num / 8]; + ret = rockchip_translate_pull_value(type, pull); + if (ret < 0) { + debug("unsupported pull setting %d\n", pull); + return ret; + } + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3528_PULL_BITS_PER_PIN) - 1) << (bit + 16); + rmask = data | (data >> 16); + data |= (ret << bit); + + return regmap_update_bits(regmap, reg, rmask, data); +} + +#define RK3528_SMT_BITS_PER_PIN 1 +#define RK3528_SMT_PINS_PER_REG 8 +#define RK3528_SMT_GPIO0_OFFSET 0x400 +#define RK3528_SMT_GPIO1_OFFSET 0x20410 +#define RK3528_SMT_GPIO2_OFFSET 0x30420 +#define RK3528_SMT_GPIO3_OFFSET 0x20430 +#define RK3528_SMT_GPIO4_OFFSET 0x10440 + +static int rk3528_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, + struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + + *regmap = priv->regmap_base; + + if (bank->bank_num == 0) { + *reg = RK3528_SMT_GPIO0_OFFSET; + } else if (bank->bank_num == 1) { + *reg = RK3528_SMT_GPIO1_OFFSET; + } else if (bank->bank_num == 2) { + *reg = RK3528_SMT_GPIO2_OFFSET; + } else if (bank->bank_num == 3) { + *reg = RK3528_SMT_GPIO3_OFFSET; + } else if (bank->bank_num == 4) { + *reg = RK3528_SMT_GPIO4_OFFSET; + } else { + *reg = 0; + debug("unsupported bank_num %d\n", bank->bank_num); + } + + *reg += ((pin_num / RK3528_SMT_PINS_PER_REG) * 4); + *bit = pin_num % RK3528_SMT_PINS_PER_REG; + *bit *= RK3528_SMT_BITS_PER_PIN; + + return 0; +} + +static int rk3528_set_schmitt(struct rockchip_pin_bank *bank, + int pin_num, int enable) +{ + struct regmap *regmap; + int reg; + u32 data, rmask; + u8 bit; + + rk3528_calc_schmitt_reg_and_bit(bank, pin_num, ®map, ®, &bit); + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3528_SMT_BITS_PER_PIN) - 1) << (bit + 16); + rmask = data | (data >> 16); + data |= (enable << bit); + + return regmap_update_bits(regmap, reg, rmask, data); +} + +static struct rockchip_pin_bank rk3528_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS_OFFSET(0, 32, "gpio0", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0, 0, 0, 0), + PIN_BANK_IOMUX_FLAGS_OFFSET(1, 32, "gpio1", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0x20020, 0x20028, 0x20030, 0x20038), + PIN_BANK_IOMUX_FLAGS_OFFSET(2, 32, "gpio2", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0x30040, 0, 0, 0), + PIN_BANK_IOMUX_FLAGS_OFFSET(3, 32, "gpio3", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0x20060, 0x20068, 0x20070, 0), + PIN_BANK_IOMUX_FLAGS_OFFSET(4, 32, "gpio4", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0x10080, 0x10088, 0x10090, 0x10098), +}; + +static const struct rockchip_pin_ctrl rk3528_pin_ctrl = { + .pin_banks = rk3528_pin_banks, + .nr_banks = ARRAY_SIZE(rk3528_pin_banks), + .grf_mux_offset = 0x0, + .set_mux = rk3528_set_mux, + .set_pull = rk3528_set_pull, + .set_drive = rk3528_set_drive, + .set_schmitt = rk3528_set_schmitt, +}; + +static const struct udevice_id rk3528_pinctrl_ids[] = { + { + .compatible = "rockchip,rk3528-pinctrl", + .data = (ulong)&rk3528_pin_ctrl + }, + { } +}; + +U_BOOT_DRIVER(rockchip_rk3528_pinctrl) = { + .name = "rockchip_rk3528_pinctrl", + .id = UCLASS_PINCTRL, + .of_match = rk3528_pinctrl_ids, + .priv_auto = sizeof(struct rockchip_pinctrl_priv), + .ops = &rockchip_pinctrl_ops, +#if CONFIG_IS_ENABLED(OF_REAL) + .bind = dm_scan_fdt_dev, +#endif + .probe = rockchip_pinctrl_probe, +}; diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3576.c b/drivers/pinctrl/rockchip/pinctrl-rk3576.c new file mode 100644 index 00000000000..66e1142ac1f --- /dev/null +++ b/drivers/pinctrl/rockchip/pinctrl-rk3576.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2024 Rockchip Electronics Co., Ltd + */ + +#include <dm.h> +#include <dm/pinctrl.h> +#include <regmap.h> +#include <syscon.h> + +#include "pinctrl-rockchip.h" +#include <dt-bindings/pinctrl/rockchip.h> + +static int rk3576_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + int iomux_num = (pin / 8); + struct regmap *regmap; + int reg, mask; + u8 bit; + u32 data, rmask; + + regmap = priv->regmap_base; + reg = bank->iomux[iomux_num].offset; + if ((pin % 8) >= 4) + reg += 0x4; + bit = (pin % 4) * 4; + mask = 0xf; + + data = (mask << (bit + 16)); + rmask = data | (data >> 16); + data |= (mux & mask) << bit; + + if (bank->bank_num == 0 && pin >= RK_PB4 && pin <= RK_PB7) + reg += 0x1FF4; /* GPIO0_IOC_GPIO0B_IOMUX_SEL_H */ + + return regmap_update_bits(regmap, reg, rmask, data); +} + +#define RK3576_DRV_BITS_PER_PIN 4 +#define RK3576_DRV_PINS_PER_REG 4 +#define RK3576_DRV_GPIO0_AL_OFFSET 0x10 +#define RK3576_DRV_GPIO0_BH_OFFSET 0x2014 +#define RK3576_DRV_GPIO1_OFFSET 0x6020 +#define RK3576_DRV_GPIO2_OFFSET 0x6040 +#define RK3576_DRV_GPIO3_OFFSET 0x6060 +#define RK3576_DRV_GPIO4_AL_OFFSET 0x6080 +#define RK3576_DRV_GPIO4_CL_OFFSET 0xA090 +#define RK3576_DRV_GPIO4_DL_OFFSET 0xB098 + +static void rk3576_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + + *regmap = priv->regmap_base; + if (bank->bank_num == 0 && pin_num < 12) { + *reg = RK3576_DRV_GPIO0_AL_OFFSET; + } else if (bank->bank_num == 0) { + *reg = RK3576_DRV_GPIO0_BH_OFFSET - 0xc; + } else if (bank->bank_num == 1) { + *reg = RK3576_DRV_GPIO1_OFFSET; + } else if (bank->bank_num == 2) { + *reg = RK3576_DRV_GPIO2_OFFSET; + } else if (bank->bank_num == 3) { + *reg = RK3576_DRV_GPIO3_OFFSET; + } else if (bank->bank_num == 4 && pin_num < 16) { + *reg = RK3576_DRV_GPIO4_AL_OFFSET; + } else if (bank->bank_num == 4 && pin_num < 24) { + *reg = RK3576_DRV_GPIO4_CL_OFFSET - 0x10; + } else if (bank->bank_num == 4) { + *reg = RK3576_DRV_GPIO4_DL_OFFSET - 0x18; + } else { + *reg = 0; + debug("unsupported bank_num %d\n", bank->bank_num); + } + + *reg += ((pin_num / RK3576_DRV_PINS_PER_REG) * 4); + *bit = pin_num % RK3576_DRV_PINS_PER_REG; + *bit *= RK3576_DRV_BITS_PER_PIN; +} + +static int rk3576_set_drive(struct rockchip_pin_bank *bank, + int pin_num, int strength) +{ + struct regmap *regmap; + int reg; + u32 data, rmask; + u8 bit; + int drv = ((strength & BIT(2)) >> 2) | ((strength & BIT(0)) << 2) | (strength & BIT(1)); + + rk3576_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3576_DRV_BITS_PER_PIN) - 1) << (bit + 16); + rmask = data | (data >> 16); + data |= (drv << bit); + + return regmap_update_bits(regmap, reg, rmask, data); +} + +#define RK3576_PULL_BITS_PER_PIN 2 +#define RK3576_PULL_PINS_PER_REG 8 +#define RK3576_PULL_GPIO0_AL_OFFSET 0x20 +#define RK3576_PULL_GPIO0_BH_OFFSET 0x2028 +#define RK3576_PULL_GPIO1_OFFSET 0x6110 +#define RK3576_PULL_GPIO2_OFFSET 0x6120 +#define RK3576_PULL_GPIO3_OFFSET 0x6130 +#define RK3576_PULL_GPIO4_AL_OFFSET 0x6140 +#define RK3576_PULL_GPIO4_CL_OFFSET 0xA148 +#define RK3576_PULL_GPIO4_DL_OFFSET 0xB14C + +static void rk3576_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + + *regmap = priv->regmap_base; + if (bank->bank_num == 0 && pin_num < 12) { + *reg = RK3576_PULL_GPIO0_AL_OFFSET; + } else if (bank->bank_num == 0) { + *reg = RK3576_PULL_GPIO0_BH_OFFSET - 0x4; + } else if (bank->bank_num == 1) { + *reg = RK3576_PULL_GPIO1_OFFSET; + } else if (bank->bank_num == 2) { + *reg = RK3576_PULL_GPIO2_OFFSET; + } else if (bank->bank_num == 3) { + *reg = RK3576_PULL_GPIO3_OFFSET; + } else if (bank->bank_num == 4 && pin_num < 16) { + *reg = RK3576_PULL_GPIO4_AL_OFFSET; + } else if (bank->bank_num == 4 && pin_num < 24) { + *reg = RK3576_PULL_GPIO4_CL_OFFSET - 0x8; + } else if (bank->bank_num == 4) { + *reg = RK3576_PULL_GPIO4_DL_OFFSET - 0xc; + } else { + *reg = 0; + debug("unsupported bank_num %d\n", bank->bank_num); + } + + *reg += ((pin_num / RK3576_PULL_PINS_PER_REG) * 4); + *bit = pin_num % RK3576_PULL_PINS_PER_REG; + *bit *= RK3576_PULL_BITS_PER_PIN; +} + +static int rk3576_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct regmap *regmap; + int reg, ret; + u8 bit, type; + u32 data, rmask; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + return -ENOTSUPP; + + rk3576_calc_pull_reg_and_bit(bank, pin_num, ®map, ®, &bit); + type = 1; /* FIXME: was always set to 1 in vendor kernel */ + ret = rockchip_translate_pull_value(type, pull); + if (ret < 0) { + debug("unsupported pull setting %d\n", pull); + return ret; + } + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3576_PULL_BITS_PER_PIN) - 1) << (bit + 16); + rmask = data | (data >> 16); + data |= (ret << bit); + + return regmap_update_bits(regmap, reg, rmask, data); +} + +#define RK3576_SMT_BITS_PER_PIN 1 +#define RK3576_SMT_PINS_PER_REG 8 +#define RK3576_SMT_GPIO0_AL_OFFSET 0x30 +#define RK3576_SMT_GPIO0_BH_OFFSET 0x2040 +#define RK3576_SMT_GPIO1_OFFSET 0x6210 +#define RK3576_SMT_GPIO2_OFFSET 0x6220 +#define RK3576_SMT_GPIO3_OFFSET 0x6230 +#define RK3576_SMT_GPIO4_AL_OFFSET 0x6240 +#define RK3576_SMT_GPIO4_CL_OFFSET 0xA248 +#define RK3576_SMT_GPIO4_DL_OFFSET 0xB24C + +static void rk3576_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, + struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + + *regmap = priv->regmap_base; + if (bank->bank_num == 0 && pin_num < 12) { + *reg = RK3576_SMT_GPIO0_AL_OFFSET; + } else if (bank->bank_num == 0) { + *reg = RK3576_SMT_GPIO0_BH_OFFSET - 0x4; + } else if (bank->bank_num == 1) { + *reg = RK3576_SMT_GPIO1_OFFSET; + } else if (bank->bank_num == 2) { + *reg = RK3576_SMT_GPIO2_OFFSET; + } else if (bank->bank_num == 3) { + *reg = RK3576_SMT_GPIO3_OFFSET; + } else if (bank->bank_num == 4 && pin_num < 16) { + *reg = RK3576_SMT_GPIO4_AL_OFFSET; + } else if (bank->bank_num == 4 && pin_num < 24) { + *reg = RK3576_SMT_GPIO4_CL_OFFSET - 0x8; + } else if (bank->bank_num == 4) { + *reg = RK3576_SMT_GPIO4_DL_OFFSET - 0xc; + } else { + *reg = 0; + debug("unsupported bank_num %d\n", bank->bank_num); + } + + *reg += ((pin_num / RK3576_SMT_PINS_PER_REG) * 4); + *bit = pin_num % RK3576_SMT_PINS_PER_REG; + *bit *= RK3576_SMT_BITS_PER_PIN; +} + +static int rk3576_set_schmitt(struct rockchip_pin_bank *bank, + int pin_num, int enable) +{ + struct regmap *regmap; + int reg; + u32 data, rmask; + u8 bit; + + rk3576_calc_schmitt_reg_and_bit(bank, pin_num, ®map, ®, &bit); + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3576_SMT_BITS_PER_PIN) - 1) << (bit + 16); + rmask = data | (data >> 16); + data |= (enable << bit); + + return regmap_update_bits(regmap, reg, rmask, data); +} + +static struct rockchip_pin_bank rk3576_pin_banks[] = { + RK3576_PIN_BANK_FLAGS(0, 32, "gpio0", IOMUX_WIDTH_4BIT, + 0, 0x8, 0x2004, 0x200C), + RK3576_PIN_BANK_FLAGS(1, 32, "gpio1", IOMUX_WIDTH_4BIT, + 0x4020, 0x4028, 0x4030, 0x4038), + RK3576_PIN_BANK_FLAGS(2, 32, "gpio2", IOMUX_WIDTH_4BIT, + 0x4040, 0x4048, 0x4050, 0x4058), + RK3576_PIN_BANK_FLAGS(3, 32, "gpio3", IOMUX_WIDTH_4BIT, + 0x4060, 0x4068, 0x4070, 0x4078), + RK3576_PIN_BANK_FLAGS(4, 32, "gpio4", IOMUX_WIDTH_4BIT, + 0x4080, 0x4088, 0xA390, 0xB398), +}; + +static const struct rockchip_pin_ctrl rk3576_pin_ctrl = { + .pin_banks = rk3576_pin_banks, + .nr_banks = ARRAY_SIZE(rk3576_pin_banks), + .grf_mux_offset = 0x0, + .set_mux = rk3576_set_mux, + .set_pull = rk3576_set_pull, + .set_drive = rk3576_set_drive, + .set_schmitt = rk3576_set_schmitt, +}; + +static const struct udevice_id rk3576_pinctrl_ids[] = { + { + .compatible = "rockchip,rk3576-pinctrl", + .data = (ulong)&rk3576_pin_ctrl + }, + { } +}; + +U_BOOT_DRIVER(pinctrl_rk3576) = { + .name = "rockchip_rk3576_pinctrl", + .id = UCLASS_PINCTRL, + .of_match = rk3576_pinctrl_ids, + .priv_auto = sizeof(struct rockchip_pinctrl_priv), + .ops = &rockchip_pinctrl_ops, +#if CONFIG_IS_ENABLED(OF_REAL) + .bind = dm_scan_fdt_dev, +#endif + .probe = rockchip_pinctrl_probe, +}; diff --git a/drivers/pinctrl/rockchip/pinctrl-rockchip.h b/drivers/pinctrl/rockchip/pinctrl-rockchip.h index df7bc684d29..5e3c9c90760 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rockchip.h +++ b/drivers/pinctrl/rockchip/pinctrl-rockchip.h @@ -458,6 +458,9 @@ struct rockchip_pin_bank { #define MR_PMUGRF(ID, PIN, FUNC, REG, VAL) \ PIN_BANK_MUX_ROUTE_FLAGS(ID, PIN, FUNC, REG, VAL, ROUTE_TYPE_PMUGRF) +#define RK3576_PIN_BANK_FLAGS(ID, PIN, LABEL, M, O1, O2, O3, O4) \ + PIN_BANK_IOMUX_FLAGS_OFFSET(ID, PIN, LABEL, M, M, M, M, O1, O2, O3, O4) + #define RK3588_PIN_BANK_FLAGS(ID, PIN, LABEL, M, P) \ PIN_BANK_IOMUX_FLAGS_PULL_FLAGS(ID, PIN, LABEL, M, M, M, M, P, P, P, P) diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig index cbd61795986..65e8192a99a 100644 --- a/drivers/pinctrl/sunxi/Kconfig +++ b/drivers/pinctrl/sunxi/Kconfig @@ -124,6 +124,16 @@ config PINCTRL_SUN50I_H616_R default MACH_SUN50I_H616 select PINCTRL_SUNXI +config PINCTRL_SUN50I_A100 + bool "Support for the Allwinner A100/A133 PIO" + default MACH_SUN50I_A133 + select PINCTRL_SUNXI + +config PINCTRL_SUN50I_A100_R + bool "Support for the Allwinner A100/A133 R-PIO" + default MACH_SUN50I_A133 + select PINCTRL_SUNXI + config PINCTRL_SUN20I_D1 bool "Support for the Allwinner D1/R528 PIO" default MACH_SUN8I_R528 diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 37ea93715d1..c38edf7d4f5 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -774,6 +774,41 @@ static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h616_r_pinctrl_desc .num_banks = 1, }; +static const struct sunxi_pinctrl_function sun50i_a100_pinctrl_functions[] = { + { "emac0", 5 }, /* PH0-PH16 */ + { "gpio_in", 0 }, + { "gpio_out", 1 }, + { "mmc0", 2 }, /* PF0-PF5 */ + { "mmc1", 2 }, /* PG0-PG5 */ + { "mmc2", 3 }, /* PC0-PC16 */ + { "spi0", 4 }, /* PC2-PC4, PC7, PC12, PC15-PC16 */ +#if IS_ENABLED(CONFIG_UART0_PORT_F) + { "uart0", 3 }, /* PF2-PF4 */ +#else + { "uart0", 2 }, /* PB9-PB10 */ +#endif +}; + +static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a100_pinctrl_desc = { + .functions = sun50i_a100_pinctrl_functions, + .num_functions = ARRAY_SIZE(sun50i_a100_pinctrl_functions), + .first_bank = SUNXI_GPIO_A, + .num_banks = 8, +}; + +static const struct sunxi_pinctrl_function sun50i_a100_r_pinctrl_functions[] = { + { "gpio_in", 0 }, + { "gpio_out", 1 }, + { "s_i2c0", 2 }, +}; + +static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a100_r_pinctrl_desc = { + .functions = sun50i_a100_r_pinctrl_functions, + .num_functions = ARRAY_SIZE(sun50i_a100_r_pinctrl_functions), + .first_bank = SUNXI_GPIO_L, + .num_banks = 1, +}; + static const struct udevice_id sunxi_pinctrl_ids[] = { #ifdef CONFIG_PINCTRL_SUNIV_F1C100S { @@ -937,6 +972,18 @@ static const struct udevice_id sunxi_pinctrl_ids[] = { .data = (ulong)&sun50i_h616_r_pinctrl_desc, }, #endif +#ifdef CONFIG_PINCTRL_SUN50I_A100 + { + .compatible = "allwinner,sun50i-a100-pinctrl", + .data = (ulong)&sun50i_a100_pinctrl_desc, + }, +#endif +#ifdef CONFIG_PINCTRL_SUN50I_A100_R + { + .compatible = "allwinner,sun50i-a100-r-pinctrl", + .data = (ulong)&sun50i_a100_r_pinctrl_desc, + }, +#endif {} }; diff --git a/drivers/pinctrl/tegra/funcmux-tegra20.c b/drivers/pinctrl/tegra/funcmux-tegra20.c index 951ae196161..f60d5aad3a4 100644 --- a/drivers/pinctrl/tegra/funcmux-tegra20.c +++ b/drivers/pinctrl/tegra/funcmux-tegra20.c @@ -62,8 +62,15 @@ int funcmux_select(enum periph_id id, int config) pinmux_tristate_disable(PMUX_PINGRP_SDIO1); bad_config = 0; break; + case FUNCMUX_UART1_SDB_SDD: + pinmux_set_func(PMUX_PINGRP_SDB, PMUX_FUNC_UARTA); + pinmux_set_func(PMUX_PINGRP_SDD, PMUX_FUNC_UARTA); + pinmux_tristate_disable(PMUX_PINGRP_SDB); + pinmux_tristate_disable(PMUX_PINGRP_SDD); + bad_config = 0; + break; } - if (!bad_config) { + if (!bad_config && config != FUNCMUX_UART1_SDB_SDD) { /* * Tegra appears to boot with function UARTA pre- * selected on mux group SDB. If two mux groups are diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c index ad7112a05e6..bc02825ee1f 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -14,7 +14,7 @@ static void tegra_pinctrl_set_drive(struct udevice *config, int drvcnt) { struct pmux_drvgrp_config *drive_group; - int i, ret, pad_id; + int i, ret, pad_id, count = 0; const char **pads; drive_group = kmalloc_array(drvcnt, sizeof(*drive_group), GFP_KERNEL); @@ -23,18 +23,18 @@ static void tegra_pinctrl_set_drive(struct udevice *config, int drvcnt) return; } - drive_group[0].slwf = dev_read_u32_default(config, "nvidia,slew-rate-falling", 0); - drive_group[0].slwr = dev_read_u32_default(config, "nvidia,slew-rate-rising", 0); - drive_group[0].drvup = dev_read_u32_default(config, "nvidia,pull-up-strength", 0); - drive_group[0].drvdn = dev_read_u32_default(config, "nvidia,pull-down-strength", 0); + drive_group[0].slwf = dev_read_u32_default(config, "nvidia,slew-rate-falling", PMUX_SLWF_NONE); + drive_group[0].slwr = dev_read_u32_default(config, "nvidia,slew-rate-rising", PMUX_SLWR_NONE); + drive_group[0].drvup = dev_read_u32_default(config, "nvidia,pull-up-strength", PMUX_DRVUP_NONE); + drive_group[0].drvdn = dev_read_u32_default(config, "nvidia,pull-down-strength", PMUX_DRVDN_NONE); #ifdef TEGRA_PMX_GRPS_HAVE_LPMD - drive_group[0].lpmd = dev_read_u32_default(config, "nvidia,low-power-mode", 0); + drive_group[0].lpmd = dev_read_u32_default(config, "nvidia,low-power-mode", PMUX_LPMD_NONE); #endif #ifdef TEGRA_PMX_GRPS_HAVE_SCHMT - drive_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", 0); + drive_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", PMUX_SCHMT_NONE); #endif #ifdef TEGRA_PMX_GRPS_HAVE_HSM - drive_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", 0); + drive_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", PMUX_HSM_NONE); #endif for (i = 1; i < drvcnt; i++) @@ -46,31 +46,110 @@ static void tegra_pinctrl_set_drive(struct udevice *config, int drvcnt) goto exit; } + /* + * i goes through all drive instances defined, while + * count is increased only if a valid configuration is found + */ for (i = 0; i < drvcnt; i++) { for (pad_id = 0; pad_id < PMUX_DRVGRP_COUNT; pad_id++) if (tegra_pinctrl_to_drvgrp[pad_id]) if (!strcmp(pads[i], tegra_pinctrl_to_drvgrp[pad_id])) { - drive_group[i].drvgrp = pad_id; + drive_group[count].drvgrp = pad_id; break; } - debug("%s drvmap: %d, %d, %d, %d, %d\n", pads[i], - drive_group[i].drvgrp, drive_group[i].slwf, - drive_group[i].slwr, drive_group[i].drvup, - drive_group[i].drvdn); + if (pad_id == PMUX_DRVGRP_COUNT) { + log_debug("%s: drive %s is not valid\n", __func__, pads[i]); + continue; + } + + log_debug("%s(%d) drvmap: %d, %d, %d, %d, %d\n", pads[count], count, + drive_group[count].drvgrp, drive_group[count].slwf, + drive_group[count].slwr, drive_group[count].drvup, + drive_group[count].drvdn); + + count++; } - pinmux_config_drvgrp_table(drive_group, drvcnt); + pinmux_config_drvgrp_table(drive_group, count); free(pads); exit: kfree(drive_group); } +#ifdef TEGRA_PMX_SOC_HAS_MIPI_PAD_CTRL_GRPS +static void tegra_pinctrl_set_mipipad(struct udevice *config, int padcnt) +{ + struct pmux_mipipadctrlgrp_config *mipipad_group; + int i, ret, pad_id, count = 0; + const char *function; + const char **pads; + + mipipad_group = kmalloc_array(padcnt, sizeof(*mipipad_group), GFP_KERNEL); + if (!mipipad_group) { + log_debug("%s: cannot allocate mipi pad group array\n", __func__); + return; + } + + /* decode function id and fill the first copy of pmux_mipipadctrlgrp_config */ + function = dev_read_string(config, "nvidia,function"); + if (function) + for (i = 0; i < PMUX_FUNC_COUNT; i++) + if (tegra_pinctrl_to_func[i]) + if (!strcmp(function, tegra_pinctrl_to_func[i])) + break; + + if (!function || i == PMUX_FUNC_COUNT) { + log_debug("%s: pin function is not defined or is not valid\n", __func__); + goto exit; + } + + mipipad_group[0].func = i; + + for (i = 1; i < padcnt; i++) + memcpy(&mipipad_group[i], &mipipad_group[0], sizeof(mipipad_group[0])); + + ret = dev_read_string_list(config, "nvidia,pins", &pads); + if (ret < 0) { + log_debug("%s: could not parse property nvidia,pins\n", __func__); + goto exit; + } + + /* + * i goes through all pin instances defined, while + * count is increased only if a valid configuration is found + */ + for (i = 0; i < padcnt; i++) { + for (pad_id = 0; pad_id < PMUX_MIPIPADCTRLGRP_COUNT; pad_id++) + if (tegra_pinctrl_to_mipipadgrp[pad_id]) + if (!strcmp(pads[i], tegra_pinctrl_to_mipipadgrp[pad_id])) { + mipipad_group[count].grp = pad_id; + break; + } + + if (pad_id == PMUX_MIPIPADCTRLGRP_COUNT) { + log_debug("%s: drive %s is not valid\n", __func__, pads[i]); + continue; + } + + count++; + } + + pinmux_config_mipipadctrlgrp_table(mipipad_group, count); + + free(pads); +exit: + kfree(mipipad_group); +} +#else +static void tegra_pinctrl_set_mipipad(struct udevice *config, int padcnt) { } +#endif + static void tegra_pinctrl_set_pin(struct udevice *config, int pincnt) { struct pmux_pingrp_config *pinmux_group; - int i, ret, pin_id; + int i, ret, pin_id, count = 0; const char *function; const char **pins; @@ -88,33 +167,38 @@ static void tegra_pinctrl_set_pin(struct udevice *config, int pincnt) if (!strcmp(function, tegra_pinctrl_to_func[i])) break; + if (!function || i == PMUX_FUNC_COUNT) { + log_debug("%s: pin function is not defined or is not valid\n", __func__); + goto exit; + } + pinmux_group[0].func = i; - pinmux_group[0].pull = dev_read_u32_default(config, "nvidia,pull", 0); - pinmux_group[0].tristate = dev_read_u32_default(config, "nvidia,tristate", 0); + pinmux_group[0].pull = dev_read_u32_default(config, "nvidia,pull", PMUX_PULL_NORMAL); + pinmux_group[0].tristate = dev_read_u32_default(config, "nvidia,tristate", PMUX_TRI_TRISTATE); #ifdef TEGRA_PMX_PINS_HAVE_E_INPUT - pinmux_group[0].io = dev_read_u32_default(config, "nvidia,enable-input", 0); + pinmux_group[0].io = dev_read_u32_default(config, "nvidia,enable-input", PMUX_PIN_NONE); #endif #ifdef TEGRA_PMX_PINS_HAVE_LOCK - pinmux_group[0].lock = dev_read_u32_default(config, "nvidia,lock", 0); + pinmux_group[0].lock = dev_read_u32_default(config, "nvidia,lock", PMUX_PIN_LOCK_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_OD - pinmux_group[0].od = dev_read_u32_default(config, "nvidia,open-drain", 0); + pinmux_group[0].od = dev_read_u32_default(config, "nvidia,open-drain", PMUX_PIN_OD_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_IO_RESET - pinmux_group[0].ioreset = dev_read_u32_default(config, "nvidia,io-reset", 0); + pinmux_group[0].ioreset = dev_read_u32_default(config, "nvidia,io-reset", PMUX_PIN_IO_RESET_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_RCV_SEL - pinmux_group[0].rcv_sel = dev_read_u32_default(config, "nvidia,rcv-sel", 0); + pinmux_group[0].rcv_sel = dev_read_u32_default(config, "nvidia,rcv-sel", PMUX_PIN_RCV_SEL_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_E_IO_HV - pinmux_group[0].e_io_hv = dev_read_u32_default(config, "nvidia,io-hv", 0); + pinmux_group[0].e_io_hv = dev_read_u32_default(config, "nvidia,io-hv", PMUX_PIN_E_IO_HV_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_SCHMT - pinmux_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", 0); + pinmux_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", PMUX_SCHMT_NONE); #endif #ifdef TEGRA_PMX_PINS_HAVE_HSM - pinmux_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", 0); + pinmux_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", PMUX_HSM_NONE); #endif for (i = 1; i < pincnt; i++) @@ -126,20 +210,31 @@ static void tegra_pinctrl_set_pin(struct udevice *config, int pincnt) goto exit; } + /* + * i goes through all pin instances defined, while + * count is increased only if a valid configuration is found + */ for (i = 0; i < pincnt; i++) { for (pin_id = 0; pin_id < PMUX_PINGRP_COUNT; pin_id++) if (tegra_pinctrl_to_pingrp[pin_id]) if (!strcmp(pins[i], tegra_pinctrl_to_pingrp[pin_id])) { - pinmux_group[i].pingrp = pin_id; + pinmux_group[count].pingrp = pin_id; break; } - debug("%s pinmap: %d, %d, %d, %d\n", pins[i], - pinmux_group[i].pingrp, pinmux_group[i].func, - pinmux_group[i].pull, pinmux_group[i].tristate); + if (pin_id == PMUX_PINGRP_COUNT) { + log_debug("%s: pin %s is not valid\n", __func__, pins[i]); + continue; + } + + log_debug("%s(%d) pinmap: %d, %d, %d, %d\n", pins[count], count, + pinmux_group[count].pingrp, pinmux_group[count].func, + pinmux_group[count].pull, pinmux_group[count].tristate); + + count++; } - pinmux_config_pingrp_table(pinmux_group, pincnt); + pinmux_config_pingrp_table(pinmux_group, count); free(pins); exit: @@ -170,6 +265,9 @@ static int tegra_pinctrl_set_state(struct udevice *dev, struct udevice *config) if (!strncmp(name, "drive_", 6)) /* Drive node is detected */ tegra_pinctrl_set_drive(child, ret); + else if (!strncmp(name, "mipi_pad_ctrl_", 14)) + /* Handle T124 specific pinconfig */ + tegra_pinctrl_set_mipipad(child, ret); else /* Pin node is detected */ tegra_pinctrl_set_pin(child, ret); @@ -236,6 +334,7 @@ static int tegra_pinctrl_bind(struct udevice *dev) static const struct udevice_id tegra_pinctrl_ids[] = { { .compatible = "nvidia,tegra30-pinmux" }, { .compatible = "nvidia,tegra114-pinmux" }, + { .compatible = "nvidia,tegra124-pinmux" }, { }, }; diff --git a/drivers/pinctrl/tegra/pinctrl-tegra20.c b/drivers/pinctrl/tegra/pinctrl-tegra20.c index d59b3ec7b5d..c32d590a7e0 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra20.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra20.c @@ -37,6 +37,11 @@ static void tegra_pinctrl_set_pin(struct udevice *config) if (!strcmp(pins[i], tegra_pinctrl_to_pingrp[pin_id])) break; + if (pin_id == PMUX_PINGRP_COUNT) { + log_debug("%s: %s(%d) is not valid\n", __func__, pins[i], pin_id); + continue; + } + if (pull >= 0) pinmux_set_pullupdown(pin_id, pull); @@ -58,13 +63,16 @@ static void tegra_pinctrl_set_func(struct udevice *config) const char **pins; function = dev_read_string(config, "nvidia,function"); - if (function) + if (function) { for (i = 0; i < PMUX_FUNC_COUNT; i++) if (tegra_pinctrl_to_func[i]) if (!strcmp(function, tegra_pinctrl_to_func[i])) break; - func_id = i; + func_id = i; + } else { + func_id = PMUX_FUNC_COUNT; + } count = dev_read_string_list(config, "nvidia,pins", &pins); if (count < 0) { @@ -78,6 +86,12 @@ static void tegra_pinctrl_set_func(struct udevice *config) if (!strcmp(pins[i], tegra_pinctrl_to_pingrp[pin_id])) break; + if (func_id == PMUX_FUNC_COUNT || pin_id == PMUX_PINGRP_COUNT) { + log_debug("%s: pin %s(%d) or function %s(%d) is not valid\n", + __func__, pins[i], pin_id, function, func_id); + continue; + } + debug("%s(%d) muxed to %s(%d)\n", pins[i], pin_id, function, func_id); pinmux_set_func(pin_id, func_id); diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 4b81aeb7497..eed65058e66 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -116,6 +116,12 @@ config AXP717_POWER ---help--- Select this to enable support for the AXP717 PMIC found on some boards. +config AXP803_POWER + bool "AXP803 PMIC support" + select AXP_PMIC_BUS + ---help--- + Select this to enable support for the AXP803 PMIC found on some boards. + config AXP809_POWER bool "axp809 pmic support" depends on MACH_SUN9I @@ -142,10 +148,20 @@ config SY8106A_POWER endchoice +config AXP_I2C_ADDRESS + hex "AXP PMIC I2C address" + depends on ARCH_SUNXI && !SUNXI_NO_PMIC + default 0x36 if AXP305_POWER + default 0x36 if AXP313_POWER + default 0x30 if AXP152_POWER + default 0x34 + ---help--- + I2C address of the AXP PMIC, used for the SPL only. + config AXP_DCDC1_VOLT int "axp pmic dcdc1 voltage" - depends on AXP221_POWER || AXP809_POWER || AXP818_POWER - default 3300 if AXP818_POWER || MACH_SUN8I_R40 + depends on AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP803_POWER + default 3300 if AXP818_POWER || MACH_SUN8I_R40 || AXP803_POWER default 3000 if MACH_SUN6I || MACH_SUN8I || MACH_SUN9I ---help--- Set the voltage (mV) to program the axp pmic dcdc1 at, set to 0 to @@ -158,11 +174,12 @@ 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 || AXP313_POWER || AXP717_POWER + depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER || AXP717_POWER || AXP803_POWER default 900 if AXP818_POWER default 1400 if AXP152_POWER || AXP209_POWER default 1000 if AXP313_POWER default 1000 if AXP717_POWER + default 1000 if AXP803_POWER default 1200 if MACH_SUN6I default 1100 if MACH_SUN8I default 0 if MACH_SUN9I @@ -219,7 +236,7 @@ config AXP_DCDC4_VOLT config AXP_DCDC5_VOLT int "axp pmic dcdc5 voltage" - depends on AXP221_POWER || AXP809_POWER || AXP818_POWER + depends on AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP803_POWER default 1500 if MACH_SUN6I || MACH_SUN8I || MACH_SUN9I ---help--- Set the voltage (mV) to program the axp pmic dcdc5 at, set to 0 to diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 3f4d56f5139..3363191fdc8 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_AXP313_POWER) += axp_spl.o obj-$(CONFIG_AXP717_POWER) += axp_spl.o obj-$(CONFIG_AXP809_POWER) += axp809.o obj-$(CONFIG_AXP818_POWER) += axp818.o +obj-$(CONFIG_AXP803_POWER) += axp_spl.o endif obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o obj-$(CONFIG_SY8106A_POWER) += sy8106a.o diff --git a/drivers/power/axp_spl.c b/drivers/power/axp_spl.c index 3c86eb20ab4..7c51a9b3dfb 100644 --- a/drivers/power/axp_spl.c +++ b/drivers/power/axp_spl.c @@ -36,6 +36,23 @@ static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = { #define AXP_SHUTDOWN_REG 0x27 #define AXP_SHUTDOWN_MASK BIT(0) +#elif defined(CONFIG_AXP803_POWER) /* AXP803 */ + +static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = { + { 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA }, + { 0x10, BIT(1), 0x21, 0x7f, 500, 1300, 10, 70 }, + { 0x10, BIT(2), 0x22, 0x7f, 500, 1300, 10, 70 }, + { 0x10, BIT(3), 0x23, 0x7f, 500, 1300, 10, 70 }, + { 0x10, BIT(4), 0x24, 0x7f, 800, 1840, 10, 32 }, + { 0x10, BIT(5), 0x25, 0x7f, 600, 1520, 10, 50 }, +}; + +#define AXP_CHIP_VERSION 0x3 +#define AXP_CHIP_VERSION_MASK 0xcf +#define AXP_CHIP_ID 0x41 +#define AXP_SHUTDOWN_REG 0x32 +#define AXP_SHUTDOWN_MASK BIT(7) + #elif defined(CONFIG_AXP313_POWER) /* AXP313 */ static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = { diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index bd82d2f7044..5f5218bd8b5 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -47,6 +47,13 @@ config IMX8MP_HSIOMIX_BLKCTRL help Enable support for manipulating NXP i.MX8MP on-SoC HSIOMIX block controller. +config IMX8MP_MEDIAMIX_BLKCTRL + bool "Enable i.MX8MP MEDIAMIX domain driver" + depends on POWER_DOMAIN && IMX8MP + select CLK + help + Enable support for manipulating NXP i.MX8MP on-SoC MEDIAMIX block controller. + config MTK_POWER_DOMAIN bool "Enable the MediaTek power domain driver" depends on POWER_DOMAIN && ARCH_MEDIATEK diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 110646c503a..4d20c97d26c 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -3,12 +3,13 @@ # Copyright (c) 2016, NVIDIA CORPORATION. # -obj-$(CONFIG_$(XPL_)POWER_DOMAIN) += power-domain-uclass.o +obj-$(CONFIG_$(PHASE_)POWER_DOMAIN) += power-domain-uclass.o obj-$(CONFIG_APPLE_PMGR_POWER_DOMAIN) += apple-pmgr.o obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o obj-$(CONFIG_IMX8M_POWER_DOMAIN) += imx8m-power-domain.o obj-$(CONFIG_IMX8MP_HSIOMIX_BLKCTRL) += imx8mp-hsiomix.o +obj-$(CONFIG_IMX8MP_MEDIAMIX_BLKCTRL) += imx8mp-mediamix.o obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o obj-$(CONFIG_MESON_EE_POWER_DOMAIN) += meson-ee-pwrc.o diff --git a/drivers/power/domain/imx8m-power-domain.c b/drivers/power/domain/imx8m-power-domain.c index c22fbe60675..e54ba5d9a54 100644 --- a/drivers/power/domain/imx8m-power-domain.c +++ b/drivers/power/domain/imx8m-power-domain.c @@ -40,6 +40,7 @@ DECLARE_GLOBAL_DATA_PTR; #define IMX8MN_MIPI_A53_DOMAIN BIT(2) #define IMX8MP_HSIOMIX_A53_DOMAIN BIT(19) +#define IMX8MP_MEDIAMIX_A53_DOMAIN BIT(12) #define IMX8MP_USB2_PHY_A53_DOMAIN BIT(5) #define IMX8MP_USB1_PHY_A53_DOMAIN BIT(4) #define IMX8MP_PCIE_PHY_A53_DOMAIN BIT(3) @@ -63,6 +64,7 @@ DECLARE_GLOBAL_DATA_PTR; #define IMX8MN_MIPI_SW_Pxx_REQ BIT(0) #define IMX8MP_HSIOMIX_Pxx_REQ BIT(17) +#define IMX8MP_MEDIAMIX_Pxx_REQ BIT(10) #define IMX8MP_USB2_PHY_Pxx_REQ BIT(3) #define IMX8MP_USB1_PHY_Pxx_REQ BIT(2) #define IMX8MP_PCIE_PHY_SW_Pxx_REQ BIT(1) @@ -81,6 +83,9 @@ DECLARE_GLOBAL_DATA_PTR; #define IMX8MP_HSIOMIX_PWRDNACKN BIT(28) #define IMX8MP_HSIOMIX_PWRDNREQN BIT(12) +#define IMX8MP_MEDIAMIX_PWRDNACKN BIT(30) +#define IMX8MP_MEDIAMIX_PWRDNREQN BIT(14) + /* * The PGC offset values in Reference Manual * (Rev. 1, 01/2018 and the older ones) GPC chapter's @@ -101,6 +106,7 @@ DECLARE_GLOBAL_DATA_PTR; #define IMX8MP_PGC_PCIE 13 #define IMX8MP_PGC_USB1 14 #define IMX8MP_PGC_USB2 15 +#define IMX8MP_PGC_MEDIAMIX 22 #define IMX8MP_PGC_HSIOMIX 29 #define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40) @@ -303,6 +309,17 @@ static const struct imx_pgc_domain imx8mp_pgc_domains[] = { .pgc = BIT(IMX8MP_PGC_HSIOMIX), .keep_clocks = true, }, + + [IMX8MP_POWER_DOMAIN_MEDIAMIX] = { + .bits = { + .pxx = IMX8MP_MEDIAMIX_Pxx_REQ, + .map = IMX8MP_MEDIAMIX_A53_DOMAIN, + .hskreq = IMX8MP_MEDIAMIX_PWRDNREQN, + .hskack = IMX8MP_MEDIAMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_MEDIAMIX), + .keep_clocks = true, + }, }; static const struct imx_pgc_regs imx8mp_pgc_regs = { diff --git a/drivers/power/domain/imx8mp-mediamix.c b/drivers/power/domain/imx8mp-mediamix.c new file mode 100644 index 00000000000..78c32ca3d3a --- /dev/null +++ b/drivers/power/domain/imx8mp-mediamix.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * i.MX8 MEDIAMIX control block driver + * Copyright (C) 2024 Miquel Raynal <miquel.raynal@bootlin.com> + * Inspired from Marek Vasut <marex@denx.de> work on the hsiomix driver. + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <power-domain-uclass.h> +#include <linux/delay.h> + +#include <dt-bindings/power/imx8mp-power.h> + +#define BLK_SFT_RSTN 0x0 +#define BLK_CLK_EN 0x4 + +struct imx8mp_mediamix_priv { + void __iomem *base; + struct clk clk_apb; + struct clk clk_axi; + struct clk clk_disp2; + struct power_domain pd_bus; + struct power_domain pd_lcdif2; +}; + +static int imx8mp_mediamix_on(struct power_domain *power_domain) +{ + struct udevice *dev = power_domain->dev; + struct imx8mp_mediamix_priv *priv = dev_get_priv(dev); + struct power_domain *domain; + struct clk *clk; + u32 reset; + int ret; + + switch (power_domain->id) { + case IMX8MP_MEDIABLK_PD_LCDIF_2: + domain = &priv->pd_lcdif2; + clk = &priv->clk_disp2; + reset = BIT(11) | BIT(12) | BIT(24); + break; + default: + return -EINVAL; + } + + /* Make sure bus domain is awake */ + ret = power_domain_on(&priv->pd_bus); + if (ret) + return ret; + + /* Put devices into reset */ + clrbits_le32(priv->base + BLK_SFT_RSTN, reset); + + /* Enable upstream clocks */ + ret = clk_enable(&priv->clk_apb); + if (ret) + goto dis_bus_pd; + + ret = clk_enable(&priv->clk_axi); + if (ret) + goto dis_apb_clk; + + /* Enable blk-ctrl clock to allow reset to propagate */ + ret = clk_enable(clk); + if (ret) + goto dis_axi_clk; + setbits_le32(priv->base + BLK_CLK_EN, reset); + + /* Power up upstream GPC domain */ + ret = power_domain_on(domain); + if (ret) + goto dis_lcdif_clk; + + /* Wait for reset to propagate */ + udelay(5); + + /* Release reset */ + setbits_le32(priv->base + BLK_SFT_RSTN, reset); + + return 0; + +dis_lcdif_clk: + clk_disable(clk); +dis_axi_clk: + clk_disable(&priv->clk_axi); +dis_apb_clk: + clk_disable(&priv->clk_apb); +dis_bus_pd: + power_domain_off(&priv->pd_bus); + + return ret; +} + +static int imx8mp_mediamix_off(struct power_domain *power_domain) +{ + struct udevice *dev = power_domain->dev; + struct imx8mp_mediamix_priv *priv = dev_get_priv(dev); + struct power_domain *domain; + struct clk *clk; + u32 reset; + + switch (power_domain->id) { + case IMX8MP_MEDIABLK_PD_LCDIF_2: + domain = &priv->pd_lcdif2; + clk = &priv->clk_disp2; + reset = BIT(11) | BIT(12) | BIT(24); + break; + default: + return -EINVAL; + } + + /* Put devices into reset and disable clocks */ + clrbits_le32(priv->base + BLK_SFT_RSTN, reset); + clrbits_le32(priv->base + BLK_CLK_EN, reset); + + /* Power down upstream GPC domain */ + power_domain_off(domain); + + clk_disable(clk); + clk_disable(&priv->clk_axi); + clk_disable(&priv->clk_apb); + + /* Allow bus domain to suspend */ + power_domain_off(&priv->pd_bus); + + return 0; +} + +static int imx8mp_mediamix_of_xlate(struct power_domain *power_domain, + struct ofnode_phandle_args *args) +{ + power_domain->id = args->args[0]; + + return 0; +} + +static int imx8mp_mediamix_bind(struct udevice *dev) +{ + /* Bind child lcdif */ + return dm_scan_fdt_dev(dev); +} + +static int imx8mp_mediamix_probe(struct udevice *dev) +{ + struct imx8mp_mediamix_priv *priv = dev_get_priv(dev); + int ret; + + priv->base = dev_read_addr_ptr(dev); + + ret = clk_get_by_name(dev, "apb", &priv->clk_apb); + if (ret < 0) + return ret; + + ret = clk_get_by_name(dev, "axi", &priv->clk_axi); + if (ret < 0) + return ret; + + ret = clk_get_by_name(dev, "disp2", &priv->clk_disp2); + if (ret < 0) + return ret; + + ret = power_domain_get_by_name(dev, &priv->pd_bus, "bus"); + if (ret < 0) + return ret; + + ret = power_domain_get_by_name(dev, &priv->pd_lcdif2, "lcdif2"); + if (ret < 0) + goto free_bus_pd; + + return 0; + +free_bus_pd: + power_domain_free(&priv->pd_bus); + return ret; +} + +static int imx8mp_mediamix_remove(struct udevice *dev) +{ + struct imx8mp_mediamix_priv *priv = dev_get_priv(dev); + + power_domain_free(&priv->pd_lcdif2); + power_domain_free(&priv->pd_bus); + + return 0; +} + +static const struct udevice_id imx8mp_mediamix_ids[] = { + { .compatible = "fsl,imx8mp-media-blk-ctrl" }, + { } +}; + +struct power_domain_ops imx8mp_mediamix_ops = { + .on = imx8mp_mediamix_on, + .off = imx8mp_mediamix_off, + .of_xlate = imx8mp_mediamix_of_xlate, +}; + +U_BOOT_DRIVER(imx8mp_mediamix) = { + .name = "imx8mp_mediamix", + .id = UCLASS_POWER_DOMAIN, + .of_match = imx8mp_mediamix_ids, + .bind = imx8mp_mediamix_bind, + .probe = imx8mp_mediamix_probe, + .remove = imx8mp_mediamix_remove, + .priv_auto = sizeof(struct imx8mp_mediamix_priv), + .ops = &imx8mp_mediamix_ops, +}; diff --git a/drivers/power/domain/ti-power-domain.c b/drivers/power/domain/ti-power-domain.c index 5e7a4c5648d..c3519307340 100644 --- a/drivers/power/domain/ti-power-domain.c +++ b/drivers/power/domain/ti-power-domain.c @@ -94,6 +94,8 @@ static const struct soc_attr ti_k3_soc_pd_data[] = { .family = "J721E", .data = &j721e_pd_platdata, }, +#endif +#if IS_ENABLED(CONFIG_SOC_K3_J7200) { .family = "J7200", .data = &j7200_pd_platdata, @@ -116,6 +118,10 @@ static const struct soc_attr ti_k3_soc_pd_data[] = { .family = "J784S4", .data = &j784s4_pd_platdata, }, + { + .family = "J742S2", + .data = &j784s4_pd_platdata, + }, #endif { /* sentinel */ } }; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index bbcbcee4c35..5a61cd45b8c 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -295,6 +295,16 @@ config DM_PMIC_SANDBOX Driver binding info: doc/device-tree-bindings/pmic/sandbox.txt +config DM_PMIC_CPCAP + bool "Enable Driver Model for Motorola CPCAP" + help + The CPCAP is a Motorola/ST-Ericsson creation, a multifunctional IC + whose main purpose is power control. It was used in a wide variety of + Motorola products, both Tegra and OMAP based. The most notable devices + using this PMIC are the Motorola Droid 4, Atrix 4G, and Droid X2. + Unlike most PMICs, this one is not I2C based; it uses the SPI bus. The + core driver provides both read and write access to the device registers. + config PMIC_S5M8767 bool "Enable Driver Model for the Samsung S5M8767 PMIC" ---help--- diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index bc138f563ff..2210b1a64ae 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -4,41 +4,42 @@ # Lukasz Majewski <l.majewski@samsung.com> obj-$(CONFIG_$(PHASE_)DM_PMIC) += pmic-uclass.o -obj-$(CONFIG_$(XPL_)DM_PMIC_FAN53555) += fan53555.o -obj-$(CONFIG_$(XPL_)DM_PMIC_DA9063) += da9063.o -obj-$(CONFIG_$(XPL_)DM_PMIC_MAX77663) += max77663.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_FAN53555) += fan53555.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_DA9063) += da9063.o +obj-$(CONFIG_$(PHASE_)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 -obj-$(CONFIG_$(XPL_)DM_PMIC_BD71837) += bd71837.o -obj-$(CONFIG_$(XPL_)DM_PMIC_MP5416) += mp5416.o -obj-$(CONFIG_$(XPL_)DM_PMIC_PFUZE100) += pfuze100.o -obj-$(CONFIG_$(XPL_)DM_PMIC_PCA9450) += pca9450.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_BD71837) += bd71837.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_MP5416) += mp5416.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_PFUZE100) += pfuze100.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_PCA9450) += pca9450.o obj-$(CONFIG_PMIC_S2MPS11) += s2mps11.o obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o obj-$(CONFIG_PMIC_AB8500) += ab8500.o obj-$(CONFIG_PMIC_ACT8846) += act8846.o obj-$(CONFIG_PMIC_AS3722) += as3722.o as3722_gpio.o -obj-$(CONFIG_$(XPL_)PMIC_AXP) += axp.o +obj-$(CONFIG_$(PHASE_)PMIC_AXP) += axp.o obj-$(CONFIG_PMIC_MAX8997) += max8997.o obj-$(CONFIG_PMIC_QCOM) += pmic_qcom.o obj-$(CONFIG_$(PHASE_)PMIC_RK8XX) += rk8xx.o -obj-$(CONFIG_$(XPL_)PMIC_RN5T567) += rn5t567.o +obj-$(CONFIG_$(PHASE_)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_$(XPL_)DM_PMIC_TPS80031) += tps80031.o -obj-$(CONFIG_$(XPL_)PMIC_PALMAS) += palmas.o -obj-$(CONFIG_$(XPL_)PMIC_LP873X) += lp873x.o -obj-$(CONFIG_$(XPL_)PMIC_LP87565) += lp87565.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_TPS80031) += tps80031.o +obj-$(CONFIG_$(PHASE_)PMIC_PALMAS) += palmas.o +obj-$(CONFIG_$(PHASE_)PMIC_LP873X) += lp873x.o +obj-$(CONFIG_$(PHASE_)PMIC_LP87565) += lp87565.o obj-$(CONFIG_PMIC_STPMIC1) += stpmic1.o obj-$(CONFIG_PMIC_TPS65217) += pmic_tps65217.o obj-$(CONFIG_PMIC_TPS65219) += tps65219.o obj-$(CONFIG_PMIC_TPS65941) += tps65941.o obj-$(CONFIG_PMIC_RAA215300) += raa215300.o obj-$(CONFIG_POWER_TPS65218) += pmic_tps65218.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_CPCAP) += cpcap.o -ifeq ($(CONFIG_$(XPL_)POWER_LEGACY),y) +ifeq ($(CONFIG_$(PHASE_)POWER_LEGACY),y) obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o obj-$(CONFIG_POWER_PCA9450) += pmic_pca9450.o obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o @@ -47,5 +48,5 @@ obj-$(CONFIG_POWER_HI6553) += pmic_hi6553.o obj-$(CONFIG_POWER_MC34VR500) += pmic_mc34vr500.o endif -obj-$(CONFIG_$(XPL_)POWER_TPS62362) += pmic_tps62362.o +obj-$(CONFIG_$(PHASE_)POWER_TPS62362) += pmic_tps62362.o obj-$(CONFIG_SPL_POWER_TPS65910) += pmic_tps65910.o diff --git a/drivers/power/pmic/axp.c b/drivers/power/pmic/axp.c index 521a39dd566..c300fd2bbc2 100644 --- a/drivers/power/pmic/axp.c +++ b/drivers/power/pmic/axp.c @@ -51,6 +51,7 @@ static const struct pmic_child_info axp_pmic_child_info[] = { { "cldo", "axp_regulator" }, { "dc", "axp_regulator" }, { "dldo", "axp_regulator" }, + { "drivevbus", "axp_drivevbus" }, { "eldo", "axp_regulator" }, { "fldo", "axp_regulator" }, { "ldo", "axp_regulator" }, diff --git a/drivers/power/pmic/cpcap.c b/drivers/power/pmic/cpcap.c new file mode 100644 index 00000000000..f2076afff43 --- /dev/null +++ b/drivers/power/pmic/cpcap.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <dm/lists.h> +#include <log.h> +#include <power/pmic.h> +#include <power/cpcap.h> +#include <spi.h> +#include <linux/delay.h> +#include <linux/err.h> + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "sw", .driver = CPCAP_SW_DRIVER }, + { .prefix = "v", .driver = CPCAP_LDO_DRIVER }, + { }, +}; + +static int cpcap_write(struct udevice *dev, uint reg, const uint8_t *buff, int len) +{ + u8 buf[4]; + u16 data = *(u16 *)buff; + int ret; + + buf[0] = ((reg >> 8) & 0xff) | 0x80; + buf[1] = reg & 0xff; + buf[2] = data >> 8 & 0xff; + buf[3] = data & 0xff; + + ret = dm_spi_xfer(dev, 32, buf, NULL, SPI_XFER_ONCE); + + log_debug("%s: reg 0x%x, data 0x%04x, ret %d\n", __func__, reg, data, ret); + + return ret; +} + +static int cpcap_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + u8 buf[4]; + int ret; + + buf[0] = (reg >> 8) & 0xff; + buf[1] = reg & 0xff; + buf[2] = 0; + buf[3] = 0; + + ret = dm_spi_xfer(dev, 32, buf, buf, SPI_XFER_ONCE); + *buff = (buf[2] << 8) | buf[3]; + + log_debug("%s: reg 0x%x, data 0x%04x, ret %d\n", __func__, reg, *buff, ret); + return ret; +} + +static int cpcap_bind(struct udevice *dev) +{ + ofnode regulators_node; + int children; + + /* Regulator device node of PMIC */ + regulators_node = dev_read_subnode(dev, "regulator"); + if (!ofnode_valid(regulators_node)) { + log_err("%s regulator subnode not found!\n", dev->name); + return -ENXIO; + } + + /* Actual regulators container */ + regulators_node = ofnode_find_subnode(regulators_node, "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); + + return dm_scan_fdt_dev(dev); +} + +static int cpcap_probe(struct udevice *dev) +{ + struct spi_slave *slave = dev_get_parent_priv(dev); + int ret; + + ret = spi_claim_bus(slave); + if (ret) { + log_err("SPI bus allocation failed (%d)\n", ret); + return ret; + } + + u16 id = pmic_reg_read(dev, CPCAP_REG_VERSC1); + + u16 ven = (id >> 6) & 0x7; + u16 rev = ((id >> 3) & 0x7) | ((id << 3) & 0x38); + + log_debug("%s: vendor %s rev: %i.%i (%x)\n", __func__, + ven == CPCAP_VENDOR_ST ? "ST" : "TI", + CPCAP_REVISION_MAJOR(rev), CPCAP_REVISION_MINOR(rev), + rev); + return 0; +} + +static struct dm_pmic_ops cpcap_ops = { + .read = cpcap_read, + .write = cpcap_write, +}; + +static const struct udevice_id cpcap_ids[] = { + { .compatible = "motorola,cpcap" }, + { .compatible = "st,6556002" }, + { } +}; + +U_BOOT_DRIVER(pmic_cpcap) = { + .name = "cpcap_pmic", + .id = UCLASS_PMIC, + .of_match = cpcap_ids, + .bind = cpcap_bind, + .probe = cpcap_probe, + .ops = &cpcap_ops, +}; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 958f337c7e7..95912ef5633 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -57,6 +57,13 @@ config SPL_REGULATOR_AXP Enable support in SPL for the regulators (DCDCs, LDOs) in the X-Powers AXP152, AXP2xx, and AXP8xx PMICs. +config REGULATOR_AXP_DRIVEVBUS + bool "Enable driver for X-Powers AXP PMIC drivevbus" + depends on DM_REGULATOR && PMIC_AXP + help + Enable support for sensing or driving the USB VBUS on + X-Powers AXP2xx and AXP8xx PMICs. + config REGULATOR_AXP_USB_POWER bool "Enable driver for X-Powers AXP PMIC USB power supply" depends on DM_REGULATOR && PMIC_AXP @@ -224,6 +231,13 @@ config DM_REGULATOR_QCOM_RPMH implements get/set api for a limited set of regulators used by u-boot. +config DM_REGULATOR_QCOM_USB_VBUS + bool "Enable driver model for Qualcomm USB vbus regulator" + depends on DM_REGULATOR + ---help--- + Enable support for the Qualcomm USB Vbus regulator. The driver + implements get/set api for the regulator to be used by u-boot. + config SPL_DM_REGULATOR_GPIO bool "Enable Driver Model for GPIO REGULATOR in SPL" depends on DM_REGULATOR_GPIO && SPL_GPIO @@ -478,3 +492,20 @@ config DM_REGULATOR_TPS65219 features for REGULATOR TPS65219 and the family of TPS65219 PMICs. TPS65219 series of PMICs have 3 single phase BUCKs & 4 LDOs. The driver implements get/set api for value and enable. + +config REGULATOR_RZG2L_USBPHY + bool "Enable driver for RZ/G2L USB PHY VBUS supply" + depends on DM_REGULATOR + help + Enable this option to support controlling the VBUS supply in + the USB PHY peripheral of the Renesas RZ/G2L SoC. This option + is required in order to use the USB OTG port. + +config DM_REGULATOR_CPCAP + bool "Enable driver for CPCAP PMIC regulators" + depends on DM_REGULATOR && DM_PMIC_CPCAP + ---help--- + Enable implementation of driver-model regulator uclass features for + REGULATOR CPCAP. The driver supports both DC-to-DC Step-Down Switching + (SW) Regulators and Low-Dropout Linear (LDO) Regulators found in CPCAP + PMIC and implements get/set api for voltage and state. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index ca6c89d13b5..0ee5d908a2a 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -4,41 +4,45 @@ # Przemyslaw Marczak <p.marczak@samsung.com> # -obj-$(CONFIG_$(XPL_)DM_REGULATOR) += regulator-uclass.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR) += regulator-uclass.o obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o -obj-$(CONFIG_$(XPL_)REGULATOR_AXP) += axp_regulator.o -obj-$(CONFIG_$(XPL_)REGULATOR_AXP_USB_POWER) += axp_usb_power.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_DA9063) += da9063.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_MAX77663) += max77663_regulator.o +obj-$(CONFIG_$(PHASE_)REGULATOR_AXP) += axp_regulator.o +obj-$(CONFIG_$(PHASE_)REGULATOR_AXP_DRIVEVBUS) += axp_drivevbus.o +obj-$(CONFIG_$(PHASE_)REGULATOR_AXP_USB_POWER) += axp_usb_power.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_DA9063) += da9063.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_MAX77663) += max77663_regulator.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_DM_REGULATOR_NPCM8XX) += npcm8xx_regulator.o -obj-$(CONFIG_$(XPL_)DM_PMIC_PFUZE100) += pfuze100.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_BD71837) += bd71837.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_PCA9450) += pca9450.o -obj-$(CONFIG_$(XPL_)REGULATOR_PWM) += pwm_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_FAN53555) += fan53555.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_COMMON) += regulator_common.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_FIXED) += fixed.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_GPIO) += gpio-regulator.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_PFUZE100) += pfuze100.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_BD71837) += bd71837.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PCA9450) += pca9450.o +obj-$(CONFIG_$(PHASE_)REGULATOR_PWM) += pwm_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_FAN53555) += fan53555.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_COMMON) += regulator_common.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_FIXED) += fixed.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_GPIO) += gpio-regulator.o obj-$(CONFIG_DM_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o +obj-$(CONFIG_DM_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus_regulator.o obj-$(CONFIG_$(PHASE_)REGULATOR_RK8XX) += rk8xx.o obj-$(CONFIG_DM_REGULATOR_S2MPS11) += s2mps11_regulator.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_PALMAS) += palmas_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_PBIAS) += pbias_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_LP873X) += lp873x_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PALMAS) += palmas_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PBIAS) += pbias_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_LP873X) += lp873x_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_LP87565) += lp87565_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_TPS65911) += tps65911_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_TPS65911) += tps65911_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_TPS6287X) += tps6287x_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_TPS80031) += tps80031_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_STPMIC1) += stpmic1.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_TPS6287X) += tps6287x_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_TPS80031) += tps80031_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_STPMIC1) += stpmic1.o obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_ANATOP) += anatop_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_ANATOP) += anatop_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS65219) += tps65219_regulator.o +obj-$(CONFIG_REGULATOR_RZG2L_USBPHY) += rzg2l-usbphy-regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_CPCAP) += cpcap_regulator.o diff --git a/drivers/power/regulator/axp_drivevbus.c b/drivers/power/regulator/axp_drivevbus.c new file mode 100644 index 00000000000..c463de8786b --- /dev/null +++ b/drivers/power/regulator/axp_drivevbus.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <dm.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#define AXP_VBUS_IPSOUT 0x30 +#define AXP_VBUS_IPSOUT_DRIVEBUS BIT(2) +#define AXP_MISC_CTRL 0x8f +#define AXP_MISC_CTRL_N_VBUSEN_FUNC BIT(4) + +static int axp_drivevbus_get_enable(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev->parent, AXP_VBUS_IPSOUT); + if (ret < 0) + return ret; + + return !!(ret & AXP_VBUS_IPSOUT_DRIVEBUS); +} + +static int axp_drivevbus_set_enable(struct udevice *dev, bool enable) +{ + return pmic_clrsetbits(dev->parent, AXP_VBUS_IPSOUT, + AXP_VBUS_IPSOUT_DRIVEBUS, + enable ? AXP_VBUS_IPSOUT_DRIVEBUS : 0); +} + +static const struct dm_regulator_ops axp_drivevbus_ops = { + .get_enable = axp_drivevbus_get_enable, + .set_enable = axp_drivevbus_set_enable, +}; + +static int axp_drivevbus_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_plat = dev_get_uclass_plat(dev); + int ret; + + uc_plat->type = REGULATOR_TYPE_FIXED; + + if (dev_read_bool(dev->parent, "x-powers,drive-vbus-en")) { + ret = pmic_clrsetbits(dev->parent, AXP_MISC_CTRL, + AXP_MISC_CTRL_N_VBUSEN_FUNC, 0); + if (ret) + return ret; + } + + return 0; +} + +U_BOOT_DRIVER(axp_drivevbus) = { + .name = "axp_drivevbus", + .id = UCLASS_REGULATOR, + .probe = axp_drivevbus_probe, + .ops = &axp_drivevbus_ops, +}; diff --git a/drivers/power/regulator/cpcap_regulator.c b/drivers/power/regulator/cpcap_regulator.c new file mode 100644 index 00000000000..04cd6651374 --- /dev/null +++ b/drivers/power/regulator/cpcap_regulator.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/cpcap.h> +#include <linux/delay.h> +#include <linux/err.h> + +/* CPCAP_REG_ASSIGN2 bits - Resource Assignment 2 */ +#define CPCAP_BIT_VSDIO_SEL BIT(15) +#define CPCAP_BIT_VDIG_SEL BIT(14) +#define CPCAP_BIT_VCAM_SEL BIT(13) +#define CPCAP_BIT_SW6_SEL BIT(12) +#define CPCAP_BIT_SW5_SEL BIT(11) +#define CPCAP_BIT_SW4_SEL BIT(10) +#define CPCAP_BIT_SW3_SEL BIT(9) +#define CPCAP_BIT_SW2_SEL BIT(8) +#define CPCAP_BIT_SW1_SEL BIT(7) + +/* CPCAP_REG_ASSIGN3 bits - Resource Assignment 3 */ +#define CPCAP_BIT_VUSBINT2_SEL BIT(15) +#define CPCAP_BIT_VUSBINT1_SEL BIT(14) +#define CPCAP_BIT_VVIB_SEL BIT(13) +#define CPCAP_BIT_VWLAN1_SEL BIT(12) +#define CPCAP_BIT_VRF1_SEL BIT(11) +#define CPCAP_BIT_VHVIO_SEL BIT(10) +#define CPCAP_BIT_VDAC_SEL BIT(9) +#define CPCAP_BIT_VUSB_SEL BIT(8) +#define CPCAP_BIT_VSIM_SEL BIT(7) +#define CPCAP_BIT_VRFREF_SEL BIT(6) +#define CPCAP_BIT_VPLL_SEL BIT(5) +#define CPCAP_BIT_VFUSE_SEL BIT(4) +#define CPCAP_BIT_VCSI_SEL BIT(3) +#define CPCAP_BIT_SPARE_14_2 BIT(2) +#define CPCAP_BIT_VWLAN2_SEL BIT(1) +#define CPCAP_BIT_VRF2_SEL BIT(0) +#define CPCAP_BIT_NONE 0 + +/* CPCAP_REG_ASSIGN4 bits - Resource Assignment 4 */ +#define CPCAP_BIT_VAUDIO_SEL BIT(0) + +/* + * Off mode configuration bit. Used currently only by SW5 on omap4. There's + * the following comment in Motorola Linux kernel tree for it: + * + * When set in the regulator mode, the regulator assignment will be changed + * to secondary when the regulator is disabled. The mode will be set back to + * primary when the regulator is turned on. + */ +#define CPCAP_REG_OFF_MODE_SEC BIT(15) + +#define CPCAP_REG(_reg, _assignment_reg, _assignment_mask, _mode_mask, \ + _volt_mask, _volt_shft, _mode_val, _off_mode_val, _val_tbl, \ + _mode_cntr, _volt_trans_time, _turn_on_time, _bit_offset) { \ + .reg = CPCAP_REG_##_reg, \ + .assignment_reg = CPCAP_REG_##_assignment_reg, \ + .assignment_mask = CPCAP_BIT_##_assignment_mask, \ + .mode_mask = _mode_mask, \ + .volt_mask = _volt_mask, \ + .volt_shft = _volt_shft, \ + .mode_val = _mode_val, \ + .off_mode_val = _off_mode_val, \ + .val_tbl_sz = ARRAY_SIZE(_val_tbl), \ + .val_tbl = _val_tbl, \ + .mode_cntr = _mode_cntr, \ + .volt_trans_time = _volt_trans_time, \ + .turn_on_time = _turn_on_time, \ + .bit_offset_from_cpcap_lowest_voltage = _bit_offset, \ +} + +static const struct cpcap_regulator_data tegra20_regulators[CPCAP_REGULATORS_COUNT] = { + /* BUCK */ + [CPCAP_SW1] = CPCAP_REG(S1C1, ASSIGN2, SW1_SEL, 0x6f00, 0x007f, + 0, 0x6800, 0, sw1_val_tbl, 0, 0, 1500, 0x0c), + [CPCAP_SW2] = CPCAP_REG(S2C1, ASSIGN2, SW2_SEL, 0x6f00, 0x007f, + 0, 0x4804, 0, sw2_sw4_val_tbl, 0, 0, 1500, 0x18), + [CPCAP_SW3] = CPCAP_REG(S3C, ASSIGN2, SW3_SEL, 0x0578, 0x0003, + 0, 0x043c, 0, sw3_val_tbl, 0, 0, 0, 0), + [CPCAP_SW4] = CPCAP_REG(S4C1, ASSIGN2, SW4_SEL, 0x6f00, 0x007f, + 0, 0x4909, 0, sw2_sw4_val_tbl, 0, 0, 1500, 0x18), + [CPCAP_SW5] = CPCAP_REG(S5C, ASSIGN2, SW5_SEL, 0x0028, 0x0000, + 0, 0x0020, 0, sw5_val_tbl, 0, 0, 1500, 0), + [CPCAP_SW6] = CPCAP_REG(S6C, ASSIGN2, SW6_SEL, 0x0000, 0x0000, + 0, 0, 0, unknown_val_tbl, 0, 0, 0, 0), + /* LDO */ + [CPCAP_VCAM] = CPCAP_REG(VCAMC, ASSIGN2, VCAM_SEL, 0x0087, 0x0030, + 4, 0x7, 0, vcam_val_tbl, 0, 420, 1000, 0), + [CPCAP_VCSI] = CPCAP_REG(VCSIC, ASSIGN3, VCSI_SEL, 0x0047, 0x0010, + 4, 0x7, 0, vcsi_val_tbl, 0, 350, 1000, 0), + [CPCAP_VDAC] = CPCAP_REG(VDACC, ASSIGN3, VDAC_SEL, 0x0087, 0x0030, + 4, 0x0, 0, vdac_val_tbl, 0, 420, 1000, 0), + [CPCAP_VDIG] = CPCAP_REG(VDIGC, ASSIGN2, VDIG_SEL, 0x0087, 0x0030, + 4, 0x0, 0, vdig_val_tbl, 0, 420, 1000, 0), + [CPCAP_VFUSE] = CPCAP_REG(VFUSEC, ASSIGN3, VFUSE_SEL, 0x00a0, 0x000f, + 0, 0x0, 0, vfuse_val_tbl, 0, 420, 1000, 0), + [CPCAP_VHVIO] = CPCAP_REG(VHVIOC, ASSIGN3, VHVIO_SEL, 0x0017, 0x0000, + 0, 0x2, 0, vhvio_val_tbl, 0, 0, 1000, 0), + [CPCAP_VSDIO] = CPCAP_REG(VSDIOC, ASSIGN2, VSDIO_SEL, 0x0087, 0x0038, + 3, 0x2, 0, vsdio_val_tbl, 0, 420, 1000, 0), + [CPCAP_VPLL] = CPCAP_REG(VPLLC, ASSIGN3, VPLL_SEL, 0x0047, 0x0018, + 3, 0x1, 0, vpll_val_tbl, 0, 420, 100, 0), + [CPCAP_VRF1] = CPCAP_REG(VRF1C, ASSIGN3, VRF1_SEL, 0x00ac, 0x0002, + 1, 0x0, 0, vrf1_val_tbl, 0, 10, 1000, 0), + [CPCAP_VRF2] = CPCAP_REG(VRF2C, ASSIGN3, VRF2_SEL, 0x0023, 0x0008, + 3, 0x0, 0, vrf2_val_tbl, 0, 10, 1000, 0), + [CPCAP_VRFREF] = CPCAP_REG(VRFREFC, ASSIGN3, VRFREF_SEL, 0x0023, 0x0008, + 3, 0x0, 0, vrfref_val_tbl, 0, 420, 100, 0), + [CPCAP_VWLAN1] = CPCAP_REG(VWLAN1C, ASSIGN3, VWLAN1_SEL, 0x0047, 0x0010, + 4, 0x0, 0, vwlan1_val_tbl, 0, 420, 1000, 0), + [CPCAP_VWLAN2] = CPCAP_REG(VWLAN2C, ASSIGN3, VWLAN2_SEL, 0x020c, 0x00c0, + 6, 0xd, 0, vwlan2_val_tbl, 0, 420, 1000, 0), + [CPCAP_VSIM] = CPCAP_REG(VSIMC, ASSIGN3, NONE, 0x0023, 0x0008, + 3, 0x0, 0, vsim_val_tbl, 0, 420, 1000, 0), + [CPCAP_VSIMCARD] = CPCAP_REG(VSIMC, ASSIGN3, NONE, 0x1e80, 0x0008, + 3, 0x1E00, 0, vsimcard_val_tbl, 0, 420, 1000, 0), + [CPCAP_VVIB] = CPCAP_REG(VVIBC, ASSIGN3, VVIB_SEL, 0x0001, 0x000c, + 2, 0x1, 0, vvib_val_tbl, 0, 500, 500, 0), + [CPCAP_VUSB] = CPCAP_REG(VUSBC, ASSIGN3, VUSB_SEL, 0x011c, 0x0040, + 6, 0xc, 0, vusb_val_tbl, 0, 0, 1000, 0), + [CPCAP_VAUDIO] = CPCAP_REG(VAUDIOC, ASSIGN4, VAUDIO_SEL, 0x0016, 0x0001, + 0, 0x5, 0, vaudio_val_tbl, 0, 0, 1000, 0), +}; + +static int cpcap_regulator_get_value(struct udevice *dev) +{ + const struct cpcap_regulator_data *regulator = + &tegra20_regulators[dev->driver_data]; + int value, volt_shift = regulator->volt_shft; + + value = pmic_reg_read(dev->parent, regulator->reg); + if (value < 0) + return value; + + if (!(value & regulator->mode_mask)) + return 0; + + value &= regulator->volt_mask; + value -= regulator->bit_offset_from_cpcap_lowest_voltage; + + return regulator->val_tbl[value >> volt_shift]; +} + +static int cpcap_regulator_set_value(struct udevice *dev, int uV) +{ + const struct cpcap_regulator_data *regulator = + &tegra20_regulators[dev->driver_data]; + int value, ret, volt_shift = regulator->volt_shft; + + if (dev->driver_data == CPCAP_VRF1) { + if (uV > 2500000) + value = 0; + else + value = regulator->volt_mask; + } else { + for (value = 0; value < regulator->val_tbl_sz; value++) + if (regulator->val_tbl[value] >= uV) + break; + + if (value >= regulator->val_tbl_sz) + value = regulator->val_tbl_sz; + + value <<= volt_shift; + value += regulator->bit_offset_from_cpcap_lowest_voltage; + } + + ret = pmic_clrsetbits(dev->parent, regulator->reg, regulator->volt_mask, + value); + if (ret) + return ret; + + if (regulator->volt_trans_time) + udelay(regulator->volt_trans_time); + + return 0; +} + +static int cpcap_regulator_get_enable(struct udevice *dev) +{ + const struct cpcap_regulator_data *regulator = + &tegra20_regulators[dev->driver_data]; + int value; + + value = pmic_reg_read(dev->parent, regulator->reg); + if (value < 0) + return value; + + return (value & regulator->mode_mask) ? 1 : 0; +} + +static int cpcap_regulator_set_enable(struct udevice *dev, bool enable) +{ + const struct cpcap_regulator_data *regulator = + &tegra20_regulators[dev->driver_data]; + int ret; + + if (enable) { + ret = pmic_clrsetbits(dev->parent, regulator->reg, regulator->mode_mask, + regulator->mode_val); + if (ret) + return ret; + } + + if (regulator->mode_val & CPCAP_REG_OFF_MODE_SEC) { + ret = pmic_clrsetbits(dev->parent, regulator->assignment_reg, + regulator->assignment_mask, + enable ? 0 : regulator->assignment_mask); + if (ret) + return ret; + } + + if (!enable) { + ret = pmic_clrsetbits(dev->parent, regulator->reg, regulator->mode_mask, + regulator->off_mode_val); + if (ret) + return ret; + } + + if (regulator->turn_on_time) + udelay(regulator->turn_on_time); + + return 0; +} + +static int cpcap_regulator_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = dev_get_uclass_plat(dev); + int id; + + for (id = 0; id < CPCAP_REGULATORS_COUNT; id++) + if (cpcap_regulator_to_name[id]) + if (!strcmp(dev->name, cpcap_regulator_to_name[id])) + break; + + switch (id) { + case CPCAP_SW1 ... CPCAP_SW6: + uc_pdata->type = REGULATOR_TYPE_BUCK; + break; + + case CPCAP_VCAM ... CPCAP_VAUDIO: + uc_pdata->type = REGULATOR_TYPE_LDO; + break; + + default: + log_err("CPCAP: Invalid regulator ID\n"); + return -ENODEV; + } + + dev->driver_data = id; + return 0; +} + +static const struct dm_regulator_ops cpcap_regulator_ops = { + .get_value = cpcap_regulator_get_value, + .set_value = cpcap_regulator_set_value, + .get_enable = cpcap_regulator_get_enable, + .set_enable = cpcap_regulator_set_enable, +}; + +U_BOOT_DRIVER(cpcap_sw) = { + .name = CPCAP_SW_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &cpcap_regulator_ops, + .probe = cpcap_regulator_probe, +}; + +U_BOOT_DRIVER(cpcap_ldo) = { + .name = CPCAP_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &cpcap_regulator_ops, + .probe = cpcap_regulator_probe, +}; diff --git a/drivers/power/regulator/qcom-rpmh-regulator.c b/drivers/power/regulator/qcom-rpmh-regulator.c index cd2b1a654c1..954deca5ed7 100644 --- a/drivers/power/regulator/qcom-rpmh-regulator.c +++ b/drivers/power/regulator/qcom-rpmh-regulator.c @@ -466,6 +466,25 @@ static const struct rpmh_vreg_hw_data pmic5_nldo515 = { .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_ldo), }; +static const struct rpmh_vreg_hw_data pmic5_ftsmps527 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), + .n_voltages = 215, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_smps), +}; + +static const struct rpmh_vreg_hw_data pmic5_pldo515_mv = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(1800000, 0, 187, 8000), + .n_voltages = 188, + .hpm_min_load_uA = 10000, + .pmic_mode_map = pmic_mode_map_pmic5_ldo, + .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_ldo), +}; + #define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \ { \ .name = _name, \ @@ -558,6 +577,28 @@ static const struct rpmh_vreg_init_data pmc8380_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pmm8654au_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps527, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps527, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps527, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps527, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps527, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps527, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps527, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps527, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps527, "vdd-s9"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-s9"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2-l3"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l2-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-s9"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo515, "vdd-s9"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo515, "vdd-l6-l7"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo515, "vdd-l6-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo515_mv, "vdd-l8-l9"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l8-l9"), + {} +}; + /* probe an individual regulator */ static int rpmh_regulator_probe(struct udevice *dev) { @@ -688,6 +729,10 @@ static const struct udevice_id rpmh_regulator_ids[] = { .compatible = "qcom,pmc8380-rpmh-regulators", .data = (ulong)pmc8380_vreg_data, }, + { + .compatible = "qcom,pmm8654au-rpmh-regulators", + .data = (ulong)pmm8654au_vreg_data, + }, { /* sentinal */ }, }; diff --git a/drivers/power/regulator/qcom_usb_vbus_regulator.c b/drivers/power/regulator/qcom_usb_vbus_regulator.c new file mode 100644 index 00000000000..2d58ef5e111 --- /dev/null +++ b/drivers/power/regulator/qcom_usb_vbus_regulator.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2025, Linaro Limited + */ +#define pr_fmt(fmt) "qcom_usb_vbus: " fmt + +#include <bitfield.h> +#include <errno.h> +#include <dm.h> +#include <fdtdec.h> +#include <log.h> +#include <asm/gpio.h> +#include <linux/bitops.h> +#include <linux/printk.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#define CMD_OTG 0x50 +#define OTG_EN BIT(0) +// The 0 bit in this register's bit field is undocumented +#define OTG_CFG 0x56 +#define OTG_EN_SRC_CFG BIT(1) + +struct qcom_usb_vbus_priv { + phys_addr_t base; +}; + +static int qcom_usb_vbus_regulator_of_to_plat(struct udevice *dev) +{ + struct qcom_usb_vbus_priv *priv = dev_get_priv(dev); + + priv->base = dev_read_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + return 0; +} + +static int qcom_usb_vbus_regulator_get_enable(struct udevice *dev) +{ + struct qcom_usb_vbus_priv *priv = dev_get_priv(dev); + int otg_en_reg = priv->base + CMD_OTG; + int ret; + + ret = pmic_reg_read(dev->parent, otg_en_reg); + if (ret < 0) + log_err("failed to read usb vbus: %d\n", ret); + else + ret &= OTG_EN; + + return ret; +} + +static int qcom_usb_vbus_regulator_set_enable(struct udevice *dev, bool enable) +{ + struct qcom_usb_vbus_priv *priv = dev_get_priv(dev); + int otg_en_reg = priv->base + CMD_OTG; + int ret; + + if (enable) { + ret = pmic_clrsetbits(dev->parent, otg_en_reg, 0, OTG_EN); + if (ret < 0) { + log_err("error enabling: %d\n", ret); + return ret; + } + } else { + ret = pmic_clrsetbits(dev->parent, otg_en_reg, OTG_EN, 0); + if (ret < 0) { + log_err("error disabling: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int qcom_usb_vbus_regulator_probe(struct udevice *dev) +{ + struct qcom_usb_vbus_priv *priv = dev_get_priv(dev); + int otg_cfg_reg = priv->base + OTG_CFG; + int ret; + + /* Disable HW logic for VBUS enable */ + ret = pmic_clrsetbits(dev->parent, otg_cfg_reg, OTG_EN_SRC_CFG, 0); + if (ret < 0) { + log_err("error setting EN_SRC_CFG: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct dm_regulator_ops qcom_usb_vbus_regulator_ops = { + .get_enable = qcom_usb_vbus_regulator_get_enable, + .set_enable = qcom_usb_vbus_regulator_set_enable, +}; + +static const struct udevice_id qcom_usb_vbus_regulator_ids[] = { + { .compatible = "qcom,pm8150b-vbus-reg"}, + { }, +}; + +U_BOOT_DRIVER(qcom_usb_vbus_regulator) = { + .name = "qcom-usb-vbus-regulator", + .id = UCLASS_REGULATOR, + .of_match = qcom_usb_vbus_regulator_ids, + .of_to_plat = qcom_usb_vbus_regulator_of_to_plat, + .ops = &qcom_usb_vbus_regulator_ops, + .probe = qcom_usb_vbus_regulator_probe, + .priv_auto = sizeof(struct qcom_usb_vbus_priv), +}; diff --git a/drivers/power/regulator/rzg2l-usbphy-regulator.c b/drivers/power/regulator/rzg2l-usbphy-regulator.c new file mode 100644 index 00000000000..451f04c140e --- /dev/null +++ b/drivers/power/regulator/rzg2l-usbphy-regulator.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 Renesas Electronics Corporation + */ + +#include <asm/io.h> +#include <dm.h> +#include <power/regulator.h> +#include <renesas/rzg2l-usbphy.h> + +#define VBENCTL 0x03c +#define VBENCTL_VBUS_SEL BIT(0) + +static int rzg2l_usbphy_regulator_set_enable(struct udevice *dev, bool enable) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev->parent); + + if (enable) + clrbits_le32(priv->regs + VBENCTL, VBENCTL_VBUS_SEL); + else + setbits_le32(priv->regs + VBENCTL, VBENCTL_VBUS_SEL); + + return 0; +} + +static int rzg2l_usbphy_regulator_get_enable(struct udevice *dev) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev->parent); + + return !!readl(priv->regs + VBENCTL) & VBENCTL_VBUS_SEL; +} + +static const struct dm_regulator_ops rzg2l_usbphy_regulator_ops = { + .get_enable = rzg2l_usbphy_regulator_get_enable, + .set_enable = rzg2l_usbphy_regulator_set_enable, +}; + +U_BOOT_DRIVER(rzg2l_usbphy_regulator) = { + .name = "rzg2l_usbphy_regulator", + .id = UCLASS_REGULATOR, + .ops = &rzg2l_usbphy_regulator_ops, +}; diff --git a/drivers/power/regulator/scmi_regulator.c b/drivers/power/regulator/scmi_regulator.c index 99f6506f162..79db1a6a8aa 100644 --- a/drivers/power/regulator/scmi_regulator.c +++ b/drivers/power/regulator/scmi_regulator.c @@ -175,12 +175,19 @@ U_BOOT_DRIVER(scmi_regulator) = { static int scmi_regulator_bind(struct udevice *dev) { struct driver *drv; + ofnode regul_node; ofnode node; int ret; + regul_node = ofnode_find_subnode(dev_ofnode(dev), "regulators"); + if (!ofnode_valid(regul_node)) { + dev_err(dev, "no regulators node\n"); + return -ENXIO; + } + drv = DM_DRIVER_GET(scmi_regulator); - ofnode_for_each_subnode(node, dev_ofnode(dev)) { + ofnode_for_each_subnode(node, regul_node) { ret = device_bind(dev, drv, ofnode_get_name(node), NULL, node, NULL); if (ret) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6e79868d0ef..de312656746 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -105,6 +105,14 @@ config PWM_TEGRA 32KHz clock is supported by the driver but the duty cycle is configurable. +config PWM_STM32 + bool "Enable support for STM32 PWM" + depends on DM_PWM && MFD_STM32_TIMERS + help + This enables PWM driver for STMicroelectronics STM32 pulse width + modulation. It uses STM32 timer devices that can have up to 4 output + channels, with complementary outputs and configurable polarity. + config PWM_SUNXI bool "Enable support for the Allwinner Sunxi PWM" depends on DM_PWM diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index e4d10c8dc3e..76305b93bc9 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -22,5 +22,6 @@ obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o +obj-$(CONFIG_PWM_STM32) += pwm-stm32.o obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o obj-$(CONFIG_PWM_TI_EHRPWM) += pwm-ti-ehrpwm.o diff --git a/drivers/pwm/pwm-mtk.c b/drivers/pwm/pwm-mtk.c index 5cf2eba2ba0..898e353b370 100644 --- a/drivers/pwm/pwm-mtk.c +++ b/drivers/pwm/pwm-mtk.c @@ -30,6 +30,7 @@ enum mtk_pwm_reg_ver { PWM_REG_V1, PWM_REG_V2, + PWM_REG_V3, }; static const unsigned int mtk_pwm_reg_offset_v1[] = { @@ -40,6 +41,10 @@ static const unsigned int mtk_pwm_reg_offset_v2[] = { 0x0080, 0x00c0, 0x0100, 0x0140, 0x0180, 0x01c0, 0x0200, 0x0240 }; +static const unsigned int mtk_pwm_reg_offset_v3[] = { + 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x600, 0x700, 0x0800 +}; + struct mtk_pwm_soc { unsigned int num_pwms; bool pwm45_fixup; @@ -60,6 +65,10 @@ static void mtk_pwm_w32(struct udevice *dev, uint channel, uint reg, uint val) u32 offset; switch (priv->soc->reg_ver) { + case PWM_REG_V3: + offset = mtk_pwm_reg_offset_v3[channel]; + break; + case PWM_REG_V2: offset = mtk_pwm_reg_offset_v2[channel]; break; @@ -203,6 +212,12 @@ static const struct mtk_pwm_soc mt7986_data = { .reg_ver = PWM_REG_V1, }; +static const struct mtk_pwm_soc mt7987_data = { + .num_pwms = 3, + .pwm45_fixup = false, + .reg_ver = PWM_REG_V3, +}; + static const struct mtk_pwm_soc mt7988_data = { .num_pwms = 8, .pwm45_fixup = false, @@ -215,6 +230,7 @@ static const struct udevice_id mtk_pwm_ids[] = { { .compatible = "mediatek,mt7629-pwm", .data = (ulong)&mt7629_data }, { .compatible = "mediatek,mt7981-pwm", .data = (ulong)&mt7981_data }, { .compatible = "mediatek,mt7986-pwm", .data = (ulong)&mt7986_data }, + { .compatible = "mediatek,mt7987-pwm", .data = (ulong)&mt7987_data }, { .compatible = "mediatek,mt7988-pwm", .data = (ulong)&mt7988_data }, { } }; diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c new file mode 100644 index 00000000000..5fa649b5903 --- /dev/null +++ b/drivers/pwm/pwm-stm32.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025, STMicroelectronics - All Rights Reserved + * Author: Cheick Traore <cheick.traore@foss.st.com> + * + * Originally based on the Linux kernel v6.10 drivers/pwm/pwm-stm32.c. + */ + +#include <div64.h> +#include <dm.h> +#include <pwm.h> +#include <asm/io.h> +#include <asm/arch/timers.h> +#include <dm/device_compat.h> +#include <linux/time.h> + +#define CCMR_CHANNEL_SHIFT 8 +#define CCMR_CHANNEL_MASK 0xFF + +struct stm32_pwm_priv { + bool have_complementary_output; + bool invert_polarity; +}; + +static u32 active_channels(struct stm32_timers_plat *plat) +{ + return readl(plat->base + TIM_CCER) & TIM_CCER_CCXE; +} + +static int stm32_pwm_set_config(struct udevice *dev, uint channel, + uint period_ns, uint duty_ns) +{ + struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev)); + struct stm32_timers_priv *priv = dev_get_priv(dev_get_parent(dev)); + unsigned long long prd, div, dty; + unsigned int prescaler = 0; + u32 ccmr, mask, shift; + + if (duty_ns > period_ns) + return -EINVAL; + + /* + * Period and prescaler values depends on clock rate + * First we need to find the minimal value for prescaler such that + * + * period_ns * clkrate + * ------------------------------ < max_arr + 1 + * NSEC_PER_SEC * (prescaler + 1) + * + * This equation is equivalent to + * + * period_ns * clkrate + * ---------------------------- < prescaler + 1 + * NSEC_PER_SEC * (max_arr + 1) + * + * Using integer division and knowing that the right hand side is + * integer, this is further equivalent to + * + * (period_ns * clkrate) // (NSEC_PER_SEC * (max_arr + 1)) ≤ prescaler + */ + + div = (unsigned long long)priv->rate * period_ns; + do_div(div, NSEC_PER_SEC); + prd = div; + + do_div(div, priv->max_arr + 1); + prescaler = div; + if (prescaler > MAX_TIM_PSC) + return -EINVAL; + + do_div(prd, prescaler + 1); + if (!prd) + return -EINVAL; + + /* + * All channels share the same prescaler and counter so when two + * channels are active at the same time we can't change them + */ + if (active_channels(plat) & ~(1 << channel * 4)) { + u32 psc, arr; + + psc = readl(plat->base + TIM_PSC); + arr = readl(plat->base + TIM_ARR); + if (psc != prescaler || arr != prd - 1) + return -EBUSY; + } + + writel(prescaler, plat->base + TIM_PSC); + writel(prd - 1, plat->base + TIM_ARR); + setbits_le32(plat->base + TIM_CR1, TIM_CR1_ARPE); + + /* Calculate the duty cycles */ + dty = prd * duty_ns; + do_div(dty, period_ns); + + writel(dty, plat->base + TIM_CCRx(channel + 1)); + + /* Configure output mode */ + shift = (channel & 0x1) * CCMR_CHANNEL_SHIFT; + ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift; + mask = CCMR_CHANNEL_MASK << shift; + if (channel < 2) + clrsetbits_le32(plat->base + TIM_CCMR1, mask, ccmr); + else + clrsetbits_le32(plat->base + TIM_CCMR2, mask, ccmr); + + setbits_le32(plat->base + TIM_BDTR, TIM_BDTR_MOE); + + return 0; +} + +static int stm32_pwm_set_enable(struct udevice *dev, uint channel, + bool enable) +{ + struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev)); + struct stm32_pwm_priv *priv = dev_get_priv(dev); + u32 mask; + + /* Enable channel */ + mask = TIM_CCER_CC1E << (channel * 4); + if (priv->have_complementary_output) + mask |= TIM_CCER_CC1NE << (channel * 4); + + if (enable) { + setbits_le32(plat->base + TIM_CCER, mask); + /* Make sure that registers are updated */ + setbits_le32(plat->base + TIM_EGR, TIM_EGR_UG); + /* Enable controller */ + setbits_le32(plat->base + TIM_CR1, TIM_CR1_CEN); + } else { + clrbits_le32(plat->base + TIM_CCER, mask); + /* When all channels are disabled, we can disable the controller */ + if (!active_channels(plat)) + clrbits_le32(plat->base + TIM_CR1, TIM_CR1_CEN); + } + + return 0; +} + +static int stm32_pwm_set_invert(struct udevice *dev, uint channel, + bool polarity) +{ + struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev)); + struct stm32_pwm_priv *priv = dev_get_priv(dev); + u32 mask; + + mask = TIM_CCER_CC1P << (channel * 4); + if (priv->have_complementary_output) + mask |= TIM_CCER_CC1NP << (channel * 4); + + clrsetbits_le32(plat->base + TIM_CCER, mask, polarity ? mask : 0); + + return 0; +} + +static void stm32_pwm_detect_complementary(struct udevice *dev) +{ + struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev)); + struct stm32_pwm_priv *priv = dev_get_priv(dev); + u32 ccer; + + /* + * If complementary bit doesn't exist writing 1 will have no + * effect so we can detect it. + */ + setbits_le32(plat->base + TIM_CCER, TIM_CCER_CC1NE); + ccer = readl(plat->base + TIM_CCER); + clrbits_le32(plat->base + TIM_CCER, TIM_CCER_CC1NE); + + priv->have_complementary_output = (ccer != 0); +} + +static int stm32_pwm_probe(struct udevice *dev) +{ + struct stm32_timers_priv *timer = dev_get_priv(dev_get_parent(dev)); + + if (timer->rate > 1000000000) { + dev_err(dev, "Clock freq too high (%lu)\n", timer->rate); + return -EINVAL; + } + + stm32_pwm_detect_complementary(dev); + + return 0; +} + +static const struct pwm_ops stm32_pwm_ops = { + .set_config = stm32_pwm_set_config, + .set_enable = stm32_pwm_set_enable, + .set_invert = stm32_pwm_set_invert, +}; + +static const struct udevice_id stm32_pwm_ids[] = { + { .compatible = "st,stm32-pwm" }, + { } +}; + +U_BOOT_DRIVER(stm32_pwm) = { + .name = "stm32_pwm", + .id = UCLASS_PWM, + .of_match = stm32_pwm_ids, + .ops = &stm32_pwm_ops, + .probe = stm32_pwm_probe, + .priv_auto = sizeof(struct stm32_pwm_priv), +}; diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig index 899d7585489..2a40b0c9f81 100644 --- a/drivers/ram/Kconfig +++ b/drivers/ram/Kconfig @@ -71,7 +71,7 @@ choice depends on K3_DDRSS prompt "K3 DDRSS Arch Support" - default K3_J721E_DDRSS if SOC_K3_J721E || SOC_K3_J721S2 || SOC_K3_J784S4 + default K3_J721E_DDRSS if SOC_K3_J721E || SOC_K3_J7200 || SOC_K3_J721S2 || SOC_K3_J784S4 default K3_AM64_DDRSS if SOC_K3_AM642 default K3_AM64_DDRSS if SOC_K3_AM625 default K3_AM62A_DDRSS if SOC_K3_AM62A7 || SOC_K3_AM62P5 || SOC_K3_J722S diff --git a/drivers/ram/renesas/dbsc5/dram.c b/drivers/ram/renesas/dbsc5/dram.c index 6f78afb0ab5..ca8a7fb4783 100644 --- a/drivers/ram/renesas/dbsc5/dram.c +++ b/drivers/ram/renesas/dbsc5/dram.c @@ -1325,6 +1325,11 @@ static const u32 PI_DARRAY3_1_CSx_Fx[CS_CNT][3] = { #define DBSC_DBACEN 0x200 #define DBSC_DBRFEN 0x204 #define DBSC_DBCMD 0x208 +#define DBSC_DBCMD_CMD_OPCODE_PD 0x8 +#define DBSC_DBCMD_CMD_OPCODE_MRW 0xe +#define DBSC_DBCMD_CMD_OPCODE_MRR 0xf +#define DBSC_DBCMD_CMD_CHANNEL_ALL 0x8 +#define DBSC_DBCMD_CMD_RANK_ALL 0x4 #define DBSC_DBWAIT 0x210 #define DBSC_DBBL 0x400 #define DBSC_DBBLA 0x400 @@ -1736,24 +1741,20 @@ static void dbsc5_reg_write(void __iomem *addr, u32 data) } /** - * dbsc5_reg_write() - DRAM Command Write Access + * dbsc5_wait_dbwait() - DRAM Command Wait Access Completion * @dev: DBSC5 device - * @cmd DRAM command. * - * First, execute the dummy read to DBSC_DBCMD. - * Confirm that no DBSC command operation is in progress 0. - * Write the contents of the command to be sent to DRAM. + * Wait for DRAM access completion. This is used before sending a command + * to the DRAM to assure no other command is in flight already, or while + * waiting for MRR command to complete. */ -static void dbsc5_send_dbcmd2(struct udevice *dev, u32 cmd) +static void dbsc5_wait_dbwait(struct udevice *dev) { struct renesas_dbsc5_dram_priv *priv = dev_get_priv(dev); void __iomem *regs_dbsc_d = priv->regs + DBSC5_DBSC_D_OFFSET; u32 val; int ret; - /* dummy read */ - readl(regs_dbsc_d + DBSC_DBCMD); - ret = readl_poll_timeout(regs_dbsc_d + DBSC_DBWAIT, val, ((val & BIT(0)) == 0), 1000000); if (ret < 0) { printf("%s DBWAIT bit 0 timeout\n", __func__); @@ -1765,6 +1766,32 @@ static void dbsc5_send_dbcmd2(struct udevice *dev, u32 cmd) printf("%s DBWAIT + 0x4000 bit 0 timeout\n", __func__); hang(); } +} + +/** + * dbsc5_send_dbcmd2() - DRAM Command Write Access + * @dev: DBSC5 device + * @opcode DRAM controller opcode + * @channel DRAM controller channel (0..3) + * @rank DRAM controller rank (0..1) + * @arg Command and argument bits (command specific encoding) + * + * First, execute the dummy read to DBSC_DBCMD. + * Confirm that no DBSC command operation is in progress 0. + * Write the contents of the command to be sent to DRAM. + */ +static void dbsc5_send_dbcmd2(struct udevice *dev, const u8 opcode, + const u8 channel, const u8 rank, + const u16 arg) +{ + const u32 cmd = (opcode << 24) | (channel << 20) | (rank << 16) | arg; + struct renesas_dbsc5_dram_priv *priv = dev_get_priv(dev); + void __iomem *regs_dbsc_d = priv->regs + DBSC5_DBSC_D_OFFSET; + + /* dummy read */ + readl(regs_dbsc_d + DBSC_DBCMD); + + dbsc5_wait_dbwait(dev); dbsc5_reg_write(regs_dbsc_d + DBSC_DBCMD, cmd); } @@ -2950,10 +2977,14 @@ static u32 dbsc5_pi_training(struct udevice *dev) writel(0x21, regs_dbsc_d + DBSC_DBDFICNT(ch)); /* Dummy PDE */ - dbsc5_send_dbcmd2(dev, 0x8840000); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_PD, + DBSC_DBCMD_CMD_CHANNEL_ALL, + DBSC_DBCMD_CMD_RANK_ALL, 0); /* PDX */ - dbsc5_send_dbcmd2(dev, 0x8840001); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_PD, + DBSC_DBCMD_CMD_CHANNEL_ALL, + DBSC_DBCMD_CMD_RANK_ALL, 1); /* Wait init_complete */ for (retry = 0; retry < retry_max; retry++) { @@ -4085,25 +4116,27 @@ static u32 dbsc5_read_training(struct udevice *dev) } /** - * dbsc5_ddr_register_set() - DDR mode register setting + * dbsc5_ddr_register_mr28_set() - DDR mode register MR28 set * @dev: DBSC5 device * * Set the mode register 28 of the SDRAM. * ZQ Mode: Command-Based ZQ Calibration * ZQ interval: Background Cal Interval < 64ms */ -static void dbsc5_ddr_register_set(struct udevice *dev) +static void dbsc5_ddr_register_mr28_set(struct udevice *dev) { - dbsc5_send_dbcmd2(dev, 0xE841C24); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRW, + DBSC_DBCMD_CMD_CHANNEL_ALL, + DBSC_DBCMD_CMD_RANK_ALL, (28 << 8) | 0x24); } /** - * dbsc5_ddr_register_read() - DDR mode register read + * dbsc5_ddr_register_mr27_mr57_read() - DDR mode register MR27/MR57 read * @dev: DBSC5 device * * Set the mode register 27 and 57 of the SDRAM. */ -static void dbsc5_ddr_register_read(struct udevice *dev) +static void dbsc5_ddr_register_mr27_mr57_read(struct udevice *dev) { struct renesas_dbsc5_dram_priv *priv = dev_get_priv(dev); @@ -4111,17 +4144,21 @@ static void dbsc5_ddr_register_read(struct udevice *dev) return; /* MR27 rank0 */ - dbsc5_send_dbcmd2(dev, 0xF801B00); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRR, + DBSC_DBCMD_CMD_CHANNEL_ALL, 0, 27 << 8); /* MR57 rank0 */ - dbsc5_send_dbcmd2(dev, 0xF803900); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRR, + DBSC_DBCMD_CMD_CHANNEL_ALL, 0, 57 << 8); if (!priv->ch_have_this_cs[1]) return; /* MR27 rank1 */ - dbsc5_send_dbcmd2(dev, 0xF811B00); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRR, + DBSC_DBCMD_CMD_CHANNEL_ALL, 1, 27 << 8); /* MR57 rank1 */ - dbsc5_send_dbcmd2(dev, 0xF813900); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRR, + DBSC_DBCMD_CMD_CHANNEL_ALL, 1, 57 << 8); } /** @@ -4200,8 +4237,8 @@ static u32 dbsc5_init_ddr(struct udevice *dev) dbsc5_dbsc_regset(dev); /* Frequency selection change (F1->F2) */ - dbsc5_ddr_setval_all_ch(dev, PHY_FREQ_SEL_INDEX, 0x1); - dbsc5_ddr_setval_all_ch(dev, PHY_FREQ_SEL_MULTICAST_EN, 0x0); + dbsc5_ddr_setval_all_ch(dev, PHY_FREQ_SEL_INDEX, 0x1); + dbsc5_ddr_setval_all_ch(dev, PHY_FREQ_SEL_MULTICAST_EN, 0x0); /* dfi_init_start (start ddrphy) & execute pi_training */ phytrainingok = dbsc5_pi_training(dev); @@ -4278,10 +4315,10 @@ static u32 dbsc5_init_ddr(struct udevice *dev) /* setup DDR mode registers */ /* MRS */ - dbsc5_ddr_register_set(dev); + dbsc5_ddr_register_mr28_set(dev); /* MRR */ - dbsc5_ddr_register_read(dev); + dbsc5_ddr_register_mr27_mr57_read(dev); /* training complete, setup DBSC */ dbsc5_dbsc_regset_post(dev); @@ -4296,10 +4333,13 @@ static u32 dbsc5_init_ddr(struct udevice *dev) /** * dbsc5_get_board_data() - Obtain board specific DRAM configuration + * @dev: DBSC5 device + * @modemr0: MODEMR0 register content * * Return board specific DRAM configuration structure pointer. */ -__weak const struct renesas_dbsc5_board_config *dbsc5_get_board_data(void) +__weak const struct renesas_dbsc5_board_config * +dbsc5_get_board_data(struct udevice *dev, const u32 modemr0) { return &renesas_v4h_dbsc5_board_config; } @@ -4335,7 +4375,7 @@ static int renesas_dbsc5_dram_probe(struct udevice *dev) u32 ch, cs; /* Get board data */ - priv->dbsc5_board_config = dbsc5_get_board_data(); + priv->dbsc5_board_config = dbsc5_get_board_data(dev, modemr0); priv->ddr_phyvalid = (u32)(priv->dbsc5_board_config->bdcfg_phyvalid); priv->max_density = 0; priv->cpg_regs = (void __iomem *)ofnode_get_addr(cnode); diff --git a/drivers/ram/rockchip/Makefile b/drivers/ram/rockchip/Makefile index 36dc0500dab..fd94aad0cd4 100644 --- a/drivers/ram/rockchip/Makefile +++ b/drivers/ram/rockchip/Makefile @@ -13,7 +13,9 @@ obj-$(CONFIG_ROCKCHIP_RK3288) = sdram_rk3288.o obj-$(CONFIG_ROCKCHIP_RK3308) = sdram_rk3308.o obj-$(CONFIG_ROCKCHIP_RK3328) = sdram_rk3328.o sdram_pctl_px30.o sdram_phy_px30.o obj-$(CONFIG_ROCKCHIP_RK3399) += sdram_rk3399.o +obj-$(CONFIG_ROCKCHIP_RK3528) += sdram_rk3528.o obj-$(CONFIG_ROCKCHIP_RK3568) += sdram_rk3568.o +obj-$(CONFIG_ROCKCHIP_RK3576) += sdram_rk3576.o obj-$(CONFIG_ROCKCHIP_RK3588) += sdram_rk3588.o obj-$(CONFIG_ROCKCHIP_RV1126) += sdram_rv1126.o sdram_pctl_px30.o obj-$(CONFIG_ROCKCHIP_SDRAM_COMMON) += sdram_common.o diff --git a/drivers/ram/rockchip/sdram_rk3528.c b/drivers/ram/rockchip/sdram_rk3528.c new file mode 100644 index 00000000000..89d325bea66 --- /dev/null +++ b/drivers/ram/rockchip/sdram_rk3528.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright Contributors to the U-Boot project. + +#include <dm.h> +#include <ram.h> +#include <asm/arch-rockchip/sdram.h> + +#define PMUGRF_BASE 0xff370000 +#define OS_REG18_REG 0x248 + +static int rk3528_dmc_get_info(struct udevice *dev, struct ram_info *info) +{ + info->base = CFG_SYS_SDRAM_BASE; + info->size = rockchip_sdram_size(PMUGRF_BASE + OS_REG18_REG); + + return 0; +} + +static struct ram_ops rk3528_dmc_ops = { + .get_info = rk3528_dmc_get_info, +}; + +static const struct udevice_id rk3528_dmc_ids[] = { + { .compatible = "rockchip,rk3528-dmc" }, + { } +}; + +U_BOOT_DRIVER(rockchip_rk3528_dmc) = { + .name = "rockchip_rk3528_dmc", + .id = UCLASS_RAM, + .of_match = rk3528_dmc_ids, + .ops = &rk3528_dmc_ops, +}; diff --git a/drivers/ram/rockchip/sdram_rk3576.c b/drivers/ram/rockchip/sdram_rk3576.c new file mode 100644 index 00000000000..5a66032ef8f --- /dev/null +++ b/drivers/ram/rockchip/sdram_rk3576.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2024 Rockchip Electronics Co., Ltd. + */ + +#include <dm.h> +#include <ram.h> +#include <asm/arch-rockchip/sdram.h> + +#define PMU1GRF_BASE 0x26026000 +#define OS_REG2_REG 0x208 + +static int rk3576_dmc_get_info(struct udevice *dev, struct ram_info *info) +{ + info->base = CFG_SYS_SDRAM_BASE; + info->size = rockchip_sdram_size(PMU1GRF_BASE + OS_REG2_REG); + + return 0; +} + +static struct ram_ops rk3576_dmc_ops = { + .get_info = rk3576_dmc_get_info, +}; + +static const struct udevice_id rk3576_dmc_ids[] = { + { .compatible = "rockchip,rk3576-dmc" }, + { } +}; + +U_BOOT_DRIVER(rockchip_rk3576_dmc) = { + .name = "rockchip_rk3576_dmc", + .id = UCLASS_RAM, + .of_match = rk3576_dmc_ids, + .ops = &rk3576_dmc_ops, +}; diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 2790b168b19..a3ecea05e20 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -13,6 +13,7 @@ config REMOTEPROC depends on DM # Please keep the configuration alphabetically sorted. + config K3_SYSTEM_CONTROLLER bool "Support for TI' K3 System Controller" select REMOTEPROC @@ -22,6 +23,16 @@ config K3_SYSTEM_CONTROLLER help Say 'y' here to add support for TI' K3 System Controller. +config REMOTEPROC_ADI_SC5XX + bool "Support for ADI SC5xx SHARC cores" + select REMOTEPROC + depends on DM + depends on ARCH_SC5XX + depends on SYSCON + help + Say 'y' here to add support for loading code onto SHARC cores in + an ADSP-SC5xx SoC from Analog Devices + config REMOTEPROC_RENESAS_APMU bool "Support for Renesas R-Car Gen4 APMU start of CR52 processor" select REMOTEPROC @@ -55,6 +66,7 @@ config REMOTEPROC_TI_K3_ARM64 depends on DM depends on ARCH_K3 depends on OF_CONTROL + default y if SYS_K3_SPL_ATF help Say y here to support TI's ARM64 processor subsystems on various TI K3 family of SoCs through the remote processor @@ -70,6 +82,16 @@ config REMOTEPROC_TI_K3_DSP on various TI K3 family of SoCs through the remote processor framework. +config REMOTEPROC_TI_K3_M4F + bool "TI K3 M4F remoteproc support" + select REMOTEPROC + depends on ARCH_K3 + depends on TI_SCI_PROTOCOL + help + Say y here to support TI's M4F remote processor subsystems + on various TI K3 family of SoCs through the remote processor + framework. + config REMOTEPROC_TI_K3_R5F bool "TI K3 R5F remoteproc support" select REMOTEPROC diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 3a092b7660e..47bd57c7890 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -4,15 +4,17 @@ # Texas Instruments Incorporated - https://www.ti.com/ # -obj-$(CONFIG_$(XPL_)REMOTEPROC) += rproc-uclass.o rproc-elf-loader.o +obj-$(CONFIG_$(PHASE_)REMOTEPROC) += rproc-uclass.o rproc-elf-loader.o # Remote proc drivers - Please keep this list alphabetically sorted. obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.o +obj-$(CONFIG_REMOTEPROC_ADI_SC5XX) += adi_sc5xx_rproc.o obj-$(CONFIG_REMOTEPROC_RENESAS_APMU) += renesas_apmu.o obj-$(CONFIG_REMOTEPROC_SANDBOX) += sandbox_testproc.o obj-$(CONFIG_REMOTEPROC_STM32_COPRO) += stm32_copro.o obj-$(CONFIG_REMOTEPROC_TI_K3_ARM64) += ti_k3_arm64_rproc.o obj-$(CONFIG_REMOTEPROC_TI_K3_DSP) += ti_k3_dsp_rproc.o +obj-$(CONFIG_REMOTEPROC_TI_K3_M4F) += ti_k3_m4_rproc.o obj-$(CONFIG_REMOTEPROC_TI_K3_R5F) += ti_k3_r5f_rproc.o obj-$(CONFIG_REMOTEPROC_TI_POWER) += ti_power_proc.o obj-$(CONFIG_REMOTEPROC_TI_PRU) += pru_rproc.o diff --git a/drivers/remoteproc/adi_sc5xx_rproc.c b/drivers/remoteproc/adi_sc5xx_rproc.c new file mode 100644 index 00000000000..86acd1b98c7 --- /dev/null +++ b/drivers/remoteproc/adi_sc5xx_rproc.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * + * Analog Devices SC5xx remoteproc driver for loading code onto SHARC cores + */ + +#include <dm.h> +#include <regmap.h> +#include <remoteproc.h> +#include <syscon.h> +#include <dm/device_compat.h> +#include <linux/delay.h> +#include <linux/io.h> + +/* Register offsets */ +#ifdef CONFIG_SC58X +#define ADI_RCU_REG_CTL 0x00 +#define ADI_RCU_REG_STAT 0x04 +#define ADI_RCU_REG_CRCTL 0x08 +#define ADI_RCU_REG_CRSTAT 0x0c +#define ADI_RCU_REG_SIDIS 0x10 +#define ADI_RCU_REG_SISTAT 0x14 +#define ADI_RCU_REG_BCODE 0x1c +#define ADI_RCU_REG_SVECT0 0x20 +#define ADI_RCU_REG_SVECT1 0x24 +#define ADI_RCU_REG_SVECT2 0x28 +#define ADI_RCU_REG_MSG 0x60 +#define ADI_RCU_REG_MSG_SET 0x64 +#define ADI_RCU_REG_MSG_CLR 0x68 +#else +#define ADI_RCU_REG_CTL 0x00 +#define ADI_RCU_REG_STAT 0x04 +#define ADI_RCU_REG_CRCTL 0x08 +#define ADI_RCU_REG_CRSTAT 0x0c +#define ADI_RCU_REG_SRRQSTAT 0x18 +#define ADI_RCU_REG_SIDIS 0x1c +#define ADI_RCU_REG_SISTAT 0x20 +#define ADI_RCU_REG_SVECT_LCK 0x24 +#define ADI_RCU_REG_BCODE 0x28 +#define ADI_RCU_REG_SVECT0 0x2c +#define ADI_RCU_REG_SVECT1 0x30 +#define ADI_RCU_REG_SVECT2 0x34 +#define ADI_RCU_REG_MSG 0x6c +#define ADI_RCU_REG_MSG_SET 0x70 +#define ADI_RCU_REG_MSG_CLR 0x74 +#endif /* CONFIG_SC58X */ + +/* Register bit definitions */ +#define ADI_RCU_CTL_SYSRST BIT(0) + +/* Bit values for the RCU0_MSG register */ +#define RCU0_MSG_C0IDLE 0x00000100 /* Core 0 Idle */ +#define RCU0_MSG_C1IDLE 0x00000200 /* Core 1 Idle */ +#define RCU0_MSG_C2IDLE 0x00000400 /* Core 2 Idle */ +#define RCU0_MSG_CRR0 0x00001000 /* Core 0 reset request */ +#define RCU0_MSG_CRR1 0x00002000 /* Core 1 reset request */ +#define RCU0_MSG_CRR2 0x00004000 /* Core 2 reset request */ +#define RCU0_MSG_C1ACTIVATE 0x00080000 /* Core 1 Activated */ +#define RCU0_MSG_C2ACTIVATE 0x00100000 /* Core 2 Activated */ + +struct sc5xx_rproc_data { + /* Address to load to svect when rebooting core */ + u32 load_addr; + + /* RCU parameters */ + struct regmap *rcu; + u32 svect_offset; + u32 coreid; +}; + +struct block_code_flag { + u32 bcode:4, /* 0-3 */ + bflag_save:1, /* 4 */ + bflag_aux:1, /* 5 */ + breserved:1, /* 6 */ + bflag_forward:1, /* 7 */ + bflag_fill:1, /* 8 */ + bflag_quickboot:1, /* 9 */ + bflag_callback:1, /* 10 */ + bflag_init:1, /* 11 */ + bflag_ignore:1, /* 12 */ + bflag_indirect:1, /* 13 */ + bflag_first:1, /* 14 */ + bflag_final:1, /* 15 */ + bhdrchk:8, /* 16-23 */ + bhdrsign:8; /* 0xAD, 0xAC or 0xAB */ +}; + +struct ldr_hdr { + struct block_code_flag bcode_flag; + u32 target_addr; + u32 byte_count; + u32 argument; +}; + +static int is_final(struct ldr_hdr *hdr) +{ + return hdr->bcode_flag.bflag_final; +} + +static int is_empty(struct ldr_hdr *hdr) +{ + return hdr->bcode_flag.bflag_ignore || (hdr->byte_count == 0); +} + +static int adi_valid_firmware(struct ldr_hdr *adi_ldr_hdr) +{ + if (!adi_ldr_hdr->byte_count && + (adi_ldr_hdr->bcode_flag.bhdrsign == 0xAD || + adi_ldr_hdr->bcode_flag.bhdrsign == 0xAC || + adi_ldr_hdr->bcode_flag.bhdrsign == 0xAB)) + return 1; + + return 0; +} + +static int sharc_load(struct udevice *dev, ulong addr, ulong size) +{ + struct sc5xx_rproc_data *priv = dev_get_priv(dev); + size_t offset; + u8 *buf = (u8 *)addr; + struct ldr_hdr *ldr = (struct ldr_hdr *)addr; + struct ldr_hdr *block_hdr; + struct ldr_hdr *next_hdr; + + if (!adi_valid_firmware(ldr)) { + dev_err(dev, "Firmware at 0x%lx does not appear to be an LDR image\n", addr); + dev_err(dev, "Note: Signed firmware is not currently supported\n"); + return -EINVAL; + } + + do { + block_hdr = (struct ldr_hdr *)buf; + offset = sizeof(struct ldr_hdr) + (block_hdr->bcode_flag.bflag_fill ? + 0 : block_hdr->byte_count); + next_hdr = (struct ldr_hdr *)(buf + offset); + + if (block_hdr->bcode_flag.bflag_first) + priv->load_addr = (unsigned long)block_hdr->target_addr; + + if (!is_empty(block_hdr)) { + if (block_hdr->bcode_flag.bflag_fill) { + memset_io((void *)(phys_addr_t)block_hdr->target_addr, + block_hdr->argument, + block_hdr->byte_count); + } else { + memcpy_toio((void *)(phys_addr_t)block_hdr->target_addr, + buf + sizeof(struct ldr_hdr), + block_hdr->byte_count); + } + } + + if (is_final(block_hdr)) + break; + + buf += offset; + } while (1); + + return 0; +} + +static void sharc_reset(struct sc5xx_rproc_data *priv) +{ + u32 coreid = priv->coreid; + u32 val; + + /* First put core in reset. + * Clear CRSTAT bit for given coreid. + */ + regmap_write(priv->rcu, ADI_RCU_REG_CRSTAT, 1 << coreid); + + /* Set SIDIS to disable the system interface */ + regmap_read(priv->rcu, ADI_RCU_REG_SIDIS, &val); + regmap_write(priv->rcu, ADI_RCU_REG_SIDIS, val | (1 << (coreid - 1))); + + /* + * Wait for access to coreX have been disabled and all the pending + * transactions have completed + */ + udelay(50); + + /* Set CRCTL bit to put core in reset */ + regmap_read(priv->rcu, ADI_RCU_REG_CRCTL, &val); + regmap_write(priv->rcu, ADI_RCU_REG_CRCTL, val | (1 << coreid)); + + /* Poll until Core is in reset */ + while (!(regmap_read(priv->rcu, ADI_RCU_REG_CRSTAT, &val), val & (1 << coreid))) + ; + + /* Clear SIDIS to reenable the system interface */ + regmap_read(priv->rcu, ADI_RCU_REG_SIDIS, &val); + regmap_write(priv->rcu, ADI_RCU_REG_SIDIS, val & ~(1 << (coreid - 1))); + + udelay(50); + + /* Take Core out of reset */ + regmap_read(priv->rcu, ADI_RCU_REG_CRCTL, &val); + regmap_write(priv->rcu, ADI_RCU_REG_CRCTL, val & ~(1 << coreid)); + + /* Wait for done */ + udelay(50); +} + +static int sharc_start(struct udevice *dev) +{ + struct sc5xx_rproc_data *priv = dev_get_priv(dev); + + /* Write load address to appropriate SVECT for core */ + regmap_write(priv->rcu, priv->svect_offset, priv->load_addr); + + sharc_reset(priv); + + /* Clear the IDLE bit when start the SHARC core */ + regmap_write(priv->rcu, ADI_RCU_REG_MSG_CLR, RCU0_MSG_C0IDLE << priv->coreid); + + /* Notify CCES */ + regmap_write(priv->rcu, ADI_RCU_REG_MSG_SET, RCU0_MSG_C1ACTIVATE << (priv->coreid - 1)); + return 0; +} + +static const struct dm_rproc_ops sc5xx_ops = { + .load = sharc_load, + .start = sharc_start, +}; + +static int sc5xx_probe(struct udevice *dev) +{ + struct sc5xx_rproc_data *priv = dev_get_priv(dev); + u32 coreid; + + if (dev_read_u32(dev, "coreid", &coreid)) { + dev_err(dev, "Missing property coreid\n"); + return -ENOENT; + } + + priv->coreid = coreid; + switch (coreid) { + case 1: + priv->svect_offset = ADI_RCU_REG_SVECT1; + break; + case 2: + priv->svect_offset = ADI_RCU_REG_SVECT2; + break; + default: + dev_err(dev, "Invalid value %d for coreid, must be 1 or 2\n", coreid); + return -EINVAL; + } + + priv->rcu = syscon_regmap_lookup_by_phandle(dev, "adi,rcu"); + if (IS_ERR(priv->rcu)) + return PTR_ERR(priv->rcu); + + dev_err(dev, "sc5xx remoteproc core %d available\n", priv->coreid); + + return 0; +} + +static const struct udevice_id sc5xx_ids[] = { + { .compatible = "adi,sc5xx-rproc" }, + { } +}; + +U_BOOT_DRIVER(adi_sc5xx_rproc) = { + .name = "adi_sc5xx_rproc", + .of_match = sc5xx_ids, + .id = UCLASS_REMOTEPROC, + .ops = &sc5xx_ops, + .probe = sc5xx_probe, + .priv_auto = sizeof(struct sc5xx_rproc_data), + .flags = 0, +}; diff --git a/drivers/remoteproc/ti_k3_dsp_rproc.c b/drivers/remoteproc/ti_k3_dsp_rproc.c index e90f75a188c..5a7d6377283 100644 --- a/drivers/remoteproc/ti_k3_dsp_rproc.c +++ b/drivers/remoteproc/ti_k3_dsp_rproc.c @@ -15,6 +15,7 @@ #include <clk.h> #include <reset.h> #include <asm/io.h> +#include <asm/system.h> #include <power-domain.h> #include <dm/device_compat.h> #include <linux/err.h> @@ -56,7 +57,9 @@ struct k3_dsp_boot_data { * @data: Pointer to DSP specific boot data structure * @mem: Array of available memories * @num_mem: Number of available memories - * @in_use: flag to tell if the core is already in use. + * @cached_addr: Cached memory address + * @cached_size: Cached memory size + * @in_use: flag to tell if the core is already in use. */ struct k3_dsp_privdata { struct reset_ctl dsp_rst; @@ -64,6 +67,8 @@ struct k3_dsp_privdata { struct k3_dsp_boot_data *data; struct k3_dsp_mem *mem; int num_mems; + void __iomem *cached_addr; + size_t cached_size; bool in_use; }; @@ -158,6 +163,13 @@ static int k3_dsp_load(struct udevice *dev, ulong addr, ulong size) goto unprepare; } + if (dsp->cached_addr && IS_ENABLED(CONFIG_SYS_DISABLE_DCACHE_OPS)) { + dev_dbg(dev, "final flush 0x%lx to 0x%lx\n", + (ulong)dsp->cached_addr, dsp->cached_size); + __asm_invalidate_dcache_range((u64)dsp->cached_addr, + (u64)dsp->cached_addr + (u64)dsp->cached_size); + } + boot_vector = rproc_elf_get_boot_addr(dev, addr); if (boot_vector & (data->boot_align_addr - 1)) { ret = -EINVAL; @@ -253,7 +265,6 @@ static void *k3_dsp_da_to_va(struct udevice *dev, ulong da, ulong len) { struct k3_dsp_privdata *dsp = dev_get_priv(dev); phys_addr_t bus_addr, dev_addr; - void __iomem *va = NULL; size_t size; u32 offset; int i; @@ -263,6 +274,16 @@ static void *k3_dsp_da_to_va(struct udevice *dev, ulong da, ulong len) if (len <= 0) return NULL; + if (dsp->cached_addr && IS_ENABLED(CONFIG_SYS_DISABLE_DCACHE_OPS)) { + dev_dbg(dev, "flush 0x%lx to 0x%lx\n", (ulong)dsp->cached_addr, + dsp->cached_size); + __asm_invalidate_dcache_range((u64)dsp->cached_addr, + (u64)dsp->cached_addr + (u64)dsp->cached_size); + } + + dsp->cached_size = len; + dsp->cached_addr = NULL; + for (i = 0; i < dsp->num_mems; i++) { bus_addr = dsp->mem[i].bus_addr; dev_addr = dsp->mem[i].dev_addr; @@ -270,19 +291,20 @@ static void *k3_dsp_da_to_va(struct udevice *dev, ulong da, ulong len) if (da >= dev_addr && ((da + len) <= (dev_addr + size))) { offset = da - dev_addr; - va = dsp->mem[i].cpu_addr + offset; - return (__force void *)va; + dsp->cached_addr = dsp->mem[i].cpu_addr + offset; } if (da >= bus_addr && (da + len) <= (bus_addr + size)) { offset = da - bus_addr; - va = dsp->mem[i].cpu_addr + offset; - return (__force void *)va; + dsp->cached_addr = dsp->mem[i].cpu_addr + offset; } } /* Assume it is DDR region and return da */ - return map_physmem(da, len, MAP_NOCACHE); + if (!dsp->cached_addr) + dsp->cached_addr = map_physmem(da, len, MAP_NOCACHE); + + return dsp->cached_addr; } static const struct dm_rproc_ops k3_dsp_ops = { diff --git a/drivers/remoteproc/ti_k3_m4_rproc.c b/drivers/remoteproc/ti_k3_m4_rproc.c new file mode 100644 index 00000000000..31b9de71579 --- /dev/null +++ b/drivers/remoteproc/ti_k3_m4_rproc.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments' K3 M4 Remoteproc driver + * + * Copyright (C) 2024 Texas Instruments Incorporated - http://www.ti.com/ + * Hari Nagalla <hnagalla@ti.com> + */ + +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <remoteproc.h> +#include <errno.h> +#include <clk.h> +#include <reset.h> +#include <asm/io.h> +#include <power-domain.h> +#include <dm/device_compat.h> +#include <linux/err.h> +#include <linux/sizes.h> +#include <linux/soc/ti/ti_sci_protocol.h> +#include "ti_sci_proc.h" +#include <mach/security.h> + +/** + * struct k3_m4_mem - internal memory structure + * @cpu_addr: MPU virtual address of the memory region + * @bus_addr: Bus address used to access the memory region + * @dev_addr: Device address from remoteproc view + * @size: Size of the memory region + */ +struct k3_m4_mem { + void __iomem *cpu_addr; + phys_addr_t bus_addr; + phys_addr_t dev_addr; + size_t size; +}; + +/** + * struct k3_m4_mem_data - memory definitions for m4 remote core + * @name: name for this memory entry + * @dev_addr: device address for the memory entry + */ +struct k3_m4_mem_data { + const char *name; + const u32 dev_addr; +}; + +/** + * struct k3_m4_boot_data - internal data structure used for boot + * @boot_align_addr: Boot vector address alignment granularity + */ +struct k3_m4_boot_data { + u32 boot_align_addr; +}; + +/** + * struct k3_m4_privdata - Structure representing Remote processor data. + * @m4_rst: m4 rproc reset control data + * @tsp: Pointer to TISCI proc contrl handle + * @data: Pointer to DSP specific boot data structure + * @mem: Array of available memories + * @num_mem: Number of available memories + */ +struct k3_m4_privdata { + struct reset_ctl m4_rst; + struct ti_sci_proc tsp; + struct k3_m4_boot_data *data; + struct k3_m4_mem *mem; + int num_mems; +}; + +/* + * The M4 cores have a local reset that affects only the CPU, and a + * generic module reset that powers on the device and allows the M4 internal + * memories to be accessed while the local reset is asserted. This function is + * used to release the global reset on M4F to allow loading into the M4F + * internal RAMs. This helper function is invoked in k3_m4_load() before any + * actual firmware loading happens and is undone only in k3_m4_stop(). The local + * reset cannot be released on M4 cores until after the firmware images are loaded. + */ +static int k3_m4_prepare(struct udevice *dev) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + int ret; + + ret = ti_sci_proc_power_domain_on(&m4->tsp); + if (ret) + dev_err(dev, "cannot enable internal RAM loading, ret = %d\n", + ret); + + return ret; +} + +/* + * This function is the counterpart to k3_m4_prepare() and is used to assert + * the global reset on M4 cores. This completes the second step of powering + * down the M4 cores. The cores themselves are halted through the local reset + * in first step. This function is invoked in k3_m4_stop() after the local + * reset is asserted. + */ +static int k3_m4_unprepare(struct udevice *dev) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + + return ti_sci_proc_power_domain_off(&m4->tsp); +} + +/** + * k3_m4_load() - Load up the Remote processor image + * @dev: rproc device pointer + * @addr: Address at which image is available + * @size: size of the image + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_m4_load(struct udevice *dev, ulong addr, ulong size) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + void *image_addr = (void *)addr; + int ret; + + ret = ti_sci_proc_request(&m4->tsp); + if (ret) + return ret; + + ret = k3_m4_prepare(dev); + if (ret) { + dev_err(dev, "Prepare failed for core %d\n", + m4->tsp.proc_id); + goto proc_release; + } + + ti_secure_image_post_process(&image_addr, &size); + + ret = rproc_elf_load_image(dev, addr, size); + if (ret < 0) { + dev_err(dev, "Loading elf failed %d\n", ret); + goto unprepare; + } + +unprepare: + if (ret) + k3_m4_unprepare(dev); +proc_release: + ti_sci_proc_release(&m4->tsp); + return ret; +} + +/** + * k3_m4_start() - Start the remote processor + * @dev: rproc device pointer + * + * Return: 0 if all went ok, else return appropriate error + */ +static int k3_m4_start(struct udevice *dev) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + int ret; + + ret = ti_sci_proc_request(&m4->tsp); + if (ret) + return ret; + + ret = reset_deassert(&m4->m4_rst); + + ti_sci_proc_release(&m4->tsp); + + return ret; +} + +static int k3_m4_stop(struct udevice *dev) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + + ti_sci_proc_request(&m4->tsp); + reset_assert(&m4->m4_rst); + k3_m4_unprepare(dev); + ti_sci_proc_release(&m4->tsp); + + return 0; +} + +static void *k3_m4_da_to_va(struct udevice *dev, ulong da, ulong len) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + phys_addr_t bus_addr, dev_addr; + void __iomem *va = NULL; + size_t size; + u32 offset; + int i; + + if (len <= 0) + return NULL; + + for (i = 0; i < m4->num_mems; i++) { + bus_addr = m4->mem[i].bus_addr; + dev_addr = m4->mem[i].dev_addr; + size = m4->mem[i].size; + + if (da >= dev_addr && ((da + len) <= (dev_addr + size))) { + offset = da - dev_addr; + va = m4->mem[i].cpu_addr + offset; + return (__force void *)va; + } + + if (da >= bus_addr && (da + len) <= (bus_addr + size)) { + offset = da - bus_addr; + va = m4->mem[i].cpu_addr + offset; + return (__force void *)va; + } + } + + /* Assume it is DDR region and return da */ + return map_physmem(da, len, MAP_NOCACHE); +} + +static const struct dm_rproc_ops k3_m4_ops = { + .load = k3_m4_load, + .start = k3_m4_start, + .stop = k3_m4_stop, + .device_to_virt = k3_m4_da_to_va, +}; + +static int ti_sci_proc_of_to_priv(struct udevice *dev, struct ti_sci_proc *tsp) +{ + u32 ids[2]; + int ret; + + tsp->sci = ti_sci_get_by_phandle(dev, "ti,sci"); + if (IS_ERR(tsp->sci)) { + dev_err(dev, "ti_sci get failed: %ld\n", PTR_ERR(tsp->sci)); + return PTR_ERR(tsp->sci); + } + + ret = dev_read_u32_array(dev, "ti,sci-proc-ids", ids, 2); + if (ret) { + dev_err(dev, "Proc IDs not populated %d\n", ret); + return ret; + } + + tsp->ops = &tsp->sci->ops.proc_ops; + tsp->proc_id = ids[0]; + tsp->host_id = ids[1]; + tsp->dev_id = dev_read_u32_default(dev, "ti,sci-dev-id", + TI_SCI_RESOURCE_NULL); + if (tsp->dev_id == TI_SCI_RESOURCE_NULL) { + dev_err(dev, "Device ID not populated %d\n", ret); + return -ENODEV; + } + + return 0; +} + +static const struct k3_m4_mem_data am6_m4_mems[] = { + { .name = "iram", .dev_addr = 0x0 }, + { .name = "dram", .dev_addr = 0x30000 }, +}; + +static int k3_m4_of_get_memories(struct udevice *dev) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + int i; + + m4->num_mems = ARRAY_SIZE(am6_m4_mems); + m4->mem = calloc(m4->num_mems, sizeof(*m4->mem)); + if (!m4->mem) + return -ENOMEM; + + for (i = 0; i < m4->num_mems; i++) { + m4->mem[i].bus_addr = dev_read_addr_size_name(dev, + am6_m4_mems[i].name, + (fdt_addr_t *)&m4->mem[i].size); + if (m4->mem[i].bus_addr == FDT_ADDR_T_NONE) { + dev_err(dev, "%s bus address not found\n", + am6_m4_mems[i].name); + return -EINVAL; + } + m4->mem[i].cpu_addr = map_physmem(m4->mem[i].bus_addr, + m4->mem[i].size, + MAP_NOCACHE); + m4->mem[i].dev_addr = am6_m4_mems[i].dev_addr; + } + + return 0; +} + +/** + * k3_of_to_priv() - generate private data from device tree + * @dev: corresponding k3 m4 processor device + * @m4: pointer to driver specific private data + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_m4_of_to_priv(struct udevice *dev, struct k3_m4_privdata *m4) +{ + int ret; + + ret = reset_get_by_index(dev, 0, &m4->m4_rst); + if (ret) { + dev_err(dev, "reset_get() failed: %d\n", ret); + return ret; + } + + ret = ti_sci_proc_of_to_priv(dev, &m4->tsp); + if (ret) + return ret; + + ret = k3_m4_of_get_memories(dev); + if (ret) + return ret; + + m4->data = (struct k3_m4_boot_data *)dev_get_driver_data(dev); + + return 0; +} + +/** + * k3_m4_probe() - Basic probe + * @dev: corresponding k3 remote processor device + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_m4_probe(struct udevice *dev) +{ + struct k3_m4_privdata *m4; + int ret; + + m4 = dev_get_priv(dev); + ret = k3_m4_of_to_priv(dev, m4); + if (ret) + return ret; + + /* + * The M4 local resets are deasserted by default on Power-On-Reset. + * Assert the local resets to ensure the M4s don't execute bogus code + * in .load() callback when the module reset is released to support + * internal memory loading. This is needed for M4 cores. + */ + reset_assert(&m4->m4_rst); + + return 0; +} + +static int k3_m4_remove(struct udevice *dev) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + + free(m4->mem); + + return 0; +} + +static const struct k3_m4_boot_data m4_data = { + .boot_align_addr = SZ_1K, +}; + +static const struct udevice_id k3_m4_ids[] = { + { .compatible = "ti,am64-m4fss", .data = (ulong)&m4_data, }, + {} +}; + +U_BOOT_DRIVER(k3_m4) = { + .name = "k3_m4", + .of_match = k3_m4_ids, + .id = UCLASS_REMOTEPROC, + .ops = &k3_m4_ops, + .probe = k3_m4_probe, + .remove = k3_m4_remove, + .priv_auto = sizeof(struct k3_m4_privdata), +}; diff --git a/drivers/remoteproc/ti_k3_r5f_rproc.c b/drivers/remoteproc/ti_k3_r5f_rproc.c index d78b3fa1bbd..57268e7f8ff 100644 --- a/drivers/remoteproc/ti_k3_r5f_rproc.c +++ b/drivers/remoteproc/ti_k3_r5f_rproc.c @@ -886,6 +886,7 @@ static const struct udevice_id k3_r5f_rproc_ids[] = { { .compatible = "ti,j7200-r5f", .data = (ulong)&j7200_j721s2_data, }, { .compatible = "ti,j721s2-r5f", .data = (ulong)&j7200_j721s2_data, }, { .compatible = "ti,am62-r5f", .data = (ulong)&am62_data, }, + { .compatible = "ti,am64-r5f", .data = (ulong)&j7200_j721s2_data, }, {} }; @@ -930,6 +931,7 @@ static const struct udevice_id k3_r5fss_ids[] = { { .compatible = "ti,j7200-r5fss"}, { .compatible = "ti,j721s2-r5fss"}, { .compatible = "ti,am62-r5fss"}, + { .compatible = "ti,am64-r5fss"}, {} }; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index fe5c1214f57..a0d079c4555 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -49,6 +49,13 @@ config TEGRA186_RESET Enable support for manipulating Tegra's on-SoC reset signals via IPC requests to the BPMP (Boot and Power Management Processor). +config RESET_AIROHA + bool "Reset controller driver for Airoha SoCs" + depends on DM_RESET && ARCH_AIROHA + default y + help + Support for reset controller on Airoha SoCs. + config RESET_TI_SCI bool "TI System Control Interface (TI SCI) reset driver" depends on DM_RESET && TI_SCI_PROTOCOL @@ -235,4 +242,20 @@ config RESET_AT91 This enables the Reset Controller driver support for Microchip/Atmel SoCs. Mainly used to expose assert/deassert methods to other drivers that require it. + +config RESET_RZG2L_USBPHY_CTRL + bool "Enable support for Renesas RZ/G2L USB 2.0 PHY control" + depends on DM_RESET + select REGULATOR_RZG2L_USBPHY + help + Enable support for controlling USB 2.0 PHY resets on the Renesas + RZ/G2L SoC. This is required for USB 2.0 functionality to work on this + SoC. + +config RESET_SPACEMIT_K1 + bool "Support for SPACEMIT's K1 Reset driver" + depends on DM_RESET + help + Support for SPACEMIT's K1 Reset system. Basic Assert/Deassert + is supported. endmenu diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index d99a78c9828..1dd3cd99a14 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -10,13 +10,14 @@ obj-$(CONFIG_STI_RESET) += sti-reset.o obj-$(CONFIG_STM32_RESET) += stm32-reset.o obj-$(CONFIG_TEGRA_CAR_RESET) += tegra-car-reset.o obj-$(CONFIG_TEGRA186_RESET) += tegra186-reset.o +obj-$(CONFIG_RESET_AIROHA) += reset-airoha.o obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o obj-$(CONFIG_RESET_AST2500) += reset-ast2500.o obj-$(CONFIG_RESET_AST2600) += reset-ast2600.o -obj-$(CONFIG_RESET_ROCKCHIP) += reset-rockchip.o rst-rk3588.o +obj-$(CONFIG_RESET_ROCKCHIP) += reset-rockchip.o rst-rk3528.o rst-rk3576.o rst-rk3588.o obj-$(CONFIG_RESET_MESON) += reset-meson.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_MEDIATEK) += reset-mediatek.o @@ -33,3 +34,5 @@ obj-$(CONFIG_RESET_ZYNQMP) += reset-zynqmp.o obj-$(CONFIG_RESET_DRA7) += reset-dra7.o obj-$(CONFIG_RESET_AT91) += reset-at91.o obj-$(CONFIG_$(PHASE_)RESET_JH7110) += reset-jh7110.o +obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o +obj-$(CONFIG_RESET_SPACEMIT_K1) += reset-spacemit-k1.o diff --git a/drivers/reset/reset-airoha.c b/drivers/reset/reset-airoha.c new file mode 100644 index 00000000000..e878af6167c --- /dev/null +++ b/drivers/reset/reset-airoha.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Based on Linux drivers/clk/clk-en7523.c reworked + * and detached to a dedicated driver + * + * Author: Lorenzo Bianconi <lorenzo@kernel.org> (original driver) + * Christian Marangi <ansuelsmth@gmail.com> + */ + +#include <dm.h> +#include <linux/io.h> +#include <reset-uclass.h> + +#include <dt-bindings/reset/airoha,en7581-reset.h> + +#define RST_NR_PER_BANK 32 + +#define REG_RESET_CONTROL2 0x830 +#define REG_RESET_CONTROL1 0x834 + +struct airoha_reset_priv { + const u16 *bank_ofs; + const u16 *idx_map; + void __iomem *base; +}; + +static const u16 en7581_rst_ofs[] = { + REG_RESET_CONTROL2, + REG_RESET_CONTROL1, +}; + +static const u16 en7581_rst_map[] = { + /* RST_CTRL2 */ + [EN7581_XPON_PHY_RST] = 0, + [EN7581_CPU_TIMER2_RST] = 2, + [EN7581_HSUART_RST] = 3, + [EN7581_UART4_RST] = 4, + [EN7581_UART5_RST] = 5, + [EN7581_I2C2_RST] = 6, + [EN7581_XSI_MAC_RST] = 7, + [EN7581_XSI_PHY_RST] = 8, + [EN7581_NPU_RST] = 9, + [EN7581_I2S_RST] = 10, + [EN7581_TRNG_RST] = 11, + [EN7581_TRNG_MSTART_RST] = 12, + [EN7581_DUAL_HSI0_RST] = 13, + [EN7581_DUAL_HSI1_RST] = 14, + [EN7581_HSI_RST] = 15, + [EN7581_DUAL_HSI0_MAC_RST] = 16, + [EN7581_DUAL_HSI1_MAC_RST] = 17, + [EN7581_HSI_MAC_RST] = 18, + [EN7581_WDMA_RST] = 19, + [EN7581_WOE0_RST] = 20, + [EN7581_WOE1_RST] = 21, + [EN7581_HSDMA_RST] = 22, + [EN7581_TDMA_RST] = 24, + [EN7581_EMMC_RST] = 25, + [EN7581_SOE_RST] = 26, + [EN7581_PCIE2_RST] = 27, + [EN7581_XFP_MAC_RST] = 28, + [EN7581_USB_HOST_P1_RST] = 29, + [EN7581_USB_HOST_P1_U3_PHY_RST] = 30, + /* RST_CTRL1 */ + [EN7581_PCM1_ZSI_ISI_RST] = RST_NR_PER_BANK + 0, + [EN7581_FE_PDMA_RST] = RST_NR_PER_BANK + 1, + [EN7581_FE_QDMA_RST] = RST_NR_PER_BANK + 2, + [EN7581_PCM_SPIWP_RST] = RST_NR_PER_BANK + 4, + [EN7581_CRYPTO_RST] = RST_NR_PER_BANK + 6, + [EN7581_TIMER_RST] = RST_NR_PER_BANK + 8, + [EN7581_PCM1_RST] = RST_NR_PER_BANK + 11, + [EN7581_UART_RST] = RST_NR_PER_BANK + 12, + [EN7581_GPIO_RST] = RST_NR_PER_BANK + 13, + [EN7581_GDMA_RST] = RST_NR_PER_BANK + 14, + [EN7581_I2C_MASTER_RST] = RST_NR_PER_BANK + 16, + [EN7581_PCM2_ZSI_ISI_RST] = RST_NR_PER_BANK + 17, + [EN7581_SFC_RST] = RST_NR_PER_BANK + 18, + [EN7581_UART2_RST] = RST_NR_PER_BANK + 19, + [EN7581_GDMP_RST] = RST_NR_PER_BANK + 20, + [EN7581_FE_RST] = RST_NR_PER_BANK + 21, + [EN7581_USB_HOST_P0_RST] = RST_NR_PER_BANK + 22, + [EN7581_GSW_RST] = RST_NR_PER_BANK + 23, + [EN7581_SFC2_PCM_RST] = RST_NR_PER_BANK + 25, + [EN7581_PCIE0_RST] = RST_NR_PER_BANK + 26, + [EN7581_PCIE1_RST] = RST_NR_PER_BANK + 27, + [EN7581_CPU_TIMER_RST] = RST_NR_PER_BANK + 28, + [EN7581_PCIE_HB_RST] = RST_NR_PER_BANK + 29, + [EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31, +}; + +static int airoha_reset_update(struct airoha_reset_priv *priv, + unsigned long id, bool assert) +{ + void __iomem *addr = priv->base + priv->bank_ofs[id / RST_NR_PER_BANK]; + u32 val; + + val = readl(addr); + if (assert) + val |= BIT(id % RST_NR_PER_BANK); + else + val &= ~BIT(id % RST_NR_PER_BANK); + writel(val, addr); + + return 0; +} + +static int airoha_reset_assert(struct reset_ctl *reset_ctl) +{ + struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev); + int id = reset_ctl->id; + + return airoha_reset_update(priv, id, true); +} + +static int airoha_reset_deassert(struct reset_ctl *reset_ctl) +{ + struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev); + int id = reset_ctl->id; + + return airoha_reset_update(priv, id, false); +} + +static int airoha_reset_status(struct reset_ctl *reset_ctl) +{ + struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev); + int id = reset_ctl->id; + void __iomem *addr; + + addr = priv->base + priv->bank_ofs[id / RST_NR_PER_BANK]; + + return !!(readl(addr) & BIT(id % RST_NR_PER_BANK)); +} + +static int airoha_reset_xlate(struct reset_ctl *reset_ctl, + struct ofnode_phandle_args *args) +{ + struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev); + + if (args->args[0] >= ARRAY_SIZE(en7581_rst_map)) + return -EINVAL; + + reset_ctl->id = priv->idx_map[args->args[0]]; + + return 0; +} + +static struct reset_ops airoha_reset_ops = { + .of_xlate = airoha_reset_xlate, + .rst_assert = airoha_reset_assert, + .rst_deassert = airoha_reset_deassert, + .rst_status = airoha_reset_status, +}; + +static int airoha_reset_probe(struct udevice *dev) +{ + struct airoha_reset_priv *priv = dev_get_priv(dev); + + priv->base = dev_remap_addr(dev); + if (!priv->base) + return -ENOMEM; + + priv->bank_ofs = en7581_rst_ofs; + priv->idx_map = en7581_rst_map; + + return 0; +} + +U_BOOT_DRIVER(airoha_reset) = { + .name = "airoha-reset", + .id = UCLASS_RESET, + .probe = airoha_reset_probe, + .ops = &airoha_reset_ops, + .priv_auto = sizeof(struct airoha_reset_priv), +}; diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c new file mode 100644 index 00000000000..622d7b9cf4f --- /dev/null +++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 Renesas Electronics Corporation + */ + +#include <asm/io.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/device_compat.h> +#include <dm/lists.h> +#include <renesas/rzg2l-usbphy.h> +#include <reset-uclass.h> +#include <reset.h> + +#define RESET 0x000 + +#define RESET_SEL_PLLRESET BIT(12) +#define RESET_PLLRESET BIT(8) + +#define RESET_SEL_P2RESET BIT(5) +#define RESET_SEL_P1RESET BIT(4) +#define RESET_PHYRST_2 BIT(1) +#define RESET_PHYRST_1 BIT(0) + +#define PHY_RESET_MASK (RESET_PHYRST_1 | RESET_PHYRST_2) + +#define NUM_PORTS 2 + +static int rzg2l_usbphy_ctrl_assert(struct reset_ctl *reset_ctl) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(reset_ctl->dev); + u32 val; + + val = readl(priv->regs + RESET); + val |= reset_ctl->id ? RESET_PHYRST_2 : RESET_PHYRST_1; + + /* If both ports are in reset, we can also place the PLL into reset. */ + if ((val & PHY_RESET_MASK) == PHY_RESET_MASK) + val |= RESET_PLLRESET; + + writel(val, priv->regs + RESET); + return 0; +} + +static int rzg2l_usbphy_ctrl_deassert(struct reset_ctl *reset_ctl) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(reset_ctl->dev); + u32 val = reset_ctl->id ? RESET_PHYRST_2 : RESET_PHYRST_1; + + /* If either port is out of reset, the PLL must also be out of reset. */ + val |= RESET_PLLRESET; + + clrbits_le32(priv->regs + RESET, val); + return 0; +} + +static int rzg2l_usbphy_ctrl_of_xlate(struct reset_ctl *reset_ctl, + struct ofnode_phandle_args *args) +{ + if (args->args[0] >= NUM_PORTS) + return -EINVAL; + + reset_ctl->id = args->args[0]; + return 0; +} + +struct reset_ops rzg2l_usbphy_ctrl_ops = { + .rst_assert = rzg2l_usbphy_ctrl_assert, + .rst_deassert = rzg2l_usbphy_ctrl_deassert, + .of_xlate = rzg2l_usbphy_ctrl_of_xlate, +}; + +static int rzg2l_usbphy_ctrl_probe(struct udevice *dev) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev); + struct reset_ctl rst; + int ret; + + priv->regs = dev_read_addr(dev); + + ret = reset_get_by_index(dev, 0, &rst); + if (ret < 0) { + dev_err(dev, "failed to get reset line: %d\n", ret); + return ret; + } + + ret = reset_deassert(&rst); + if (ret < 0) { + dev_err(dev, "failed to de-assert reset line: %d\n", ret); + return ret; + } + + /* put pll and phy into reset state */ + setbits_le32(priv->regs + RESET, + RESET_SEL_PLLRESET | RESET_PLLRESET | + RESET_SEL_P1RESET | RESET_PHYRST_1 | + RESET_SEL_P2RESET | RESET_PHYRST_2); + + return 0; +} + +static const struct udevice_id rzg2l_usbphy_ctrl_ids[] = { + { .compatible = "renesas,rzg2l-usbphy-ctrl", }, + { /* sentinel */ } +}; + +static int rzg2l_usbphy_ctrl_bind(struct udevice *dev) +{ + struct driver *drv; + ofnode node; + int ret; + + node = ofnode_find_subnode(dev_ofnode(dev), "regulator-vbus"); + if (!ofnode_valid(node)) { + dev_err(dev, "Failed to find vbus regulator devicetree node\n"); + return -ENOENT; + } + + drv = lists_driver_lookup_name("rzg2l_usbphy_regulator"); + if (!drv) { + dev_err(dev, "Failed to find vbus regulator driver\n"); + return -ENOENT; + } + + ret = device_bind(dev, drv, dev->name, NULL, node, NULL); + if (ret) { + dev_err(dev, "Failed to bind vbus regulator: %d\n", ret); + return ret; + } + + return 0; +} + +U_BOOT_DRIVER(rzg2l_usbphy_ctrl) = { + .name = "rzg2l_usbphy_ctrl", + .id = UCLASS_RESET, + .of_match = rzg2l_usbphy_ctrl_ids, + .bind = rzg2l_usbphy_ctrl_bind, + .probe = rzg2l_usbphy_ctrl_probe, + .ops = &rzg2l_usbphy_ctrl_ops, + .priv_auto = sizeof(struct rzg2l_usbphy_ctrl_priv), +}; diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c index 76d108080d9..e57729f0ef9 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c @@ -23,6 +23,7 @@ #include <linux/bitops.h> #include <linux/io.h> #include <linux/sizes.h> +#include <linux/kconfig.h> #define BANK_INCREMENT 4 #define NR_BANKS 8 @@ -114,6 +115,8 @@ static int socfpga_reset_remove(struct udevice *dev) if (socfpga_reset_keep_enabled()) { puts("Deasserting all peripheral resets\n"); writel(0, data->modrst_base + 4); + if (IS_ENABLED(CONFIG_TARGET_SOCFPGA_ARRIA10)) + writel(0, data->modrst_base + 8); } return 0; diff --git a/drivers/reset/reset-spacemit-k1.c b/drivers/reset/reset-spacemit-k1.c new file mode 100644 index 00000000000..613e002fc4f --- /dev/null +++ b/drivers/reset/reset-spacemit-k1.c @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Spacemit Inc. + * Copyright (C) 2025 Huan Zhou <pericycle.cc@gmail.com> + */ + +#include <asm/io.h> +#include <config.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dt-bindings/reset/spacemit-k1-reset.h> +#include <linux/bitops.h> +#include <reset-uclass.h> + +/* APBC register offset */ +#define APBC_UART1_CLK_RST 0x0 +#define APBC_UART2_CLK_RST 0x4 +#define APBC_GPIO_CLK_RST 0x8 +#define APBC_PWM0_CLK_RST 0xc +#define APBC_PWM1_CLK_RST 0x10 +#define APBC_PWM2_CLK_RST 0x14 +#define APBC_PWM3_CLK_RST 0x18 +#define APBC_TWSI8_CLK_RST 0x20 +#define APBC_UART3_CLK_RST 0x24 +#define APBC_RTC_CLK_RST 0x28 +#define APBC_TWSI0_CLK_RST 0x2c +#define APBC_TWSI1_CLK_RST 0x30 +#define APBC_TIMERS1_CLK_RST 0x34 +#define APBC_TWSI2_CLK_RST 0x38 +#define APBC_AIB_CLK_RST 0x3c +#define APBC_TWSI4_CLK_RST 0x40 +#define APBC_TIMERS2_CLK_RST 0x44 +#define APBC_ONEWIRE_CLK_RST 0x48 +#define APBC_TWSI5_CLK_RST 0x4c +#define APBC_DRO_CLK_RST 0x58 +#define APBC_IR_CLK_RST 0x5c +#define APBC_TWSI6_CLK_RST 0x60 +#define APBC_TWSI7_CLK_RST 0x68 +#define APBC_TSEN_CLK_RST 0x6c + +#define APBC_UART4_CLK_RST 0x70 +#define APBC_UART5_CLK_RST 0x74 +#define APBC_UART6_CLK_RST 0x78 +#define APBC_SSP3_CLK_RST 0x7c + +#define APBC_SSPA0_CLK_RST 0x80 +#define APBC_SSPA1_CLK_RST 0x84 + +#define APBC_IPC_AP2AUD_CLK_RST 0x90 +#define APBC_UART7_CLK_RST 0x94 +#define APBC_UART8_CLK_RST 0x98 +#define APBC_UART9_CLK_RST 0x9c + +#define APBC_CAN0_CLK_RST 0xa0 +#define APBC_PWM4_CLK_RST 0xa8 +#define APBC_PWM5_CLK_RST 0xac +#define APBC_PWM6_CLK_RST 0xb0 +#define APBC_PWM7_CLK_RST 0xb4 +#define APBC_PWM8_CLK_RST 0xb8 +#define APBC_PWM9_CLK_RST 0xbc +#define APBC_PWM10_CLK_RST 0xc0 +#define APBC_PWM11_CLK_RST 0xc4 +#define APBC_PWM12_CLK_RST 0xc8 +#define APBC_PWM13_CLK_RST 0xcc +#define APBC_PWM14_CLK_RST 0xd0 +#define APBC_PWM15_CLK_RST 0xd4 +#define APBC_PWM16_CLK_RST 0xd8 +#define APBC_PWM17_CLK_RST 0xdc +#define APBC_PWM18_CLK_RST 0xe0 +#define APBC_PWM19_CLK_RST 0xe4 +/* end of APBC register offset */ + +/* MPMU register offset */ +#define MPMU_WDTPCR 0x200 +/* end of MPMU register offset */ + +/* APMU register offset */ +#define APMU_JPG_CLK_RES_CTRL 0x20 +#define APMU_CSI_CCIC2_CLK_RES_CTRL 0x24 +#define APMU_ISP_CLK_RES_CTRL 0x38 +#define APMU_LCD_CLK_RES_CTRL1 0x44 +#define APMU_LCD_SPI_CLK_RES_CTRL 0x48 +#define APMU_LCD_CLK_RES_CTRL2 0x4c +#define APMU_CCIC_CLK_RES_CTRL 0x50 +#define APMU_SDH0_CLK_RES_CTRL 0x54 +#define APMU_SDH1_CLK_RES_CTRL 0x58 +#define APMU_USB_CLK_RES_CTRL 0x5c +#define APMU_QSPI_CLK_RES_CTRL 0x60 +#define APMU_USB_CLK_RES_CTRL 0x5c +#define APMU_DMA_CLK_RES_CTRL 0x64 +#define APMU_AES_CLK_RES_CTRL 0x68 +#define APMU_VPU_CLK_RES_CTRL 0xa4 +#define APMU_GPU_CLK_RES_CTRL 0xcc +#define APMU_SDH2_CLK_RES_CTRL 0xe0 +#define APMU_PMUA_MC_CTRL 0xe8 +#define APMU_PMU_CC2_AP 0x100 +#define APMU_PMUA_EM_CLK_RES_CTRL 0x104 + +#define APMU_AUDIO_CLK_RES_CTRL 0x14c +#define APMU_HDMI_CLK_RES_CTRL 0x1B8 + +#define APMU_PCIE_CLK_RES_CTRL_0 0x3cc +#define APMU_PCIE_CLK_RES_CTRL_1 0x3d4 +#define APMU_PCIE_CLK_RES_CTRL_2 0x3dc + +#define APMU_EMAC0_CLK_RES_CTRL 0x3e4 +#define APMU_EMAC1_CLK_RES_CTRL 0x3ec +/* end of APMU register offset */ + +/* APBC2 register offset */ +#define APBC2_UART1_CLK_RST 0x00 +#define APBC2_SSP2_CLK_RST 0x04 +#define APBC2_TWSI3_CLK_RST 0x08 +#define APBC2_RTC_CLK_RST 0x0c +#define APBC2_TIMERS0_CLK_RST 0x10 +#define APBC2_KPC_CLK_RST 0x14 +#define APBC2_GPIO_CLK_RST 0x1c +/* end of APBC2 register offset */ + +enum spacemit_reset_base_type { + RST_BASE_TYPE_MPMU = 0, + RST_BASE_TYPE_APMU = 1, + RST_BASE_TYPE_APBC = 2, + RST_BASE_TYPE_APBS = 3, + RST_BASE_TYPE_CIU = 4, + RST_BASE_TYPE_DCIU = 5, + RST_BASE_TYPE_DDRC = 6, + RST_BASE_TYPE_AUDC = 7, + RST_BASE_TYPE_APBC2 = 8, +}; + +struct spacemit_reset_signal { + u32 offset; + u32 mask; + u32 deassert_val; + u32 assert_val; + enum spacemit_reset_base_type type; +}; + +struct spacemit_reset_base { + void __iomem *mpmu_base; + void __iomem *apmu_base; + void __iomem *apbc_base; + void __iomem *apbs_base; + void __iomem *ciu_base; + void __iomem *dciu_base; + void __iomem *ddrc_base; + void __iomem *audio_ctrl_base; + void __iomem *apbc2_base; +}; + +struct spacemit_reset { + struct spacemit_reset_base io_base; + const struct spacemit_reset_signal *signals; +}; + +enum { + RESET_TWSI6_SPL = 0, + RESET_TWSI8_SPL, + RESET_SDH_AXI_SPL, + RESET_SDH0_SPL, + RESET_USB_AXI_SPL, + RESET_USBP1_AXI_SPL, + RESET_USB3_0_SPL, + RESET_QSPI_SPL, + RESET_QSPI_BUS_SPL, + RESET_AES_SPL, + RESET_SDH2_SPL, + RESET_NUMBER_SPL +}; + +static const struct spacemit_reset_signal + k1_reset_signals[RESET_NUMBER] = { + [RESET_UART1] = { APBC_UART1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART2] = { APBC_UART2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_GPIO] = { APBC_GPIO_CLK_RST, BIT(2), 0, + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM0] = { APBC_PWM0_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM1] = { APBC_PWM1_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM2] = { APBC_PWM2_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM3] = { APBC_PWM3_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM4] = { APBC_PWM4_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM5] = { APBC_PWM5_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM6] = { APBC_PWM6_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM7] = { APBC_PWM7_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM8] = { APBC_PWM8_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM9] = { APBC_PWM9_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM10] = { APBC_PWM10_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM11] = { APBC_PWM11_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM12] = { APBC_PWM12_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM13] = { APBC_PWM13_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM14] = { APBC_PWM14_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM15] = { APBC_PWM15_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM16] = { APBC_PWM16_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM17] = { APBC_PWM17_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM18] = { APBC_PWM18_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM19] = { APBC_PWM19_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_SSP3] = { APBC_SSP3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART3] = { APBC_UART3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_RTC] = { APBC_RTC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI0] = { APBC_TWSI0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TIMERS1] = { APBC_TIMERS1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_AIB] = { APBC_AIB_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TIMERS2] = { APBC_TIMERS2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_ONEWIRE] = { APBC_ONEWIRE_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_SSPA0] = { APBC_SSPA0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_SSPA1] = { APBC_SSPA1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_DRO] = { APBC_DRO_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_IR] = { APBC_IR_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI1] = { APBC_TWSI1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TSEN] = { APBC_TSEN_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI2] = { APBC_TWSI2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI4] = { APBC_TWSI4_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI5] = { APBC_TWSI5_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI6] = { APBC_TWSI6_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI7] = { APBC_TWSI7_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI8] = { APBC_TWSI8_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_IPC_AP2AUD] = { APBC_IPC_AP2AUD_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART4] = { APBC_UART4_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART5] = { APBC_UART5_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART6] = { APBC_UART6_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART7] = { APBC_UART7_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART8] = { APBC_UART8_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART9] = { APBC_UART9_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_CAN0] = { APBC_CAN0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + /* MPMU */ + [RESET_WDT] = { MPMU_WDTPCR, BIT(2), 0, BIT(2), RST_BASE_TYPE_MPMU }, + /* APMU */ + [RESET_JPG] = { APMU_JPG_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_CSI] = { APMU_CSI_CCIC2_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_CCIC2_PHY] = { APMU_CSI_CCIC2_CLK_RES_CTRL, + BIT(2), + BIT(2), + 0, + RST_BASE_TYPE_APMU }, + [RESET_CCIC3_PHY] = { APMU_CSI_CCIC2_CLK_RES_CTRL, + BIT(29), + BIT(29), + 0, + RST_BASE_TYPE_APMU }, + [RESET_ISP] = { APMU_ISP_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_ISP_AHB] = { APMU_ISP_CLK_RES_CTRL, BIT(3), BIT(3), 0, RST_BASE_TYPE_APMU }, + [RESET_ISP_CI] = { APMU_ISP_CLK_RES_CTRL, BIT(16), BIT(16), 0, RST_BASE_TYPE_APMU }, + [RESET_ISP_CPP] = { APMU_ISP_CLK_RES_CTRL, BIT(27), BIT(27), 0, RST_BASE_TYPE_APMU }, + [RESET_LCD] = { APMU_LCD_CLK_RES_CTRL1, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU }, + [RESET_DSI_ESC] = { APMU_LCD_CLK_RES_CTRL1, BIT(3), BIT(3), 0, RST_BASE_TYPE_APMU }, + [RESET_V2D] = { APMU_LCD_CLK_RES_CTRL1, BIT(27), BIT(27), 0, RST_BASE_TYPE_APMU }, + [RESET_MIPI] = { APMU_LCD_CLK_RES_CTRL1, BIT(15), BIT(15), 0, RST_BASE_TYPE_APMU }, + [RESET_LCD_SPI] = { APMU_LCD_SPI_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_LCD_SPI_BUS] = { APMU_LCD_SPI_CLK_RES_CTRL, + BIT(4), + BIT(4), + 0, + RST_BASE_TYPE_APMU }, + [RESET_LCD_SPI_HBUS] = { APMU_LCD_SPI_CLK_RES_CTRL, + BIT(2), + BIT(2), + 0, + RST_BASE_TYPE_APMU }, + [RESET_LCD_MCLK] = { APMU_LCD_CLK_RES_CTRL2, BIT(9), BIT(9), 0, RST_BASE_TYPE_APMU }, + [RESET_CCIC_4X] = { APMU_CCIC_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_CCIC1_PHY] = { APMU_CCIC_CLK_RES_CTRL, BIT(2), BIT(2), 0, RST_BASE_TYPE_APMU }, + [RESET_SDH_AXI] = { APMU_SDH0_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_SDH0] = { APMU_SDH0_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_SDH1] = { APMU_SDH1_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_USB_AXI] = { APMU_USB_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_USBP1_AXI] = { APMU_USB_CLK_RES_CTRL, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU }, + [RESET_USB3_0] = { APMU_USB_CLK_RES_CTRL, + BIT(9) | BIT(10) | BIT(11), + BIT(9) | BIT(10) | BIT(11), + 0, + RST_BASE_TYPE_APMU }, + [RESET_QSPI] = { APMU_QSPI_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_QSPI_BUS] = { APMU_QSPI_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_DMA] = { APMU_DMA_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_AES] = { APMU_AES_CLK_RES_CTRL, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU }, + [RESET_VPU] = { APMU_VPU_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_GPU] = { APMU_GPU_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_SDH2] = { APMU_SDH2_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_MC] = { APMU_PMUA_MC_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_EM_AXI] = { APMU_PMUA_EM_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_EM] = { APMU_PMUA_EM_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_AUDIO_SYS] = { APMU_AUDIO_CLK_RES_CTRL, + BIT(0) | BIT(2) | BIT(3), + BIT(0) | BIT(2) | BIT(3), + 0, + RST_BASE_TYPE_APMU }, + [RESET_HDMI] = { APMU_HDMI_CLK_RES_CTRL, BIT(9), BIT(9), 0, RST_BASE_TYPE_APMU }, + [RESET_PCIE0] = { APMU_PCIE_CLK_RES_CTRL_0, + BIT(3) | BIT(4) | BIT(5) | BIT(8), + BIT(3) | BIT(4) | BIT(5), + BIT(8), + RST_BASE_TYPE_APMU }, + [RESET_PCIE1] = { APMU_PCIE_CLK_RES_CTRL_1, + BIT(3) | BIT(4) | BIT(5) | BIT(8), + BIT(3) | BIT(4) | BIT(5), + BIT(8), + RST_BASE_TYPE_APMU }, + [RESET_PCIE2] = { APMU_PCIE_CLK_RES_CTRL_2, 0x138, 0x38, 0x100, RST_BASE_TYPE_APMU }, + [RESET_EMAC0] = { APMU_EMAC0_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_EMAC1] = { APMU_EMAC1_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_SEC_UART1] = { APBC2_UART1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + [RESET_SEC_SSP2] = { APBC2_SSP2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + [RESET_SEC_TWSI3] = { APBC2_TWSI3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + [RESET_SEC_RTC] = { APBC2_RTC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + [RESET_SEC_TIMERS0] = { APBC2_TIMERS0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + [RESET_SEC_KPC] = { APBC2_KPC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + [RESET_SEC_GPIO] = { APBC2_GPIO_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + }; + +static u32 spacemit_reset_read(struct spacemit_reset *reset, u32 id) +{ + void __iomem *base; + + switch (reset->signals[id].type) { + case RST_BASE_TYPE_APMU: + base = reset->io_base.apmu_base; + break; + case RST_BASE_TYPE_APBC: + base = reset->io_base.apbc_base; + break; + default: + base = reset->io_base.apbc_base; + break; + } + + return readl(base + reset->signals[id].offset); +} + +static void spacemit_reset_write(struct spacemit_reset *reset, u32 value, u32 id) +{ + void __iomem *base; + + switch (reset->signals[id].type) { + case RST_BASE_TYPE_APMU: + base = reset->io_base.apmu_base; + break; + case RST_BASE_TYPE_APBC: + base = reset->io_base.apbc_base; + break; + default: + base = reset->io_base.apbc_base; + break; + } + + writel(value, base + reset->signals[id].offset); +} + +static void spacemit_reset_set(struct reset_ctl *rst, u32 id, bool assert) +{ + u32 value; + struct spacemit_reset *reset = dev_get_priv(rst->dev); + + value = spacemit_reset_read(reset, id); + + if (assert) { + value &= ~reset->signals[id].mask; + value |= reset->signals[id].assert_val; + } else { + value &= ~reset->signals[id].mask; + value |= reset->signals[id].deassert_val; + } + + spacemit_reset_write(reset, value, id); +} + +static int spacemit_reset_update(struct reset_ctl *rst, bool assert) +{ + if (rst->id < RESET_UART1 || rst->id >= RESET_NUMBER) + return 0; + + /* can not write to twsi8 */ + if (rst->id == RESET_TWSI8) + return 0; + + spacemit_reset_set(rst, rst->id, assert); + return 0; +} + +static int spacemit_reset_assert(struct reset_ctl *rst) +{ + return spacemit_reset_update(rst, true); +} + +static int spacemit_reset_deassert(struct reset_ctl *rst) +{ + return spacemit_reset_update(rst, false); +} + +static int spacemit_k1_reset_probe(struct udevice *dev) +{ + struct spacemit_reset *reset = dev_get_priv(dev); + + reset->io_base.mpmu_base = (void *)dev_remap_addr_index(dev, 0); + if (!reset->io_base.mpmu_base) { + pr_err("failed to map mpmu registers\n"); + goto out; + } + + reset->io_base.apmu_base = (void *)dev_remap_addr_index(dev, 1); + if (!reset->io_base.apmu_base) { + pr_err("failed to map apmu registers\n"); + goto out; + } + + reset->io_base.apbc_base = (void *)dev_remap_addr_index(dev, 2); + if (!reset->io_base.apbc_base) { + pr_err("failed to map apbc registers\n"); + goto out; + } + + reset->io_base.apbs_base = (void *)dev_remap_addr_index(dev, 3); + if (!reset->io_base.apbs_base) { + pr_err("failed to map apbs registers\n"); + goto out; + } + + reset->io_base.ciu_base = (void *)dev_remap_addr_index(dev, 4); + if (!reset->io_base.ciu_base) { + pr_err("failed to map ciu registers\n"); + goto out; + } + + reset->io_base.dciu_base = (void *)dev_remap_addr_index(dev, 5); + if (!reset->io_base.dciu_base) { + pr_err("failed to map dragon ciu registers\n"); + goto out; + } + + reset->io_base.ddrc_base = (void *)dev_remap_addr_index(dev, 6); + if (!reset->io_base.ddrc_base) { + pr_err("failed to map ddrc registers\n"); + goto out; + } + + reset->io_base.apbc2_base = (void *)dev_remap_addr_index(dev, 7); + if (!reset->io_base.apbc2_base) { + pr_err("failed to map apbc2 registers\n"); + goto out; + } + + reset->signals = k1_reset_signals; + +out: + return 0; +} + +const struct reset_ops k1_reset_ops = { + .rst_assert = spacemit_reset_assert, + .rst_deassert = spacemit_reset_deassert, +}; + +static const struct udevice_id k1_reset_ids[] = { + { .compatible = "spacemit,k1-reset", }, + {}, +}; + +U_BOOT_DRIVER(k1_reset) = { + .name = "spacemit,k1-reset", + .id = UCLASS_RESET, + .ops = &k1_reset_ops, + .of_match = k1_reset_ids, + .probe = spacemit_k1_reset_probe, + .priv_auto = sizeof(struct spacemit_reset), +}; diff --git a/drivers/reset/rst-rk3528.c b/drivers/reset/rst-rk3528.c new file mode 100644 index 00000000000..f6e760d468d --- /dev/null +++ b/drivers/reset/rst-rk3528.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + * Based on Sebastian Reichel's implementation for RK3588 + */ + +#include <dm.h> +#include <asm/arch-rockchip/clock.h> +#include <dt-bindings/reset/rockchip,rk3528-cru.h> + +/* 0xFF4A0000 + 0x0A00 */ +#define RK3528_CRU_RESET_OFFSET(id, reg, bit) [id] = (0 + reg * 16 + bit) + +/* mapping table for reset ID to register offset */ +static const int rk3528_register_offset[] = { + /* CRU_SOFTRST_CON03 */ + RK3528_CRU_RESET_OFFSET(SRST_CORE0_PO, 3, 0), + RK3528_CRU_RESET_OFFSET(SRST_CORE1_PO, 3, 1), + RK3528_CRU_RESET_OFFSET(SRST_CORE2_PO, 3, 2), + RK3528_CRU_RESET_OFFSET(SRST_CORE3_PO, 3, 3), + RK3528_CRU_RESET_OFFSET(SRST_CORE0, 3, 4), + RK3528_CRU_RESET_OFFSET(SRST_CORE1, 3, 5), + RK3528_CRU_RESET_OFFSET(SRST_CORE2, 3, 6), + RK3528_CRU_RESET_OFFSET(SRST_CORE3, 3, 7), + RK3528_CRU_RESET_OFFSET(SRST_NL2, 3, 8), + RK3528_CRU_RESET_OFFSET(SRST_CORE_BIU, 3, 9), + RK3528_CRU_RESET_OFFSET(SRST_CORE_CRYPTO, 3, 10), + + /* CRU_SOFTRST_CON05 */ + RK3528_CRU_RESET_OFFSET(SRST_P_DBG, 5, 13), + RK3528_CRU_RESET_OFFSET(SRST_POT_DBG, 5, 14), + RK3528_CRU_RESET_OFFSET(SRST_NT_DBG, 5, 15), + + /* CRU_SOFTRST_CON06 */ + RK3528_CRU_RESET_OFFSET(SRST_P_CORE_GRF, 6, 2), + RK3528_CRU_RESET_OFFSET(SRST_P_DAPLITE_BIU, 6, 3), + RK3528_CRU_RESET_OFFSET(SRST_P_CPU_BIU, 6, 4), + RK3528_CRU_RESET_OFFSET(SRST_REF_PVTPLL_CORE, 6, 7), + + /* CRU_SOFTRST_CON08 */ + RK3528_CRU_RESET_OFFSET(SRST_A_BUS_VOPGL_BIU, 8, 1), + RK3528_CRU_RESET_OFFSET(SRST_A_BUS_H_BIU, 8, 3), + RK3528_CRU_RESET_OFFSET(SRST_A_SYSMEM_BIU, 8, 8), + RK3528_CRU_RESET_OFFSET(SRST_A_BUS_BIU, 8, 10), + RK3528_CRU_RESET_OFFSET(SRST_H_BUS_BIU, 8, 11), + RK3528_CRU_RESET_OFFSET(SRST_P_BUS_BIU, 8, 12), + RK3528_CRU_RESET_OFFSET(SRST_P_DFT2APB, 8, 13), + RK3528_CRU_RESET_OFFSET(SRST_P_BUS_GRF, 8, 15), + + /* CRU_SOFTRST_CON09 */ + RK3528_CRU_RESET_OFFSET(SRST_A_BUS_M_BIU, 9, 0), + RK3528_CRU_RESET_OFFSET(SRST_A_GIC, 9, 1), + RK3528_CRU_RESET_OFFSET(SRST_A_SPINLOCK, 9, 2), + RK3528_CRU_RESET_OFFSET(SRST_A_DMAC, 9, 4), + RK3528_CRU_RESET_OFFSET(SRST_P_TIMER, 9, 5), + RK3528_CRU_RESET_OFFSET(SRST_TIMER0, 9, 6), + RK3528_CRU_RESET_OFFSET(SRST_TIMER1, 9, 7), + RK3528_CRU_RESET_OFFSET(SRST_TIMER2, 9, 8), + RK3528_CRU_RESET_OFFSET(SRST_TIMER3, 9, 9), + RK3528_CRU_RESET_OFFSET(SRST_TIMER4, 9, 10), + RK3528_CRU_RESET_OFFSET(SRST_TIMER5, 9, 11), + RK3528_CRU_RESET_OFFSET(SRST_P_JDBCK_DAP, 9, 12), + RK3528_CRU_RESET_OFFSET(SRST_JDBCK_DAP, 9, 13), + RK3528_CRU_RESET_OFFSET(SRST_P_WDT_NS, 9, 15), + + /* CRU_SOFTRST_CON10 */ + RK3528_CRU_RESET_OFFSET(SRST_T_WDT_NS, 10, 0), + RK3528_CRU_RESET_OFFSET(SRST_H_TRNG_NS, 10, 3), + RK3528_CRU_RESET_OFFSET(SRST_P_UART0, 10, 7), + RK3528_CRU_RESET_OFFSET(SRST_S_UART0, 10, 8), + RK3528_CRU_RESET_OFFSET(SRST_PKA_CRYPTO, 10, 10), + RK3528_CRU_RESET_OFFSET(SRST_A_CRYPTO, 10, 11), + RK3528_CRU_RESET_OFFSET(SRST_H_CRYPTO, 10, 12), + RK3528_CRU_RESET_OFFSET(SRST_P_DMA2DDR, 10, 13), + RK3528_CRU_RESET_OFFSET(SRST_A_DMA2DDR, 10, 14), + + /* CRU_SOFTRST_CON11 */ + RK3528_CRU_RESET_OFFSET(SRST_P_PWM0, 11, 4), + RK3528_CRU_RESET_OFFSET(SRST_PWM0, 11, 5), + RK3528_CRU_RESET_OFFSET(SRST_P_PWM1, 11, 7), + RK3528_CRU_RESET_OFFSET(SRST_PWM1, 11, 8), + RK3528_CRU_RESET_OFFSET(SRST_P_SCR, 11, 10), + RK3528_CRU_RESET_OFFSET(SRST_A_DCF, 11, 11), + RK3528_CRU_RESET_OFFSET(SRST_P_INTMUX, 11, 12), + + /* CRU_SOFTRST_CON25 */ + RK3528_CRU_RESET_OFFSET(SRST_A_VPU_BIU, 25, 6), + RK3528_CRU_RESET_OFFSET(SRST_H_VPU_BIU, 25, 7), + RK3528_CRU_RESET_OFFSET(SRST_P_VPU_BIU, 25, 8), + RK3528_CRU_RESET_OFFSET(SRST_A_VPU, 25, 9), + RK3528_CRU_RESET_OFFSET(SRST_H_VPU, 25, 10), + RK3528_CRU_RESET_OFFSET(SRST_P_CRU_PCIE, 25, 11), + RK3528_CRU_RESET_OFFSET(SRST_P_VPU_GRF, 25, 12), + RK3528_CRU_RESET_OFFSET(SRST_H_SFC, 25, 13), + RK3528_CRU_RESET_OFFSET(SRST_S_SFC, 25, 14), + RK3528_CRU_RESET_OFFSET(SRST_C_EMMC, 25, 15), + + /* CRU_SOFTRST_CON26 */ + RK3528_CRU_RESET_OFFSET(SRST_H_EMMC, 26, 0), + RK3528_CRU_RESET_OFFSET(SRST_A_EMMC, 26, 1), + RK3528_CRU_RESET_OFFSET(SRST_B_EMMC, 26, 2), + RK3528_CRU_RESET_OFFSET(SRST_T_EMMC, 26, 3), + RK3528_CRU_RESET_OFFSET(SRST_P_GPIO1, 26, 4), + RK3528_CRU_RESET_OFFSET(SRST_DB_GPIO1, 26, 5), + RK3528_CRU_RESET_OFFSET(SRST_A_VPU_L_BIU, 26, 6), + RK3528_CRU_RESET_OFFSET(SRST_P_VPU_IOC, 26, 8), + RK3528_CRU_RESET_OFFSET(SRST_H_SAI_I2S0, 26, 9), + RK3528_CRU_RESET_OFFSET(SRST_M_SAI_I2S0, 26, 10), + RK3528_CRU_RESET_OFFSET(SRST_H_SAI_I2S2, 26, 11), + RK3528_CRU_RESET_OFFSET(SRST_M_SAI_I2S2, 26, 12), + RK3528_CRU_RESET_OFFSET(SRST_P_ACODEC, 26, 13), + + /* CRU_SOFTRST_CON27 */ + RK3528_CRU_RESET_OFFSET(SRST_P_GPIO3, 27, 0), + RK3528_CRU_RESET_OFFSET(SRST_DB_GPIO3, 27, 1), + RK3528_CRU_RESET_OFFSET(SRST_P_SPI1, 27, 4), + RK3528_CRU_RESET_OFFSET(SRST_SPI1, 27, 5), + RK3528_CRU_RESET_OFFSET(SRST_P_UART2, 27, 7), + RK3528_CRU_RESET_OFFSET(SRST_S_UART2, 27, 8), + RK3528_CRU_RESET_OFFSET(SRST_P_UART5, 27, 9), + RK3528_CRU_RESET_OFFSET(SRST_S_UART5, 27, 10), + RK3528_CRU_RESET_OFFSET(SRST_P_UART6, 27, 11), + RK3528_CRU_RESET_OFFSET(SRST_S_UART6, 27, 12), + RK3528_CRU_RESET_OFFSET(SRST_P_UART7, 27, 13), + RK3528_CRU_RESET_OFFSET(SRST_S_UART7, 27, 14), + RK3528_CRU_RESET_OFFSET(SRST_P_I2C3, 27, 15), + + /* CRU_SOFTRST_CON28 */ + RK3528_CRU_RESET_OFFSET(SRST_I2C3, 28, 0), + RK3528_CRU_RESET_OFFSET(SRST_P_I2C5, 28, 1), + RK3528_CRU_RESET_OFFSET(SRST_I2C5, 28, 2), + RK3528_CRU_RESET_OFFSET(SRST_P_I2C6, 28, 3), + RK3528_CRU_RESET_OFFSET(SRST_I2C6, 28, 4), + RK3528_CRU_RESET_OFFSET(SRST_A_MAC, 28, 5), + + /* CRU_SOFTRST_CON30 */ + RK3528_CRU_RESET_OFFSET(SRST_P_PCIE, 30, 1), + RK3528_CRU_RESET_OFFSET(SRST_PCIE_PIPE_PHY, 30, 2), + RK3528_CRU_RESET_OFFSET(SRST_PCIE_POWER_UP, 30, 3), + RK3528_CRU_RESET_OFFSET(SRST_P_PCIE_PHY, 30, 6), + RK3528_CRU_RESET_OFFSET(SRST_P_PIPE_GRF, 30, 7), + + /* CRU_SOFTRST_CON32 */ + RK3528_CRU_RESET_OFFSET(SRST_H_SDIO0, 32, 2), + RK3528_CRU_RESET_OFFSET(SRST_H_SDIO1, 32, 4), + RK3528_CRU_RESET_OFFSET(SRST_TS_0, 32, 5), + RK3528_CRU_RESET_OFFSET(SRST_TS_1, 32, 6), + RK3528_CRU_RESET_OFFSET(SRST_P_CAN2, 32, 7), + RK3528_CRU_RESET_OFFSET(SRST_CAN2, 32, 8), + RK3528_CRU_RESET_OFFSET(SRST_P_CAN3, 32, 9), + RK3528_CRU_RESET_OFFSET(SRST_CAN3, 32, 10), + RK3528_CRU_RESET_OFFSET(SRST_P_SARADC, 32, 11), + RK3528_CRU_RESET_OFFSET(SRST_SARADC, 32, 12), + RK3528_CRU_RESET_OFFSET(SRST_SARADC_PHY, 32, 13), + RK3528_CRU_RESET_OFFSET(SRST_P_TSADC, 32, 14), + RK3528_CRU_RESET_OFFSET(SRST_TSADC, 32, 15), + + /* CRU_SOFTRST_CON33 */ + RK3528_CRU_RESET_OFFSET(SRST_A_USB3OTG, 33, 1), + + /* CRU_SOFTRST_CON34 */ + RK3528_CRU_RESET_OFFSET(SRST_A_GPU_BIU, 34, 3), + RK3528_CRU_RESET_OFFSET(SRST_P_GPU_BIU, 34, 5), + RK3528_CRU_RESET_OFFSET(SRST_A_GPU, 34, 8), + RK3528_CRU_RESET_OFFSET(SRST_REF_PVTPLL_GPU, 34, 9), + + /* CRU_SOFTRST_CON36 */ + RK3528_CRU_RESET_OFFSET(SRST_H_RKVENC_BIU, 36, 3), + RK3528_CRU_RESET_OFFSET(SRST_A_RKVENC_BIU, 36, 4), + RK3528_CRU_RESET_OFFSET(SRST_P_RKVENC_BIU, 36, 5), + RK3528_CRU_RESET_OFFSET(SRST_H_RKVENC, 36, 6), + RK3528_CRU_RESET_OFFSET(SRST_A_RKVENC, 36, 7), + RK3528_CRU_RESET_OFFSET(SRST_CORE_RKVENC, 36, 8), + RK3528_CRU_RESET_OFFSET(SRST_H_SAI_I2S1, 36, 9), + RK3528_CRU_RESET_OFFSET(SRST_M_SAI_I2S1, 36, 10), + RK3528_CRU_RESET_OFFSET(SRST_P_I2C1, 36, 11), + RK3528_CRU_RESET_OFFSET(SRST_I2C1, 36, 12), + RK3528_CRU_RESET_OFFSET(SRST_P_I2C0, 36, 13), + RK3528_CRU_RESET_OFFSET(SRST_I2C0, 36, 14), + + /* CRU_SOFTRST_CON37 */ + RK3528_CRU_RESET_OFFSET(SRST_P_SPI0, 37, 2), + RK3528_CRU_RESET_OFFSET(SRST_SPI0, 37, 3), + RK3528_CRU_RESET_OFFSET(SRST_P_GPIO4, 37, 8), + RK3528_CRU_RESET_OFFSET(SRST_DB_GPIO4, 37, 9), + RK3528_CRU_RESET_OFFSET(SRST_P_RKVENC_IOC, 37, 10), + RK3528_CRU_RESET_OFFSET(SRST_H_SPDIF, 37, 14), + RK3528_CRU_RESET_OFFSET(SRST_M_SPDIF, 37, 15), + + /* CRU_SOFTRST_CON38 */ + RK3528_CRU_RESET_OFFSET(SRST_H_PDM, 38, 0), + RK3528_CRU_RESET_OFFSET(SRST_M_PDM, 38, 1), + RK3528_CRU_RESET_OFFSET(SRST_P_UART1, 38, 2), + RK3528_CRU_RESET_OFFSET(SRST_S_UART1, 38, 3), + RK3528_CRU_RESET_OFFSET(SRST_P_UART3, 38, 4), + RK3528_CRU_RESET_OFFSET(SRST_S_UART3, 38, 5), + RK3528_CRU_RESET_OFFSET(SRST_P_RKVENC_GRF, 38, 6), + RK3528_CRU_RESET_OFFSET(SRST_P_CAN0, 38, 7), + RK3528_CRU_RESET_OFFSET(SRST_CAN0, 38, 8), + RK3528_CRU_RESET_OFFSET(SRST_P_CAN1, 38, 9), + RK3528_CRU_RESET_OFFSET(SRST_CAN1, 38, 10), + + /* CRU_SOFTRST_CON39 */ + RK3528_CRU_RESET_OFFSET(SRST_A_VO_BIU, 39, 3), + RK3528_CRU_RESET_OFFSET(SRST_H_VO_BIU, 39, 4), + RK3528_CRU_RESET_OFFSET(SRST_P_VO_BIU, 39, 5), + RK3528_CRU_RESET_OFFSET(SRST_H_RGA2E, 39, 7), + RK3528_CRU_RESET_OFFSET(SRST_A_RGA2E, 39, 8), + RK3528_CRU_RESET_OFFSET(SRST_CORE_RGA2E, 39, 9), + RK3528_CRU_RESET_OFFSET(SRST_H_VDPP, 39, 10), + RK3528_CRU_RESET_OFFSET(SRST_A_VDPP, 39, 11), + RK3528_CRU_RESET_OFFSET(SRST_CORE_VDPP, 39, 12), + RK3528_CRU_RESET_OFFSET(SRST_P_VO_GRF, 39, 13), + RK3528_CRU_RESET_OFFSET(SRST_P_CRU, 39, 15), + + /* CRU_SOFTRST_CON40 */ + RK3528_CRU_RESET_OFFSET(SRST_A_VOP_BIU, 40, 1), + RK3528_CRU_RESET_OFFSET(SRST_H_VOP, 40, 2), + RK3528_CRU_RESET_OFFSET(SRST_D_VOP0, 40, 3), + RK3528_CRU_RESET_OFFSET(SRST_D_VOP1, 40, 4), + RK3528_CRU_RESET_OFFSET(SRST_A_VOP, 40, 5), + RK3528_CRU_RESET_OFFSET(SRST_P_HDMI, 40, 6), + RK3528_CRU_RESET_OFFSET(SRST_HDMI, 40, 7), + RK3528_CRU_RESET_OFFSET(SRST_P_HDMIPHY, 40, 14), + RK3528_CRU_RESET_OFFSET(SRST_H_HDCP_KEY, 40, 15), + + /* CRU_SOFTRST_CON41 */ + RK3528_CRU_RESET_OFFSET(SRST_A_HDCP, 41, 0), + RK3528_CRU_RESET_OFFSET(SRST_H_HDCP, 41, 1), + RK3528_CRU_RESET_OFFSET(SRST_P_HDCP, 41, 2), + RK3528_CRU_RESET_OFFSET(SRST_H_CVBS, 41, 3), + RK3528_CRU_RESET_OFFSET(SRST_D_CVBS_VOP, 41, 4), + RK3528_CRU_RESET_OFFSET(SRST_D_4X_CVBS_VOP, 41, 5), + RK3528_CRU_RESET_OFFSET(SRST_A_JPEG_DECODER, 41, 6), + RK3528_CRU_RESET_OFFSET(SRST_H_JPEG_DECODER, 41, 7), + RK3528_CRU_RESET_OFFSET(SRST_A_VO_L_BIU, 41, 9), + RK3528_CRU_RESET_OFFSET(SRST_A_MAC_VO, 41, 10), + + /* CRU_SOFTRST_CON42 */ + RK3528_CRU_RESET_OFFSET(SRST_A_JPEG_BIU, 42, 0), + RK3528_CRU_RESET_OFFSET(SRST_H_SAI_I2S3, 42, 1), + RK3528_CRU_RESET_OFFSET(SRST_M_SAI_I2S3, 42, 2), + RK3528_CRU_RESET_OFFSET(SRST_MACPHY, 42, 3), + RK3528_CRU_RESET_OFFSET(SRST_P_VCDCPHY, 42, 4), + RK3528_CRU_RESET_OFFSET(SRST_P_GPIO2, 42, 5), + RK3528_CRU_RESET_OFFSET(SRST_DB_GPIO2, 42, 6), + RK3528_CRU_RESET_OFFSET(SRST_P_VO_IOC, 42, 7), + RK3528_CRU_RESET_OFFSET(SRST_H_SDMMC0, 42, 9), + RK3528_CRU_RESET_OFFSET(SRST_P_OTPC_NS, 42, 11), + RK3528_CRU_RESET_OFFSET(SRST_SBPI_OTPC_NS, 42, 12), + RK3528_CRU_RESET_OFFSET(SRST_USER_OTPC_NS, 42, 13), + + /* CRU_SOFTRST_CON43 */ + RK3528_CRU_RESET_OFFSET(SRST_HDMIHDP0, 43, 2), + RK3528_CRU_RESET_OFFSET(SRST_H_USBHOST, 43, 3), + RK3528_CRU_RESET_OFFSET(SRST_H_USBHOST_ARB, 43, 4), + RK3528_CRU_RESET_OFFSET(SRST_HOST_UTMI, 43, 6), + RK3528_CRU_RESET_OFFSET(SRST_P_UART4, 43, 7), + RK3528_CRU_RESET_OFFSET(SRST_S_UART4, 43, 8), + RK3528_CRU_RESET_OFFSET(SRST_P_I2C4, 43, 9), + RK3528_CRU_RESET_OFFSET(SRST_I2C4, 43, 10), + RK3528_CRU_RESET_OFFSET(SRST_P_I2C7, 43, 11), + RK3528_CRU_RESET_OFFSET(SRST_I2C7, 43, 12), + RK3528_CRU_RESET_OFFSET(SRST_P_USBPHY, 43, 13), + RK3528_CRU_RESET_OFFSET(SRST_USBPHY_POR, 43, 14), + RK3528_CRU_RESET_OFFSET(SRST_USBPHY_OTG, 43, 15), + + /* CRU_SOFTRST_CON44 */ + RK3528_CRU_RESET_OFFSET(SRST_USBPHY_HOST, 44, 0), + RK3528_CRU_RESET_OFFSET(SRST_P_DDRPHY_CRU, 44, 4), + RK3528_CRU_RESET_OFFSET(SRST_H_RKVDEC_BIU, 44, 6), + RK3528_CRU_RESET_OFFSET(SRST_A_RKVDEC_BIU, 44, 7), + RK3528_CRU_RESET_OFFSET(SRST_A_RKVDEC, 44, 8), + RK3528_CRU_RESET_OFFSET(SRST_H_RKVDEC, 44, 9), + RK3528_CRU_RESET_OFFSET(SRST_HEVC_CA_RKVDEC, 44, 11), + RK3528_CRU_RESET_OFFSET(SRST_REF_PVTPLL_RKVDEC, 44, 12), + + /* CRU_SOFTRST_CON45 */ + RK3528_CRU_RESET_OFFSET(SRST_P_DDR_BIU, 45, 1), + RK3528_CRU_RESET_OFFSET(SRST_P_DDRC, 45, 2), + RK3528_CRU_RESET_OFFSET(SRST_P_DDRMON, 45, 3), + RK3528_CRU_RESET_OFFSET(SRST_TIMER_DDRMON, 45, 4), + RK3528_CRU_RESET_OFFSET(SRST_P_MSCH_BIU, 45, 5), + RK3528_CRU_RESET_OFFSET(SRST_P_DDR_GRF, 45, 6), + RK3528_CRU_RESET_OFFSET(SRST_P_DDR_HWLP, 45, 8), + RK3528_CRU_RESET_OFFSET(SRST_P_DDRPHY, 45, 9), + RK3528_CRU_RESET_OFFSET(SRST_MSCH_BIU, 45, 10), + RK3528_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL, 45, 11), + RK3528_CRU_RESET_OFFSET(SRST_DDR_UPCTL, 45, 12), + RK3528_CRU_RESET_OFFSET(SRST_DDRMON, 45, 13), + RK3528_CRU_RESET_OFFSET(SRST_A_DDR_SCRAMBLE, 45, 14), + RK3528_CRU_RESET_OFFSET(SRST_A_SPLIT, 45, 15), + + /* CRU_SOFTRST_CON46 */ + RK3528_CRU_RESET_OFFSET(SRST_DDR_PHY, 46, 0), +}; + +int rk3528_reset_bind_lut(struct udevice *pdev, u32 reg_offset, u32 reg_number) +{ + return rockchip_reset_bind_lut(pdev, rk3528_register_offset, + reg_offset, reg_number); +} diff --git a/drivers/reset/rst-rk3576.c b/drivers/reset/rst-rk3576.c new file mode 100644 index 00000000000..a6b83a2fd74 --- /dev/null +++ b/drivers/reset/rst-rk3576.c @@ -0,0 +1,647 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2021 Rockchip Electronics Co., Ltd. + * Copyright (c) 2024 Collabora Ltd. + * Author: Detlev Casanova <detlev.casanova@collabora.com> + * Based on Sebastian Reichel's implementation for RK3588 + */ + +#include <dm.h> +#include <asm/arch-rockchip/clock.h> +#include <dt-bindings/reset/rockchip,rk3576-cru.h> + +/* 0x27200000 + 0x0A00 */ +#define RK3576_CRU_RESET_OFFSET(id, reg, bit) [id] = (0 + (reg) * 16 + (bit)) +/* 0x27208000 + 0x0A00 */ +#define RK3576_PHPCRU_RESET_OFFSET(id, reg, bit) [id] = (0x8000 * 4 + (reg) * 16 + (bit)) +/* 0x27210000 + 0x0A00 */ +#define RK3576_SECURENSCRU_RESET_OFFSET(id, reg, bit) [id] = (0x10000 * 4 + (reg) * 16 + (bit)) +/* 0x27220000 + 0x0A00 */ +#define RK3576_PMU1CRU_RESET_OFFSET(id, reg, bit) [id] = (0x20000 * 4 + (reg) * 16 + (bit)) + +/* mapping table for reset ID to register offset */ +static const int rk3576_register_offset[] = { + /* SOFTRST_CON01 */ + RK3576_CRU_RESET_OFFSET(SRST_A_TOP_BIU, 1, 3), + RK3576_CRU_RESET_OFFSET(SRST_P_TOP_BIU, 1, 5), + RK3576_CRU_RESET_OFFSET(SRST_A_TOP_MID_BIU, 1, 6), + RK3576_CRU_RESET_OFFSET(SRST_A_SECURE_HIGH_BIU, 1, 7), + RK3576_CRU_RESET_OFFSET(SRST_H_TOP_BIU, 1, 14), + + /* SOFTRST_CON02 */ + RK3576_CRU_RESET_OFFSET(SRST_H_VO0VOP_CHANNEL_BIU, 2, 0), + RK3576_CRU_RESET_OFFSET(SRST_A_VO0VOP_CHANNEL_BIU, 2, 1), + + /* SOFTRST_CON06 */ + RK3576_CRU_RESET_OFFSET(SRST_BISRINTF, 6, 2), + + /* SOFTRST_CON07 */ + RK3576_CRU_RESET_OFFSET(SRST_H_AUDIO_BIU, 7, 2), + RK3576_CRU_RESET_OFFSET(SRST_H_ASRC_2CH_0, 7, 3), + RK3576_CRU_RESET_OFFSET(SRST_H_ASRC_2CH_1, 7, 4), + RK3576_CRU_RESET_OFFSET(SRST_H_ASRC_4CH_0, 7, 5), + RK3576_CRU_RESET_OFFSET(SRST_H_ASRC_4CH_1, 7, 6), + RK3576_CRU_RESET_OFFSET(SRST_ASRC_2CH_0, 7, 7), + RK3576_CRU_RESET_OFFSET(SRST_ASRC_2CH_1, 7, 8), + RK3576_CRU_RESET_OFFSET(SRST_ASRC_4CH_0, 7, 9), + RK3576_CRU_RESET_OFFSET(SRST_ASRC_4CH_1, 7, 10), + RK3576_CRU_RESET_OFFSET(SRST_M_SAI0_8CH, 7, 12), + RK3576_CRU_RESET_OFFSET(SRST_H_SAI0_8CH, 7, 13), + RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_RX0, 7, 14), + RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_RX0, 7, 15), + + /* SOFTRST_CON08 */ + RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_RX1, 8, 0), + RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_RX1, 8, 1), + RK3576_CRU_RESET_OFFSET(SRST_M_SAI1_8CH, 8, 5), + RK3576_CRU_RESET_OFFSET(SRST_H_SAI1_8CH, 8, 6), + RK3576_CRU_RESET_OFFSET(SRST_M_SAI2_2CH, 8, 8), + RK3576_CRU_RESET_OFFSET(SRST_H_SAI2_2CH, 8, 10), + RK3576_CRU_RESET_OFFSET(SRST_M_SAI3_2CH, 8, 12), + RK3576_CRU_RESET_OFFSET(SRST_H_SAI3_2CH, 8, 14), + + /* SOFTRST_CON09 */ + RK3576_CRU_RESET_OFFSET(SRST_M_SAI4_2CH, 9, 0), + RK3576_CRU_RESET_OFFSET(SRST_H_SAI4_2CH, 9, 2), + RK3576_CRU_RESET_OFFSET(SRST_H_ACDCDIG_DSM, 9, 3), + RK3576_CRU_RESET_OFFSET(SRST_M_ACDCDIG_DSM, 9, 4), + RK3576_CRU_RESET_OFFSET(SRST_PDM1, 9, 5), + RK3576_CRU_RESET_OFFSET(SRST_H_PDM1, 9, 7), + RK3576_CRU_RESET_OFFSET(SRST_M_PDM1, 9, 8), + RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_TX0, 9, 9), + RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_TX0, 9, 10), + RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_TX1, 9, 11), + RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_TX1, 9, 12), + + /* SOFTRST_CON11 */ + RK3576_CRU_RESET_OFFSET(SRST_A_BUS_BIU, 11, 3), + RK3576_CRU_RESET_OFFSET(SRST_P_BUS_BIU, 11, 4), + RK3576_CRU_RESET_OFFSET(SRST_P_CRU, 11, 5), + RK3576_CRU_RESET_OFFSET(SRST_H_CAN0, 11, 6), + RK3576_CRU_RESET_OFFSET(SRST_CAN0, 11, 7), + RK3576_CRU_RESET_OFFSET(SRST_H_CAN1, 11, 8), + RK3576_CRU_RESET_OFFSET(SRST_CAN1, 11, 9), + RK3576_CRU_RESET_OFFSET(SRST_P_INTMUX2BUS, 11, 12), + RK3576_CRU_RESET_OFFSET(SRST_P_VCCIO_IOC, 11, 13), + RK3576_CRU_RESET_OFFSET(SRST_H_BUS_BIU, 11, 14), + RK3576_CRU_RESET_OFFSET(SRST_KEY_SHIFT, 11, 15), + + /* SOFTRST_CON12 */ + RK3576_CRU_RESET_OFFSET(SRST_P_I2C1, 12, 0), + RK3576_CRU_RESET_OFFSET(SRST_P_I2C2, 12, 1), + RK3576_CRU_RESET_OFFSET(SRST_P_I2C3, 12, 2), + RK3576_CRU_RESET_OFFSET(SRST_P_I2C4, 12, 3), + RK3576_CRU_RESET_OFFSET(SRST_P_I2C5, 12, 4), + RK3576_CRU_RESET_OFFSET(SRST_P_I2C6, 12, 5), + RK3576_CRU_RESET_OFFSET(SRST_P_I2C7, 12, 6), + RK3576_CRU_RESET_OFFSET(SRST_P_I2C8, 12, 7), + RK3576_CRU_RESET_OFFSET(SRST_P_I2C9, 12, 8), + RK3576_CRU_RESET_OFFSET(SRST_P_WDT_BUSMCU, 12, 9), + RK3576_CRU_RESET_OFFSET(SRST_T_WDT_BUSMCU, 12, 10), + RK3576_CRU_RESET_OFFSET(SRST_A_GIC, 12, 11), + RK3576_CRU_RESET_OFFSET(SRST_I2C1, 12, 12), + RK3576_CRU_RESET_OFFSET(SRST_I2C2, 12, 13), + RK3576_CRU_RESET_OFFSET(SRST_I2C3, 12, 14), + RK3576_CRU_RESET_OFFSET(SRST_I2C4, 12, 15), + + /* SOFTRST_CON13 */ + RK3576_CRU_RESET_OFFSET(SRST_I2C5, 13, 0), + RK3576_CRU_RESET_OFFSET(SRST_I2C6, 13, 1), + RK3576_CRU_RESET_OFFSET(SRST_I2C7, 13, 2), + RK3576_CRU_RESET_OFFSET(SRST_I2C8, 13, 3), + RK3576_CRU_RESET_OFFSET(SRST_I2C9, 13, 4), + RK3576_CRU_RESET_OFFSET(SRST_P_SARADC, 13, 6), + RK3576_CRU_RESET_OFFSET(SRST_SARADC, 13, 7), + RK3576_CRU_RESET_OFFSET(SRST_P_TSADC, 13, 8), + RK3576_CRU_RESET_OFFSET(SRST_TSADC, 13, 9), + RK3576_CRU_RESET_OFFSET(SRST_P_UART0, 13, 10), + RK3576_CRU_RESET_OFFSET(SRST_P_UART2, 13, 11), + RK3576_CRU_RESET_OFFSET(SRST_P_UART3, 13, 12), + RK3576_CRU_RESET_OFFSET(SRST_P_UART4, 13, 13), + RK3576_CRU_RESET_OFFSET(SRST_P_UART5, 13, 14), + RK3576_CRU_RESET_OFFSET(SRST_P_UART6, 13, 15), + + /* SOFTRST_CON14 */ + RK3576_CRU_RESET_OFFSET(SRST_P_UART7, 14, 0), + RK3576_CRU_RESET_OFFSET(SRST_P_UART8, 14, 1), + RK3576_CRU_RESET_OFFSET(SRST_P_UART9, 14, 2), + RK3576_CRU_RESET_OFFSET(SRST_P_UART10, 14, 3), + RK3576_CRU_RESET_OFFSET(SRST_P_UART11, 14, 4), + RK3576_CRU_RESET_OFFSET(SRST_S_UART0, 14, 5), + RK3576_CRU_RESET_OFFSET(SRST_S_UART2, 14, 6), + RK3576_CRU_RESET_OFFSET(SRST_S_UART3, 14, 9), + RK3576_CRU_RESET_OFFSET(SRST_S_UART4, 14, 12), + RK3576_CRU_RESET_OFFSET(SRST_S_UART5, 14, 15), + + /* SOFTRST_CON15 */ + RK3576_CRU_RESET_OFFSET(SRST_S_UART6, 15, 2), + RK3576_CRU_RESET_OFFSET(SRST_S_UART7, 15, 5), + RK3576_CRU_RESET_OFFSET(SRST_S_UART8, 15, 8), + RK3576_CRU_RESET_OFFSET(SRST_S_UART9, 15, 9), + RK3576_CRU_RESET_OFFSET(SRST_S_UART10, 15, 10), + RK3576_CRU_RESET_OFFSET(SRST_S_UART11, 15, 11), + RK3576_CRU_RESET_OFFSET(SRST_P_SPI0, 15, 13), + RK3576_CRU_RESET_OFFSET(SRST_P_SPI1, 15, 14), + RK3576_CRU_RESET_OFFSET(SRST_P_SPI2, 15, 15), + + /* SOFTRST_CON16 */ + RK3576_CRU_RESET_OFFSET(SRST_P_SPI3, 16, 0), + RK3576_CRU_RESET_OFFSET(SRST_P_SPI4, 16, 1), + RK3576_CRU_RESET_OFFSET(SRST_SPI0, 16, 2), + RK3576_CRU_RESET_OFFSET(SRST_SPI1, 16, 3), + RK3576_CRU_RESET_OFFSET(SRST_SPI2, 16, 4), + RK3576_CRU_RESET_OFFSET(SRST_SPI3, 16, 5), + RK3576_CRU_RESET_OFFSET(SRST_SPI4, 16, 6), + RK3576_CRU_RESET_OFFSET(SRST_P_WDT0, 16, 7), + RK3576_CRU_RESET_OFFSET(SRST_T_WDT0, 16, 8), + RK3576_CRU_RESET_OFFSET(SRST_P_SYS_GRF, 16, 9), + RK3576_CRU_RESET_OFFSET(SRST_P_PWM1, 16, 10), + RK3576_CRU_RESET_OFFSET(SRST_PWM1, 16, 11), + + /* SOFTRST_CON17 */ + RK3576_CRU_RESET_OFFSET(SRST_P_BUSTIMER0, 17, 3), + RK3576_CRU_RESET_OFFSET(SRST_P_BUSTIMER1, 17, 4), + RK3576_CRU_RESET_OFFSET(SRST_TIMER0, 17, 6), + RK3576_CRU_RESET_OFFSET(SRST_TIMER1, 17, 7), + RK3576_CRU_RESET_OFFSET(SRST_TIMER2, 17, 8), + RK3576_CRU_RESET_OFFSET(SRST_TIMER3, 17, 9), + RK3576_CRU_RESET_OFFSET(SRST_TIMER4, 17, 10), + RK3576_CRU_RESET_OFFSET(SRST_TIMER5, 17, 11), + RK3576_CRU_RESET_OFFSET(SRST_P_BUSIOC, 17, 12), + RK3576_CRU_RESET_OFFSET(SRST_P_MAILBOX0, 17, 13), + RK3576_CRU_RESET_OFFSET(SRST_P_GPIO1, 17, 15), + + /* SOFTRST_CON18 */ + RK3576_CRU_RESET_OFFSET(SRST_GPIO1, 18, 0), + RK3576_CRU_RESET_OFFSET(SRST_P_GPIO2, 18, 1), + RK3576_CRU_RESET_OFFSET(SRST_GPIO2, 18, 2), + RK3576_CRU_RESET_OFFSET(SRST_P_GPIO3, 18, 3), + RK3576_CRU_RESET_OFFSET(SRST_GPIO3, 18, 4), + RK3576_CRU_RESET_OFFSET(SRST_P_GPIO4, 18, 5), + RK3576_CRU_RESET_OFFSET(SRST_GPIO4, 18, 6), + RK3576_CRU_RESET_OFFSET(SRST_A_DECOM, 18, 7), + RK3576_CRU_RESET_OFFSET(SRST_P_DECOM, 18, 8), + RK3576_CRU_RESET_OFFSET(SRST_D_DECOM, 18, 9), + RK3576_CRU_RESET_OFFSET(SRST_TIMER6, 18, 11), + RK3576_CRU_RESET_OFFSET(SRST_TIMER7, 18, 12), + RK3576_CRU_RESET_OFFSET(SRST_TIMER8, 18, 13), + RK3576_CRU_RESET_OFFSET(SRST_TIMER9, 18, 14), + RK3576_CRU_RESET_OFFSET(SRST_TIMER10, 18, 15), + + /* SOFTRST_CON19 */ + RK3576_CRU_RESET_OFFSET(SRST_TIMER11, 19, 0), + RK3576_CRU_RESET_OFFSET(SRST_A_DMAC0, 19, 1), + RK3576_CRU_RESET_OFFSET(SRST_A_DMAC1, 19, 2), + RK3576_CRU_RESET_OFFSET(SRST_A_DMAC2, 19, 3), + RK3576_CRU_RESET_OFFSET(SRST_A_SPINLOCK, 19, 4), + RK3576_CRU_RESET_OFFSET(SRST_REF_PVTPLL_BUS, 19, 5), + RK3576_CRU_RESET_OFFSET(SRST_H_I3C0, 19, 7), + RK3576_CRU_RESET_OFFSET(SRST_H_I3C1, 19, 9), + RK3576_CRU_RESET_OFFSET(SRST_H_BUS_CM0_BIU, 19, 11), + RK3576_CRU_RESET_OFFSET(SRST_F_BUS_CM0_CORE, 19, 12), + RK3576_CRU_RESET_OFFSET(SRST_T_BUS_CM0_JTAG, 19, 13), + + /* SOFTRST_CON20 */ + RK3576_CRU_RESET_OFFSET(SRST_P_INTMUX2PMU, 20, 0), + RK3576_CRU_RESET_OFFSET(SRST_P_INTMUX2DDR, 20, 1), + RK3576_CRU_RESET_OFFSET(SRST_P_PVTPLL_BUS, 20, 3), + RK3576_CRU_RESET_OFFSET(SRST_P_PWM2, 20, 4), + RK3576_CRU_RESET_OFFSET(SRST_PWM2, 20, 5), + RK3576_CRU_RESET_OFFSET(SRST_FREQ_PWM1, 20, 8), + RK3576_CRU_RESET_OFFSET(SRST_COUNTER_PWM1, 20, 9), + RK3576_CRU_RESET_OFFSET(SRST_I3C0, 20, 12), + RK3576_CRU_RESET_OFFSET(SRST_I3C1, 20, 13), + + /* SOFTRST_CON21 */ + RK3576_CRU_RESET_OFFSET(SRST_P_DDR_MON_CH0, 21, 1), + RK3576_CRU_RESET_OFFSET(SRST_P_DDR_BIU, 21, 2), + RK3576_CRU_RESET_OFFSET(SRST_P_DDR_UPCTL_CH0, 21, 3), + RK3576_CRU_RESET_OFFSET(SRST_TM_DDR_MON_CH0, 21, 4), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_BIU, 21, 5), + RK3576_CRU_RESET_OFFSET(SRST_DFI_CH0, 21, 6), + RK3576_CRU_RESET_OFFSET(SRST_DDR_MON_CH0, 21, 10), + RK3576_CRU_RESET_OFFSET(SRST_P_DDR_HWLP_CH0, 21, 13), + RK3576_CRU_RESET_OFFSET(SRST_P_DDR_MON_CH1, 21, 14), + RK3576_CRU_RESET_OFFSET(SRST_P_DDR_HWLP_CH1, 21, 15), + + /* SOFTRST_CON22 */ + RK3576_CRU_RESET_OFFSET(SRST_P_DDR_UPCTL_CH1, 22, 0), + RK3576_CRU_RESET_OFFSET(SRST_TM_DDR_MON_CH1, 22, 1), + RK3576_CRU_RESET_OFFSET(SRST_DFI_CH1, 22, 2), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR01_MSCH0, 22, 3), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR01_MSCH1, 22, 4), + RK3576_CRU_RESET_OFFSET(SRST_DDR_MON_CH1, 22, 6), + RK3576_CRU_RESET_OFFSET(SRST_DDR_SCRAMBLE_CH0, 22, 9), + RK3576_CRU_RESET_OFFSET(SRST_DDR_SCRAMBLE_CH1, 22, 10), + RK3576_CRU_RESET_OFFSET(SRST_P_AHB2APB, 22, 12), + RK3576_CRU_RESET_OFFSET(SRST_H_AHB2APB, 22, 13), + RK3576_CRU_RESET_OFFSET(SRST_H_DDR_BIU, 22, 14), + RK3576_CRU_RESET_OFFSET(SRST_F_DDR_CM0_CORE, 22, 15), + + /* SOFTRST_CON23 */ + RK3576_CRU_RESET_OFFSET(SRST_P_DDR01_MSCH0, 23, 1), + RK3576_CRU_RESET_OFFSET(SRST_P_DDR01_MSCH1, 23, 2), + RK3576_CRU_RESET_OFFSET(SRST_DDR_TIMER0, 23, 4), + RK3576_CRU_RESET_OFFSET(SRST_DDR_TIMER1, 23, 5), + RK3576_CRU_RESET_OFFSET(SRST_T_WDT_DDR, 23, 6), + RK3576_CRU_RESET_OFFSET(SRST_P_WDT, 23, 7), + RK3576_CRU_RESET_OFFSET(SRST_P_TIMER, 23, 8), + RK3576_CRU_RESET_OFFSET(SRST_T_DDR_CM0_JTAG, 23, 9), + RK3576_CRU_RESET_OFFSET(SRST_P_DDR_GRF, 23, 11), + + /* SOFTRST_CON25 */ + RK3576_CRU_RESET_OFFSET(SRST_DDR_UPCTL_CH0, 25, 1), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_0_CH0, 25, 2), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_1_CH0, 25, 3), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_2_CH0, 25, 4), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_3_CH0, 25, 5), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_4_CH0, 25, 6), + + /* SOFTRST_CON26 */ + RK3576_CRU_RESET_OFFSET(SRST_DDR_UPCTL_CH1, 26, 1), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_0_CH1, 26, 2), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_1_CH1, 26, 3), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_2_CH1, 26, 4), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_3_CH1, 26, 5), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_4_CH1, 26, 6), + + /* SOFTRST_CON27 */ + RK3576_CRU_RESET_OFFSET(SRST_REF_PVTPLL_DDR, 27, 0), + RK3576_CRU_RESET_OFFSET(SRST_P_PVTPLL_DDR, 27, 1), + + /* SOFTRST_CON28 */ + RK3576_CRU_RESET_OFFSET(SRST_A_RKNN0, 28, 9), + RK3576_CRU_RESET_OFFSET(SRST_A_RKNN0_BIU, 28, 11), + RK3576_CRU_RESET_OFFSET(SRST_L_RKNN0_BIU, 28, 12), + + /* SOFTRST_CON29 */ + RK3576_CRU_RESET_OFFSET(SRST_A_RKNN1, 29, 0), + RK3576_CRU_RESET_OFFSET(SRST_A_RKNN1_BIU, 29, 2), + RK3576_CRU_RESET_OFFSET(SRST_L_RKNN1_BIU, 29, 3), + + /* SOFTRST_CON31 */ + RK3576_CRU_RESET_OFFSET(SRST_NPU_DAP, 31, 0), + RK3576_CRU_RESET_OFFSET(SRST_L_NPUSUBSYS_BIU, 31, 1), + RK3576_CRU_RESET_OFFSET(SRST_P_NPUTOP_BIU, 31, 9), + RK3576_CRU_RESET_OFFSET(SRST_P_NPU_TIMER, 31, 10), + RK3576_CRU_RESET_OFFSET(SRST_NPUTIMER0, 31, 12), + RK3576_CRU_RESET_OFFSET(SRST_NPUTIMER1, 31, 13), + RK3576_CRU_RESET_OFFSET(SRST_P_NPU_WDT, 31, 14), + RK3576_CRU_RESET_OFFSET(SRST_T_NPU_WDT, 31, 15), + + /* SOFTRST_CON32 */ + RK3576_CRU_RESET_OFFSET(SRST_A_RKNN_CBUF, 32, 0), + RK3576_CRU_RESET_OFFSET(SRST_A_RVCORE0, 32, 1), + RK3576_CRU_RESET_OFFSET(SRST_P_NPU_GRF, 32, 2), + RK3576_CRU_RESET_OFFSET(SRST_P_PVTPLL_NPU, 32, 3), + RK3576_CRU_RESET_OFFSET(SRST_NPU_PVTPLL, 32, 4), + RK3576_CRU_RESET_OFFSET(SRST_H_NPU_CM0_BIU, 32, 6), + RK3576_CRU_RESET_OFFSET(SRST_F_NPU_CM0_CORE, 32, 7), + RK3576_CRU_RESET_OFFSET(SRST_T_NPU_CM0_JTAG, 32, 8), + RK3576_CRU_RESET_OFFSET(SRST_A_RKNNTOP_BIU, 32, 11), + RK3576_CRU_RESET_OFFSET(SRST_H_RKNN_CBUF, 32, 12), + RK3576_CRU_RESET_OFFSET(SRST_H_RKNNTOP_BIU, 32, 13), + + /* SOFTRST_CON33 */ + RK3576_CRU_RESET_OFFSET(SRST_H_NVM_BIU, 33, 2), + RK3576_CRU_RESET_OFFSET(SRST_A_NVM_BIU, 33, 3), + RK3576_CRU_RESET_OFFSET(SRST_S_FSPI, 33, 6), + RK3576_CRU_RESET_OFFSET(SRST_H_FSPI, 33, 7), + RK3576_CRU_RESET_OFFSET(SRST_C_EMMC, 33, 8), + RK3576_CRU_RESET_OFFSET(SRST_H_EMMC, 33, 9), + RK3576_CRU_RESET_OFFSET(SRST_A_EMMC, 33, 10), + RK3576_CRU_RESET_OFFSET(SRST_B_EMMC, 33, 11), + RK3576_CRU_RESET_OFFSET(SRST_T_EMMC, 33, 12), + + /* SOFTRST_CON34 */ + RK3576_CRU_RESET_OFFSET(SRST_P_GRF, 34, 1), + RK3576_CRU_RESET_OFFSET(SRST_P_PHP_BIU, 34, 5), + RK3576_CRU_RESET_OFFSET(SRST_A_PHP_BIU, 34, 9), + RK3576_CRU_RESET_OFFSET(SRST_P_PCIE0, 34, 13), + RK3576_CRU_RESET_OFFSET(SRST_PCIE0_POWER_UP, 34, 15), + + /* SOFTRST_CON35 */ + RK3576_CRU_RESET_OFFSET(SRST_A_USB3OTG1, 35, 3), + RK3576_CRU_RESET_OFFSET(SRST_A_MMU0, 35, 11), + RK3576_CRU_RESET_OFFSET(SRST_A_SLV_MMU0, 35, 13), + RK3576_CRU_RESET_OFFSET(SRST_A_MMU1, 35, 14), + + /* SOFTRST_CON36 */ + RK3576_CRU_RESET_OFFSET(SRST_A_SLV_MMU1, 36, 0), + RK3576_CRU_RESET_OFFSET(SRST_P_PCIE1, 36, 7), + RK3576_CRU_RESET_OFFSET(SRST_PCIE1_POWER_UP, 36, 9), + + /* SOFTRST_CON37 */ + RK3576_CRU_RESET_OFFSET(SRST_RXOOB0, 37, 0), + RK3576_CRU_RESET_OFFSET(SRST_RXOOB1, 37, 1), + RK3576_CRU_RESET_OFFSET(SRST_PMALIVE0, 37, 2), + RK3576_CRU_RESET_OFFSET(SRST_PMALIVE1, 37, 3), + RK3576_CRU_RESET_OFFSET(SRST_A_SATA0, 37, 4), + RK3576_CRU_RESET_OFFSET(SRST_A_SATA1, 37, 5), + RK3576_CRU_RESET_OFFSET(SRST_ASIC1, 37, 6), + RK3576_CRU_RESET_OFFSET(SRST_ASIC0, 37, 7), + + /* SOFTRST_CON40 */ + RK3576_CRU_RESET_OFFSET(SRST_P_CSIDPHY1, 40, 2), + RK3576_CRU_RESET_OFFSET(SRST_SCAN_CSIDPHY1, 40, 3), + + /* SOFTRST_CON42 */ + RK3576_CRU_RESET_OFFSET(SRST_P_SDGMAC_GRF, 42, 3), + RK3576_CRU_RESET_OFFSET(SRST_P_SDGMAC_BIU, 42, 4), + RK3576_CRU_RESET_OFFSET(SRST_A_SDGMAC_BIU, 42, 5), + RK3576_CRU_RESET_OFFSET(SRST_H_SDGMAC_BIU, 42, 6), + RK3576_CRU_RESET_OFFSET(SRST_A_GMAC0, 42, 7), + RK3576_CRU_RESET_OFFSET(SRST_A_GMAC1, 42, 8), + RK3576_CRU_RESET_OFFSET(SRST_P_GMAC0, 42, 9), + RK3576_CRU_RESET_OFFSET(SRST_P_GMAC1, 42, 10), + RK3576_CRU_RESET_OFFSET(SRST_H_SDIO, 42, 12), + + /* SOFTRST_CON43 */ + RK3576_CRU_RESET_OFFSET(SRST_H_SDMMC0, 43, 2), + RK3576_CRU_RESET_OFFSET(SRST_S_FSPI1, 43, 3), + RK3576_CRU_RESET_OFFSET(SRST_H_FSPI1, 43, 4), + RK3576_CRU_RESET_OFFSET(SRST_A_DSMC_BIU, 43, 6), + RK3576_CRU_RESET_OFFSET(SRST_A_DSMC, 43, 7), + RK3576_CRU_RESET_OFFSET(SRST_P_DSMC, 43, 8), + RK3576_CRU_RESET_OFFSET(SRST_H_HSGPIO, 43, 10), + RK3576_CRU_RESET_OFFSET(SRST_HSGPIO, 43, 11), + RK3576_CRU_RESET_OFFSET(SRST_A_HSGPIO, 43, 13), + + /* SOFTRST_CON45 */ + RK3576_CRU_RESET_OFFSET(SRST_H_RKVDEC, 45, 3), + RK3576_CRU_RESET_OFFSET(SRST_H_RKVDEC_BIU, 45, 5), + RK3576_CRU_RESET_OFFSET(SRST_A_RKVDEC_BIU, 45, 6), + RK3576_CRU_RESET_OFFSET(SRST_RKVDEC_HEVC_CA, 45, 8), + RK3576_CRU_RESET_OFFSET(SRST_RKVDEC_CORE, 45, 9), + + /* SOFTRST_CON47 */ + RK3576_CRU_RESET_OFFSET(SRST_A_USB_BIU, 47, 3), + RK3576_CRU_RESET_OFFSET(SRST_P_USBUFS_BIU, 47, 4), + RK3576_CRU_RESET_OFFSET(SRST_A_USB3OTG0, 47, 5), + RK3576_CRU_RESET_OFFSET(SRST_A_UFS_BIU, 47, 10), + RK3576_CRU_RESET_OFFSET(SRST_A_MMU2, 47, 12), + RK3576_CRU_RESET_OFFSET(SRST_A_SLV_MMU2, 47, 13), + RK3576_CRU_RESET_OFFSET(SRST_A_UFS_SYS, 47, 15), + + /* SOFTRST_CON48 */ + RK3576_CRU_RESET_OFFSET(SRST_A_UFS, 48, 0), + RK3576_CRU_RESET_OFFSET(SRST_P_USBUFS_GRF, 48, 1), + RK3576_CRU_RESET_OFFSET(SRST_P_UFS_GRF, 48, 2), + + /* SOFTRST_CON49 */ + RK3576_CRU_RESET_OFFSET(SRST_H_VPU_BIU, 49, 6), + RK3576_CRU_RESET_OFFSET(SRST_A_JPEG_BIU, 49, 7), + RK3576_CRU_RESET_OFFSET(SRST_A_RGA_BIU, 49, 10), + RK3576_CRU_RESET_OFFSET(SRST_A_VDPP_BIU, 49, 11), + RK3576_CRU_RESET_OFFSET(SRST_A_EBC_BIU, 49, 12), + RK3576_CRU_RESET_OFFSET(SRST_H_RGA2E_0, 49, 13), + RK3576_CRU_RESET_OFFSET(SRST_A_RGA2E_0, 49, 14), + RK3576_CRU_RESET_OFFSET(SRST_CORE_RGA2E_0, 49, 15), + + /* SOFTRST_CON50 */ + RK3576_CRU_RESET_OFFSET(SRST_A_JPEG, 50, 0), + RK3576_CRU_RESET_OFFSET(SRST_H_JPEG, 50, 1), + RK3576_CRU_RESET_OFFSET(SRST_H_VDPP, 50, 2), + RK3576_CRU_RESET_OFFSET(SRST_A_VDPP, 50, 3), + RK3576_CRU_RESET_OFFSET(SRST_CORE_VDPP, 50, 4), + RK3576_CRU_RESET_OFFSET(SRST_H_RGA2E_1, 50, 5), + RK3576_CRU_RESET_OFFSET(SRST_A_RGA2E_1, 50, 6), + RK3576_CRU_RESET_OFFSET(SRST_CORE_RGA2E_1, 50, 7), + RK3576_CRU_RESET_OFFSET(SRST_H_EBC, 50, 10), + RK3576_CRU_RESET_OFFSET(SRST_A_EBC, 50, 11), + RK3576_CRU_RESET_OFFSET(SRST_D_EBC, 50, 12), + + /* SOFTRST_CON51 */ + RK3576_CRU_RESET_OFFSET(SRST_H_VEPU0_BIU, 51, 2), + RK3576_CRU_RESET_OFFSET(SRST_A_VEPU0_BIU, 51, 3), + RK3576_CRU_RESET_OFFSET(SRST_H_VEPU0, 51, 4), + RK3576_CRU_RESET_OFFSET(SRST_A_VEPU0, 51, 5), + RK3576_CRU_RESET_OFFSET(SRST_VEPU0_CORE, 51, 6), + + /* SOFTRST_CON53 */ + RK3576_CRU_RESET_OFFSET(SRST_A_VI_BIU, 53, 3), + RK3576_CRU_RESET_OFFSET(SRST_H_VI_BIU, 53, 4), + RK3576_CRU_RESET_OFFSET(SRST_P_VI_BIU, 53, 5), + RK3576_CRU_RESET_OFFSET(SRST_D_VICAP, 53, 6), + RK3576_CRU_RESET_OFFSET(SRST_A_VICAP, 53, 7), + RK3576_CRU_RESET_OFFSET(SRST_H_VICAP, 53, 8), + RK3576_CRU_RESET_OFFSET(SRST_ISP0, 53, 10), + RK3576_CRU_RESET_OFFSET(SRST_ISP0_VICAP, 53, 11), + + /* SOFTRST_CON54 */ + RK3576_CRU_RESET_OFFSET(SRST_CORE_VPSS, 54, 1), + RK3576_CRU_RESET_OFFSET(SRST_P_CSI_HOST_0, 54, 4), + RK3576_CRU_RESET_OFFSET(SRST_P_CSI_HOST_1, 54, 5), + RK3576_CRU_RESET_OFFSET(SRST_P_CSI_HOST_2, 54, 6), + RK3576_CRU_RESET_OFFSET(SRST_P_CSI_HOST_3, 54, 7), + RK3576_CRU_RESET_OFFSET(SRST_P_CSI_HOST_4, 54, 8), + + /* SOFTRST_CON59 */ + RK3576_CRU_RESET_OFFSET(SRST_CIFIN, 59, 0), + RK3576_CRU_RESET_OFFSET(SRST_VICAP_I0CLK, 59, 1), + RK3576_CRU_RESET_OFFSET(SRST_VICAP_I1CLK, 59, 2), + RK3576_CRU_RESET_OFFSET(SRST_VICAP_I2CLK, 59, 3), + RK3576_CRU_RESET_OFFSET(SRST_VICAP_I3CLK, 59, 4), + RK3576_CRU_RESET_OFFSET(SRST_VICAP_I4CLK, 59, 5), + + /* SOFTRST_CON61 */ + RK3576_CRU_RESET_OFFSET(SRST_A_VOP_BIU, 61, 4), + RK3576_CRU_RESET_OFFSET(SRST_A_VOP2_BIU, 61, 5), + RK3576_CRU_RESET_OFFSET(SRST_H_VOP_BIU, 61, 6), + RK3576_CRU_RESET_OFFSET(SRST_P_VOP_BIU, 61, 7), + RK3576_CRU_RESET_OFFSET(SRST_H_VOP, 61, 8), + RK3576_CRU_RESET_OFFSET(SRST_A_VOP, 61, 9), + RK3576_CRU_RESET_OFFSET(SRST_D_VP0, 61, 13), + + /* SOFTRST_CON62 */ + RK3576_CRU_RESET_OFFSET(SRST_D_VP1, 62, 0), + RK3576_CRU_RESET_OFFSET(SRST_D_VP2, 62, 1), + RK3576_CRU_RESET_OFFSET(SRST_P_VOP2_BIU, 62, 2), + RK3576_CRU_RESET_OFFSET(SRST_P_VOPGRF, 62, 3), + + /* SOFTRST_CON63 */ + RK3576_CRU_RESET_OFFSET(SRST_H_VO0_BIU, 63, 5), + RK3576_CRU_RESET_OFFSET(SRST_P_VO0_BIU, 63, 7), + RK3576_CRU_RESET_OFFSET(SRST_A_HDCP0_BIU, 63, 9), + RK3576_CRU_RESET_OFFSET(SRST_P_VO0_GRF, 63, 10), + RK3576_CRU_RESET_OFFSET(SRST_A_HDCP0, 63, 12), + RK3576_CRU_RESET_OFFSET(SRST_H_HDCP0, 63, 13), + RK3576_CRU_RESET_OFFSET(SRST_HDCP0, 63, 14), + + /* SOFTRST_CON64 */ + RK3576_CRU_RESET_OFFSET(SRST_P_DSIHOST0, 64, 5), + RK3576_CRU_RESET_OFFSET(SRST_DSIHOST0, 64, 6), + RK3576_CRU_RESET_OFFSET(SRST_P_HDMITX0, 64, 7), + RK3576_CRU_RESET_OFFSET(SRST_HDMITX0_REF, 64, 9), + RK3576_CRU_RESET_OFFSET(SRST_P_EDP0, 64, 13), + RK3576_CRU_RESET_OFFSET(SRST_EDP0_24M, 64, 14), + + /* SOFTRST_CON65 */ + RK3576_CRU_RESET_OFFSET(SRST_M_SAI5_8CH, 65, 4), + RK3576_CRU_RESET_OFFSET(SRST_H_SAI5_8CH, 65, 5), + RK3576_CRU_RESET_OFFSET(SRST_M_SAI6_8CH, 65, 8), + RK3576_CRU_RESET_OFFSET(SRST_H_SAI6_8CH, 65, 9), + RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_TX2, 65, 10), + RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_TX2, 65, 13), + RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_RX2, 65, 14), + RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_RX2, 65, 15), + + /* SOFTRST_CON66 */ + RK3576_CRU_RESET_OFFSET(SRST_H_SAI8_8CH, 66, 0), + RK3576_CRU_RESET_OFFSET(SRST_M_SAI8_8CH, 66, 2), + + /* SOFTRST_CON67 */ + RK3576_CRU_RESET_OFFSET(SRST_H_VO1_BIU, 67, 5), + RK3576_CRU_RESET_OFFSET(SRST_P_VO1_BIU, 67, 6), + RK3576_CRU_RESET_OFFSET(SRST_M_SAI7_8CH, 67, 9), + RK3576_CRU_RESET_OFFSET(SRST_H_SAI7_8CH, 67, 10), + RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_TX3, 67, 11), + RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_TX4, 67, 12), + RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_TX5, 67, 13), + RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_TX3, 67, 14), + + /* SOFTRST_CON68 */ + RK3576_CRU_RESET_OFFSET(SRST_DP0, 68, 0), + RK3576_CRU_RESET_OFFSET(SRST_P_VO1_GRF, 68, 2), + RK3576_CRU_RESET_OFFSET(SRST_A_HDCP1_BIU, 68, 3), + RK3576_CRU_RESET_OFFSET(SRST_A_HDCP1, 68, 4), + RK3576_CRU_RESET_OFFSET(SRST_H_HDCP1, 68, 5), + RK3576_CRU_RESET_OFFSET(SRST_HDCP1, 68, 6), + RK3576_CRU_RESET_OFFSET(SRST_H_SAI9_8CH, 68, 9), + RK3576_CRU_RESET_OFFSET(SRST_M_SAI9_8CH, 68, 11), + RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_TX4, 68, 12), + RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_TX5, 68, 13), + + /* SOFTRST_CON69 */ + RK3576_CRU_RESET_OFFSET(SRST_GPU, 69, 3), + RK3576_CRU_RESET_OFFSET(SRST_A_S_GPU_BIU, 69, 6), + RK3576_CRU_RESET_OFFSET(SRST_A_M0_GPU_BIU, 69, 7), + RK3576_CRU_RESET_OFFSET(SRST_P_GPU_BIU, 69, 9), + RK3576_CRU_RESET_OFFSET(SRST_P_GPU_GRF, 69, 13), + RK3576_CRU_RESET_OFFSET(SRST_GPU_PVTPLL, 69, 14), + RK3576_CRU_RESET_OFFSET(SRST_P_PVTPLL_GPU, 69, 15), + + /* SOFTRST_CON72 */ + RK3576_CRU_RESET_OFFSET(SRST_A_CENTER_BIU, 72, 4), + RK3576_CRU_RESET_OFFSET(SRST_A_DMA2DDR, 72, 5), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_SHAREMEM, 72, 6), + RK3576_CRU_RESET_OFFSET(SRST_A_DDR_SHAREMEM_BIU, 72, 7), + RK3576_CRU_RESET_OFFSET(SRST_H_CENTER_BIU, 72, 8), + RK3576_CRU_RESET_OFFSET(SRST_P_CENTER_GRF, 72, 9), + RK3576_CRU_RESET_OFFSET(SRST_P_DMA2DDR, 72, 10), + RK3576_CRU_RESET_OFFSET(SRST_P_SHAREMEM, 72, 11), + RK3576_CRU_RESET_OFFSET(SRST_P_CENTER_BIU, 72, 12), + + /* SOFTRST_CON75 */ + RK3576_CRU_RESET_OFFSET(SRST_LINKSYM_HDMITXPHY0, 75, 1), + + /* SOFTRST_CON78 */ + RK3576_CRU_RESET_OFFSET(SRST_DP0_PIXELCLK, 78, 1), + RK3576_CRU_RESET_OFFSET(SRST_PHY_DP0_TX, 78, 2), + RK3576_CRU_RESET_OFFSET(SRST_DP1_PIXELCLK, 78, 3), + RK3576_CRU_RESET_OFFSET(SRST_DP2_PIXELCLK, 78, 4), + + /* SOFTRST_CON79 */ + RK3576_CRU_RESET_OFFSET(SRST_H_VEPU1_BIU, 79, 1), + RK3576_CRU_RESET_OFFSET(SRST_A_VEPU1_BIU, 79, 2), + RK3576_CRU_RESET_OFFSET(SRST_H_VEPU1, 79, 3), + RK3576_CRU_RESET_OFFSET(SRST_A_VEPU1, 79, 4), + RK3576_CRU_RESET_OFFSET(SRST_VEPU1_CORE, 79, 5), + + /* PPLL_SOFTRST_CON00 */ + RK3576_PHPCRU_RESET_OFFSET(SRST_P_PHPPHY_CRU, 0, 1), + RK3576_PHPCRU_RESET_OFFSET(SRST_P_APB2ASB_SLV_CHIP_TOP, 0, 3), + RK3576_PHPCRU_RESET_OFFSET(SRST_P_PCIE2_COMBOPHY0, 0, 5), + RK3576_PHPCRU_RESET_OFFSET(SRST_P_PCIE2_COMBOPHY0_GRF, 0, 6), + RK3576_PHPCRU_RESET_OFFSET(SRST_P_PCIE2_COMBOPHY1, 0, 7), + RK3576_PHPCRU_RESET_OFFSET(SRST_P_PCIE2_COMBOPHY1_GRF, 0, 8), + + /* PPLL_SOFTRST_CON01 */ + RK3576_PHPCRU_RESET_OFFSET(SRST_PCIE0_PIPE_PHY, 1, 5), + RK3576_PHPCRU_RESET_OFFSET(SRST_PCIE1_PIPE_PHY, 1, 8), + + /* SECURENS_SOFTRST_CON00 */ + RK3576_SECURENSCRU_RESET_OFFSET(SRST_H_CRYPTO_NS, 0, 3), + RK3576_SECURENSCRU_RESET_OFFSET(SRST_H_TRNG_NS, 0, 4), + RK3576_SECURENSCRU_RESET_OFFSET(SRST_P_OTPC_NS, 0, 8), + RK3576_SECURENSCRU_RESET_OFFSET(SRST_OTPC_NS, 0, 9), + + /* PMU1_SOFTRST_CON00 */ + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_HDPTX_GRF, 0, 0), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_HDPTX_APB, 0, 1), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_MIPI_DCPHY, 0, 2), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_DCPHY_GRF, 0, 3), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_BOT0_APB2ASB, 0, 4), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_BOT1_APB2ASB, 0, 5), + RK3576_PMU1CRU_RESET_OFFSET(SRST_USB2DEBUG, 0, 6), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_CSIPHY_GRF, 0, 7), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_CSIPHY, 0, 8), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_USBPHY_GRF_0, 0, 9), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_USBPHY_GRF_1, 0, 10), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_USBDP_GRF, 0, 11), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_USBDPPHY, 0, 12), + RK3576_PMU1CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY_INIT, 0, 15), + + /* PMU1_SOFTRST_CON01 */ + RK3576_PMU1CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY_CMN, 1, 0), + RK3576_PMU1CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY_LANE, 1, 1), + RK3576_PMU1CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY_PCS, 1, 2), + RK3576_PMU1CRU_RESET_OFFSET(SRST_M_MIPI_DCPHY, 1, 3), + RK3576_PMU1CRU_RESET_OFFSET(SRST_S_MIPI_DCPHY, 1, 4), + RK3576_PMU1CRU_RESET_OFFSET(SRST_SCAN_CSIPHY, 1, 5), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_VCCIO6_IOC, 1, 6), + RK3576_PMU1CRU_RESET_OFFSET(SRST_OTGPHY_0, 1, 7), + RK3576_PMU1CRU_RESET_OFFSET(SRST_OTGPHY_1, 1, 8), + RK3576_PMU1CRU_RESET_OFFSET(SRST_HDPTX_INIT, 1, 9), + RK3576_PMU1CRU_RESET_OFFSET(SRST_HDPTX_CMN, 1, 10), + RK3576_PMU1CRU_RESET_OFFSET(SRST_HDPTX_LANE, 1, 11), + RK3576_PMU1CRU_RESET_OFFSET(SRST_HDMITXHDP, 1, 13), + + /* PMU1_SOFTRST_CON02 */ + RK3576_PMU1CRU_RESET_OFFSET(SRST_MPHY_INIT, 2, 0), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_MPHY_GRF, 2, 1), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_VCCIO7_IOC, 2, 3), + + /* PMU1_SOFTRST_CON03 */ + RK3576_PMU1CRU_RESET_OFFSET(SRST_H_PMU1_BIU, 3, 9), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU1_NIU, 3, 10), + RK3576_PMU1CRU_RESET_OFFSET(SRST_H_PMU_CM0_BIU, 3, 11), + RK3576_PMU1CRU_RESET_OFFSET(SRST_PMU_CM0_CORE, 3, 12), + RK3576_PMU1CRU_RESET_OFFSET(SRST_PMU_CM0_JTAG, 3, 13), + + /* PMU1_SOFTRST_CON04 */ + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_CRU_PMU1, 4, 1), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU1_GRF, 4, 3), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU1_IOC, 4, 4), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU1WDT, 4, 5), + RK3576_PMU1CRU_RESET_OFFSET(SRST_T_PMU1WDT, 4, 6), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMUTIMER, 4, 7), + RK3576_PMU1CRU_RESET_OFFSET(SRST_PMUTIMER0, 4, 9), + RK3576_PMU1CRU_RESET_OFFSET(SRST_PMUTIMER1, 4, 10), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU1PWM, 4, 11), + RK3576_PMU1CRU_RESET_OFFSET(SRST_PMU1PWM, 4, 12), + + /* PMU1_SOFTRST_CON05 */ + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_I2C0, 5, 1), + RK3576_PMU1CRU_RESET_OFFSET(SRST_I2C0, 5, 2), + RK3576_PMU1CRU_RESET_OFFSET(SRST_S_UART1, 5, 5), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_UART1, 5, 6), + RK3576_PMU1CRU_RESET_OFFSET(SRST_PDM0, 5, 13), + RK3576_PMU1CRU_RESET_OFFSET(SRST_H_PDM0, 5, 15), + + /* PMU1_SOFTRST_CON06 */ + RK3576_PMU1CRU_RESET_OFFSET(SRST_M_PDM0, 6, 0), + RK3576_PMU1CRU_RESET_OFFSET(SRST_H_VAD, 6, 1), + + /* PMU1_SOFTRST_CON07 */ + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU0GRF, 7, 4), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU0IOC, 7, 5), + RK3576_PMU1CRU_RESET_OFFSET(SRST_P_GPIO0, 7, 6), + RK3576_PMU1CRU_RESET_OFFSET(SRST_DB_GPIO0, 7, 7), +}; + +int rk3576_reset_bind_lut(struct udevice *pdev, u32 reg_offset, u32 reg_number) +{ + return rockchip_reset_bind_lut(pdev, rk3576_register_offset, + reg_offset, reg_number); +} diff --git a/drivers/rng/msm_rng.c b/drivers/rng/msm_rng.c index f790d3b60f9..aab602c5ed0 100644 --- a/drivers/rng/msm_rng.c +++ b/drivers/rng/msm_rng.c @@ -44,6 +44,11 @@ static int msm_rng_read(struct udevice *dev, void *data, size_t len) u32 *retdata = data; size_t maxsize; u32 val; + int ret; + + ret = clk_enable(&priv->clk); + if (ret < 0) + return ret; /* calculate max size bytes to transfer back to caller */ maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, len); @@ -66,6 +71,8 @@ static int msm_rng_read(struct udevice *dev, void *data, size_t len) break; } while (currsize < maxsize); + clk_disable(&priv->clk); + return 0; } @@ -76,7 +83,7 @@ static int msm_rng_enable(struct msm_rng_priv *priv, int enable) if (enable) { /* Enable PRNG only if it is not already enabled */ val = readl_relaxed(priv->base + PRNG_CONFIG); - if (val & PRNG_CONFIG_HW_ENABLE) { + if (!(val & PRNG_CONFIG_HW_ENABLE)) { val = readl_relaxed(priv->base + PRNG_LFSR_CFG); val &= ~PRNG_LFSR_CFG_MASK; val |= PRNG_LFSR_CFG_CLOCKS; @@ -118,7 +125,9 @@ static int msm_rng_probe(struct udevice *dev) if (ret < 0) return ret; - return msm_rng_enable(priv, 1); + ret = msm_rng_enable(priv, 1); + clk_disable(&priv->clk); + return ret; } static int msm_rng_remove(struct udevice *dev) diff --git a/drivers/rng/rockchip_rng.c b/drivers/rng/rockchip_rng.c index 2426648fbd5..d854ea90044 100644 --- a/drivers/rng/rockchip_rng.c +++ b/drivers/rng/rockchip_rng.c @@ -70,6 +70,27 @@ #define TRNG_v1_VERSION_CODE 0x46BC /* end of TRNG V1 register define */ +/* start of RKRNG register define */ +#define RKRNG_CTRL 0x0010 +#define RKRNG_CTRL_INST_REQ BIT(0) +#define RKRNG_CTRL_RESEED_REQ BIT(1) +#define RKRNG_CTRL_TEST_REQ BIT(2) +#define RKRNG_CTRL_SW_DRNG_REQ BIT(3) +#define RKRNG_CTRL_SW_TRNG_REQ BIT(4) + +#define RKRNG_STATE 0x0014 +#define RKRNG_STATE_INST_ACK BIT(0) +#define RKRNG_STATE_RESEED_ACK BIT(1) +#define RKRNG_STATE_TEST_ACK BIT(2) +#define RKRNG_STATE_SW_DRNG_ACK BIT(3) +#define RKRNG_STATE_SW_TRNG_ACK BIT(4) + +/* DRNG_DATA_0 ~ DNG_DATA_7 */ +#define RKRNG_DRNG_DATA_0 0x0070 +#define RKRNG_DRNG_DATA_7 0x008C + +/* end of RKRNG register define */ + #define RK_RNG_TIME_OUT 50000 /* max 50ms */ #define trng_write(pdata, pos, val) writel(val, (pdata)->base + (pos)) @@ -228,6 +249,49 @@ exit: return retval; } +static int rkrng_init(struct udevice *dev) +{ + struct rk_rng_plat *pdata = dev_get_priv(dev); + u32 reg = 0; + + rk_clrreg(pdata->base + RKRNG_CTRL, 0xffff); + + reg = trng_read(pdata, RKRNG_STATE); + trng_write(pdata, RKRNG_STATE, reg); + + return 0; +} + +static int rkrng_rng_read(struct udevice *dev, void *data, size_t len) +{ + struct rk_rng_plat *pdata = dev_get_priv(dev); + u32 reg = 0; + int retval; + + if (len > RK_HW_RNG_MAX) + return -EINVAL; + + reg = RKRNG_CTRL_SW_DRNG_REQ; + + rk_clrsetreg(pdata->base + RKRNG_CTRL, 0xffff, reg); + + retval = readl_poll_timeout(pdata->base + RKRNG_STATE, reg, + (reg & RKRNG_STATE_SW_DRNG_ACK), + RK_RNG_TIME_OUT); + if (retval) + goto exit; + + trng_write(pdata, RKRNG_STATE, reg); + + rk_rng_read_regs(pdata->base + RKRNG_DRNG_DATA_0, data, len); + +exit: + /* close TRNG */ + rk_clrreg(pdata->base + RKRNG_CTRL, 0xffff); + + return retval; +} + static int rockchip_rng_read(struct udevice *dev, void *data, size_t len) { unsigned char *buf = data; @@ -295,6 +359,11 @@ static const struct rk_rng_soc_data rk_trngv1_soc_data = { .rk_rng_read = rk_trngv1_rng_read, }; +static const struct rk_rng_soc_data rkrng_soc_data = { + .rk_rng_init = rkrng_init, + .rk_rng_read = rkrng_rng_read, +}; + static const struct dm_rng_ops rockchip_rng_ops = { .read = rockchip_rng_read, }; @@ -313,13 +382,21 @@ static const struct udevice_id rockchip_rng_match[] = { .data = (ulong)&rk_cryptov1_soc_data, }, { + .compatible = "rockchip,rk3568-rng", + .data = (ulong)&rk_cryptov2_soc_data, + }, + { .compatible = "rockchip,cryptov2-rng", .data = (ulong)&rk_cryptov2_soc_data, }, { - .compatible = "rockchip,trngv1", + .compatible = "rockchip,rk3588-rng", .data = (ulong)&rk_trngv1_soc_data, }, + { + .compatible = "rockchip,rkrng", + .data = (ulong)&rkrng_soc_data, + }, {}, }; diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index cd0b84c0622..7d022ce718a 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -28,6 +28,10 @@ DEFINE_CACHE_ALIGN_BUFFER(u8, tempbuff, 512); /* temporary data buffer */ #define SCSI_MAX_BLK 0xFFFF #define SCSI_LBA48_READ 0xFFFFFFF +#define SCSI_UNMAP_PARAM_RESERVED 0 +#define SCSI_UNMAP_PARAM_LEN 22 +#define SCSI_UNMAP_PARAM_DATA_LEN 16 + static void scsi_print_error(struct scsi_cmd *pccb) { /* Dummy function that could print an error for debugging */ @@ -78,6 +82,23 @@ static void scsi_setup_inquiry(struct scsi_cmd *pccb) pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ } +static void scsi_setup_sync_cache(struct scsi_cmd *pccb, lbaint_t start, + unsigned short blocks) +{ + pccb->cmd[0] = SCSI_SYNC_CACHE; + pccb->cmd[1] = 0; + pccb->cmd[2] = (unsigned char)(start >> 24) & 0xff; + pccb->cmd[3] = (unsigned char)(start >> 16) & 0xff; + pccb->cmd[4] = (unsigned char)(start >> 8) & 0xff; + pccb->cmd[5] = (unsigned char)start & 0xff; + pccb->cmd[6] = 0; + pccb->cmd[7] = (unsigned char)(blocks >> 8) & 0xff; + pccb->cmd[8] = (unsigned char)blocks & 0xff; + pccb->cmd[9] = 0; + pccb->cmdlen = 10; + pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ +} + static void scsi_setup_read_ext(struct scsi_cmd *pccb, lbaint_t start, unsigned short blocks) { @@ -90,7 +111,7 @@ static void scsi_setup_read_ext(struct scsi_cmd *pccb, lbaint_t start, pccb->cmd[6] = 0; pccb->cmd[7] = (unsigned char)(blocks >> 8) & 0xff; pccb->cmd[8] = (unsigned char)blocks & 0xff; - pccb->cmd[6] = 0; + pccb->cmd[9] = 0; pccb->cmdlen = 10; pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ debug("scsi_setup_read_ext: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n", @@ -121,6 +142,51 @@ static void scsi_setup_write_ext(struct scsi_cmd *pccb, lbaint_t start, pccb->cmd[7], pccb->cmd[8]); } +static void scsi_setup_erase_ext(struct scsi_cmd *pccb, lbaint_t start, + unsigned short blocks) +{ + u8 *param = tempbuff; + const u8 param_size = 24; + + memset(param, 0, param_size); + param[0] = SCSI_UNMAP_PARAM_RESERVED; + param[1] = SCSI_UNMAP_PARAM_LEN; + param[2] = SCSI_UNMAP_PARAM_RESERVED; + param[3] = SCSI_UNMAP_PARAM_DATA_LEN; + + param[8] = 0x0; + param[9] = 0x0; + param[10] = 0x0; + param[11] = 0x0; + param[12] = (start >> 24) & 0xff; + param[13] = (start >> 16) & 0xff; + param[14] = (start >> 8) & 0xff; + param[15] = (start) & 0xff; + param[16] = (blocks >> 24) & 0xff; + param[17] = (blocks >> 16) & 0xff; + param[18] = (blocks >> 8) & 0xff; + param[19] = (blocks) & 0xff; + + memset(pccb->cmd, 0, sizeof(pccb->cmd)); + pccb->cmd[0] = SCSI_UNMAP; + pccb->cmd[1] = 0; + pccb->cmd[6] = 0; + pccb->cmd[7] = 0; + pccb->cmd[8] = param_size; + pccb->cmd[9] = 0; + pccb->cmdlen = 10; + + pccb->pdata = param; + pccb->datalen = param_size; + pccb->dma_dir = DMA_TO_DEVICE; + + debug("%s: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n", + __func__, + pccb->cmd[0], pccb->cmd[1], + pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5], + pccb->cmd[7], pccb->cmd[8]); +} + static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, void *buffer) { @@ -240,11 +306,59 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, } buf_addr += pccb->datalen; } while (blks != 0); + + /* Flush the SCSI cache so we don't lose data on board reset. */ + scsi_setup_sync_cache(pccb, 0, 0); + if (scsi_exec(bdev, pccb)) + scsi_print_error(pccb); + debug("%s: end startblk " LBAF ", blccnt %x buffer %lX\n", __func__, start, smallblks, buf_addr); return blkcnt; } +/******************************************************************************* + * scsi_erase + */ +static ulong scsi_erase(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt) +{ + struct blk_desc *block_dev = dev_get_uclass_plat(dev); + struct udevice *bdev = dev->parent; + struct scsi_plat *uc_plat = dev_get_uclass_plat(bdev); + lbaint_t start, blks, max_blks; + struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; + + /* Setup device */ + pccb->target = block_dev->target; + pccb->lun = block_dev->lun; + start = blknr; + blks = blkcnt; + if (uc_plat->max_bytes_per_req) + max_blks = uc_plat->max_bytes_per_req / block_dev->blksz; + else + max_blks = SCSI_MAX_BLK; + + debug("\n%s: dev %d startblk " LBAF ", blccnt " LBAF "\n", + __func__, block_dev->devnum, start, blks); + do { + if (blks > max_blks) { + scsi_setup_erase_ext(pccb, start, max_blks); + start += max_blks; + blks -= max_blks; + } else { + scsi_setup_erase_ext(pccb, start, blks); + start += blks; + blks = 0; + } + if (scsi_exec(bdev, pccb)) { + scsi_print_error(pccb); + blkcnt -= blks; + break; + } + } while (blks != 0); + return blkcnt; +} + #if IS_ENABLED(CONFIG_BOUNCE_BUFFER) static int scsi_buffer_aligned(struct udevice *dev, struct bounce_buffer *state) { @@ -592,6 +706,7 @@ int scsi_scan(bool verbose) static const struct blk_ops scsi_blk_ops = { .read = scsi_read, .write = scsi_write, + .erase = scsi_erase, #if IS_ENABLED(CONFIG_BOUNCE_BUFFER) .buffer_aligned = scsi_buffer_aligned, #endif /* CONFIG_BOUNCE_BUFFER */ diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index c4f4a8d78df..589b526381f 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -519,6 +519,8 @@ config DEBUG_UART_BASE default 0x0 if DEBUG_UART_SANDBOX default 0xff000000 if DEBUG_UART_ZYNQ && ARCH_ZYNQMP default 0xe0000000 if DEBUG_UART_ZYNQ && ARCH_ZYNQ + default 0xff000000 if DEBUG_UART_PL011 && ARCH_VERSAL + default 0xf1920000 if DEBUG_UART_PL011 && (ARCH_VERSAL_NET || ARCH_VERSAL2) help This is the base address of your UART for memory-mapped UARTs. @@ -554,6 +556,7 @@ config DEBUG_UART_CLOCK default 0 if DEBUG_MVEBU_A3700_UART default 100000000 if DEBUG_UART_ZYNQ && ARCH_ZYNQMP default 50000000 if DEBUG_UART_ZYNQ && ARCH_ZYNQ + default 100000000 if DEBUG_UART_PL011 && (ARCH_VERSAL || ARCH_VERSAL_NET || ARCH_VERSAL2) help The UART input clock determines the speed of the internal UART circuitry. The baud rate is derived from this by dividing the input @@ -789,6 +792,12 @@ config SPL_SYS_NS16550_SERIAL default y if SYS_NS16550_SERIAL || ARCH_SUNXI || ARCH_OMAP2PLUS select SYS_NS16550 +config TPL_SYS_NS16550_SERIAL + bool "NS16550 UART or compatible legacy driver in TPL" + depends on TPL && !TPL_DM_SERIAL + default y if SPL_SYS_NS16550_SERIAL + select SYS_NS16550 + config SYS_NS16550 bool "NS16550 UART or compatible" help diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index ebe692a9963..c32e3fcd439 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -11,7 +11,7 @@ endif obj-$(CONFIG_PL01X_SERIAL) += serial_pl01x.o obj-$(CONFIG_PL011_SERIAL) += serial_pl01x.o -obj-$(CONFIG_$(XPL_)SYS_NS16550_SERIAL) += serial_ns16550.o +obj-$(CONFIG_$(PHASE_)SYS_NS16550_SERIAL) += serial_ns16550.o obj-$(CONFIG_ALTERA_UART) += altera_uart.o obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o @@ -63,7 +63,4 @@ obj-$(CONFIG_XEN_SERIAL) += serial_xen.o obj-$(CONFIG_XTENSA_SEMIHOSTING_SERIAL) += serial_xtensa_semihosting.o obj-$(CONFIG_S5P4418_PL011_SERIAL) += serial_s5p4418_pl011.o -ifndef CONFIG_XPL_BUILD -obj-$(CONFIG_USB_TTY) += usbtty.o -endif obj-$(CONFIG_UART4_SERIAL) += serial_adi_uart4.o diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 0e267d097c5..4f7de3ea215 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -112,9 +112,9 @@ static void serial_out_dynamic(struct ns16550_plat *plat, u8 *addr, } else if (plat->reg_width == 4) { if (plat->flags & NS16550_FLAG_ENDIAN) { if (plat->flags & NS16550_FLAG_BE) - out_be32(addr, value); + out_be32((u32 *)addr, value); else - out_le32(addr, value); + out_le32((u32 *)addr, value); } else { writel(value, addr); } @@ -132,9 +132,9 @@ static int serial_in_dynamic(struct ns16550_plat *plat, u8 *addr) } else if (plat->reg_width == 4) { if (plat->flags & NS16550_FLAG_ENDIAN) { if (plat->flags & NS16550_FLAG_BE) - return in_be32(addr); + return in_be32((u32 *)addr); else - return in_le32(addr); + return in_le32((u32 *)addr); } else { return readl(addr); } @@ -294,13 +294,9 @@ void ns16550_putc(struct ns16550 *com_port, char c) #if !CONFIG_IS_ENABLED(NS16550_MIN_FUNCTIONS) char ns16550_getc(struct ns16550 *com_port) { - while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) { -#if !defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_TTY) - extern void usbtty_poll(void); - usbtty_poll(); -#endif + while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) schedule(); - } + return serial_in(&com_port->rbr); } diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c index 77a1558db68..cc0491bc3c8 100644 --- a/drivers/serial/sandbox.c +++ b/drivers/serial/sandbox.c @@ -63,7 +63,7 @@ static int sandbox_serial_probe(struct udevice *dev) if (state->term_raw != STATE_TERM_RAW) disable_ctrlc(1); - membuff_init(&priv->buf, priv->serial_buf, sizeof(priv->serial_buf)); + membuf_init(&priv->buf, priv->serial_buf, sizeof(priv->serial_buf)); return 0; } @@ -138,15 +138,15 @@ static int sandbox_serial_pending(struct udevice *dev, bool input) return 0; os_usleep(100); - avail = membuff_putraw(&priv->buf, 100, false, &data); + avail = membuf_putraw(&priv->buf, 100, false, &data); if (!avail) return 1; /* buffer full */ count = os_read(0, data, avail); if (count > 0) - membuff_putraw(&priv->buf, count, true, &data); + membuf_putraw(&priv->buf, count, true, &data); - return membuff_avail(&priv->buf); + return membuf_avail(&priv->buf); } static int sandbox_serial_getc(struct udevice *dev) @@ -156,7 +156,7 @@ static int sandbox_serial_getc(struct udevice *dev) if (!sandbox_serial_pending(dev, true)) return -EAGAIN; /* buffer empty */ - return membuff_getbyte(&priv->buf); + return membuf_getbyte(&priv->buf); } #ifdef CONFIG_DEBUG_UART_SANDBOX diff --git a/drivers/serial/serial_mxc.c b/drivers/serial/serial_mxc.c index c5fd740be4d..28f4435d01d 100644 --- a/drivers/serial/serial_mxc.c +++ b/drivers/serial/serial_mxc.c @@ -3,6 +3,7 @@ * (c) 2007 Sascha Hauer <s.hauer@pengutronix.de> */ +#include <clk.h> #include <dm.h> #include <errno.h> #include <watchdog.h> @@ -312,7 +313,17 @@ int mxc_serial_setbrg(struct udevice *dev, int baudrate) static int mxc_serial_probe(struct udevice *dev) { struct mxc_serial_plat *plat = dev_get_plat(dev); +#if CONFIG_IS_ENABLED(CLK_CCF) + int ret; + ret = clk_get_bulk(dev, &plat->clks); + if (ret) + return ret; + + ret = clk_enable_bulk(&plat->clks); + if (ret) + return ret; +#endif _mxc_serial_init(plat->reg, plat->use_dte); return 0; diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c index 1ee58142b3f..1675a9cb9d1 100644 --- a/drivers/serial/serial_stm32.c +++ b/drivers/serial/serial_stm32.c @@ -299,13 +299,19 @@ static inline struct stm32_uart_info *_debug_uart_info(void) static inline void _debug_uart_init(void) { - void __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE); - struct stm32_uart_info *uart_info = _debug_uart_info(); + void __maybe_unused __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE); + struct stm32_uart_info *uart_info __maybe_unused = _debug_uart_info(); - _stm32_serial_init(base, uart_info); - _stm32_serial_setbrg(base, uart_info, - CONFIG_DEBUG_UART_CLOCK, - CONFIG_BAUDRATE); + /* + * debug_uart_init() is only usable when SPL_BUILD is enabled + * (STM32MP1 case only) + */ + if (IS_ENABLED(CONFIG_DEBUG_UART) && IS_ENABLED(CONFIG_SPL_BUILD)) { + _stm32_serial_init(base, uart_info); + _stm32_serial_setbrg(base, uart_info, + CONFIG_DEBUG_UART_CLOCK, + CONFIG_BAUDRATE); + } } static inline void _debug_uart_putc(int c) diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c deleted file mode 100644 index b7d77fbb6a9..00000000000 --- a/drivers/serial/usbtty.c +++ /dev/null @@ -1,983 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * (C) Copyright 2006 - * Bryan O'Donoghue, bodonoghue@codehermit.ie - */ - -#include <config.h> -#include <circbuf.h> -#include <env.h> -#include <serial.h> -#include <stdio_dev.h> -#include <asm/unaligned.h> -#include "usbtty.h" -#include "usb_cdc_acm.h" -#include "usbdescriptors.h" - -#ifdef DEBUG -#define TTYDBG(fmt,args...)\ - serial_printf("[%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args) -#else -#define TTYDBG(fmt,args...) do{}while(0) -#endif - -#if 1 -#define TTYERR(fmt,args...)\ - serial_printf("ERROR![%s] %s %d: "fmt, __FILE__,__FUNCTION__,\ - __LINE__,##args) -#else -#define TTYERR(fmt,args...) do{}while(0) -#endif - -/* - * Defines - */ -#define NUM_CONFIGS 1 -#define MAX_INTERFACES 2 -#define NUM_ENDPOINTS 3 -#define ACM_TX_ENDPOINT 3 -#define ACM_RX_ENDPOINT 2 -#define GSERIAL_TX_ENDPOINT 2 -#define GSERIAL_RX_ENDPOINT 1 -#define NUM_ACM_INTERFACES 2 -#define NUM_GSERIAL_INTERFACES 1 -#define CFG_USBD_DATA_INTERFACE_STR "Bulk Data Interface" -#define CFG_USBD_CTRL_INTERFACE_STR "Control Interface" - -/* - * Buffers to hold input and output data - */ -#define USBTTY_BUFFER_SIZE 2048 -static circbuf_t usbtty_input; -static circbuf_t usbtty_output; - -/* - * Instance variables - */ -static struct stdio_dev usbttydev; -static struct usb_device_instance device_instance[1]; -static struct usb_bus_instance bus_instance[1]; -static struct usb_configuration_instance config_instance[NUM_CONFIGS]; -static struct usb_interface_instance interface_instance[MAX_INTERFACES]; -static struct usb_alternate_instance alternate_instance[MAX_INTERFACES]; -/* one extra for control endpoint */ -static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS+1]; - -/* - * Global flag - */ -int usbtty_configured_flag = 0; - -/* - * Serial number - */ -static char serial_number[16]; - -/* - * Descriptors, Strings, Local variables. - */ - -/* defined and used by gadget/ep0.c */ -extern struct usb_string_descriptor **usb_strings; - -/* Indicies, References */ -static unsigned short rx_endpoint = 0; -static unsigned short tx_endpoint = 0; -static unsigned short interface_count = 0; -static struct usb_string_descriptor *usbtty_string_table[STR_COUNT]; - -/* USB Descriptor Strings */ -static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4}; -static u8 wstrManufacturer[2 + 2*(sizeof(CONFIG_USBD_MANUFACTURER)-1)]; -static u8 wstrProduct[2 + 2*(sizeof(CONFIG_USBD_PRODUCT_NAME)-1)]; -static u8 wstrSerial[2 + 2*(sizeof(serial_number) - 1)]; -static u8 wstrConfiguration[2 + 2*(sizeof(CFG_USBD_CONFIGURATION_STR)-1)]; -static u8 wstrDataInterface[2 + 2*(sizeof(CFG_USBD_DATA_INTERFACE_STR)-1)]; -static u8 wstrCtrlInterface[2 + 2*(sizeof(CFG_USBD_DATA_INTERFACE_STR)-1)]; - -/* Standard USB Data Structures */ -static struct usb_interface_descriptor interface_descriptors[MAX_INTERFACES]; -static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS]; -static struct usb_configuration_descriptor *configuration_descriptor = 0; -static struct usb_device_descriptor device_descriptor = { - .bLength = sizeof(struct usb_device_descriptor), - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = cpu_to_le16(USB_BCD_VERSION), - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE, - .idVendor = cpu_to_le16(CONFIG_USBD_VENDORID), - .bcdDevice = cpu_to_le16(USBTTY_BCD_DEVICE), - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIAL, - .bNumConfigurations = NUM_CONFIGS -}; - -/* - * Static CDC ACM specific descriptors - */ - -struct acm_config_desc { - struct usb_configuration_descriptor configuration_desc; - - /* Master Interface */ - struct usb_interface_descriptor interface_desc; - - struct usb_class_header_function_descriptor usb_class_header; - struct usb_class_call_management_descriptor usb_class_call_mgt; - struct usb_class_abstract_control_descriptor usb_class_acm; - struct usb_class_union_function_descriptor usb_class_union; - struct usb_endpoint_descriptor notification_endpoint; - - /* Slave Interface */ - struct usb_interface_descriptor data_class_interface; - struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS-1]; -} __attribute__((packed)); - -static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = { - { - .configuration_desc ={ - .bLength = - sizeof(struct usb_configuration_descriptor), - .bDescriptorType = USB_DT_CONFIG, - .wTotalLength = - cpu_to_le16(sizeof(struct acm_config_desc)), - .bNumInterfaces = NUM_ACM_INTERFACES, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG, - .bmAttributes = - BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED, - .bMaxPower = USBTTY_MAXPOWER - }, - /* Interface 1 */ - .interface_desc = { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 0, - .bNumEndpoints = 0x01, - .bInterfaceClass = - COMMUNICATIONS_INTERFACE_CLASS_CONTROL, - .bInterfaceSubClass = COMMUNICATIONS_ACM_SUBCLASS, - .bInterfaceProtocol = COMMUNICATIONS_V25TER_PROTOCOL, - .iInterface = STR_CTRL_INTERFACE, - }, - .usb_class_header = { - .bFunctionLength = - sizeof(struct usb_class_header_function_descriptor), - .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_ST_HEADER, - .bcdCDC = cpu_to_le16(110), - }, - .usb_class_call_mgt = { - .bFunctionLength = - sizeof(struct usb_class_call_management_descriptor), - .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_ST_CMF, - .bmCapabilities = 0x00, - .bDataInterface = 0x01, - }, - .usb_class_acm = { - .bFunctionLength = - sizeof(struct usb_class_abstract_control_descriptor), - .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_ST_ACMF, - .bmCapabilities = 0x00, - }, - .usb_class_union = { - .bFunctionLength = - sizeof(struct usb_class_union_function_descriptor), - .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_ST_UF, - .bMasterInterface = 0x00, - .bSlaveInterface0 = 0x01, - }, - .notification_endpoint = { - .bLength = - sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = UDC_INT_ENDPOINT | USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize - = cpu_to_le16(CFG_USBD_SERIAL_INT_PKTSIZE), - .bInterval = 0xFF, - }, - - /* Interface 2 */ - .data_class_interface = { - .bLength = - sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0x01, - .bAlternateSetting = 0x00, - .bNumEndpoints = 0x02, - .bInterfaceClass = - COMMUNICATIONS_INTERFACE_CLASS_DATA, - .bInterfaceSubClass = DATA_INTERFACE_SUBCLASS_NONE, - .bInterfaceProtocol = DATA_INTERFACE_PROTOCOL_NONE, - .iInterface = STR_DATA_INTERFACE, - }, - .data_endpoints = { - { - .bLength = - sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = UDC_OUT_ENDPOINT | USB_DIR_OUT, - .bmAttributes = - USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = - cpu_to_le16(CFG_USBD_SERIAL_BULK_PKTSIZE), - .bInterval = 0xFF, - }, - { - .bLength = - sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = UDC_IN_ENDPOINT | USB_DIR_IN, - .bmAttributes = - USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = - cpu_to_le16(CFG_USBD_SERIAL_BULK_PKTSIZE), - .bInterval = 0xFF, - }, - }, - }, -}; - -static struct rs232_emu rs232_desc={ - .dter = 115200, - .stop_bits = 0x00, - .parity = 0x00, - .data_bits = 0x08 -}; - -/* - * Static Generic Serial specific data - */ - -struct gserial_config_desc { - - struct usb_configuration_descriptor configuration_desc; - struct usb_interface_descriptor interface_desc[NUM_GSERIAL_INTERFACES]; - struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS]; - -} __attribute__((packed)); - -static struct gserial_config_desc -gserial_configuration_descriptors[NUM_CONFIGS] ={ - { - .configuration_desc ={ - .bLength = sizeof(struct usb_configuration_descriptor), - .bDescriptorType = USB_DT_CONFIG, - .wTotalLength = - cpu_to_le16(sizeof(struct gserial_config_desc)), - .bNumInterfaces = NUM_GSERIAL_INTERFACES, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG, - .bmAttributes = - BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED, - .bMaxPower = USBTTY_MAXPOWER - }, - .interface_desc = { - { - .bLength = - sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 0, - .bNumEndpoints = NUM_ENDPOINTS, - .bInterfaceClass = - COMMUNICATIONS_INTERFACE_CLASS_VENDOR, - .bInterfaceSubClass = - COMMUNICATIONS_NO_SUBCLASS, - .bInterfaceProtocol = - COMMUNICATIONS_NO_PROTOCOL, - .iInterface = STR_DATA_INTERFACE - }, - }, - .data_endpoints = { - { - .bLength = - sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = UDC_OUT_ENDPOINT | USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = - cpu_to_le16(CFG_USBD_SERIAL_OUT_PKTSIZE), - .bInterval= 0xFF, - }, - { - .bLength = - sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = UDC_IN_ENDPOINT | USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = - cpu_to_le16(CFG_USBD_SERIAL_IN_PKTSIZE), - .bInterval = 0xFF, - }, - { - .bLength = - sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = UDC_INT_ENDPOINT | USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = - cpu_to_le16(CFG_USBD_SERIAL_INT_PKTSIZE), - .bInterval = 0xFF, - }, - }, - }, -}; - -/* - * Static Function Prototypes - */ - -static void usbtty_init_strings (void); -static void usbtty_init_instances (void); -static void usbtty_init_endpoints (void); -static void usbtty_init_terminal_type(short type); -static void usbtty_event_handler (struct usb_device_instance *device, - usb_device_event_t event, int data); -static int usbtty_cdc_setup(struct usb_device_request *request, - struct urb *urb); -static int usbtty_configured (void); -static int write_buffer (circbuf_t * buf); -static int fill_buffer (circbuf_t * buf); - -void usbtty_poll (void); - -/* utility function for converting char* to wide string used by USB */ -static void str2wide (char *str, u16 * wide) -{ - int i; - for (i = 0; i < strlen (str) && str[i]; i++){ - #if defined(__LITTLE_ENDIAN) - wide[i] = (u16) str[i]; - #elif defined(__BIG_ENDIAN) - wide[i] = ((u16)(str[i])<<8); - #else - #error "__LITTLE_ENDIAN or __BIG_ENDIAN undefined" - #endif - } -} - -/* - * Test whether a character is in the RX buffer - */ - -int usbtty_tstc(struct stdio_dev *dev) -{ - struct usb_endpoint_instance *endpoint = - &endpoint_instance[rx_endpoint]; - - /* If no input data exists, allow more RX to be accepted */ - if(usbtty_input.size <= 0){ - udc_unset_nak(endpoint->endpoint_address&0x03); - } - - usbtty_poll (); - return (usbtty_input.size > 0); -} - -/* - * Read a single byte from the usb client port. Returns 1 on success, 0 - * otherwise. When the function is succesfull, the character read is - * written into its argument c. - */ - -int usbtty_getc(struct stdio_dev *dev) -{ - char c; - struct usb_endpoint_instance *endpoint = - &endpoint_instance[rx_endpoint]; - - while (usbtty_input.size <= 0) { - udc_unset_nak(endpoint->endpoint_address&0x03); - usbtty_poll (); - } - - buf_pop (&usbtty_input, &c, 1); - udc_set_nak(endpoint->endpoint_address&0x03); - - return c; -} - -/* - * Output a single byte to the usb client port. - */ -void usbtty_putc(struct stdio_dev *dev, const char c) -{ - if (!usbtty_configured ()) - return; - - /* If \n, also do \r */ - if (c == '\n') - buf_push (&usbtty_output, "\r", 1); - - buf_push(&usbtty_output, &c, 1); - - /* Poll at end to handle new data... */ - if ((usbtty_output.size + 2) >= usbtty_output.totalsize) { - usbtty_poll (); - } -} - -/* usbtty_puts() helper function for finding the next '\n' in a string */ -static int next_nl_pos (const char *s) -{ - int i; - - for (i = 0; s[i] != '\0'; i++) { - if (s[i] == '\n') - return i; - } - return i; -} - -/* - * Output a string to the usb client port - implementing flow control - */ - -static void __usbtty_puts (const char *str, int len) -{ - int maxlen = usbtty_output.totalsize; - int space, n; - - /* break str into chunks < buffer size, if needed */ - while (len > 0) { - usbtty_poll (); - - space = maxlen - usbtty_output.size; - /* Empty buffer here, if needed, to ensure space... */ - if (space) { - write_buffer (&usbtty_output); - - n = min(space, min(len, maxlen)); - buf_push (&usbtty_output, str, n); - - str += n; - len -= n; - } - } -} - -void usbtty_puts(struct stdio_dev *dev, const char *str) -{ - int n; - int len; - - if (!usbtty_configured ()) - return; - - len = strlen (str); - /* add '\r' for each '\n' */ - while (len > 0) { - n = next_nl_pos (str); - - if (str[n] == '\n') { - __usbtty_puts(str, n); - __usbtty_puts("\r\n", 2); - str += (n + 1); - len -= (n + 1); - } else { - /* No \n found. All done. */ - __usbtty_puts (str, n); - break; - } - } - - /* Poll at end to handle new data... */ - usbtty_poll (); -} - -/* - * Initialize the usb client port. - * - */ -int drv_usbtty_init (void) -{ - int rc; - char * sn; - char * tt; - int snlen; - - /* Get serial number */ - sn = env_get("serial#"); - if (!sn) - sn = "000000000000"; - snlen = strlen(sn); - if (snlen > sizeof(serial_number) - 1) { - printf ("Warning: serial number %s is too long (%d > %lu)\n", - sn, snlen, (ulong)(sizeof(serial_number) - 1)); - snlen = sizeof(serial_number) - 1; - } - memcpy (serial_number, sn, snlen); - serial_number[snlen] = '\0'; - - /* Decide on which type of UDC device to be. - */ - tt = env_get("usbtty"); - if (!tt) - tt = "generic"; - usbtty_init_terminal_type(strcmp(tt,"cdc_acm")); - - /* prepare buffers... */ - buf_init (&usbtty_input, USBTTY_BUFFER_SIZE); - buf_init (&usbtty_output, USBTTY_BUFFER_SIZE); - - /* Now, set up USB controller and infrastructure */ - udc_init (); /* Basic USB initialization */ - - usbtty_init_strings (); - usbtty_init_instances (); - - usbtty_init_endpoints (); - - udc_startup_events (device_instance);/* Enable dev, init udc pointers */ - udc_connect (); /* Enable pullup for host detection */ - - /* Device initialization */ - memset (&usbttydev, 0, sizeof (usbttydev)); - - strcpy (usbttydev.name, "usbtty"); - usbttydev.ext = 0; /* No extensions */ - usbttydev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_OUTPUT; - usbttydev.tstc = usbtty_tstc; /* 'tstc' function */ - usbttydev.getc = usbtty_getc; /* 'getc' function */ - usbttydev.putc = usbtty_putc; /* 'putc' function */ - usbttydev.puts = usbtty_puts; /* 'puts' function */ - - rc = stdio_register (&usbttydev); - - return (rc == 0) ? 1 : rc; -} - -static void usbtty_init_strings (void) -{ - struct usb_string_descriptor *string; - - usbtty_string_table[STR_LANG] = - (struct usb_string_descriptor*)wstrLang; - - string = (struct usb_string_descriptor *) wstrManufacturer; - string->bLength = sizeof(wstrManufacturer); - string->bDescriptorType = USB_DT_STRING; - str2wide (CONFIG_USBD_MANUFACTURER, string->wData); - usbtty_string_table[STR_MANUFACTURER]=string; - - string = (struct usb_string_descriptor *) wstrProduct; - string->bLength = sizeof(wstrProduct); - string->bDescriptorType = USB_DT_STRING; - str2wide (CONFIG_USBD_PRODUCT_NAME, string->wData); - usbtty_string_table[STR_PRODUCT]=string; - - string = (struct usb_string_descriptor *) wstrSerial; - string->bLength = sizeof(serial_number); - string->bDescriptorType = USB_DT_STRING; - str2wide (serial_number, string->wData); - usbtty_string_table[STR_SERIAL]=string; - - string = (struct usb_string_descriptor *) wstrConfiguration; - string->bLength = sizeof(wstrConfiguration); - string->bDescriptorType = USB_DT_STRING; - str2wide (CFG_USBD_CONFIGURATION_STR, string->wData); - usbtty_string_table[STR_CONFIG]=string; - - string = (struct usb_string_descriptor *) wstrDataInterface; - string->bLength = sizeof(wstrDataInterface); - string->bDescriptorType = USB_DT_STRING; - str2wide (CFG_USBD_DATA_INTERFACE_STR, string->wData); - usbtty_string_table[STR_DATA_INTERFACE]=string; - - string = (struct usb_string_descriptor *) wstrCtrlInterface; - string->bLength = sizeof(wstrCtrlInterface); - string->bDescriptorType = USB_DT_STRING; - str2wide (CFG_USBD_CTRL_INTERFACE_STR, string->wData); - usbtty_string_table[STR_CTRL_INTERFACE]=string; - - /* Now, initialize the string table for ep0 handling */ - usb_strings = usbtty_string_table; -} - -#define init_wMaxPacketSize(x) le16_to_cpu(get_unaligned(\ - &ep_descriptor_ptrs[(x) - 1]->wMaxPacketSize)); - -static void usbtty_init_instances (void) -{ - int i; - - /* initialize device instance */ - memset (device_instance, 0, sizeof (struct usb_device_instance)); - device_instance->device_state = STATE_INIT; - device_instance->device_descriptor = &device_descriptor; - device_instance->event = usbtty_event_handler; - device_instance->cdc_recv_setup = usbtty_cdc_setup; - device_instance->bus = bus_instance; - device_instance->configurations = NUM_CONFIGS; - device_instance->configuration_instance_array = config_instance; - - /* initialize bus instance */ - memset (bus_instance, 0, sizeof (struct usb_bus_instance)); - bus_instance->device = device_instance; - bus_instance->endpoint_array = endpoint_instance; - bus_instance->max_endpoints = 1; - bus_instance->maxpacketsize = 64; - bus_instance->serial_number_str = serial_number; - - /* configuration instance */ - memset (config_instance, 0, - sizeof (struct usb_configuration_instance)); - config_instance->interfaces = interface_count; - config_instance->configuration_descriptor = configuration_descriptor; - config_instance->interface_instance_array = interface_instance; - - /* interface instance */ - memset (interface_instance, 0, - sizeof (struct usb_interface_instance)); - interface_instance->alternates = 1; - interface_instance->alternates_instance_array = alternate_instance; - - /* alternates instance */ - memset (alternate_instance, 0, - sizeof (struct usb_alternate_instance)); - alternate_instance->interface_descriptor = interface_descriptors; - alternate_instance->endpoints = NUM_ENDPOINTS; - alternate_instance->endpoints_descriptor_array = ep_descriptor_ptrs; - - /* endpoint instances */ - memset (&endpoint_instance[0], 0, - sizeof (struct usb_endpoint_instance)); - endpoint_instance[0].endpoint_address = 0; - endpoint_instance[0].rcv_packetSize = EP0_MAX_PACKET_SIZE; - endpoint_instance[0].rcv_attributes = USB_ENDPOINT_XFER_CONTROL; - endpoint_instance[0].tx_packetSize = EP0_MAX_PACKET_SIZE; - endpoint_instance[0].tx_attributes = USB_ENDPOINT_XFER_CONTROL; - udc_setup_ep (device_instance, 0, &endpoint_instance[0]); - - for (i = 1; i <= NUM_ENDPOINTS; i++) { - memset (&endpoint_instance[i], 0, - sizeof (struct usb_endpoint_instance)); - - endpoint_instance[i].endpoint_address = - ep_descriptor_ptrs[i - 1]->bEndpointAddress; - - endpoint_instance[i].rcv_attributes = - ep_descriptor_ptrs[i - 1]->bmAttributes; - - endpoint_instance[i].rcv_packetSize = init_wMaxPacketSize(i); - - endpoint_instance[i].tx_attributes = - ep_descriptor_ptrs[i - 1]->bmAttributes; - - endpoint_instance[i].tx_packetSize = init_wMaxPacketSize(i); - - endpoint_instance[i].tx_attributes = - ep_descriptor_ptrs[i - 1]->bmAttributes; - - urb_link_init (&endpoint_instance[i].rcv); - urb_link_init (&endpoint_instance[i].rdy); - urb_link_init (&endpoint_instance[i].tx); - urb_link_init (&endpoint_instance[i].done); - - if (endpoint_instance[i].endpoint_address & USB_DIR_IN) - endpoint_instance[i].tx_urb = - usbd_alloc_urb (device_instance, - &endpoint_instance[i]); - else - endpoint_instance[i].rcv_urb = - usbd_alloc_urb (device_instance, - &endpoint_instance[i]); - } -} - -static void usbtty_init_endpoints (void) -{ - int i; - - bus_instance->max_endpoints = NUM_ENDPOINTS + 1; - for (i = 1; i <= NUM_ENDPOINTS; i++) { - udc_setup_ep (device_instance, i, &endpoint_instance[i]); - } -} - -/* usbtty_init_terminal_type - * - * Do some late binding for our device type. - */ -static void usbtty_init_terminal_type(short type) -{ - switch(type){ - /* CDC ACM */ - case 0: - /* Assign endpoint descriptors */ - ep_descriptor_ptrs[0] = - &acm_configuration_descriptors[0].notification_endpoint; - ep_descriptor_ptrs[1] = - &acm_configuration_descriptors[0].data_endpoints[0]; - ep_descriptor_ptrs[2] = - &acm_configuration_descriptors[0].data_endpoints[1]; - - /* Enumerate Device Descriptor */ - device_descriptor.bDeviceClass = - COMMUNICATIONS_DEVICE_CLASS; - device_descriptor.idProduct = - cpu_to_le16(CONFIG_USBD_PRODUCTID_CDCACM); - - /* Assign endpoint indices */ - tx_endpoint = ACM_TX_ENDPOINT; - rx_endpoint = ACM_RX_ENDPOINT; - - /* Configuration Descriptor */ - configuration_descriptor = - (struct usb_configuration_descriptor*) - &acm_configuration_descriptors; - - /* Interface count */ - interface_count = NUM_ACM_INTERFACES; - break; - - /* BULK IN/OUT & Default */ - case 1: - default: - /* Assign endpoint descriptors */ - ep_descriptor_ptrs[0] = - &gserial_configuration_descriptors[0].data_endpoints[0]; - ep_descriptor_ptrs[1] = - &gserial_configuration_descriptors[0].data_endpoints[1]; - ep_descriptor_ptrs[2] = - &gserial_configuration_descriptors[0].data_endpoints[2]; - - /* Enumerate Device Descriptor */ - device_descriptor.bDeviceClass = 0xFF; - device_descriptor.idProduct = - cpu_to_le16(CONFIG_USBD_PRODUCTID_GSERIAL); - /* Assign endpoint indices */ - tx_endpoint = GSERIAL_TX_ENDPOINT; - rx_endpoint = GSERIAL_RX_ENDPOINT; - - /* Configuration Descriptor */ - configuration_descriptor = - (struct usb_configuration_descriptor*) - &gserial_configuration_descriptors; - - /* Interface count */ - interface_count = NUM_GSERIAL_INTERFACES; - break; - } -} - -/******************************************************************************/ - -static struct urb *next_urb (struct usb_device_instance *device, - struct usb_endpoint_instance *endpoint) -{ - struct urb *current_urb = NULL; - int space; - - /* If there's a queue, then we should add to the last urb */ - if (!endpoint->tx_queue) { - current_urb = endpoint->tx_urb; - } else { - /* Last urb from tx chain */ - current_urb = - p2surround (struct urb, link, endpoint->tx.prev); - } - - /* Make sure this one has enough room */ - space = current_urb->buffer_length - current_urb->actual_length; - if (space > 0) { - return current_urb; - } else { /* No space here */ - /* First look at done list */ - current_urb = first_urb_detached (&endpoint->done); - if (!current_urb) { - current_urb = usbd_alloc_urb (device, endpoint); - } - - urb_append (&endpoint->tx, current_urb); - endpoint->tx_queue++; - } - return current_urb; -} - -static int write_buffer (circbuf_t * buf) -{ - if (!usbtty_configured ()) { - return 0; - } - - struct usb_endpoint_instance *endpoint = - &endpoint_instance[tx_endpoint]; - struct urb *current_urb = NULL; - - /* TX data still exists - send it now - */ - if(endpoint->sent < endpoint->tx_urb->actual_length){ - if(udc_endpoint_write (endpoint)){ - /* Write pre-empted by RX */ - return -1; - } - } - - if (buf->size) { - char *dest; - - int space_avail; - int popnum, popped; - int total = 0; - - /* Break buffer into urb sized pieces, - * and link each to the endpoint - */ - while (buf->size > 0) { - - current_urb = next_urb (device_instance, endpoint); - - dest = (char*)current_urb->buffer + - current_urb->actual_length; - - space_avail = - current_urb->buffer_length - - current_urb->actual_length; - popnum = min(space_avail, (int)buf->size); - if (popnum == 0) - break; - - popped = buf_pop (buf, dest, popnum); - if (popped == 0) - break; - current_urb->actual_length += popped; - total += popped; - - /* If endpoint->last == 0, then transfers have - * not started on this endpoint - */ - if (endpoint->last == 0) { - if(udc_endpoint_write (endpoint)){ - /* Write pre-empted by RX */ - return -1; - } - } - - }/* end while */ - return total; - } - - return 0; -} - -static int fill_buffer (circbuf_t * buf) -{ - struct usb_endpoint_instance *endpoint = - &endpoint_instance[rx_endpoint]; - - if (endpoint->rcv_urb && endpoint->rcv_urb->actual_length) { - unsigned int nb = 0; - char *src = (char *) endpoint->rcv_urb->buffer; - unsigned int rx_avail = buf->totalsize - buf->size; - - if(rx_avail >= endpoint->rcv_urb->actual_length){ - - nb = endpoint->rcv_urb->actual_length; - buf_push (buf, src, nb); - endpoint->rcv_urb->actual_length = 0; - - } - return nb; - } - return 0; -} - -static int usbtty_configured (void) -{ - return usbtty_configured_flag; -} - -/******************************************************************************/ - -static void usbtty_event_handler (struct usb_device_instance *device, - usb_device_event_t event, int data) -{ - switch (event) { - case DEVICE_RESET: - case DEVICE_BUS_INACTIVE: - usbtty_configured_flag = 0; - break; - case DEVICE_CONFIGURED: - usbtty_configured_flag = 1; - break; - - case DEVICE_ADDRESS_ASSIGNED: - usbtty_init_endpoints (); - - default: - break; - } -} - -/******************************************************************************/ - -int usbtty_cdc_setup(struct usb_device_request *request, struct urb *urb) -{ - switch (request->bRequest){ - - case ACM_SET_CONTROL_LINE_STATE: /* Implies DTE ready */ - break; - case ACM_SEND_ENCAPSULATED_COMMAND : /* Required */ - break; - case ACM_SET_LINE_ENCODING : /* DTE stop/parity bits - * per character */ - break; - case ACM_GET_ENCAPSULATED_RESPONSE : /* request response */ - break; - case ACM_GET_LINE_ENCODING : /* request DTE rate, - * stop/parity bits */ - memcpy (urb->buffer , &rs232_desc, sizeof(rs232_desc)); - urb->actual_length = sizeof(rs232_desc); - - break; - default: - return 1; - } - return 0; -} - -/******************************************************************************/ - -/* - * Since interrupt handling has not yet been implemented, we use this function - * to handle polling. This is called by the tstc,getc,putc,puts routines to - * update the USB state. - */ -void usbtty_poll (void) -{ - /* New interrupts? */ - udc_irq(); - - /* Write any output data to host buffer - * (do this before checking interrupts to avoid missing one) - */ - if (usbtty_configured ()) { - write_buffer (&usbtty_output); - } - - /* New interrupts? */ - udc_irq(); - - /* Check for new data from host.. - * (do this after checking interrupts to get latest data) - */ - if (usbtty_configured ()) { - fill_buffer (&usbtty_input); - } - - /* New interrupts? */ - udc_irq(); - -} diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h deleted file mode 100644 index b176a7961b8..00000000000 --- a/drivers/serial/usbtty.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * (C) Copyright 2006 - * Bryan O'Donoghue, bodonoghue@codehermit.ie, CodeHermit - */ - -#ifndef __USB_TTY_H__ -#define __USB_TTY_H__ - -#include <usbdevice.h> -#if defined(CONFIG_PPC) -#include <usb/mpc8xx_udc.h> -#elif defined(CONFIG_CI_UDC) -#include <usb/ci_udc.h> -#endif - -#include <usb/udc.h> -#include <version.h> - -#ifndef CFG_USBD_CONFIGURATION_STR -#define CFG_USBD_CONFIGURATION_STR "TTY via USB" -#endif - -#define CFG_USBD_SERIAL_OUT_ENDPOINT UDC_OUT_ENDPOINT -#define CFG_USBD_SERIAL_OUT_PKTSIZE UDC_OUT_PACKET_SIZE -#define CFG_USBD_SERIAL_IN_ENDPOINT UDC_IN_ENDPOINT -#define CFG_USBD_SERIAL_IN_PKTSIZE UDC_IN_PACKET_SIZE -#define CFG_USBD_SERIAL_INT_ENDPOINT UDC_INT_ENDPOINT -#define CFG_USBD_SERIAL_INT_PKTSIZE UDC_INT_PACKET_SIZE -#define CFG_USBD_SERIAL_BULK_PKTSIZE UDC_BULK_PACKET_SIZE - -#define USBTTY_DEVICE_CLASS COMMUNICATIONS_DEVICE_CLASS - -#define USBTTY_BCD_DEVICE 0x00 -#define USBTTY_MAXPOWER 0x00 - -#define STR_LANG 0x00 -#define STR_MANUFACTURER 0x01 -#define STR_PRODUCT 0x02 -#define STR_SERIAL 0x03 -#define STR_CONFIG 0x04 -#define STR_DATA_INTERFACE 0x05 -#define STR_CTRL_INTERFACE 0x06 -#define STR_COUNT 0x07 - -#endif diff --git a/drivers/soc/soc_ti_k3.c b/drivers/soc/soc_ti_k3.c index a3acca4d394..b34cbd08e07 100644 --- a/drivers/soc/soc_ti_k3.c +++ b/drivers/soc/soc_ti_k3.c @@ -18,8 +18,12 @@ struct soc_ti_k3_plat { static const char *get_family_string(u32 idreg) { const char *family; + u32 jtag_dev_id; + u32 pkg; u32 soc; + jtag_dev_id = readl(CTRLMMR_WKUP_JTAG_DEVICE_ID); + soc = (idreg & JTAG_ID_PARTNO_MASK) >> JTAG_ID_PARTNO_SHIFT; switch (soc) { @@ -51,8 +55,16 @@ static const char *get_family_string(u32 idreg) family = "J722S"; break; case JTAG_ID_PARTNO_J784S4: - family = "J784S4"; - break; + { + /* Keep default family as J784S4 */ + family = "J784S4"; + + pkg = (jtag_dev_id & JTAG_DEV_J742S2_PKG_MASK) >> JTAG_DEV_J742S2_PKG_SHIFT; + if (pkg == JTAG_ID_PKG_J742S2) + family = "J742S2"; + + break; + } default: family = "Unknown Silicon"; }; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 96ea033082b..a3513f0a3ef 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -52,6 +52,22 @@ config SPI_DIRMAP if DM_SPI +config ADI_SPI3 + bool "Enable ADI SPI Driver" + depends on ARCH_SC5XX + help + Enable the ADI (Analog Devices) SPI controller driver. This + driver enables the support for SC5XX spi controller. + +config AIROHA_SNFI_SPI + bool "Airoha SPI memory controller driver" + depends on SPI_MEM + help + Enable the Airoha SPI memory controller driver. This driver is + originally based on the Airoha SNFI IP core. It can only be + used to access SPI memory devices like SPI-NOR or SPI-NAND on + platforms embedding this IP core, like AN7581. + config ALTERA_SPI bool "Altera SPI driver" help @@ -85,7 +101,7 @@ config ATH79_SPI config ATMEL_QSPI bool "Atmel Quad SPI Controller" - depends on ARCH_AT91 + depends on ARCH_AT91 && SPI_MEM help Enable the Atmel Quad SPI controller in master mode. This driver does not support generic SPI. The implementation supports only the @@ -135,7 +151,7 @@ config BCMSTB_SPI config CORTINA_SFLASH bool "Cortina-Access Serial Flash controller driver" - depends on DM_SPI && SPI_MEM + depends on SPI_MEM help Enable the Cortina-Access Serial Flash controller driver. This driver can be used to access the SPI NOR/NAND flash on platforms embedding this diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 7051e2a00c6..da91b18b6ed 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,8 @@ obj-y += spi.o obj-$(CONFIG_SPI_MEM) += spi-mem-nodm.o endif +obj-$(CONFIG_ADI_SPI3) += adi_spi3.o +obj-$(CONFIG_AIROHA_SNFI_SPI) += airoha_snfi_spi.o obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_APPLE_SPI) += apple_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o diff --git a/drivers/spi/adi_spi3.c b/drivers/spi/adi_spi3.c new file mode 100644 index 00000000000..2125d25561a --- /dev/null +++ b/drivers/spi/adi_spi3.c @@ -0,0 +1,679 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Converted to driver model by Nathan Barrett-Morrison + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * Contact: Ian Roberts <ian.roberts@timesys.com> + * Contact: Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com> + * + */ + +#include <clk.h> +#include <dm.h> +#include <mapmem.h> +#include <spi.h> +#include <spi-mem.h> +#include <dm/device_compat.h> +#include <linux/io.h> + +#define SPI_IDLE_VAL 0xff + +#define MAX_CTRL_CS 7 + +/* SPI_CONTROL */ +#define SPI_CTL_EN 0x00000001 /* Enable */ +#define SPI_CTL_MSTR 0x00000002 /* Master/Slave */ +#define SPI_CTL_PSSE 0x00000004 /* controls modf error in master mode */ +#define SPI_CTL_ODM 0x00000008 /* Open Drain Mode */ +#define SPI_CTL_CPHA 0x00000010 /* Clock Phase */ +#define SPI_CTL_CPOL 0x00000020 /* Clock Polarity */ +#define SPI_CTL_ASSEL 0x00000040 /* Slave Select Pin Control */ +#define SPI_CTL_SELST 0x00000080 /* Slave Select Polarity in transfers */ +#define SPI_CTL_EMISO 0x00000100 /*Enable MISO */ +#define SPI_CTL_SIZE 0x00000600 /*Word Transfer Size */ +#define SPI_CTL_SIZE08 0x00000000 /*SIZE: 8 bits */ +#define SPI_CTL_SIZE16 0x00000200 /*SIZE: 16 bits */ +#define SPI_CTL_SIZE32 0x00000400 /*SIZE: 32 bits */ +#define SPI_CTL_LSBF 0x00001000 /*LSB First */ +#define SPI_CTL_FCEN 0x00002000 /*Flow-Control Enable */ +#define SPI_CTL_FCCH 0x00004000 /*Flow-Control Channel Selection */ +#define SPI_CTL_FCPL 0x00008000 /*Flow-Control Polarity */ +#define SPI_CTL_FCWM 0x00030000 /*Flow-Control Water-Mark */ +#define SPI_CTL_FIFO0 0x00000000 /*FCWM: Tx empty or Rx Full */ +#define SPI_CTL_FIFO1 0x00010000 /*FCWM: Tx empty or Rx full (>=75%) */ +#define SPI_CTL_FIFO2 0x00020000 /*FCWM: Tx empty or Rx full (>=50%) */ +#define SPI_CTL_FMODE 0x00040000 /*Fast-mode Enable */ +#define SPI_CTL_MIOM 0x00300000 /*Multiple I/O Mode */ +#define SPI_CTL_MIO_DIS 0x00000000 /*MIOM: Disable */ +#define SPI_CTL_MIO_DUAL 0x00100000 /*MIOM: Enable DIOM (Dual I/O Mode) */ +#define SPI_CTL_MIO_QUAD 0x00200000 /*MIOM: Enable QUAD (Quad SPI Mode) */ +#define SPI_CTL_SOSI 0x00400000 /*Start on MOSI */ +#define SPI_CTL_MMWEM 0x40000000 /*Start on MMWEM */ +#define SPI_CTL_MMSE 0x80000000 /*Start on MMSE */ +/* SPI_RX_CONTROL */ +#define SPI_RXCTL_REN 0x00000001 /*Receive Channel Enable */ +#define SPI_RXCTL_RTI 0x00000004 /*Receive Transfer Initiate */ +#define SPI_RXCTL_RWCEN 0x00000008 /*Receive Word Counter Enable */ +#define SPI_RXCTL_RDR 0x00000070 /*Receive Data Request */ +#define SPI_RXCTL_RDR_DIS 0x00000000 /*RDR: Disabled */ +#define SPI_RXCTL_RDR_NE 0x00000010 /*RDR: RFIFO not empty */ +#define SPI_RXCTL_RDR_25 0x00000020 /*RDR: RFIFO 25% full */ +#define SPI_RXCTL_RDR_50 0x00000030 /*RDR: RFIFO 50% full */ +#define SPI_RXCTL_RDR_75 0x00000040 /*RDR: RFIFO 75% full */ +#define SPI_RXCTL_RDR_FULL 0x00000050 /*RDR: RFIFO full */ +#define SPI_RXCTL_RDO 0x00000100 /*Receive Data Over-Run */ +#define SPI_RXCTL_RRWM 0x00003000 /*FIFO Regular Water-Mark */ +#define SPI_RXCTL_RWM_0 0x00000000 /*RRWM: RFIFO Empty */ +#define SPI_RXCTL_RWM_25 0x00001000 /*RRWM: RFIFO 25% full */ +#define SPI_RXCTL_RWM_50 0x00002000 /*RRWM: RFIFO 50% full */ +#define SPI_RXCTL_RWM_75 0x00003000 /*RRWM: RFIFO 75% full */ +#define SPI_RXCTL_RUWM 0x00070000 /*FIFO Urgent Water-Mark */ +#define SPI_RXCTL_UWM_DIS 0x00000000 /*RUWM: Disabled */ +#define SPI_RXCTL_UWM_25 0x00010000 /*RUWM: RFIFO 25% full */ +#define SPI_RXCTL_UWM_50 0x00020000 /*RUWM: RFIFO 50% full */ +#define SPI_RXCTL_UWM_75 0x00030000 /*RUWM: RFIFO 75% full */ +#define SPI_RXCTL_UWM_FULL 0x00040000 /*RUWM: RFIFO full */ +/* SPI_TX_CONTROL */ +#define SPI_TXCTL_TEN 0x00000001 /*Transmit Channel Enable */ +#define SPI_TXCTL_TTI 0x00000004 /*Transmit Transfer Initiate */ +#define SPI_TXCTL_TWCEN 0x00000008 /*Transmit Word Counter Enable */ +#define SPI_TXCTL_TDR 0x00000070 /*Transmit Data Request */ +#define SPI_TXCTL_TDR_DIS 0x00000000 /*TDR: Disabled */ +#define SPI_TXCTL_TDR_NF 0x00000010 /*TDR: TFIFO not full */ +#define SPI_TXCTL_TDR_25 0x00000020 /*TDR: TFIFO 25% empty */ +#define SPI_TXCTL_TDR_50 0x00000030 /*TDR: TFIFO 50% empty */ +#define SPI_TXCTL_TDR_75 0x00000040 /*TDR: TFIFO 75% empty */ +#define SPI_TXCTL_TDR_EMPTY 0x00000050 /*TDR: TFIFO empty */ +#define SPI_TXCTL_TDU 0x00000100 /*Transmit Data Under-Run */ +#define SPI_TXCTL_TRWM 0x00003000 /*FIFO Regular Water-Mark */ +#define SPI_TXCTL_RWM_FULL 0x00000000 /*TRWM: TFIFO full */ +#define SPI_TXCTL_RWM_25 0x00001000 /*TRWM: TFIFO 25% empty */ +#define SPI_TXCTL_RWM_50 0x00002000 /*TRWM: TFIFO 50% empty */ +#define SPI_TXCTL_RWM_75 0x00003000 /*TRWM: TFIFO 75% empty */ +#define SPI_TXCTL_TUWM 0x00070000 /*FIFO Urgent Water-Mark */ +#define SPI_TXCTL_UWM_DIS 0x00000000 /*TUWM: Disabled */ +#define SPI_TXCTL_UWM_25 0x00010000 /*TUWM: TFIFO 25% empty */ +#define SPI_TXCTL_UWM_50 0x00020000 /*TUWM: TFIFO 50% empty */ +#define SPI_TXCTL_UWM_75 0x00030000 /*TUWM: TFIFO 75% empty */ +#define SPI_TXCTL_UWM_EMPTY 0x00040000 /*TUWM: TFIFO empty */ +/* SPI_CLOCK */ +#define SPI_CLK_BAUD 0x0000FFFF /*Baud Rate */ +/* SPI_DELAY */ +#define SPI_DLY_STOP 0x000000FF /*Transfer delay time */ +#define SPI_DLY_LEADX 0x00000100 /*Extended (1 SCK) LEAD Control */ +#define SPI_DLY_LAGX 0x00000200 /*Extended (1 SCK) LAG control */ +/* SPI_SSEL */ +#define SPI_SLVSEL_SSE1 0x00000002 /*SPISSEL1 Enable */ +#define SPI_SLVSEL_SSE2 0x00000004 /*SPISSEL2 Enable */ +#define SPI_SLVSEL_SSE3 0x00000008 /*SPISSEL3 Enable */ +#define SPI_SLVSEL_SSE4 0x00000010 /*SPISSEL4 Enable */ +#define SPI_SLVSEL_SSE5 0x00000020 /*SPISSEL5 Enable */ +#define SPI_SLVSEL_SSE6 0x00000040 /*SPISSEL6 Enable */ +#define SPI_SLVSEL_SSE7 0x00000080 /*SPISSEL7 Enable */ +#define SPI_SLVSEL_SSEL1 0x00000200 /*SPISSEL1 Value */ +#define SPI_SLVSEL_SSEL2 0x00000400 /*SPISSEL2 Value */ +#define SPI_SLVSEL_SSEL3 0x00000800 /*SPISSEL3 Value */ +#define SPI_SLVSEL_SSEL4 0x00001000 /*SPISSEL4 Value */ +#define SPI_SLVSEL_SSEL5 0x00002000 /*SPISSEL5 Value */ +#define SPI_SLVSEL_SSEL6 0x00004000 /*SPISSEL6 Value */ +#define SPI_SLVSEL_SSEL7 0x00008000 /*SPISSEL7 Value */ +/* SPI_RWC */ +#define SPI_RWC_VALUE 0x0000FFFF /*Received Word-Count */ +/* SPI_RWCR */ +#define SPI_RWCR_VALUE 0x0000FFFF /*Received Word-Count Reload */ +/* SPI_TWC */ +#define SPI_TWC_VALUE 0x0000FFFF /*Transmitted Word-Count */ +/* SPI_TWCR */ +#define SPI_TWCR_VALUE 0x0000FFFF /*Transmitted Word-Count Reload */ +/* SPI_IMASK */ +#define SPI_IMSK_RUWM 0x00000002 /*Receive Water-Mark Interrupt Mask */ +#define SPI_IMSK_TUWM 0x00000004 /*Transmit Water-Mark Interrupt Mask */ +#define SPI_IMSK_ROM 0x00000010 /*Receive Over-Run Interrupt Mask */ +#define SPI_IMSK_TUM 0x00000020 /*Transmit Under-Run Interrupt Mask */ +#define SPI_IMSK_TCM 0x00000040 /*Transmit Collision Interrupt Mask */ +#define SPI_IMSK_MFM 0x00000080 /*Mode Fault Interrupt Mask */ +#define SPI_IMSK_RSM 0x00000100 /*Receive Start Interrupt Mask */ +#define SPI_IMSK_TSM 0x00000200 /*Transmit Start Interrupt Mask */ +#define SPI_IMSK_RFM 0x00000400 /*Receive Finish Interrupt Mask */ +#define SPI_IMSK_TFM 0x00000800 /*Transmit Finish Interrupt Mask */ +/* SPI_IMASKCL */ +#define SPI_IMSK_CLR_RUW 0x00000002 /*Receive Water-Mark Interrupt Mask */ +#define SPI_IMSK_CLR_TUWM 0x00000004 /*Transmit Water-Mark Interrupt Mask */ +#define SPI_IMSK_CLR_ROM 0x00000010 /*Receive Over-Run Interrupt Mask */ +#define SPI_IMSK_CLR_TUM 0x00000020 /*Transmit Under-Run Interrupt Mask */ +#define SPI_IMSK_CLR_TCM 0x00000040 /*Transmit Collision Interrupt Mask */ +#define SPI_IMSK_CLR_MFM 0x00000080 /*Mode Fault Interrupt Mask */ +#define SPI_IMSK_CLR_RSM 0x00000100 /*Receive Start Interrupt Mask */ +#define SPI_IMSK_CLR_TSM 0x00000200 /*Transmit Start Interrupt Mask */ +#define SPI_IMSK_CLR_RFM 0x00000400 /*Receive Finish Interrupt Mask */ +#define SPI_IMSK_CLR_TFM 0x00000800 /*Transmit Finish Interrupt Mask */ +/* SPI_IMASKST */ +#define SPI_IMSK_SET_RUWM 0x00000002 /*Receive Water-Mark Interrupt Mask */ +#define SPI_IMSK_SET_TUWM 0x00000004 /*Transmit Water-Mark Interrupt Mask */ +#define SPI_IMSK_SET_ROM 0x00000010 /*Receive Over-Run Interrupt Mask */ +#define SPI_IMSK_SET_TUM 0x00000020 /*Transmit Under-Run Interrupt Mask */ +#define SPI_IMSK_SET_TCM 0x00000040 /*Transmit Collision Interrupt Mask */ +#define SPI_IMSK_SET_MFM 0x00000080 /*Mode Fault Interrupt Mask */ +#define SPI_IMSK_SET_RSM 0x00000100 /*Receive Start Interrupt Mask */ +#define SPI_IMSK_SET_TSM 0x00000200 /*Transmit Start Interrupt Mask */ +#define SPI_IMSK_SET_RFM 0x00000400 /*Receive Finish Interrupt Mask */ +#define SPI_IMSK_SET_TFM 0x00000800 /*Transmit Finish Interrupt Mask */ +/* SPI_STATUS */ +#define SPI_STAT_SPIF 0x00000001 /*SPI Finished */ +#define SPI_STAT_RUWM 0x00000002 /*Receive Water-Mark Breached */ +#define SPI_STAT_TUWM 0x00000004 /*Transmit Water-Mark Breached */ +#define SPI_STAT_ROE 0x00000010 /*Receive Over-Run Indication */ +#define SPI_STAT_TUE 0x00000020 /*Transmit Under-Run Indication */ +#define SPI_STAT_TCE 0x00000040 /*Transmit Collision Indication */ +#define SPI_STAT_MODF 0x00000080 /*Mode Fault Indication */ +#define SPI_STAT_RS 0x00000100 /*Receive Start Indication */ +#define SPI_STAT_TS 0x00000200 /*Transmit Start Indication */ +#define SPI_STAT_RF 0x00000400 /*Receive Finish Indication */ +#define SPI_STAT_TF 0x00000800 /*Transmit Finish Indication */ +#define SPI_STAT_RFS 0x00007000 /*SPI_RFIFO status */ +#define SPI_STAT_RFIFO_EMPTY 0x00000000 /*RFS: RFIFO Empty */ +#define SPI_STAT_RFIFO_25 0x00001000 /*RFS: RFIFO 25% Full */ +#define SPI_STAT_RFIFO_50 0x00002000 /*RFS: RFIFO 50% Full */ +#define SPI_STAT_RFIFO_75 0x00003000 /*RFS: RFIFO 75% Full */ +#define SPI_STAT_RFIFO_FULL 0x00004000 /*RFS: RFIFO Full */ +#define SPI_STAT_TFS 0x00070000 /*SPI_TFIFO status */ +#define SPI_STAT_TFIFO_FULL 0x00000000 /*TFS: TFIFO full */ +#define SPI_STAT_TFIFO_25 0x00010000 /*TFS: TFIFO 25% empty */ +#define SPI_STAT_TFIFO_50 0x00020000 /*TFS: TFIFO 50% empty */ +#define SPI_STAT_TFIFO_75 0x00030000 /*TFS: TFIFO 75% empty */ +#define SPI_STAT_TFIFO_EMPTY 0x00040000 /*TFS: TFIFO empty */ +#define SPI_STAT_FCS 0x00100000 /*Flow-Control Stall Indication */ +#define SPI_STAT_RFE 0x00400000 /*SPI_RFIFO Empty */ +#define SPI_STAT_TFF 0x00800000 /*SPI_TFIFO Full */ +/* SPI_ILAT */ +#define SPI_ILAT_RUWMI 0x00000002 /*Receive Water Mark Interrupt */ +#define SPI_ILAT_TUWMI 0x00000004 /*Transmit Water Mark Interrupt */ +#define SPI_ILAT_ROI 0x00000010 /*Receive Over-Run Indication */ +#define SPI_ILAT_TUI 0x00000020 /*Transmit Under-Run Indication */ +#define SPI_ILAT_TCI 0x00000040 /*Transmit Collision Indication */ +#define SPI_ILAT_MFI 0x00000080 /*Mode Fault Indication */ +#define SPI_ILAT_RSI 0x00000100 /*Receive Start Indication */ +#define SPI_ILAT_TSI 0x00000200 /*Transmit Start Indication */ +#define SPI_ILAT_RFI 0x00000400 /*Receive Finish Indication */ +#define SPI_ILAT_TFI 0x00000800 /*Transmit Finish Indication */ +/* SPI_ILATCL */ +#define SPI_ILAT_CLR_RUWMI 0x00000002 /*Receive Water Mark Interrupt */ +#define SPI_ILAT_CLR_TUWMI 0x00000004 /*Transmit Water Mark Interrupt */ +#define SPI_ILAT_CLR_ROI 0x00000010 /*Receive Over-Run Indication */ +#define SPI_ILAT_CLR_TUI 0x00000020 /*Transmit Under-Run Indication */ +#define SPI_ILAT_CLR_TCI 0x00000040 /*Transmit Collision Indication */ +#define SPI_ILAT_CLR_MFI 0x00000080 /*Mode Fault Indication */ +#define SPI_ILAT_CLR_RSI 0x00000100 /*Receive Start Indication */ +#define SPI_ILAT_CLR_TSI 0x00000200 /*Transmit Start Indication */ +#define SPI_ILAT_CLR_RFI 0x00000400 /*Receive Finish Indication */ +#define SPI_ILAT_CLR_TFI 0x00000800 /*Transmit Finish Indication */ +/* SPI_MMRDH */ +#define SPI_MMRDH_MERGE 0x04000000 /*Merge Enable */ +#define SPI_MMRDH_DMY_SZ 0x00007000 /*Bytes of Dummy */ +#define SPI_MMRDH_ADDR_PINS 0x00000800 /*Pins used for Address */ +#define SPI_MMRDH_ADDR_SZ 0x00000700 /*Bytes of Read Address */ +#define SPI_MMRDH_OPCODE 0x000000FF /*Read Opcode */ + +#define SPI_MMRDH_TRIDMY_OFF 24 /*Bytes of Dummy offset */ +#define SPI_MMRDH_DMY_SZ_OFF 12 /*Bytes of Dummy offset */ +#define SPI_MMRDH_ADDR_SZ_OFF 8 /*Bytes of Read Address offset */ + +#define BIT_SSEL_VAL(x) ((1 << 8) << (x)) /* Slave Select input value bit */ +#define BIT_SSEL_EN(x) (1 << (x)) /* Slave Select enable bit*/ + +struct adi_spi_regs { + u32 revid; + u32 control; + u32 rx_control; + u32 tx_control; + u32 clock; + u32 delay; + u32 ssel; + u32 rwc; + u32 rwcr; + u32 twc; + u32 twcr; + u32 reserved0; + u32 emask; + u32 emaskcl; + u32 emaskst; + u32 reserved1; + u32 status; + u32 elat; + u32 elatcl; + u32 reserved2; + u32 rfifo; + u32 reserved3; + u32 tfifo; + u32 reserved4; + u32 mmrdh; + u32 mmtop; +}; + +struct adi_spi_platdata { + u32 max_hz; + u32 bus_num; + struct adi_spi_regs __iomem *regs; +}; + +struct adi_spi_priv { + u32 control; + u32 clock; + u32 bus_num; + u32 max_cs; + struct adi_spi_regs __iomem *regs; +}; + +/** + * By convention, this driver uses the same CS numbering that is used with the SSEL bit + * definitions (both here and in the TRM on which this is based), which are 1-indexed not + * 0-indexed. The valid CS range is therefore [1,max_cs], in contrast with other drivers + * where it is [0,max_cs-1]. + */ +static int adi_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct adi_spi_priv *priv = dev_get_priv(bus); + + if (cs == 0 || cs > priv->max_cs) { + dev_err(bus, "invalid chipselect %u\n", cs); + return -EINVAL; + } + + return 0; +} + +static int adi_spi_of_to_plat(struct udevice *bus) +{ + struct adi_spi_platdata *plat = dev_get_plat(bus); + fdt_addr_t addr; + + plat->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 500000); + plat->bus_num = dev_read_u32_default(bus, "bus-num", 0); + addr = dev_read_addr(bus); + + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + plat->regs = map_sysmem(addr, sizeof(*plat->regs)); + + return 0; +} + +static int adi_spi_probe(struct udevice *bus) +{ + struct adi_spi_platdata *plat = dev_get_plat(bus); + struct adi_spi_priv *priv = dev_get_priv(bus); + + priv->bus_num = plat->bus_num; + priv->regs = plat->regs; + priv->max_cs = dev_read_u32_default(bus, "num-cs", MAX_CTRL_CS); + + iowrite32(0x0, &plat->regs->control); + iowrite32(0x0, &plat->regs->rx_control); + iowrite32(0x0, &plat->regs->tx_control); + + return 0; +} + +static int adi_spi_remove(struct udevice *dev) +{ + return -ENODEV; +} + +static int adi_spi_claim_bus(struct udevice *dev) +{ + struct adi_spi_priv *priv; + struct udevice *bus = dev->parent; + + priv = dev_get_priv(bus); + + debug("%s: control:%i clock:%i\n", + __func__, priv->control, priv->clock); + + iowrite32(priv->control, &priv->regs->control); + iowrite32(priv->clock, &priv->regs->clock); + iowrite32(0x0, &priv->regs->delay); + + return 0; +} + +static int adi_spi_release_bus(struct udevice *dev) +{ + struct adi_spi_priv *priv; + struct udevice *bus = dev->parent; + + priv = dev_get_priv(bus); + + debug("%s: control:%i clock:%i\n", + __func__, priv->control, priv->clock); + + iowrite32(0x0, &priv->regs->rx_control); + iowrite32(0x0, &priv->regs->tx_control); + iowrite32(0x0, &priv->regs->control); + + return 0; +} + +void adi_spi_enable_ssel(struct adi_spi_priv *priv, int cs) +{ + setbits_32(&priv->regs->ssel, BIT_SSEL_EN(cs)); +} + +void adi_spi_set_ssel(struct adi_spi_priv *priv, int cs, int high) +{ + if (high) + setbits_32(&priv->regs->ssel, BIT_SSEL_VAL(cs)); + else + clrbits_32(&priv->regs->ssel, BIT_SSEL_VAL(cs)); +} + +void adi_spi_cs_activate(struct adi_spi_priv *priv, struct dm_spi_slave_plat *slave_plat) +{ + bool high = slave_plat->mode & SPI_CS_HIGH; + + adi_spi_set_ssel(priv, slave_plat->cs[0], high); + adi_spi_enable_ssel(priv, slave_plat->cs[0]); +} + +void adi_spi_cs_deactivate(struct adi_spi_priv *priv, struct dm_spi_slave_plat *slave_plat) +{ + bool high = slave_plat->mode & SPI_CS_HIGH; + + /* invert CS for matching SSEL to deactivate */ + adi_spi_set_ssel(priv, slave_plat->cs[0], !high); +} + +static void discard_rx_fifo_contents(struct adi_spi_regs *regs) +{ + while (!(ioread32(®s->status) & SPI_STAT_RFE)) + ioread32(®s->rfifo); +} + +static int adi_spi_fifo_mio_xfer(struct adi_spi_priv *priv, const u8 *tx, u8 *rx, + uint bytes, uint32_t mio_mode) +{ + u8 value; + + /* switch current SPI transfer to mio SPI mode */ + clrsetbits_32(&priv->regs->control, SPI_CTL_SOSI, mio_mode); + /* + * Data can only be transferred in one direction in multi-io SPI + * modes, trigger the transfer in respective direction. + */ + if (rx) { + iowrite32(0x0, &priv->regs->tx_control); + iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RTI, &priv->regs->rx_control); + + while (bytes--) { + while (ioread32(&priv->regs->status) & + SPI_STAT_RFE) + if (ctrlc()) + return -1; + value = ioread32(&priv->regs->rfifo); + *rx++ = value; + } + } else if (tx) { + iowrite32(0x0, &priv->regs->rx_control); + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &priv->regs->tx_control); + + while (bytes--) { + value = *tx++; + iowrite32(value, &priv->regs->tfifo); + while (ioread32(&priv->regs->status) & + SPI_STAT_TFF) + if (ctrlc()) + return -1; + } + + /* Wait till the tfifo is empty */ + while ((ioread32(&priv->regs->status) & SPI_STAT_TFS) != SPI_STAT_TFIFO_EMPTY) + if (ctrlc()) + return -1; + } else { + return -1; + } + return 0; +} + +static int adi_spi_fifo_1x_xfer(struct adi_spi_priv *priv, const u8 *tx, u8 *rx, + uint bytes) +{ + u8 value; + + /* + * Set current SPI transfer in normal mode and trigger + * the bi-direction transfer by tx write operation. + */ + iowrite32(priv->control, &priv->regs->control); + iowrite32(SPI_RXCTL_REN, &priv->regs->rx_control); + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &priv->regs->tx_control); + + while (bytes--) { + value = (tx ? *tx++ : SPI_IDLE_VAL); + debug("%s: tx:%x ", __func__, value); + iowrite32(value, &priv->regs->tfifo); + while (ioread32(&priv->regs->status) & SPI_STAT_RFE) + if (ctrlc()) + return -1; + value = ioread32(&priv->regs->rfifo); + if (rx) + *rx++ = value; + debug("rx:%x\n", value); + } + return 0; +} + +static int adi_spi_fifo_xfer(struct adi_spi_priv *priv, int buswidth, + const u8 *tx, u8 *rx, uint bytes) +{ + switch (buswidth) { + case 1: + return adi_spi_fifo_1x_xfer(priv, tx, rx, bytes); + case 2: + return adi_spi_fifo_mio_xfer(priv, tx, rx, bytes, SPI_CTL_MIO_DUAL); + case 4: + return adi_spi_fifo_mio_xfer(priv, tx, rx, bytes, SPI_CTL_MIO_QUAD); + default: + return -ENOTSUPP; + } +} + +static int adi_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct adi_spi_priv *priv = dev_get_priv(bus); + struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev); + + const u8 *tx = dout; + u8 *rx = din; + uint bytes = bitlen / 8; + int ret = 0; + + debug("%s: bus_num:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, + priv->bus_num, slave_plat->cs[0], bitlen, bytes, flags); + + if (flags & SPI_XFER_BEGIN) + adi_spi_cs_activate(priv, slave_plat); + + if (bitlen == 0) + goto done; + + /* we can only do 8 bit transfers */ + if (bitlen % 8) { + flags |= SPI_XFER_END; + goto done; + } + + /* Discard invalid rx data and empty rfifo */ + discard_rx_fifo_contents(priv->regs); + + ret = adi_spi_fifo_1x_xfer(priv, tx, rx, bytes); + + done: + if (flags & SPI_XFER_END) + adi_spi_cs_deactivate(priv, slave_plat); + + return ret; +} + +static int adi_spi_set_speed(struct udevice *bus, uint speed) +{ + struct adi_spi_platdata *plat = dev_get_plat(bus); + struct adi_spi_priv *priv = dev_get_priv(bus); + int ret; + u32 clock, spi_base_clk; + struct clk spi_clk; + + ret = clk_get_by_name(bus, "spi", &spi_clk); + if (ret < 0) { + dev_err(bus, "Can't get SPI clk: %d\n", ret); + return ret; + } + spi_base_clk = clk_get_rate(&spi_clk); + + if (speed > plat->max_hz) + speed = plat->max_hz; + + if (speed > spi_base_clk) + return -ENODEV; + + clock = spi_base_clk / speed; + if (clock) + clock--; + + priv->clock = clock; + + debug("%s: priv->clock: %x, speed: %x, get_spi_clk(): %x\n", + __func__, clock, speed, spi_base_clk); + + return 0; +} + +static int adi_spi_set_mode(struct udevice *bus, uint mode) +{ + struct adi_spi_priv *priv = dev_get_priv(bus); + u32 reg; + + reg = SPI_CTL_EN | SPI_CTL_MSTR; + if (mode & SPI_CPHA) + reg |= SPI_CTL_CPHA; + if (mode & SPI_CPOL) + reg |= SPI_CTL_CPOL; + if (mode & SPI_LSB_FIRST) + reg |= SPI_CTL_LSBF; + reg &= ~SPI_CTL_ASSEL; + + priv->control = reg; + + debug("%s: control=%d, cs_pol=%d\n", __func__, reg, mode & SPI_CS_HIGH ? 1 : 0); + + return 0; +} + +/** + * U-boot's version of spi-mem does not support mixed bus-width + * commands nor anything more than 1x mode. + * Using a custom exec_op implementation, we can support it. + */ +static int adi_spi_mem_exec_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + int rv = 0; + struct udevice *bus = slave->dev->parent; + struct adi_spi_priv *priv = dev_get_priv(bus); + struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(slave->dev); + u8 tmpbuf[64]; + int i; + + if ((op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes) > + sizeof(tmpbuf)) + return -ENOMEM; + + for (i = 0; i < op->cmd.nbytes; i++) + tmpbuf[i] = op->cmd.opcode >> + (8 * (op->cmd.nbytes - i - 1)); + for (i = 0; i < op->addr.nbytes; i++) + tmpbuf[i + op->cmd.nbytes] = op->addr.val >> + (8 * (op->addr.nbytes - i - 1)); + memset(tmpbuf + op->addr.nbytes + op->cmd.nbytes, 0xff, + op->dummy.nbytes); + + adi_spi_cs_activate(priv, slave_plat); + discard_rx_fifo_contents(priv->regs); + + if (op->cmd.nbytes) { + rv = adi_spi_fifo_xfer(priv, op->cmd.buswidth, + tmpbuf, NULL, op->cmd.nbytes); + if (rv != 0) + goto cleanup; + } + + if (op->addr.nbytes) { + rv = adi_spi_fifo_xfer(priv, op->addr.buswidth, + tmpbuf + op->cmd.nbytes, NULL, + op->addr.nbytes); + if (rv != 0) + goto cleanup; + } + + if (op->dummy.nbytes) { + rv = adi_spi_fifo_xfer(priv, op->dummy.buswidth, + tmpbuf + op->cmd.nbytes + + op->addr.nbytes, + NULL, op->dummy.nbytes); + if (rv != 0) + goto cleanup; + } + + if (op->data.dir == SPI_MEM_DATA_IN) + rv = adi_spi_fifo_xfer(priv, op->data.buswidth, + NULL, op->data.buf.in, + op->data.nbytes); + else if (op->data.dir == SPI_MEM_DATA_OUT) + rv = adi_spi_fifo_xfer(priv, op->data.buswidth, + op->data.buf.out, NULL, + op->data.nbytes); + +cleanup: + adi_spi_cs_deactivate(priv, slave_plat); + return rv; +} + +static const struct spi_controller_mem_ops adi_spi_mem_ops = { + .exec_op = adi_spi_mem_exec_op, +}; + +static const struct dm_spi_ops adi_spi_ops = { + .claim_bus = adi_spi_claim_bus, + .release_bus = adi_spi_release_bus, + .xfer = adi_spi_xfer, + .set_speed = adi_spi_set_speed, + .set_mode = adi_spi_set_mode, + .cs_info = adi_spi_cs_info, + .mem_ops = &adi_spi_mem_ops, +}; + +static const struct udevice_id adi_spi_ids[] = { + { .compatible = "adi,spi3" }, + { } +}; + +U_BOOT_DRIVER(adi_spi3) = { + .name = "adi_spi3", + .id = UCLASS_SPI, + .of_match = adi_spi_ids, + .ops = &adi_spi_ops, + .of_to_plat = adi_spi_of_to_plat, + .probe = adi_spi_probe, + .remove = adi_spi_remove, + .plat_auto = sizeof(struct adi_spi_platdata), + .priv_auto = sizeof(struct adi_spi_priv), + .per_child_auto = sizeof(struct spi_slave), +}; diff --git a/drivers/spi/airoha_snfi_spi.c b/drivers/spi/airoha_snfi_spi.c new file mode 100644 index 00000000000..3ea25b293d1 --- /dev/null +++ b/drivers/spi/airoha_snfi_spi.c @@ -0,0 +1,718 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 AIROHA Inc + * + * Based on spi-airoha-snfi.c on Linux + * + * Author: Lorenzo Bianconi <lorenzo@kernel.org> + * Author: Ray Liu <ray.liu@airoha.com> + */ + +#include <asm/unaligned.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <linux/bitfield.h> +#include <linux/dma-mapping.h> +#include <linux/mtd/spinand.h> +#include <linux/time.h> +#include <regmap.h> +#include <spi.h> +#include <spi-mem.h> + +/* SPI */ +#define REG_SPI_CTRL_READ_MODE 0x0000 +#define REG_SPI_CTRL_READ_IDLE_EN 0x0004 +#define REG_SPI_CTRL_SIDLY 0x0008 +#define REG_SPI_CTRL_CSHEXT 0x000c +#define REG_SPI_CTRL_CSLEXT 0x0010 + +#define REG_SPI_CTRL_MTX_MODE_TOG 0x0014 +#define SPI_CTRL_MTX_MODE_TOG GENMASK(3, 0) + +#define REG_SPI_CTRL_RDCTL_FSM 0x0018 +#define SPI_CTRL_RDCTL_FSM GENMASK(3, 0) + +#define REG_SPI_CTRL_MACMUX_SEL 0x001c + +#define REG_SPI_CTRL_MANUAL_EN 0x0020 +#define SPI_CTRL_MANUAL_EN BIT(0) + +#define REG_SPI_CTRL_OPFIFO_EMPTY 0x0024 +#define SPI_CTRL_OPFIFO_EMPTY BIT(0) + +#define REG_SPI_CTRL_OPFIFO_WDATA 0x0028 +#define SPI_CTRL_OPFIFO_LEN GENMASK(8, 0) +#define SPI_CTRL_OPFIFO_OP GENMASK(13, 9) + +#define REG_SPI_CTRL_OPFIFO_FULL 0x002c +#define SPI_CTRL_OPFIFO_FULL BIT(0) + +#define REG_SPI_CTRL_OPFIFO_WR 0x0030 +#define SPI_CTRL_OPFIFO_WR BIT(0) + +#define REG_SPI_CTRL_DFIFO_FULL 0x0034 +#define SPI_CTRL_DFIFO_FULL BIT(0) + +#define REG_SPI_CTRL_DFIFO_WDATA 0x0038 +#define SPI_CTRL_DFIFO_WDATA GENMASK(7, 0) + +#define REG_SPI_CTRL_DFIFO_EMPTY 0x003c +#define SPI_CTRL_DFIFO_EMPTY BIT(0) + +#define REG_SPI_CTRL_DFIFO_RD 0x0040 +#define SPI_CTRL_DFIFO_RD BIT(0) + +#define REG_SPI_CTRL_DFIFO_RDATA 0x0044 +#define SPI_CTRL_DFIFO_RDATA GENMASK(7, 0) + +#define REG_SPI_CTRL_DUMMY 0x0080 +#define SPI_CTRL_CTRL_DUMMY GENMASK(3, 0) + +#define REG_SPI_CTRL_PROBE_SEL 0x0088 +#define REG_SPI_CTRL_INTERRUPT 0x0090 +#define REG_SPI_CTRL_INTERRUPT_EN 0x0094 +#define REG_SPI_CTRL_SI_CK_SEL 0x009c +#define REG_SPI_CTRL_SW_CFGNANDADDR_VAL 0x010c +#define REG_SPI_CTRL_SW_CFGNANDADDR_EN 0x0110 +#define REG_SPI_CTRL_SFC_STRAP 0x0114 + +#define REG_SPI_CTRL_NFI2SPI_EN 0x0130 +#define SPI_CTRL_NFI2SPI_EN BIT(0) + +/* NFI2SPI */ +#define REG_SPI_NFI_CNFG 0x0000 +#define SPI_NFI_DMA_MODE BIT(0) +#define SPI_NFI_READ_MODE BIT(1) +#define SPI_NFI_DMA_BURST_EN BIT(2) +#define SPI_NFI_HW_ECC_EN BIT(8) +#define SPI_NFI_AUTO_FDM_EN BIT(9) +#define SPI_NFI_OPMODE GENMASK(14, 12) + +#define REG_SPI_NFI_PAGEFMT 0x0004 +#define SPI_NFI_PAGE_SIZE GENMASK(1, 0) +#define SPI_NFI_SPARE_SIZE GENMASK(5, 4) + +#define REG_SPI_NFI_CON 0x0008 +#define SPI_NFI_FIFO_FLUSH BIT(0) +#define SPI_NFI_RST BIT(1) +#define SPI_NFI_RD_TRIG BIT(8) +#define SPI_NFI_WR_TRIG BIT(9) +#define SPI_NFI_SEC_NUM GENMASK(15, 12) + +#define REG_SPI_NFI_INTR_EN 0x0010 +#define SPI_NFI_RD_DONE_EN BIT(0) +#define SPI_NFI_WR_DONE_EN BIT(1) +#define SPI_NFI_RST_DONE_EN BIT(2) +#define SPI_NFI_ERASE_DONE_EN BIT(3) +#define SPI_NFI_BUSY_RETURN_EN BIT(4) +#define SPI_NFI_ACCESS_LOCK_EN BIT(5) +#define SPI_NFI_AHB_DONE_EN BIT(6) +#define SPI_NFI_ALL_IRQ_EN \ + (SPI_NFI_RD_DONE_EN | SPI_NFI_WR_DONE_EN | \ + SPI_NFI_RST_DONE_EN | SPI_NFI_ERASE_DONE_EN | \ + SPI_NFI_BUSY_RETURN_EN | SPI_NFI_ACCESS_LOCK_EN | \ + SPI_NFI_AHB_DONE_EN) + +#define REG_SPI_NFI_INTR 0x0014 +#define SPI_NFI_AHB_DONE BIT(6) + +#define REG_SPI_NFI_CMD 0x0020 + +#define REG_SPI_NFI_ADDR_NOB 0x0030 +#define SPI_NFI_ROW_ADDR_NOB GENMASK(6, 4) + +#define REG_SPI_NFI_STA 0x0060 +#define REG_SPI_NFI_FIFOSTA 0x0064 +#define REG_SPI_NFI_STRADDR 0x0080 +#define REG_SPI_NFI_FDM0L 0x00a0 +#define REG_SPI_NFI_FDM0M 0x00a4 +#define REG_SPI_NFI_FDM7L 0x00d8 +#define REG_SPI_NFI_FDM7M 0x00dc +#define REG_SPI_NFI_FIFODATA0 0x0190 +#define REG_SPI_NFI_FIFODATA1 0x0194 +#define REG_SPI_NFI_FIFODATA2 0x0198 +#define REG_SPI_NFI_FIFODATA3 0x019c +#define REG_SPI_NFI_MASTERSTA 0x0224 + +#define REG_SPI_NFI_SECCUS_SIZE 0x022c +#define SPI_NFI_CUS_SEC_SIZE GENMASK(12, 0) +#define SPI_NFI_CUS_SEC_SIZE_EN BIT(16) + +#define REG_SPI_NFI_RD_CTL2 0x0510 +#define REG_SPI_NFI_RD_CTL3 0x0514 + +#define REG_SPI_NFI_PG_CTL1 0x0524 +#define SPI_NFI_PG_LOAD_CMD GENMASK(15, 8) + +#define REG_SPI_NFI_PG_CTL2 0x0528 +#define REG_SPI_NFI_NOR_PROG_ADDR 0x052c +#define REG_SPI_NFI_NOR_RD_ADDR 0x0534 + +#define REG_SPI_NFI_SNF_MISC_CTL 0x0538 +#define SPI_NFI_DATA_READ_WR_MODE GENMASK(18, 16) + +#define REG_SPI_NFI_SNF_MISC_CTL2 0x053c +#define SPI_NFI_READ_DATA_BYTE_NUM GENMASK(12, 0) +#define SPI_NFI_PROG_LOAD_BYTE_NUM GENMASK(28, 16) + +#define REG_SPI_NFI_SNF_STA_CTL1 0x0550 +#define SPI_NFI_READ_FROM_CACHE_DONE BIT(25) +#define SPI_NFI_LOAD_TO_CACHE_DONE BIT(26) + +#define REG_SPI_NFI_SNF_STA_CTL2 0x0554 + +#define REG_SPI_NFI_SNF_NFI_CNFG 0x055c +#define SPI_NFI_SPI_MODE BIT(0) + +/* SPI NAND Protocol OP */ +#define SPI_NAND_OP_GET_FEATURE 0x0f +#define SPI_NAND_OP_SET_FEATURE 0x1f +#define SPI_NAND_OP_PAGE_READ 0x13 +#define SPI_NAND_OP_READ_FROM_CACHE_SINGLE 0x03 +#define SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST 0x0b +#define SPI_NAND_OP_READ_FROM_CACHE_DUAL 0x3b +#define SPI_NAND_OP_READ_FROM_CACHE_QUAD 0x6b +#define SPI_NAND_OP_WRITE_ENABLE 0x06 +#define SPI_NAND_OP_WRITE_DISABLE 0x04 +#define SPI_NAND_OP_PROGRAM_LOAD_SINGLE 0x02 +#define SPI_NAND_OP_PROGRAM_LOAD_QUAD 0x32 +#define SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE 0x84 +#define SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD 0x34 +#define SPI_NAND_OP_PROGRAM_EXECUTE 0x10 +#define SPI_NAND_OP_READ_ID 0x9f +#define SPI_NAND_OP_BLOCK_ERASE 0xd8 +#define SPI_NAND_OP_RESET 0xff +#define SPI_NAND_OP_DIE_SELECT 0xc2 + +#define SPI_NAND_CACHE_SIZE (SZ_4K + SZ_256) +#define SPI_MAX_TRANSFER_SIZE 511 + +enum airoha_snand_mode { + SPI_MODE_AUTO, + SPI_MODE_MANUAL, + SPI_MODE_DMA, +}; + +enum airoha_snand_cs { + SPI_CHIP_SEL_HIGH, + SPI_CHIP_SEL_LOW, +}; + +struct airoha_snand_priv { + struct regmap *regmap_ctrl; + struct regmap *regmap_nfi; + struct clk *spi_clk; + + struct { + size_t page_size; + size_t sec_size; + u8 sec_num; + u8 spare_size; + } nfi_cfg; +}; + +static int airoha_snand_set_fifo_op(struct airoha_snand_priv *priv, + u8 op_cmd, int op_len) +{ + int err; + u32 val; + + err = regmap_write(priv->regmap_ctrl, REG_SPI_CTRL_OPFIFO_WDATA, + FIELD_PREP(SPI_CTRL_OPFIFO_LEN, op_len) | + FIELD_PREP(SPI_CTRL_OPFIFO_OP, op_cmd)); + if (err) + return err; + + err = regmap_read_poll_timeout(priv->regmap_ctrl, + REG_SPI_CTRL_OPFIFO_FULL, + val, !(val & SPI_CTRL_OPFIFO_FULL), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + + err = regmap_write(priv->regmap_ctrl, REG_SPI_CTRL_OPFIFO_WR, + SPI_CTRL_OPFIFO_WR); + if (err) + return err; + + return regmap_read_poll_timeout(priv->regmap_ctrl, + REG_SPI_CTRL_OPFIFO_EMPTY, + val, (val & SPI_CTRL_OPFIFO_EMPTY), + 0, 250 * USEC_PER_MSEC); +} + +static int airoha_snand_set_cs(struct airoha_snand_priv *priv, u8 cs) +{ + return airoha_snand_set_fifo_op(priv, cs, sizeof(cs)); +} + +static int airoha_snand_write_data_to_fifo(struct airoha_snand_priv *priv, + const u8 *data, int len) +{ + int i; + + for (i = 0; i < len; i++) { + int err; + u32 val; + + /* 1. Wait until dfifo is not full */ + err = regmap_read_poll_timeout(priv->regmap_ctrl, + REG_SPI_CTRL_DFIFO_FULL, val, + !(val & SPI_CTRL_DFIFO_FULL), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + + /* 2. Write data to register DFIFO_WDATA */ + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_DFIFO_WDATA, + FIELD_PREP(SPI_CTRL_DFIFO_WDATA, data[i])); + if (err) + return err; + + /* 3. Wait until dfifo is not full */ + err = regmap_read_poll_timeout(priv->regmap_ctrl, + REG_SPI_CTRL_DFIFO_FULL, val, + !(val & SPI_CTRL_DFIFO_FULL), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + } + + return 0; +} + +static int airoha_snand_read_data_from_fifo(struct airoha_snand_priv *priv, + u8 *ptr, int len) +{ + int i; + + for (i = 0; i < len; i++) { + int err; + u32 val; + + /* 1. wait until dfifo is not empty */ + err = regmap_read_poll_timeout(priv->regmap_ctrl, + REG_SPI_CTRL_DFIFO_EMPTY, val, + !(val & SPI_CTRL_DFIFO_EMPTY), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + + /* 2. read from dfifo to register DFIFO_RDATA */ + err = regmap_read(priv->regmap_ctrl, + REG_SPI_CTRL_DFIFO_RDATA, &val); + if (err) + return err; + + ptr[i] = FIELD_GET(SPI_CTRL_DFIFO_RDATA, val); + /* 3. enable register DFIFO_RD to read next byte */ + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_DFIFO_RD, SPI_CTRL_DFIFO_RD); + if (err) + return err; + } + + return 0; +} + +static int airoha_snand_set_mode(struct airoha_snand_priv *priv, + enum airoha_snand_mode mode) +{ + int err; + + switch (mode) { + case SPI_MODE_MANUAL: { + u32 val; + + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_NFI2SPI_EN, 0); + if (err) + return err; + + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_READ_IDLE_EN, 0); + if (err) + return err; + + err = regmap_read_poll_timeout(priv->regmap_ctrl, + REG_SPI_CTRL_RDCTL_FSM, val, + !(val & SPI_CTRL_RDCTL_FSM), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_MTX_MODE_TOG, 9); + if (err) + return err; + + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_MANUAL_EN, SPI_CTRL_MANUAL_EN); + if (err) + return err; + break; + } + case SPI_MODE_DMA: + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_NFI2SPI_EN, + SPI_CTRL_MANUAL_EN); + if (err < 0) + return err; + + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_MTX_MODE_TOG, 0x0); + if (err < 0) + return err; + + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_MANUAL_EN, 0x0); + if (err < 0) + return err; + break; + case SPI_MODE_AUTO: + default: + break; + } + + return regmap_write(priv->regmap_ctrl, REG_SPI_CTRL_DUMMY, 0); +} + +static int airoha_snand_write_data(struct airoha_snand_priv *priv, u8 cmd, + const u8 *data, int len) +{ + int i, data_len; + + for (i = 0; i < len; i += data_len) { + int err; + + data_len = min(len - i, SPI_MAX_TRANSFER_SIZE); + err = airoha_snand_set_fifo_op(priv, cmd, data_len); + if (err) + return err; + + err = airoha_snand_write_data_to_fifo(priv, &data[i], + data_len); + if (err < 0) + return err; + } + + return 0; +} + +static int airoha_snand_read_data(struct airoha_snand_priv *priv, u8 *data, + int len) +{ + int i, data_len; + + for (i = 0; i < len; i += data_len) { + int err; + + data_len = min(len - i, SPI_MAX_TRANSFER_SIZE); + err = airoha_snand_set_fifo_op(priv, 0xc, data_len); + if (err) + return err; + + err = airoha_snand_read_data_from_fifo(priv, &data[i], + data_len); + if (err < 0) + return err; + } + + return 0; +} + +static int airoha_snand_nfi_init(struct airoha_snand_priv *priv) +{ + int err; + + /* switch to SNFI mode */ + err = regmap_write(priv->regmap_nfi, REG_SPI_NFI_SNF_NFI_CNFG, + SPI_NFI_SPI_MODE); + if (err) + return err; + + /* Enable DMA */ + return regmap_update_bits(priv->regmap_nfi, REG_SPI_NFI_INTR_EN, + SPI_NFI_ALL_IRQ_EN, SPI_NFI_AHB_DONE_EN); +} + +static int airoha_snand_nfi_config(struct airoha_snand_priv *priv) +{ + int err; + u32 val; + + err = regmap_write(priv->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_FIFO_FLUSH | SPI_NFI_RST); + if (err) + return err; + + /* auto FDM */ + err = regmap_clear_bits(priv->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_AUTO_FDM_EN); + if (err) + return err; + + /* HW ECC */ + err = regmap_clear_bits(priv->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_HW_ECC_EN); + if (err) + return err; + + /* DMA Burst */ + err = regmap_set_bits(priv->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_DMA_BURST_EN); + if (err) + return err; + + /* page format */ + switch (priv->nfi_cfg.spare_size) { + case 26: + val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x1); + break; + case 27: + val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x2); + break; + case 28: + val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x3); + break; + default: + val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x0); + break; + } + + err = regmap_update_bits(priv->regmap_nfi, REG_SPI_NFI_PAGEFMT, + SPI_NFI_SPARE_SIZE, val); + if (err) + return err; + + switch (priv->nfi_cfg.page_size) { + case 2048: + val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x1); + break; + case 4096: + val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x2); + break; + default: + val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x0); + break; + } + + err = regmap_update_bits(priv->regmap_nfi, REG_SPI_NFI_PAGEFMT, + SPI_NFI_PAGE_SIZE, val); + if (err) + return err; + + /* sec num */ + val = FIELD_PREP(SPI_NFI_SEC_NUM, priv->nfi_cfg.sec_num); + err = regmap_update_bits(priv->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_SEC_NUM, val); + if (err) + return err; + + /* enable cust sec size */ + err = regmap_set_bits(priv->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, + SPI_NFI_CUS_SEC_SIZE_EN); + if (err) + return err; + + /* set cust sec size */ + val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, priv->nfi_cfg.sec_size); + return regmap_update_bits(priv->regmap_nfi, + REG_SPI_NFI_SECCUS_SIZE, + SPI_NFI_CUS_SEC_SIZE, val); +} + +static int airoha_snand_adjust_op_size(struct spi_slave *slave, + struct spi_mem_op *op) +{ + size_t max_len; + + max_len = 1 + op->addr.nbytes + op->dummy.nbytes; + if (max_len >= 160) + return -EOPNOTSUPP; + + if (op->data.nbytes > 160 - max_len) + op->data.nbytes = 160 - max_len; + + return 0; +} + +static bool airoha_snand_supports_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + if (!spi_mem_default_supports_op(slave, op)) + return false; + + if (op->cmd.buswidth != 1) + return false; + + return (!op->addr.nbytes || op->addr.buswidth == 1) && + (!op->dummy.nbytes || op->dummy.buswidth == 1) && + (!op->data.nbytes || op->data.buswidth == 1); +} + +static int airoha_snand_exec_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + u8 data[8], cmd, opcode = op->cmd.opcode; + struct udevice *bus = slave->dev->parent; + struct airoha_snand_priv *priv; + int i, err; + + priv = dev_get_priv(bus); + + /* switch to manual mode */ + err = airoha_snand_set_mode(priv, SPI_MODE_MANUAL); + if (err < 0) + return err; + + err = airoha_snand_set_cs(priv, SPI_CHIP_SEL_LOW); + if (err < 0) + return err; + + /* opcode */ + err = airoha_snand_write_data(priv, 0x8, &opcode, sizeof(opcode)); + if (err) + return err; + + /* addr part */ + cmd = opcode == SPI_NAND_OP_GET_FEATURE ? 0x11 : 0x8; + put_unaligned_be64(op->addr.val, data); + + for (i = ARRAY_SIZE(data) - op->addr.nbytes; + i < ARRAY_SIZE(data); i++) { + err = airoha_snand_write_data(priv, cmd, &data[i], + sizeof(data[0])); + if (err) + return err; + } + + /* dummy */ + data[0] = 0xff; + for (i = 0; i < op->dummy.nbytes; i++) { + err = airoha_snand_write_data(priv, 0x8, &data[0], + sizeof(data[0])); + if (err) + return err; + } + + /* data */ + if (op->data.dir == SPI_MEM_DATA_IN) { + err = airoha_snand_read_data(priv, op->data.buf.in, + op->data.nbytes); + if (err) + return err; + } else { + err = airoha_snand_write_data(priv, 0x8, op->data.buf.out, + op->data.nbytes); + if (err) + return err; + } + + return airoha_snand_set_cs(priv, SPI_CHIP_SEL_HIGH); +} + +static int airoha_snand_probe(struct udevice *dev) +{ + struct airoha_snand_priv *priv = dev_get_priv(dev); + int ret; + + ret = regmap_init_mem_index(dev_ofnode(dev), &priv->regmap_ctrl, 0); + if (ret) { + dev_err(dev, "failed to init spi ctrl regmap\n"); + return ret; + } + + ret = regmap_init_mem_index(dev_ofnode(dev), &priv->regmap_nfi, 1); + if (ret) { + dev_err(dev, "failed to init spi nfi regmap\n"); + return ret; + } + + priv->spi_clk = devm_clk_get(dev, "spi"); + if (IS_ERR(priv->spi_clk)) { + dev_err(dev, "unable to get spi clk\n"); + return PTR_ERR(priv->regmap_ctrl); + } + clk_enable(priv->spi_clk); + + return airoha_snand_nfi_init(priv); +} + +static int airoha_snand_nfi_set_speed(struct udevice *bus, uint speed) +{ + struct airoha_snand_priv *priv = dev_get_priv(bus); + int ret; + + ret = clk_set_rate(priv->spi_clk, speed); + if (ret < 0) + return ret; + + return 0; +} + +static int airoha_snand_nfi_set_mode(struct udevice *bus, uint mode) +{ + return 0; +} + +static int airoha_snand_nfi_setup(struct spi_slave *slave, + const struct spinand_info *spinand_info) +{ + struct udevice *bus = slave->dev->parent; + struct airoha_snand_priv *priv; + u32 sec_size, sec_num; + int pagesize, oobsize; + + priv = dev_get_priv(bus); + + pagesize = spinand_info->memorg.pagesize; + oobsize = spinand_info->memorg.oobsize; + + if (pagesize == 2 * 1024) + sec_num = 4; + else if (pagesize == 4 * 1024) + sec_num = 8; + else + sec_num = 1; + + sec_size = (pagesize + oobsize) / sec_num; + + /* init default value */ + priv->nfi_cfg.sec_size = sec_size; + priv->nfi_cfg.sec_num = sec_num; + priv->nfi_cfg.page_size = round_down(sec_size * sec_num, 1024); + priv->nfi_cfg.spare_size = 16; + + return airoha_snand_nfi_config(priv); +} + +static const struct spi_controller_mem_ops airoha_snand_mem_ops = { + .adjust_op_size = airoha_snand_adjust_op_size, + .supports_op = airoha_snand_supports_op, + .exec_op = airoha_snand_exec_op, +}; + +static const struct dm_spi_ops airoha_snfi_spi_ops = { + .mem_ops = &airoha_snand_mem_ops, + .set_speed = airoha_snand_nfi_set_speed, + .set_mode = airoha_snand_nfi_set_mode, + .setup_for_spinand = airoha_snand_nfi_setup, +}; + +static const struct udevice_id airoha_snand_ids[] = { + { .compatible = "airoha,en7581-snand" }, + { } +}; + +U_BOOT_DRIVER(airoha_snfi_spi) = { + .name = "airoha-snfi-spi", + .id = UCLASS_SPI, + .of_match = airoha_snand_ids, + .ops = &airoha_snfi_spi_ops, + .priv_auto = sizeof(struct airoha_snand_priv), + .probe = airoha_snand_probe, +}; diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 3efb661803b..8aa7a83aef4 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -10,11 +10,13 @@ */ #include <malloc.h> +#include <asm/gpio.h> #include <asm/io.h> #include <clk.h> #include <dm.h> #include <errno.h> #include <fdtdec.h> +#include <log.h> #include <dm/device_compat.h> #include <linux/bitfield.h> #include <linux/bitops.h> @@ -258,6 +260,7 @@ struct atmel_qspi_caps { struct atmel_qspi_priv_ops; +#define MAX_CS_COUNT 2 struct atmel_qspi { void __iomem *regs; void __iomem *mem; @@ -267,6 +270,7 @@ struct atmel_qspi { struct udevice *dev; ulong bus_clk_rate; u32 mr; + struct gpio_desc cs_gpios[MAX_CS_COUNT]; }; struct atmel_qspi_priv_ops { @@ -395,6 +399,26 @@ static void atmel_qspi_write(u32 value, struct atmel_qspi *aq, u32 offset) writel(value, aq->regs + offset); } +static int atmel_qspi_reg_sync(struct atmel_qspi *aq) +{ + u32 val; + + return readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_SYNCBSY), + ATMEL_QSPI_SYNC_TIMEOUT); +} + +static int atmel_qspi_update_config(struct atmel_qspi *aq) +{ + int ret; + + ret = atmel_qspi_reg_sync(aq); + if (ret) + return ret; + atmel_qspi_write(QSPI_CR_UPDCFG, aq, QSPI_CR); + return atmel_qspi_reg_sync(aq); +} + static inline bool atmel_qspi_is_compatible(const struct spi_mem_op *op, const struct atmel_qspi_mode *mode) { @@ -458,6 +482,29 @@ static bool atmel_qspi_supports_op(struct spi_slave *slave, return true; } +/* + * Switch QSPI controller between regular SPI mode or Serial Memory Mode (SMM). + */ +static int atmel_qspi_set_serial_memory_mode(struct atmel_qspi *aq, + bool enable) +{ + int ret = 0; + + /* only write if designated state differs from current state */ + if (!!(aq->mr & QSPI_MR_SMM) != enable) { + if (enable) + aq->mr |= QSPI_MR_SMM; + else + aq->mr &= ~QSPI_MR_SMM; + atmel_qspi_write(aq->mr, aq, QSPI_MR); + + if (aq->caps->has_gclk) + ret = atmel_qspi_update_config(aq); + } + + return ret; +} + static int atmel_qspi_set_cfg(struct atmel_qspi *aq, const struct spi_mem_op *op, u32 *offset) { @@ -474,7 +521,7 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, return mode; ifr |= atmel_qspi_modes[mode].config; - if (op->dummy.buswidth && op->dummy.nbytes) + if (op->dummy.nbytes) dummy_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth; /* @@ -529,43 +576,39 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, if (dummy_cycles) ifr |= QSPI_IFR_NBDUM(dummy_cycles); - /* Set data enable */ - if (op->data.nbytes) + /* Set data enable and data transfer type. */ + if (op->data.nbytes) { ifr |= QSPI_IFR_DATAEN; - /* - * If the QSPI controller is set in regular SPI mode, set it in - * Serial Memory Mode (SMM). - */ - if (aq->mr != QSPI_MR_SMM) { - atmel_qspi_write(QSPI_MR_SMM, aq, QSPI_MR); - aq->mr = QSPI_MR_SMM; + if (op->addr.nbytes) + ifr |= QSPI_IFR_TFRTYP_MEM; } + mode = atmel_qspi_set_serial_memory_mode(aq, true); + if (mode < 0) + return mode; + /* Clear pending interrupts */ (void)atmel_qspi_read(aq, QSPI_SR); - if (aq->caps->has_ricr) { - if (!op->addr.nbytes && op->data.dir == SPI_MEM_DATA_IN) - ifr |= QSPI_IFR_APBTFRTYP_READ; - - /* Set QSPI Instruction Frame registers */ + /* Set QSPI Instruction Frame registers. */ + if (op->addr.nbytes && !op->data.nbytes) atmel_qspi_write(iar, aq, QSPI_IAR); + + if (aq->caps->has_ricr) { if (op->data.dir == SPI_MEM_DATA_IN) atmel_qspi_write(icr, aq, QSPI_RICR); else atmel_qspi_write(icr, aq, QSPI_WICR); - atmel_qspi_write(ifr, aq, QSPI_IFR); } else { - if (op->data.dir == SPI_MEM_DATA_OUT) + if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) ifr |= QSPI_IFR_SAMA5D2_WRITE_TRSFR; - /* Set QSPI Instruction Frame registers */ - atmel_qspi_write(iar, aq, QSPI_IAR); atmel_qspi_write(icr, aq, QSPI_ICR); - atmel_qspi_write(ifr, aq, QSPI_IFR); } + atmel_qspi_write(ifr, aq, QSPI_IFR); + return 0; } @@ -597,26 +640,6 @@ static int atmel_qspi_transfer(struct atmel_qspi *aq, ATMEL_QSPI_TIMEOUT); } -static int atmel_qspi_reg_sync(struct atmel_qspi *aq) -{ - u32 val; - - return readl_poll_timeout(aq->regs + QSPI_SR2, val, - !(val & QSPI_SR2_SYNCBSY), - ATMEL_QSPI_SYNC_TIMEOUT); -} - -static int atmel_qspi_update_config(struct atmel_qspi *aq) -{ - int ret; - - ret = atmel_qspi_reg_sync(aq); - if (ret) - return ret; - atmel_qspi_write(QSPI_CR_UPDCFG, aq, QSPI_CR); - return atmel_qspi_reg_sync(aq); -} - static int atmel_qspi_sama7g5_set_cfg(struct atmel_qspi *aq, const struct spi_mem_op *op, u32 *offset) { @@ -668,17 +691,9 @@ static int atmel_qspi_sama7g5_set_cfg(struct atmel_qspi *aq, ifr |= QSPI_IFR_TFRTYP_MEM; } - /* - * If the QSPI controller is set in regular SPI mode, set it in - * Serial Memory Mode (SMM). - */ - if (aq->mr != QSPI_MR_SMM) { - atmel_qspi_write(QSPI_MR_SMM | QSPI_MR_DQSDLYEN, aq, QSPI_MR); - ret = atmel_qspi_update_config(aq); - if (ret) - return ret; - aq->mr = QSPI_MR_SMM; - } + ret = atmel_qspi_set_serial_memory_mode(aq, true); + if (ret < 0) + return ret; /* Clear pending interrupts */ (void)atmel_qspi_read(aq, QSPI_SR); @@ -902,11 +917,10 @@ static int atmel_qspi_sama7g5_set_speed(struct udevice *bus, uint hz) } /* Set the QSPI controller by default in Serial Memory Mode */ - atmel_qspi_write(QSPI_MR_SMM | QSPI_MR_DQSDLYEN, aq, QSPI_MR); - ret = atmel_qspi_update_config(aq); - if (ret) + aq->mr |= QSPI_MR_DQSDLYEN; + ret = atmel_qspi_set_serial_memory_mode(aq, true); + if (ret < 0) return ret; - aq->mr = QSPI_MR_SMM; /* Enable the QSPI controller. */ ret = atmel_qspi_reg_sync(aq); @@ -930,6 +944,135 @@ static int atmel_qspi_sama7g5_set_speed(struct udevice *bus, uint hz) return ret; } +static int atmel_qspi_claim_bus(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_qspi *aq = dev_get_priv(bus); + int ret; + + aq->mr &= ~QSPI_MR_CSMODE_MASK; + aq->mr |= QSPI_MR_CSMODE_LASTXFER | QSPI_MR_WDRBT; + atmel_qspi_write(aq->mr, aq, QSPI_MR); + + ret = atmel_qspi_set_serial_memory_mode(aq, false); + if (ret) + return log_ret(ret); + + /* de-assert all chip selects */ + if (IS_ENABLED(CONFIG_DM_GPIO)) { + for (int i = 0; i < ARRAY_SIZE(aq->cs_gpios); i++) { + if (dm_gpio_is_valid(&aq->cs_gpios[i])) + dm_gpio_set_value(&aq->cs_gpios[i], 0); + } + } + + atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR); + + return 0; +} + +static int atmel_qspi_release_bus(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_qspi *aq = dev_get_priv(bus); + + /* de-assert all chip selects */ + if (IS_ENABLED(CONFIG_DM_GPIO)) { + for (int i = 0; i < ARRAY_SIZE(aq->cs_gpios); i++) { + if (dm_gpio_is_valid(&aq->cs_gpios[i])) + dm_gpio_set_value(&aq->cs_gpios[i], 0); + } + } + + atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR); + + return 0; +} + +static int atmel_qspi_set_cs(struct udevice *dev, int value) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_qspi *aq = dev_get_priv(bus); + int cs = spi_chip_select(dev); + + if (IS_ENABLED(CONFIG_DM_GPIO)) { + if (!dm_gpio_is_valid(&aq->cs_gpios[cs])) + return log_ret(-ENOENT); + + return dm_gpio_set_value(&aq->cs_gpios[cs], value); + } else { + return -ENOENT; + } +} + +static int atmel_qspi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_qspi *aq = dev_get_priv(bus); + unsigned int len, len_rx, len_tx; + const u8 *txp = dout; + u8 *rxp = din; + u32 reg; + int ret; + + if (bitlen == 0) + goto out; + + if (bitlen % 8) { + flags |= SPI_XFER_END; + goto out; + } + + len = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + ret = atmel_qspi_set_cs(dev, 1); + if (ret) + return log_ret(ret); + reg = atmel_qspi_read(aq, QSPI_RD); + } + + for (len_tx = 0, len_rx = 0; len_rx < len; ) { + u32 status = atmel_qspi_read(aq, QSPI_SR); + u8 value; + + if (status & QSPI_SR_OVRES) + return log_ret(-1); + + if (len_tx < len && (status & QSPI_SR_TDRE)) { + if (txp) + value = *txp++; + else + value = 0; + atmel_qspi_write(value, aq, QSPI_TD); + len_tx++; + } + + if (status & QSPI_SR_RDRF) { + value = atmel_qspi_read(aq, QSPI_RD); + if (rxp) + *rxp++ = value; + len_rx++; + } + } + +out: + if (flags & SPI_XFER_END) { + readl_poll_timeout(aq->regs + QSPI_SR, reg, + reg & QSPI_SR_TXEMPTY, + ATMEL_QSPI_TIMEOUT); + + atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR); + + ret = atmel_qspi_set_cs(dev, 0); + if (ret) + return log_ret(ret); + } + + return 0; +} + static int atmel_qspi_set_speed(struct udevice *bus, uint hz) { struct atmel_qspi *aq = dev_get_priv(bus); @@ -939,6 +1082,7 @@ static int atmel_qspi_set_speed(struct udevice *bus, uint hz) return atmel_qspi_sama7g5_set_speed(bus, hz); /* Compute the QSPI baudrate */ + dev_dbg(bus, "bus_clk_rate: %lu, hz: %u\n", aq->bus_clk_rate, hz); scbr = DIV_ROUND_UP(aq->bus_clk_rate, hz); if (scbr > 0) scbr--; @@ -1046,10 +1190,6 @@ static int atmel_qspi_init(struct atmel_qspi *aq) /* Reset the QSPI controller */ atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR); - /* Set the QSPI controller by default in Serial Memory Mode */ - atmel_qspi_write(QSPI_MR_SMM, aq, QSPI_MR); - aq->mr = QSPI_MR_SMM; - /* Enable the QSPI controller */ atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR); @@ -1075,7 +1215,7 @@ static int atmel_qspi_probe(struct udevice *dev) aq->caps = (struct atmel_qspi_caps *)dev_get_driver_data(dev); if (!aq->caps) { dev_err(dev, "Could not retrieve QSPI caps\n"); - return -EINVAL; + return log_ret(-EINVAL); }; if (aq->caps->has_gclk) @@ -1083,36 +1223,53 @@ static int atmel_qspi_probe(struct udevice *dev) else aq->ops = &atmel_qspi_priv_ops; + if (IS_ENABLED(CONFIG_DM_GPIO)) { + ret = gpio_request_list_by_name(dev, "cs-gpios", aq->cs_gpios, + ARRAY_SIZE(aq->cs_gpios), 0); + if (ret < 0) { + pr_err("Can't get %s gpios! Error: %d", dev->name, ret); + return log_ret(ret); + } + + for (int i = 0; i < ARRAY_SIZE(aq->cs_gpios); i++) { + if (!dm_gpio_is_valid(&aq->cs_gpios[i])) + continue; + + dm_gpio_set_dir_flags(&aq->cs_gpios[i], + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + } + } + /* Map the registers */ ret = dev_read_resource_byname(dev, "qspi_base", &res); if (ret) { dev_err(dev, "missing registers\n"); - return ret; + return log_ret(ret); } aq->regs = devm_ioremap(dev, res.start, resource_size(&res)); if (IS_ERR(aq->regs)) - return PTR_ERR(aq->regs); + return log_ret(PTR_ERR(aq->regs)); /* Map the AHB memory */ ret = dev_read_resource_byname(dev, "qspi_mmap", &res); if (ret) { dev_err(dev, "missing AHB memory\n"); - return ret; + return log_ret(ret); } aq->mem = devm_ioremap(dev, res.start, resource_size(&res)); if (IS_ERR(aq->mem)) - return PTR_ERR(aq->mem); + return log_ret(PTR_ERR(aq->mem)); aq->mmap_size = resource_size(&res); ret = atmel_qspi_enable_clk(dev); if (ret) - return ret; + return log_ret(ret); aq->dev = dev; - return atmel_qspi_init(aq); + return log_ret(atmel_qspi_init(aq)); } static const struct spi_controller_mem_ops atmel_qspi_mem_ops = { @@ -1121,9 +1278,12 @@ static const struct spi_controller_mem_ops atmel_qspi_mem_ops = { }; static const struct dm_spi_ops atmel_qspi_ops = { + .claim_bus = atmel_qspi_claim_bus, + .release_bus = atmel_qspi_release_bus, + .xfer = atmel_qspi_xfer, + .mem_ops = &atmel_qspi_mem_ops, .set_speed = atmel_qspi_set_speed, .set_mode = atmel_qspi_set_mode, - .mem_ops = &atmel_qspi_mem_ops, }; static const struct atmel_qspi_caps atmel_sama5d2_qspi_caps = {}; diff --git a/drivers/spi/cadence_ospi_versal.c b/drivers/spi/cadence_ospi_versal.c index 816916de16d..fbeb0c6a85c 100644 --- a/drivers/spi/cadence_ospi_versal.c +++ b/drivers/spi/cadence_ospi_versal.c @@ -204,3 +204,22 @@ void cadence_qspi_apb_enable_linear_mode(bool enable) ~VERSAL_OSPI_LINEAR_MODE, VERSAL_AXI_MUX_SEL); } } + +int cadence_device_reset(struct udevice *bus) +{ + struct cadence_spi_priv *priv = dev_get_priv(bus); + u32 reg; + + reg = readl(priv->regbase + CQSPI_REG_CONFIG); + reg |= CQSPI_REG_CONFIG_RESET_CFG_FLD_MASK; + writel(reg, priv->regbase + CQSPI_REG_CONFIG); + + writel(reg & ~CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK, priv->regbase + CQSPI_REG_CONFIG); + udelay(5); + writel(reg | CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK, priv->regbase + CQSPI_REG_CONFIG); + udelay(150); + writel(reg & ~CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK, priv->regbase + CQSPI_REG_CONFIG); + udelay(1200); + + return 0; +} diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index 623904ecdad..9edbfaa821b 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -27,12 +27,20 @@ #define CQSPI_READ 2 #define CQSPI_WRITE 3 +/* Quirks */ +#define CQSPI_DISABLE_STIG_MODE BIT(0) + __weak int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv, const struct spi_mem_op *op) { return 0; } +__weak int cadence_device_reset(struct udevice *dev) +{ + return 0; +} + __weak int cadence_qspi_flash_reset(struct udevice *dev) { return 0; @@ -217,6 +225,7 @@ static int cadence_spi_probe(struct udevice *bus) priv->tsd2d_ns = plat->tsd2d_ns; priv->tchsh_ns = plat->tchsh_ns; priv->tslch_ns = plat->tslch_ns; + priv->quirks = plat->quirks; if (IS_ENABLED(CONFIG_ZYNQMP_FIRMWARE)) xilinx_pm_request(PM_REQUEST_NODE, PM_DEV_OSPI, @@ -251,6 +260,9 @@ static int cadence_spi_probe(struct udevice *bus) priv->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, priv->ref_clk_hz); + if (device_is_compatible(bus, "amd,versal2-ospi")) + return cadence_device_reset(bus); + /* Reset ospi flash device */ return cadence_qspi_flash_reset(bus); @@ -307,12 +319,16 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi, * which is unsupported on some flash devices during register * reads, prefer STIG mode for such small reads. */ - if (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX) + if (!op->addr.nbytes || + (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX && + !(priv->quirks & CQSPI_DISABLE_STIG_MODE))) mode = CQSPI_STIG_READ; else mode = CQSPI_READ; } else { - if (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX) + if (!op->addr.nbytes || !op->data.buf.out || + (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX && + !(priv->quirks & CQSPI_DISABLE_STIG_MODE))) mode = CQSPI_STIG_WRITE; else mode = CQSPI_WRITE; @@ -427,6 +443,10 @@ static int cadence_spi_of_to_plat(struct udevice *bus) plat->read_delay = ofnode_read_s32_default(subnode, "cdns,read-delay", -1); + const struct cqspi_driver_platdata *drvdata = + (struct cqspi_driver_platdata *)dev_get_driver_data(bus); + plat->quirks = drvdata->quirks; + debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n", __func__, plat->regbase, plat->ahbbase, plat->max_hz, plat->page_size); @@ -449,9 +469,21 @@ static const struct dm_spi_ops cadence_spi_ops = { */ }; +static const struct cqspi_driver_platdata cdns_qspi = { + .quirks = CQSPI_DISABLE_STIG_MODE, +}; + static const struct udevice_id cadence_spi_ids[] = { - { .compatible = "cdns,qspi-nor" }, - { .compatible = "ti,am654-ospi" }, + { + .compatible = "cdns,qspi-nor", + .data = (ulong)&cdns_qspi, + }, + { + .compatible = "ti,am654-ospi" + }, + { + .compatible = "amd,versal2-ospi" + }, { } }; diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index 1f9125cd239..80510f2542b 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -45,6 +45,8 @@ #define CQSPI_REG_CONFIG_CLK_POL BIT(1) #define CQSPI_REG_CONFIG_CLK_PHA BIT(2) #define CQSPI_REG_CONFIG_PHY_ENABLE_MASK BIT(3) +#define CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK BIT(5) +#define CQSPI_REG_CONFIG_RESET_CFG_FLD_MASK BIT(6) #define CQSPI_REG_CONFIG_DIRECT BIT(7) #define CQSPI_REG_CONFIG_DECODE BIT(9) #define CQSPI_REG_CONFIG_ENBL_DMA BIT(15) @@ -220,6 +222,7 @@ struct cadence_spi_plat { u32 tsd2d_ns; u32 tchsh_ns; u32 tslch_ns; + u32 quirks; bool is_dma; }; @@ -251,6 +254,7 @@ struct cadence_spi_priv { u32 tsd2d_ns; u32 tchsh_ns; u32 tslch_ns; + u32 quirks; u8 edge_mode; u8 dll_mode; bool extra_dummy; @@ -266,6 +270,11 @@ struct cadence_spi_priv { bool dtr; }; +struct cqspi_driver_platdata { + u32 hwcaps_mask; + u32 quirks; +}; + /* Functions call declaration */ void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv); void cadence_qspi_apb_controller_enable(void *reg_base_addr); @@ -310,5 +319,6 @@ int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg); int cadence_qspi_flash_reset(struct udevice *dev); ofnode cadence_qspi_get_subnode(struct udevice *dev); void cadence_qspi_apb_enable_linear_mode(bool enable); +int cadence_device_reset(struct udevice *dev); #endif /* __CADENCE_QSPI_H__ */ diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c index a8ec2f4f7b4..50bd7be5640 100644 --- a/drivers/spi/soft_spi.c +++ b/drivers/spi/soft_spi.c @@ -124,8 +124,19 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen, u8 *rxd = din; int cpha = !!(priv->mode & SPI_CPHA); int cidle = !!(priv->mode & SPI_CPOL); + int txrx = plat->flags; unsigned int j; + if (priv->mode & SPI_3WIRE) { + if (txd && rxd) + return -EINVAL; + + txrx = txd ? SPI_MASTER_NO_RX : SPI_MASTER_NO_TX; + dm_gpio_set_dir_flags(&plat->mosi, + txd ? GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE : + GPIOD_IS_IN | GPIOD_PULL_UP); + } + debug("spi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n", dev->parent->name, dev->name, *(uint *)txd, *(uint *)rxd, bitlen); @@ -160,7 +171,7 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen, */ if (cpha) soft_spi_scl(dev, !cidle); - if ((plat->flags & SPI_MASTER_NO_TX) == 0) + if ((txrx & SPI_MASTER_NO_TX) == 0) soft_spi_sda(dev, !!(tmpdout & 0x80)); udelay(plat->spi_delay_us); @@ -174,8 +185,10 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen, else soft_spi_scl(dev, cidle); tmpdin <<= 1; - if ((plat->flags & SPI_MASTER_NO_RX) == 0) - tmpdin |= dm_gpio_get_value(&plat->miso); + if ((txrx & SPI_MASTER_NO_RX) == 0) + tmpdin |= dm_gpio_get_value((priv->mode & SPI_3WIRE) ? + &plat->mosi : + &plat->miso); tmpdout <<= 1; udelay(plat->spi_delay_us); diff --git a/drivers/spi/tegra20_slink.c b/drivers/spi/tegra20_slink.c index d54a5049205..097d9164175 100644 --- a/drivers/spi/tegra20_slink.c +++ b/drivers/spi/tegra20_slink.c @@ -29,7 +29,10 @@ DECLARE_GLOBAL_DATA_PTR; #define SLINK_CMD_IDLE_SCLK_PULL_LOW (2 << 24) #define SLINK_CMD_IDLE_SCLK_PULL_HIGH (3 << 24) #define SLINK_CMD_IDLE_SCLK_MASK (3 << 24) +#define SLINK_CMD_CS_POL3 BIT(23) +#define SLINK_CMD_CS_POL2 BIT(22) #define SLINK_CMD_CK_SDA BIT(21) +#define SLINK_CMD_CS_POL1 BIT(20) #define SLINK_CMD_CS_POL BIT(13) #define SLINK_CMD_CS_VAL BIT(12) #define SLINK_CMD_CS_SOFT BIT(11) @@ -64,6 +67,13 @@ DECLARE_GLOBAL_DATA_PTR; #define SPI_TIMEOUT 1000 #define TEGRA_SPI_MAX_FREQ 52000000 +unsigned int cmd_cs_pol_bit[] = { + SLINK_CMD_CS_POL, + SLINK_CMD_CS_POL1, + SLINK_CMD_CS_POL2, + SLINK_CMD_CS_POL3, +}; + struct spi_regs { u32 command; /* SLINK_COMMAND_0 register */ u32 command2; /* SLINK_COMMAND2_0 reg */ @@ -155,6 +165,14 @@ static int tegra30_spi_claim_bus(struct udevice *dev) writel(reg, ®s->status); debug("%s: STATUS = %08x\n", __func__, readl(®s->status)); + /* Update the polarity bits */ + if (priv->mode & SPI_CS_HIGH) + setbits_le32(&priv->regs->command, + cmd_cs_pol_bit[spi_chip_select(dev)]); + else + clrbits_le32(&priv->regs->command, + cmd_cs_pol_bit[spi_chip_select(dev)]); + /* Set master mode and sw controlled CS */ reg = readl(®s->command); reg |= SLINK_CMD_M_S | SLINK_CMD_CS_SOFT; diff --git a/drivers/spmi/spmi-msm.c b/drivers/spmi/spmi-msm.c index 5cc5a9e654c..faae54e9fef 100644 --- a/drivers/spmi/spmi-msm.c +++ b/drivers/spmi/spmi-msm.c @@ -24,6 +24,9 @@ DECLARE_GLOBAL_DATA_PTR; #define PMIC_ARB_VERSION_V5_MIN 0x50000000 #define PMIC_ARB_VERSION_V7_MIN 0x70000000 +#define PMIC_ARB_FEATURES 0x0004 +#define PMIC_ARB_FEATURES_PERIPH_MASK GENMASK(10, 0) + #define APID_MAP_OFFSET_V1_V2_V3 (0x800) #define APID_MAP_OFFSET_V5 (0x900) #define APID_MAP_OFFSET_V7 (0x2000) @@ -60,6 +63,7 @@ DECLARE_GLOBAL_DATA_PTR; #define SPMI_MAX_PERIPH 256 #define SPMI_CHANNEL_READ_ONLY BIT(31) +#define SPMI_CHANNEL_VALID BIT(30) #define SPMI_CHANNEL_MASK 0xffff enum arb_ver { @@ -114,6 +118,8 @@ static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off, return -EIO; if (pid >= SPMI_MAX_PERIPH) return -EIO; + if (!(priv->channel_map[usid][pid] & SPMI_CHANNEL_VALID)) + return -EINVAL; if (priv->channel_map[usid][pid] & SPMI_CHANNEL_READ_ONLY) return -EPERM; @@ -183,6 +189,8 @@ static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off) return -EIO; if (pid >= SPMI_MAX_PERIPH) return -EIO; + if (!(priv->channel_map[usid][pid] & SPMI_CHANNEL_VALID)) + return -EINVAL; channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK; @@ -246,6 +254,32 @@ static struct dm_spmi_ops msm_spmi_ops = { .write = msm_spmi_write, }; +/* + * In order to allow multiple EEs to write to a single PPID in arbiter + * version 5 and 7, there is more than one APID mapped to each PPID. + * The owner field for each of these mappings specifies the EE which is + * allowed to write to the APID. + */ +static void msm_spmi_channel_map_v5(struct msm_spmi_priv *priv, unsigned int i, + uint8_t slave_id, uint8_t pid) +{ + /* Mark channels read-only when from different owner */ + uint32_t cnfg = readl(priv->spmi_cnfg + ARB_CHANNEL_OFFSET(i)); + uint8_t owner = SPMI_OWNERSHIP_PERIPH2OWNER(cnfg); + bool prev_valid = priv->channel_map[slave_id][pid] & SPMI_CHANNEL_VALID; + uint32_t prev_read_only = priv->channel_map[slave_id][pid] & SPMI_CHANNEL_READ_ONLY; + + if (!prev_valid) { + /* First PPID mapping */ + priv->channel_map[slave_id][pid] = i | SPMI_CHANNEL_VALID; + if (owner != priv->owner) + priv->channel_map[slave_id][pid] |= SPMI_CHANNEL_READ_ONLY; + } else if ((owner == priv->owner) && prev_read_only) { + /* Read only and we found one we own, switch */ + priv->channel_map[slave_id][pid] = i | SPMI_CHANNEL_VALID; + } +} + static int msm_spmi_probe(struct udevice *dev) { struct msm_spmi_priv *priv = dev_get_priv(dev); @@ -271,13 +305,17 @@ static int msm_spmi_probe(struct udevice *dev) } else if (hw_ver < PMIC_ARB_VERSION_V7_MIN) { priv->arb_ver = V5; priv->arb_chnl = core_addr + APID_MAP_OFFSET_V5; - priv->max_channels = SPMI_MAX_CHANNELS_V5; + priv->max_channels = min_t(u32, readl(core_addr + PMIC_ARB_FEATURES) & + PMIC_ARB_FEATURES_PERIPH_MASK, + SPMI_MAX_CHANNELS_V5); priv->spmi_cnfg = dev_read_addr_name(dev, "cnfg"); } else { /* TOFIX: handle second bus */ priv->arb_ver = V7; priv->arb_chnl = core_addr + APID_MAP_OFFSET_V7; - priv->max_channels = SPMI_MAX_CHANNELS_V7; + priv->max_channels = min_t(u32, readl(core_addr + PMIC_ARB_FEATURES) & + PMIC_ARB_FEATURES_PERIPH_MASK, + SPMI_MAX_CHANNELS_V7); priv->spmi_cnfg = dev_read_addr_name(dev, "cnfg"); } @@ -297,15 +335,16 @@ static int msm_spmi_probe(struct udevice *dev) uint8_t slave_id = (periph & 0xf0000) >> 16; uint8_t pid = (periph & 0xff00) >> 8; - priv->channel_map[slave_id][pid] = i; - - /* Mark channels read-only when from different owner */ - if (priv->arb_ver == V5 || priv->arb_ver == V7) { - uint32_t cnfg = readl(priv->spmi_cnfg + ARB_CHANNEL_OFFSET(i)); - uint8_t owner = SPMI_OWNERSHIP_PERIPH2OWNER(cnfg); + switch (priv->arb_ver) { + case V2: + case V3: + priv->channel_map[slave_id][pid] = i | SPMI_CHANNEL_VALID; + break; - if (owner != priv->owner) - priv->channel_map[slave_id][pid] |= SPMI_CHANNEL_READ_ONLY; + case V5: + case V7: + msm_spmi_channel_map_v5(priv, i, slave_id, pid); + break; } } return 0; diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig index 2030e4babc9..df83df69ffb 100644 --- a/drivers/sysinfo/Kconfig +++ b/drivers/sysinfo/Kconfig @@ -31,6 +31,13 @@ config SYSINFO_RCAR3 help Support querying SoC version information for Renesas R-Car Gen3. +config SYSINFO_IOT2050 + bool "Enable sysinfo driver for the Siemens IOT2050" + depends on TARGET_IOT2050_A53 + default y if TARGET_IOT2050_A53 + help + Support querying device information for Siemens IOT2050. + config SYSINFO_SANDBOX bool "Enable sysinfo driver for the Sandbox board" help diff --git a/drivers/sysinfo/Makefile b/drivers/sysinfo/Makefile index 680dde77fe8..26ca3150999 100644 --- a/drivers/sysinfo/Makefile +++ b/drivers/sysinfo/Makefile @@ -5,6 +5,7 @@ obj-y += sysinfo-uclass.o obj-$(CONFIG_SYSINFO_GAZERBEAM) += gazerbeam.o obj-$(CONFIG_SYSINFO_GPIO) += gpio.o +obj-$(CONFIG_SYSINFO_IOT2050) += iot2050.o obj-$(CONFIG_SYSINFO_RCAR3) += rcar3.o obj-$(CONFIG_SYSINFO_SANDBOX) += sandbox.o obj-$(CONFIG_SYSINFO_SMBIOS) += smbios.o diff --git a/drivers/sysinfo/iot2050.c b/drivers/sysinfo/iot2050.c new file mode 100644 index 00000000000..579a9f4711d --- /dev/null +++ b/drivers/sysinfo/iot2050.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Siemens AG, 2025 + */ + +#include <dm.h> +#include <sysinfo.h> +#include <net.h> +#include <u-boot/uuid.h> +#include <asm/arch/hardware.h> + +#include "iot2050.h" + +#define IOT2050_INFO_MAGIC 0x20502050 + +#define IOT2050_UUID_STR_LEN (32) + +struct iot2050_info { + u32 magic; + u16 size; + char name[20 + 1]; + char serial[16 + 1]; + char mlfb[18 + 1]; + char uuid[IOT2050_UUID_STR_LEN + 1]; + char a5e[18 + 1]; + u8 mac_addr_cnt; + u8 mac_addr[8][ARP_HLEN]; + char seboot_version[40 + 1]; + u8 padding[3]; + u32 ddr_size_mb; +} __packed; + +/** + * struct sysinfo_iot2050_priv - sysinfo private data + * @info: iot2050 board info + */ +struct sysinfo_iot2050_priv { + struct iot2050_info *info; + u8 uuid_smbios[16]; +}; + +static int sysinfo_iot2050_detect(struct udevice *dev) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + + if (!priv->info || priv->info->magic != IOT2050_INFO_MAGIC) + return -EFAULT; + + return 0; +} + +static int sysinfo_iot2050_get_str(struct udevice *dev, int id, size_t size, + char *val) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + + switch (id) { + case BOARD_NAME: + case SYSID_SM_BASEBOARD_VERSION: + strlcpy(val, priv->info->name, size); + break; + case SYSID_SM_SYSTEM_SERIAL: + strlcpy(val, priv->info->serial, size); + break; + case BOARD_MLFB: + case SYSID_SM_SYSTEM_VERSION: + strlcpy(val, priv->info->mlfb, size); + break; + case BOARD_UUID: + strlcpy(val, priv->info->uuid, size); + break; + case BOARD_A5E: + case SYSID_SM_BASEBOARD_PRODUCT: + strlcpy(val, priv->info->a5e, size); + break; + case BOARD_SEBOOT_VER: + case SYSID_PRIOR_STAGE_VERSION: + strlcpy(val, priv->info->seboot_version, size); + break; + default: + return -EINVAL; + }; + + val[size - 1] = '\0'; + return 0; +} + +static int sysinfo_iot2050_get_int(struct udevice *dev, int id, int *val) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + + switch (id) { + case SYSID_BOARD_RAM_SIZE_MB: + *val = priv->info->ddr_size_mb; + return 0; + default: + return -EINVAL; + }; +} + +static int sysinfo_iot2050_get_data(struct udevice *dev, int id, void **data, + size_t *size) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + + switch (id) { + case SYSID_SM_SYSTEM_UUID: + *data = priv->uuid_smbios; + *size = 16; + return 0; + default: + return -EINVAL; + }; +} + +static int sysinfo_iot2050_get_item_count(struct udevice *dev, int id) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + + switch (id) { + case SYSID_BOARD_MAC_ADDR: + return priv->info->mac_addr_cnt; + default: + return -EINVAL; + }; +} + +static int sysinfo_iot2050_get_data_by_index(struct udevice *dev, int id, + int index, void **data, + size_t *size) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + + switch (id) { + case SYSID_BOARD_MAC_ADDR: + if (index >= priv->info->mac_addr_cnt) + return -EINVAL; + *data = priv->info->mac_addr[index]; + *size = ARP_HLEN; + return 0; + default: + return -EINVAL; + }; +} + +static const struct sysinfo_ops sysinfo_iot2050_ops = { + .detect = sysinfo_iot2050_detect, + .get_str = sysinfo_iot2050_get_str, + .get_int = sysinfo_iot2050_get_int, + .get_data = sysinfo_iot2050_get_data, + .get_item_count = sysinfo_iot2050_get_item_count, + .get_data_by_index = sysinfo_iot2050_get_data_by_index, +}; + +/** + * @brief Convert the IOT2050 UUID string to the SMBIOS format + * + * @param uuid_raw The IOT2050 UUID string parsed from the eeprom + * @param uuid_smbios The buffer to hold the SMBIOS formatted UUID + */ +static void sysinfo_iot2050_convert_uuid(const char *uuid_iot2050, + u8 *uuid_smbios) +{ + char uuid_rfc4122_str[IOT2050_UUID_STR_LEN + 4 + 1] = {0}; + char *tmp = uuid_rfc4122_str; + + for (int i = 0; i < 16; i++) { + memcpy(tmp, uuid_iot2050 + i * 2, 2); + tmp += 2; + if (i == 3 || i == 5 || i == 7 || i == 9) + *tmp++ = '-'; + } + uuid_str_to_bin(uuid_rfc4122_str, uuid_smbios, UUID_STR_FORMAT_GUID); +} + +static int sysinfo_iot2050_probe(struct udevice *dev) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + unsigned long offset; + + offset = dev_read_u32_default(dev, "offset", + TI_SRAM_SCRATCH_BOARD_EEPROM_START); + priv->info = (struct iot2050_info *)offset; + + sysinfo_iot2050_convert_uuid(priv->info->uuid, priv->uuid_smbios); + + return 0; +} + +static const struct udevice_id sysinfo_iot2050_ids[] = { + { .compatible = "siemens,sysinfo-iot2050" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(sysinfo_iot2050) = { + .name = "sysinfo_iot2050", + .id = UCLASS_SYSINFO, + .of_match = sysinfo_iot2050_ids, + .ops = &sysinfo_iot2050_ops, + .priv_auto = sizeof(struct sysinfo_iot2050_priv), + .probe = sysinfo_iot2050_probe, +}; diff --git a/drivers/sysinfo/iot2050.h b/drivers/sysinfo/iot2050.h new file mode 100644 index 00000000000..657221db096 --- /dev/null +++ b/drivers/sysinfo/iot2050.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Siemens AG, 2025 + */ + +#include <sysinfo.h> + +enum sysinfo_id_iot2050 { + BOARD_MLFB = SYSID_USER, + BOARD_A5E, + BOARD_NAME, + BOARD_UUID, + BOARD_SEBOOT_VER, +}; diff --git a/drivers/sysinfo/sysinfo-uclass.c b/drivers/sysinfo/sysinfo-uclass.c index 3c0cd51273e..f04998ef8bb 100644 --- a/drivers/sysinfo/sysinfo-uclass.c +++ b/drivers/sysinfo/sysinfo-uclass.c @@ -119,6 +119,35 @@ int sysinfo_get_data(struct udevice *dev, int id, void **data, size_t *size) return ops->get_data(dev, id, data, size); } +int sysinfo_get_item_count(struct udevice *dev, int id) +{ + struct sysinfo_priv *priv = dev_get_uclass_priv(dev); + struct sysinfo_ops *ops = sysinfo_get_ops(dev); + + if (!priv->detected) + return -EPERM; + + if (!ops->get_item_count) + return -ENOSYS; + + return ops->get_item_count(dev, id); +} + +int sysinfo_get_data_by_index(struct udevice *dev, int id, int index, + void **data, size_t *size) +{ + struct sysinfo_priv *priv = dev_get_uclass_priv(dev); + struct sysinfo_ops *ops = sysinfo_get_ops(dev); + + if (!priv->detected) + return -EPERM; + + if (!ops->get_data_by_index) + return -ENOSYS; + + return ops->get_data_by_index(dev, id, index, data, size); +} + UCLASS_DRIVER(sysinfo) = { .id = UCLASS_SYSINFO, .name = "sysinfo", diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig index 121194e4418..4972905482a 100644 --- a/drivers/sysreset/Kconfig +++ b/drivers/sysreset/Kconfig @@ -71,6 +71,27 @@ config POWEROFF_GPIO Support for system poweroff using a GPIO pin. This can be used for systems having a single GPIO to trigger a system poweroff. +config SPL_POWEROFF_GPIO + bool "Enable support for GPIO poweroff driver in SPL" + depends on DM_GPIO && SPL + help + Support for system poweroff using a GPIO pin in SPL. This can be used + for systems having a single GPIO to trigger a system poweroff. + +config TPL_POWEROFF_GPIO + bool "Enable support for GPIO poweroff driver in TPL" + depends on DM_GPIO && TPL + help + Support for system poweroff using a GPIO pin in TPL. This can be used + for systems having a single GPIO to trigger a system poweroff. + +config VPL_POWEROFF_GPIO + bool "Enable support for GPIO poweroff driver in VPL" + depends on DM_GPIO && VPL + help + Support for system poweroff using a GPIO pin in VPL. This can be used + for systems having a single GPIO to trigger a system poweroff. + config SYSRESET_GPIO bool "Enable support for GPIO reset driver" depends on DM_GPIO @@ -79,6 +100,30 @@ config SYSRESET_GPIO example on Microblaze where reset logic can be controlled via GPIO pin which triggers cpu reset. +config SPL_SYSRESET_GPIO + bool "Enable support for GPIO reset driver in SPL" + depends on DM_GPIO && SPL + help + Reset support via GPIO pin connected reset logic in SPL. This is used + for example on Microblaze where reset logic can be controlled via + GPIO pin which triggers cpu reset. + +config TPL_SYSRESET_GPIO + bool "Enable support for GPIO reset driver in TPL" + depends on DM_GPIO && TPL + help + Reset support via GPIO pin connected reset logic in TPL. This is used + for example on Microblaze where reset logic can be controlled via + GPIO pin which triggers cpu reset. + +config VPL_SYSRESET_GPIO + bool "Enable support for GPIO reset driver in VPL" + depends on DM_GPIO && VPL + help + Reset support via GPIO pin connected reset logic in VPL. This is used + for 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 @@ -242,7 +287,6 @@ config SYSRESET_RAA215300 config SYSRESET_QCOM_PSHOLD bool "Support sysreset for Qualcomm SoCs via PSHOLD" - depends on ARCH_IPQ40XX help Add support for the system reboot on Qualcomm SoCs via PSHOLD. diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile index 796fc9effa5..ded91a4d325 100644 --- a/drivers/sysreset/Makefile +++ b/drivers/sysreset/Makefile @@ -8,8 +8,8 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += sysreset_rockchip.o obj-$(CONFIG_ARCH_STI) += sysreset_sti.o obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o obj-$(CONFIG_SYSRESET_CV1800B) += sysreset_cv1800b.o -obj-$(CONFIG_POWEROFF_GPIO) += poweroff_gpio.o -obj-$(CONFIG_SYSRESET_GPIO) += sysreset_gpio.o +obj-$(CONFIG_$(PHASE_)POWEROFF_GPIO) += poweroff_gpio.o +obj-$(CONFIG_$(PHASE_)SYSRESET_GPIO) += sysreset_gpio.o obj-$(CONFIG_$(PHASE_)SYSRESET_MAX77663) += sysreset_max77663.o obj-$(CONFIG_SYSRESET_MPC83XX) += sysreset_mpc83xx.o obj-$(CONFIG_SYSRESET_MICROBLAZE) += sysreset_microblaze.o diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile index 7a847e8388b..21db0d317fe 100644 --- a/drivers/timer/Makefile +++ b/drivers/timer/Makefile @@ -5,14 +5,14 @@ obj-y += timer-uclass.o obj-$(CONFIG_ADI_SC5XX_TIMER) += adi_sc5xx_timer.o obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o -obj-$(CONFIG_$(XPL_)ANDES_PLMT_TIMER) += andes_plmt_timer.o +obj-$(CONFIG_$(PHASE_)ANDES_PLMT_TIMER) += andes_plmt_timer.o obj-$(CONFIG_ARC_TIMER) += arc_timer.o obj-$(CONFIG_ARM_TWD_TIMER) += arm_twd_timer.o obj-$(CONFIG_AST_TIMER) += ast_timer.o obj-$(CONFIG_AST_IBEX_TIMER) += ast_ibex_timer.o obj-$(CONFIG_ATCPIT100_TIMER) += atcpit100_timer.o -obj-$(CONFIG_$(XPL_)ATMEL_PIT_TIMER) += atmel_pit_timer.o -obj-$(CONFIG_$(XPL_)ATMEL_TCB_TIMER) += atmel_tcb_timer.o +obj-$(CONFIG_$(PHASE_)ATMEL_PIT_TIMER) += atmel_pit_timer.o +obj-$(CONFIG_$(PHASE_)ATMEL_TCB_TIMER) += atmel_tcb_timer.o obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence-ttc.o obj-$(CONFIG_DESIGNWARE_APB_TIMER) += dw-apb-timer.o obj-$(CONFIG_FTTMR010_TIMER) += fttmr010_timer.o @@ -27,7 +27,7 @@ obj-$(CONFIG_RISCV_TIMER) += riscv_timer.o obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o obj-$(CONFIG_SANDBOX_TIMER) += sandbox_timer.o obj-$(CONFIG_SP804_TIMER) += sp804_timer.o -obj-$(CONFIG_$(XPL_)RISCV_ACLINT) += riscv_aclint_timer.o +obj-$(CONFIG_$(PHASE_)RISCV_ACLINT) += riscv_aclint_timer.o obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o obj-$(CONFIG_STM32_TIMER) += stm32_timer.o obj-$(CONFIG_TEGRA_TIMER) += tegra-timer.o diff --git a/drivers/timer/sandbox_timer.c b/drivers/timer/sandbox_timer.c index e8b54a02965..c1baf3c69ec 100644 --- a/drivers/timer/sandbox_timer.c +++ b/drivers/timer/sandbox_timer.c @@ -18,6 +18,11 @@ void timer_test_add_offset(unsigned long offset) sandbox_timer_offset += offset; } +ulong timer_test_get_offset(void) +{ + return sandbox_timer_offset; +}; + u64 notrace timer_early_get_count(void) { return os_get_nsec() / 1000 + sandbox_timer_offset * 1000; diff --git a/drivers/tpm/cr50_i2c.c b/drivers/tpm/cr50_i2c.c index 08ec179346e..5b2d5ccb146 100644 --- a/drivers/tpm/cr50_i2c.c +++ b/drivers/tpm/cr50_i2c.c @@ -737,9 +737,13 @@ static int cr50_i2c_report_state(struct udevice *dev, char *str, int str_max) static int cr50_i2c_open(struct udevice *dev) { + struct cr50_priv *priv = dev_get_priv(dev); char buf[80]; int ret; + if (priv->locality != -1) + return -EBUSY; + ret = process_reset(dev); if (ret) return log_msg_ret("reset", ret); diff --git a/drivers/ufs/ufs-amd-versal2.c b/drivers/ufs/ufs-amd-versal2.c index bfd844e4193..1c5ed538370 100644 --- a/drivers/ufs/ufs-amd-versal2.c +++ b/drivers/ufs/ufs-amd-versal2.c @@ -19,8 +19,6 @@ #include "ufshcd-dwc.h" #include "ufshci-dwc.h" -#define VERSAL2_UFS_DEVICE_ID 4 - #define SRAM_CSR_INIT_DONE_MASK BIT(0) #define SRAM_CSR_EXT_LD_DONE_MASK BIT(1) #define SRAM_CSR_BYPASS_MASK BIT(2) @@ -32,19 +30,12 @@ #define TIMEOUT_MICROSEC 1000000L -#define IOCTL_UFS_TXRX_CFGRDY_GET 40 -#define IOCTL_UFS_SRAM_CSR_SEL 41 - -#define PM_UFS_SRAM_CSR_WRITE 0 -#define PM_UFS_SRAM_CSR_READ 1 - struct ufs_versal2_priv { struct ufs_hba *hba; struct reset_ctl *rstc; struct reset_ctl *rstphy; u32 phy_mode; u32 host_clk; - u32 pd_dev_id; u8 attcompval0; u8 attcompval1; u8 ctlecompval0; @@ -102,41 +93,6 @@ static int ufs_versal2_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val) return 0; } -int versal2_pm_ufs_get_txrx_cfgrdy(u32 node_id, u32 *value) -{ - u32 ret_payload[PAYLOAD_ARG_CNT]; - int ret; - - if (!value) - return -EINVAL; - - ret = xilinx_pm_request(PM_IOCTL, node_id, IOCTL_UFS_TXRX_CFGRDY_GET, - 0, 0, ret_payload); - *value = ret_payload[1]; - - return ret; -} - -int versal2_pm_ufs_sram_csr_sel(u32 node_id, u32 type, u32 *value) -{ - u32 ret_payload[PAYLOAD_ARG_CNT]; - int ret; - - if (!value) - return -EINVAL; - - if (type == PM_UFS_SRAM_CSR_READ) { - ret = xilinx_pm_request(PM_IOCTL, node_id, IOCTL_UFS_SRAM_CSR_SEL, - type, 0, ret_payload); - *value = ret_payload[1]; - } else { - ret = xilinx_pm_request(PM_IOCTL, node_id, IOCTL_UFS_SRAM_CSR_SEL, - type, *value, 0); - } - - return ret; -} - static int ufs_versal2_enable_phy(struct ufs_hba *hba) { u32 offset, reg; @@ -281,7 +237,7 @@ static int ufs_versal2_phy_init(struct ufs_hba *hba) time_left = TIMEOUT_MICROSEC; do { time_left--; - ret = versal2_pm_ufs_get_txrx_cfgrdy(priv->pd_dev_id, ®); + ret = zynqmp_pm_ufs_get_txrx_cfgrdy(®); if (ret) return ret; @@ -312,8 +268,7 @@ static int ufs_versal2_phy_init(struct ufs_hba *hba) time_left = TIMEOUT_MICROSEC; do { time_left--; - ret = versal2_pm_ufs_sram_csr_sel(priv->pd_dev_id, - PM_UFS_SRAM_CSR_READ, ®); + ret = zynqmp_pm_ufs_sram_csr_read(®); if (ret) return ret; @@ -341,10 +296,10 @@ static int ufs_versal2_init(struct ufs_hba *hba) struct ufs_versal2_priv *priv = dev_get_priv(hba->dev); struct clk clk; unsigned long core_clk_rate = 0; + u32 cal; int ret = 0; priv->phy_mode = UFSHCD_DWC_PHY_MODE_ROM; - priv->pd_dev_id = VERSAL2_UFS_DEVICE_ID; ret = clk_get_by_name(hba->dev, "core_clk", &clk); if (ret) { @@ -371,6 +326,15 @@ static int ufs_versal2_init(struct ufs_hba *hba) return PTR_ERR(priv->rstphy); } + ret = zynqmp_pm_ufs_cal_reg(&cal); + if (ret) + return ret; + + priv->attcompval0 = (u8)cal; + priv->attcompval1 = (u8)(cal >> 8); + priv->ctlecompval0 = (u8)(cal >> 16); + priv->ctlecompval1 = (u8)(cal >> 24); + return ret; } @@ -397,8 +361,7 @@ static int ufs_versal2_hce_enable_notify(struct ufs_hba *hba, return ret; } - ret = versal2_pm_ufs_sram_csr_sel(priv->pd_dev_id, - PM_UFS_SRAM_CSR_READ, &sram_csr); + ret = zynqmp_pm_ufs_sram_csr_read(&sram_csr); if (ret) return ret; @@ -410,8 +373,7 @@ static int ufs_versal2_hce_enable_notify(struct ufs_hba *hba, return -EINVAL; } - ret = versal2_pm_ufs_sram_csr_sel(priv->pd_dev_id, - PM_UFS_SRAM_CSR_WRITE, &sram_csr); + ret = zynqmp_pm_ufs_sram_csr_write(&sram_csr); if (ret) return ret; diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index f7d8c40c448..91f6ad3bfef 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -19,6 +19,7 @@ #include <malloc.h> #include <hexdump.h> #include <scsi.h> +#include <ufs.h> #include <asm/io.h> #include <asm/dma-mapping.h> #include <linux/bitops.h> @@ -313,16 +314,12 @@ static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer) ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &tx_lanes); for (i = 0; i < tx_lanes; i++) { + unsigned int val = UIC_ARG_MIB_SEL(TX_LCC_ENABLE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)); if (!peer) - err = ufshcd_dme_set(hba, - UIC_ARG_MIB_SEL(TX_LCC_ENABLE, - UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)), - 0); + err = ufshcd_dme_set(hba, val, 0); else - err = ufshcd_dme_peer_set(hba, - UIC_ARG_MIB_SEL(TX_LCC_ENABLE, - UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)), - 0); + err = ufshcd_dme_peer_set(hba, val, 0); if (err) { dev_err(hba->dev, "%s: TX LCC Disable failed, peer = %d, lane = %d, err = %d\n", __func__, peer, i, err); @@ -1034,8 +1031,8 @@ static inline void ufshcd_init_query(struct ufs_hba *hba, /** * ufshcd_query_flag() - API function for sending flag query requests */ -int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, - enum flag_idn idn, bool *flag_res) +static int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, + enum flag_idn idn, bool *flag_res) { struct ufs_query_req *request = NULL; struct ufs_query_res *response = NULL; @@ -1170,9 +1167,9 @@ out: /** * ufshcd_query_descriptor_retry - API function for sending descriptor requests */ -int ufshcd_query_descriptor_retry(struct ufs_hba *hba, enum query_opcode opcode, - enum desc_idn idn, u8 index, u8 selector, - u8 *desc_buf, int *buf_len) +static int ufshcd_query_descriptor_retry(struct ufs_hba *hba, enum query_opcode opcode, + enum desc_idn idn, u8 index, u8 selector, + u8 *desc_buf, int *buf_len) { int err; int retries; @@ -1264,8 +1261,8 @@ static void ufshcd_init_desc_sizes(struct ufs_hba *hba) * ufshcd_map_desc_id_to_length - map descriptor IDN to its length * */ -int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id, - int *desc_len) +static int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id, + int *desc_len) { switch (desc_id) { case QUERY_DESC_IDN_DEVICE: @@ -1302,15 +1299,14 @@ int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id, } return 0; } -EXPORT_SYMBOL(ufshcd_map_desc_id_to_length); /** * ufshcd_read_desc_param - read the specified descriptor parameter * */ -int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, - int desc_index, u8 param_offset, u8 *param_read_buf, - u8 param_size) +static int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, + int desc_index, u8 param_offset, + u8 *param_read_buf, u8 param_size) { int ret; u8 *desc_buf; @@ -1569,8 +1565,8 @@ static int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size) * ufshcd_read_string_desc - read string descriptor * */ -int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, - u8 *buf, u32 size, bool ascii) +static int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, + u8 *buf, u32 size, bool ascii) { int err = 0; @@ -1881,7 +1877,7 @@ static void ufshcd_def_desc_sizes(struct ufs_hba *hba) hba->desc_size.hlth_desc = QUERY_DESC_HEALTH_DEF_SIZE; } -int ufs_start(struct ufs_hba *hba) +static int ufs_start(struct ufs_hba *hba) { struct ufs_dev_desc card = {0}; int ret; @@ -1962,7 +1958,7 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops) ufshcd_ops_init(hba); - /* Read capabilties registers */ + /* Read capabilities registers */ hba->capabilities = ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES); if (hba->quirks & UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS) hba->capabilities &= ~MASK_64_ADDRESSING_SUPPORT; @@ -2001,7 +1997,7 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops) REG_INTERRUPT_STATUS); ufshcd_writel(hba, 0, REG_INTERRUPT_ENABLE); - mb(); + mb(); /* flush previous writes */ /* Reset the attached device */ ufshcd_device_reset(hba); diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h index 00ecca350c3..53137fae3a8 100644 --- a/drivers/ufs/ufs.h +++ b/drivers/ufs/ufs.h @@ -4,6 +4,7 @@ #include <linux/types.h> #include <asm/io.h> +#include "ufshci.h" #include "unipro.h" struct udevice; @@ -14,11 +15,6 @@ struct udevice; #define RESPONSE_UPIU_SENSE_DATA_LENGTH 18 #define UFS_MAX_LUNS 0x7F -enum { - TASK_REQ_UPIU_SIZE_DWORDS = 8, - TASK_RSP_UPIU_SIZE_DWORDS = 8, - ALIGNED_UPIU_SIZE = 512, -}; /* UFS device power modes */ enum ufs_dev_pwr_mode { @@ -84,44 +80,6 @@ enum { /* Offset of the response code in the UPIU header */ #define UPIU_RSP_CODE_OFFSET 8 -/* To accommodate UFS2.0 required Command type */ -enum { - UTP_CMD_TYPE_UFS_STORAGE = 0x1, -}; - -enum { - UTP_SCSI_COMMAND = 0x00000000, - UTP_NATIVE_UFS_COMMAND = 0x10000000, - UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000, - UTP_REQ_DESC_INT_CMD = 0x01000000, -}; - -/* UTP Transfer Request Data Direction (DD) */ -enum { - UTP_NO_DATA_TRANSFER = 0x00000000, - UTP_HOST_TO_DEVICE = 0x02000000, - UTP_DEVICE_TO_HOST = 0x04000000, -}; - -/* Overall command status values */ -enum { - OCS_SUCCESS = 0x0, - OCS_INVALID_CMD_TABLE_ATTR = 0x1, - OCS_INVALID_PRDT_ATTR = 0x2, - OCS_MISMATCH_DATA_BUF_SIZE = 0x3, - OCS_MISMATCH_RESP_UPIU_SIZE = 0x4, - OCS_PEER_COMM_FAILURE = 0x5, - OCS_ABORTED = 0x6, - OCS_FATAL_ERROR = 0x7, - OCS_INVALID_COMMAND_STATUS = 0x0F, - MASK_OCS = 0x0F, -}; - -/* The maximum length of the data byte count field in the PRDT is 256KB */ -#define PRDT_DATA_BYTE_COUNT_MAX (256 * 1024) -/* The granularity of the data byte count field in the PRDT is 32-bit */ -#define PRDT_DATA_BYTE_COUNT_PAD 4 - #define GENERAL_UPIU_REQUEST_SIZE (sizeof(struct utp_upiu_req)) #define QUERY_DESC_MAX_SIZE 255 #define QUERY_DESC_MIN_SIZE 2 @@ -130,8 +88,8 @@ enum { (sizeof(struct utp_upiu_header))) #define RESPONSE_UPIU_SENSE_DATA_LENGTH 18 #define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\ - cpu_to_be32((byte3 << 24) | (byte2 << 16) |\ - (byte1 << 8) | (byte0)) + cpu_to_be32(((byte3) << 24) | ((byte2) << 16) |\ + ((byte1) << 8) | (byte0)) /* * UFS Protocol Information Unit related definitions */ @@ -297,79 +255,6 @@ enum desc_header_offset { QUERY_DESC_DESC_TYPE_OFFSET = 0x01, }; -struct ufshcd_sg_entry { - __le32 base_addr; - __le32 upper_addr; - __le32 reserved; - __le32 size; -}; - -#define MAX_BUFF 128 -/** - * struct utp_transfer_cmd_desc - UFS Command Descriptor structure - * @command_upiu: Command UPIU Frame address - * @response_upiu: Response UPIU Frame address - * @prd_table: Physical Region Descriptor - */ -struct utp_transfer_cmd_desc { - u8 command_upiu[ALIGNED_UPIU_SIZE]; - u8 response_upiu[ALIGNED_UPIU_SIZE]; - struct ufshcd_sg_entry prd_table[MAX_BUFF]; -}; - -/** - * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD - * @dword0: Descriptor Header DW0 - * @dword1: Descriptor Header DW1 - * @dword2: Descriptor Header DW2 - * @dword3: Descriptor Header DW3 - */ -struct request_desc_header { - __le32 dword_0; - __le32 dword_1; - __le32 dword_2; - __le32 dword_3; -}; - -/** - * struct utp_transfer_req_desc - UTRD structure - * @header: UTRD header DW-0 to DW-3 - * @command_desc_base_addr_lo: UCD base address low DW-4 - * @command_desc_base_addr_hi: UCD base address high DW-5 - * @response_upiu_length: response UPIU length DW-6 - * @response_upiu_offset: response UPIU offset DW-6 - * @prd_table_length: Physical region descriptor length DW-7 - * @prd_table_offset: Physical region descriptor offset DW-7 - */ -struct utp_transfer_req_desc { - /* DW 0-3 */ - struct request_desc_header header; - - /* DW 4-5*/ - __le32 command_desc_base_addr_lo; - __le32 command_desc_base_addr_hi; - - /* DW 6 */ - __le16 response_upiu_length; - __le16 response_upiu_offset; - - /* DW 7 */ - __le16 prd_table_length; - __le16 prd_table_offset; -}; - -/** - * struct utp_upiu_header - UPIU header structure - * @dword_0: UPIU header DW-0 - * @dword_1: UPIU header DW-1 - * @dword_2: UPIU header DW-2 - */ -struct utp_upiu_header { - __be32 dword_0; - __be32 dword_1; - __be32 dword_2; -}; - /** * struct utp_upiu_query - upiu request buffer structure for * query request. @@ -403,27 +288,6 @@ struct utp_upiu_cmd { u8 cdb[UFS_CDB_SIZE]; }; -/* - * UTMRD structure. - */ -struct utp_task_req_desc { - /* DW 0-3 */ - struct request_desc_header header; - - /* DW 4-11 - Task request UPIU structure */ - struct utp_upiu_header req_header; - __be32 input_param1; - __be32 input_param2; - __be32 input_param3; - __be32 __reserved1[2]; - - /* DW 12-19 - Task Management Response UPIU structure */ - struct utp_upiu_header rsp_header; - __be32 output_param1; - __be32 output_param2; - __be32 __reserved2[3]; -}; - /** * struct utp_upiu_req - general upiu request structure * @header:UPIU header structure DW-0 to DW-2 @@ -551,63 +415,6 @@ struct uic_command { int result; }; -/* GenSelectorIndex calculation macros for M-PHY attributes */ -#define UIC_ARG_MPHY_TX_GEN_SEL_INDEX(lane) (lane) -#define UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane) (PA_MAXDATALANES + (lane)) - -#define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\ - ((sel) & 0xFFFF)) -#define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0) -#define UIC_ARG_ATTR_TYPE(t) (((t) & 0xFF) << 16) -#define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF) - -/* Link Status*/ -enum link_status { - UFSHCD_LINK_IS_DOWN = 1, - UFSHCD_LINK_IS_UP = 2, -}; - -#define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\ - ((sel) & 0xFFFF)) -#define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0) -#define UIC_ARG_ATTR_TYPE(t) (((t) & 0xFF) << 16) -#define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF) - -/* UIC Commands */ -enum uic_cmd_dme { - UIC_CMD_DME_GET = 0x01, - UIC_CMD_DME_SET = 0x02, - UIC_CMD_DME_PEER_GET = 0x03, - UIC_CMD_DME_PEER_SET = 0x04, - UIC_CMD_DME_POWERON = 0x10, - UIC_CMD_DME_POWEROFF = 0x11, - UIC_CMD_DME_ENABLE = 0x12, - UIC_CMD_DME_RESET = 0x14, - UIC_CMD_DME_END_PT_RST = 0x15, - UIC_CMD_DME_LINK_STARTUP = 0x16, - UIC_CMD_DME_HIBER_ENTER = 0x17, - UIC_CMD_DME_HIBER_EXIT = 0x18, - UIC_CMD_DME_TEST_MODE = 0x1A, -}; - -/* UIC Config result code / Generic error code */ -enum { - UIC_CMD_RESULT_SUCCESS = 0x00, - UIC_CMD_RESULT_INVALID_ATTR = 0x01, - UIC_CMD_RESULT_FAILURE = 0x01, - UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02, - UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03, - UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04, - UIC_CMD_RESULT_BAD_INDEX = 0x05, - UIC_CMD_RESULT_LOCKED_ATTR = 0x06, - UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07, - UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08, - UIC_CMD_RESULT_BUSY = 0x09, - UIC_CMD_RESULT_DME_FAILURE = 0x0A, -}; - -#define MASK_UIC_COMMAND_RESULT 0xFF - /* Host <-> Device UniPro Link state */ enum uic_link_state { UIC_LINK_OFF_STATE = 0, /* Link powered down or disabled */ @@ -915,7 +722,7 @@ static inline int ufshcd_ops_get_max_pwr_mode(struct ufs_hba *hba, } static inline int ufshcd_ops_hce_enable_notify(struct ufs_hba *hba, - bool status) + bool status) { if (hba->ops && hba->ops->hce_enable_notify) return hba->ops->hce_enable_notify(hba, status); @@ -940,17 +747,6 @@ static inline int ufshcd_vops_device_reset(struct ufs_hba *hba) return 0; } -/* Controller UFSHCI version */ -enum { - UFSHCI_VERSION_10 = 0x00010000, /* 1.0 */ - UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */ - UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */ - UFSHCI_VERSION_21 = 0x00000210, /* 2.1 */ - UFSHCI_VERSION_30 = 0x00000300, /* 3.0 */ - UFSHCI_VERSION_31 = 0x00000310, /* 3.1 */ - UFSHCI_VERSION_40 = 0x00000400, /* 4.0 */ -}; - /* Interrupt disable masks */ enum { /* Interrupt disable mask for UFSHCI v1.0 */ @@ -964,123 +760,6 @@ enum { INTERRUPT_MASK_ALL_VER_21 = 0x71FFF, }; -/* UFSHCI Registers */ -enum { - REG_CONTROLLER_CAPABILITIES = 0x00, - REG_UFS_VERSION = 0x08, - REG_CONTROLLER_DEV_ID = 0x10, - REG_CONTROLLER_PROD_ID = 0x14, - REG_AUTO_HIBERNATE_IDLE_TIMER = 0x18, - REG_INTERRUPT_STATUS = 0x20, - REG_INTERRUPT_ENABLE = 0x24, - REG_CONTROLLER_STATUS = 0x30, - REG_CONTROLLER_ENABLE = 0x34, - REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER = 0x38, - REG_UIC_ERROR_CODE_DATA_LINK_LAYER = 0x3C, - REG_UIC_ERROR_CODE_NETWORK_LAYER = 0x40, - REG_UIC_ERROR_CODE_TRANSPORT_LAYER = 0x44, - REG_UIC_ERROR_CODE_DME = 0x48, - REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL = 0x4C, - REG_UTP_TRANSFER_REQ_LIST_BASE_L = 0x50, - REG_UTP_TRANSFER_REQ_LIST_BASE_H = 0x54, - REG_UTP_TRANSFER_REQ_DOOR_BELL = 0x58, - REG_UTP_TRANSFER_REQ_LIST_CLEAR = 0x5C, - REG_UTP_TRANSFER_REQ_LIST_RUN_STOP = 0x60, - REG_UTP_TASK_REQ_LIST_BASE_L = 0x70, - REG_UTP_TASK_REQ_LIST_BASE_H = 0x74, - REG_UTP_TASK_REQ_DOOR_BELL = 0x78, - REG_UTP_TASK_REQ_LIST_CLEAR = 0x7C, - REG_UTP_TASK_REQ_LIST_RUN_STOP = 0x80, - REG_UIC_COMMAND = 0x90, - REG_UIC_COMMAND_ARG_1 = 0x94, - REG_UIC_COMMAND_ARG_2 = 0x98, - REG_UIC_COMMAND_ARG_3 = 0x9C, - - UFSHCI_REG_SPACE_SIZE = 0xA0, - - REG_UFS_CCAP = 0x100, - REG_UFS_CRYPTOCAP = 0x104, - - UFSHCI_CRYPTO_REG_SPACE_SIZE = 0x400, -}; - -/* Controller capability masks */ -enum { - MASK_TRANSFER_REQUESTS_SLOTS = 0x0000001F, - MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000, - MASK_AUTO_HIBERN8_SUPPORT = 0x00800000, - MASK_64_ADDRESSING_SUPPORT = 0x01000000, - MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000, - MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000, -}; - -/* Interrupt Status 20h */ -#define UTP_TRANSFER_REQ_COMPL 0x1 -#define UIC_DME_END_PT_RESET 0x2 -#define UIC_ERROR 0x4 -#define UIC_TEST_MODE 0x8 -#define UIC_POWER_MODE 0x10 -#define UIC_HIBERNATE_EXIT 0x20 -#define UIC_HIBERNATE_ENTER 0x40 -#define UIC_LINK_LOST 0x80 -#define UIC_LINK_STARTUP 0x100 -#define UTP_TASK_REQ_COMPL 0x200 -#define UIC_COMMAND_COMPL 0x400 -#define DEVICE_FATAL_ERROR 0x800 -#define CONTROLLER_FATAL_ERROR 0x10000 -#define SYSTEM_BUS_FATAL_ERROR 0x20000 - -#define UFSHCD_UIC_PWR_MASK (UIC_HIBERNATE_ENTER |\ - UIC_HIBERNATE_EXIT |\ - UIC_POWER_MODE) - -#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL | UIC_POWER_MODE) - -#define UFSHCD_ERROR_MASK (UIC_ERROR |\ - DEVICE_FATAL_ERROR |\ - CONTROLLER_FATAL_ERROR |\ - SYSTEM_BUS_FATAL_ERROR) - -#define INT_FATAL_ERRORS (DEVICE_FATAL_ERROR |\ - CONTROLLER_FATAL_ERROR |\ - SYSTEM_BUS_FATAL_ERROR) - -/* Host Controller Enable 0x34h */ -#define CONTROLLER_ENABLE 0x1 -#define CONTROLLER_DISABLE 0x0 -/* HCS - Host Controller Status 30h */ -#define DEVICE_PRESENT 0x1 -#define UTP_TRANSFER_REQ_LIST_READY 0x2 -#define UTP_TASK_REQ_LIST_READY 0x4 -#define UIC_COMMAND_READY 0x8 -#define HOST_ERROR_INDICATOR 0x10 -#define DEVICE_ERROR_INDICATOR 0x20 -#define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK UFS_MASK(0x7, 8) - -#define UFSHCD_STATUS_READY (UTP_TRANSFER_REQ_LIST_READY |\ - UTP_TASK_REQ_LIST_READY |\ - UIC_COMMAND_READY) - -enum { - PWR_OK = 0x0, - PWR_LOCAL = 0x01, - PWR_REMOTE = 0x02, - PWR_BUSY = 0x03, - PWR_ERROR_CAP = 0x04, - PWR_FATAL_ERROR = 0x05, -}; - -/* UICCMD - UIC Command */ -#define COMMAND_OPCODE_MASK 0xFF -#define GEN_SELECTOR_INDEX_MASK 0xFFFF - -#define MIB_ATTRIBUTE_MASK UFS_MASK(0xFFFF, 16) -#define RESET_LEVEL 0xFF - -#define ATTR_SET_TYPE_MASK UFS_MASK(0xFF, 16) -#define CFG_RESULT_CODE_MASK 0xFF -#define GENERIC_ERROR_CODE_MASK 0xFF - #define ufshcd_writel(hba, val, reg) \ writel((val), (hba)->mmio_base + (reg)) #define ufshcd_readl(hba, reg) \ @@ -1103,12 +782,6 @@ static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val, u32 reg) ufshcd_writel(hba, tmp, reg); } -/* UTRLRSR - UTP Transfer Request Run-Stop Register 60h */ -#define UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT 0x1 - -/* UTMRLRSR - UTP Task Management Request Run-Stop Register 80h */ -#define UTP_TASK_REQ_LIST_RUN_STOP_BIT 0x1 - int ufshcd_probe(struct udevice *dev, struct ufs_hba_ops *hba_ops); #endif diff --git a/drivers/ufs/ufshci.h b/drivers/ufs/ufshci.h new file mode 100644 index 00000000000..90cbf87a3a4 --- /dev/null +++ b/drivers/ufs/ufshci.h @@ -0,0 +1,469 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef __UFSHCI_H +#define __UFSHCI_H + +enum { + TASK_REQ_UPIU_SIZE_DWORDS = 8, + TASK_RSP_UPIU_SIZE_DWORDS = 8, + ALIGNED_UPIU_SIZE = 512, +}; + +/* UFSHCI Registers */ +enum { + REG_CONTROLLER_CAPABILITIES = 0x00, + REG_MCQCAP = 0x04, + REG_UFS_VERSION = 0x08, + REG_EXT_CONTROLLER_CAPABILITIES = 0x0C, + REG_CONTROLLER_PID = 0x10, + REG_CONTROLLER_MID = 0x14, + REG_AUTO_HIBERNATE_IDLE_TIMER = 0x18, + REG_INTERRUPT_STATUS = 0x20, + REG_INTERRUPT_ENABLE = 0x24, + REG_CONTROLLER_STATUS = 0x30, + REG_CONTROLLER_ENABLE = 0x34, + REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER = 0x38, + REG_UIC_ERROR_CODE_DATA_LINK_LAYER = 0x3C, + REG_UIC_ERROR_CODE_NETWORK_LAYER = 0x40, + REG_UIC_ERROR_CODE_TRANSPORT_LAYER = 0x44, + REG_UIC_ERROR_CODE_DME = 0x48, + REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL = 0x4C, + REG_UTP_TRANSFER_REQ_LIST_BASE_L = 0x50, + REG_UTP_TRANSFER_REQ_LIST_BASE_H = 0x54, + REG_UTP_TRANSFER_REQ_DOOR_BELL = 0x58, + REG_UTP_TRANSFER_REQ_LIST_CLEAR = 0x5C, + REG_UTP_TRANSFER_REQ_LIST_RUN_STOP = 0x60, + REG_UTP_TASK_REQ_LIST_BASE_L = 0x70, + REG_UTP_TASK_REQ_LIST_BASE_H = 0x74, + REG_UTP_TASK_REQ_DOOR_BELL = 0x78, + REG_UTP_TASK_REQ_LIST_CLEAR = 0x7C, + REG_UTP_TASK_REQ_LIST_RUN_STOP = 0x80, + REG_UIC_COMMAND = 0x90, + REG_UIC_COMMAND_ARG_1 = 0x94, + REG_UIC_COMMAND_ARG_2 = 0x98, + REG_UIC_COMMAND_ARG_3 = 0x9C, + + UFSHCI_REG_SPACE_SIZE = 0xA0, + + REG_UFS_CCAP = 0x100, + REG_UFS_CRYPTOCAP = 0x104, + + REG_UFS_MEM_CFG = 0x300, + REG_UFS_MCQ_CFG = 0x380, + REG_UFS_ESILBA = 0x384, + REG_UFS_ESIUBA = 0x388, + UFSHCI_CRYPTO_REG_SPACE_SIZE = 0x400, +}; + +/* Controller capability masks */ +enum { + MASK_TRANSFER_REQUESTS_SLOTS_SDB = 0x0000001F, + MASK_TRANSFER_REQUESTS_SLOTS_MCQ = 0x000000FF, + MASK_NUMBER_OUTSTANDING_RTT = 0x0000FF00, + MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000, + MASK_EHSLUTRD_SUPPORTED = 0x00400000, + MASK_AUTO_HIBERN8_SUPPORT = 0x00800000, + MASK_64_ADDRESSING_SUPPORT = 0x01000000, + MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000, + MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000, + MASK_CRYPTO_SUPPORT = 0x10000000, + MASK_LSDB_SUPPORT = 0x20000000, + MASK_MCQ_SUPPORT = 0x40000000, +}; + +/* MCQ capability mask */ +enum { + MASK_EXT_IID_SUPPORT = 0x00000400, +}; + +enum { + REG_SQATTR = 0x0, + REG_SQLBA = 0x4, + REG_SQUBA = 0x8, + REG_SQDAO = 0xC, + REG_SQISAO = 0x10, + + REG_CQATTR = 0x20, + REG_CQLBA = 0x24, + REG_CQUBA = 0x28, + REG_CQDAO = 0x2C, + REG_CQISAO = 0x30, +}; + +enum { + REG_SQHP = 0x0, + REG_SQTP = 0x4, + REG_SQRTC = 0x8, + REG_SQCTI = 0xC, + REG_SQRTS = 0x10, +}; + +enum { + REG_CQHP = 0x0, + REG_CQTP = 0x4, +}; + +enum { + REG_CQIS = 0x0, + REG_CQIE = 0x4, +}; + +enum { + SQ_START = 0x0, + SQ_STOP = 0x1, + SQ_ICU = 0x2, +}; + +enum { + SQ_STS = 0x1, + SQ_CUS = 0x2, +}; + +#define SQ_ICU_ERR_CODE_MASK GENMASK(7, 4) +#define UFS_MASK(mask, offset) ((mask) << (offset)) + +/* UFS Version 08h */ +#define MINOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 0) +#define MAJOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 16) + +/* Controller UFSHCI version */ +enum { + UFSHCI_VERSION_10 = 0x00010000, /* 1.0 */ + UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */ + UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */ + UFSHCI_VERSION_21 = 0x00000210, /* 2.1 */ + UFSHCI_VERSION_30 = 0x00000300, /* 3.0 */ + UFSHCI_VERSION_31 = 0x00000310, /* 3.1 */ + UFSHCI_VERSION_40 = 0x00000400, /* 4.0 */ +}; + +/* + * IS - Interrupt Status - 20h + */ +#define UTP_TRANSFER_REQ_COMPL 0x1 +#define UIC_DME_END_PT_RESET 0x2 +#define UIC_ERROR 0x4 +#define UIC_TEST_MODE 0x8 +#define UIC_POWER_MODE 0x10 +#define UIC_HIBERNATE_EXIT 0x20 +#define UIC_HIBERNATE_ENTER 0x40 +#define UIC_LINK_LOST 0x80 +#define UIC_LINK_STARTUP 0x100 +#define UTP_TASK_REQ_COMPL 0x200 +#define UIC_COMMAND_COMPL 0x400 +#define DEVICE_FATAL_ERROR 0x800 +#define CONTROLLER_FATAL_ERROR 0x10000 +#define SYSTEM_BUS_FATAL_ERROR 0x20000 +#define CRYPTO_ENGINE_FATAL_ERROR 0x40000 +#define MCQ_CQ_EVENT_STATUS 0x100000 + +#define UFSHCD_UIC_HIBERN8_MASK (UIC_HIBERNATE_ENTER |\ + UIC_HIBERNATE_EXIT) + +#define UFSHCD_UIC_PWR_MASK (UFSHCD_UIC_HIBERN8_MASK |\ + UIC_POWER_MODE) + +#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL | UFSHCD_UIC_PWR_MASK) + +#define UFSHCD_ERROR_MASK (UIC_ERROR | INT_FATAL_ERRORS) + +#define INT_FATAL_ERRORS (DEVICE_FATAL_ERROR |\ + CONTROLLER_FATAL_ERROR |\ + SYSTEM_BUS_FATAL_ERROR |\ + CRYPTO_ENGINE_FATAL_ERROR |\ + UIC_LINK_LOST) + +/* HCS - Host Controller Status 30h */ +#define DEVICE_PRESENT 0x1 +#define UTP_TRANSFER_REQ_LIST_READY 0x2 +#define UTP_TASK_REQ_LIST_READY 0x4 +#define UIC_COMMAND_READY 0x8 +#define HOST_ERROR_INDICATOR 0x10 +#define DEVICE_ERROR_INDICATOR 0x20 +#define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK UFS_MASK(0x7, 8) + +#define UFSHCD_STATUS_READY (UTP_TRANSFER_REQ_LIST_READY |\ + UTP_TASK_REQ_LIST_READY |\ + UIC_COMMAND_READY) + +enum { + PWR_OK = 0x0, + PWR_LOCAL = 0x01, + PWR_REMOTE = 0x02, + PWR_BUSY = 0x03, + PWR_ERROR_CAP = 0x04, + PWR_FATAL_ERROR = 0x05, +}; + +/* HCE - Host Controller Enable 34h */ +#define CONTROLLER_ENABLE 0x1 +#define CONTROLLER_DISABLE 0x0 +#define CRYPTO_GENERAL_ENABLE 0x2 + +/* UECPA - Host UIC Error Code PHY Adapter Layer 38h */ +#define UIC_PHY_ADAPTER_LAYER_ERROR 0x80000000 +#define UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK 0x1F +#define UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK 0xF +#define UIC_PHY_ADAPTER_LAYER_GENERIC_ERROR 0x10 + +/* UECDL - Host UIC Error Code Data Link Layer 3Ch */ +#define UIC_DATA_LINK_LAYER_ERROR 0x80000000 +#define UIC_DATA_LINK_LAYER_ERROR_CODE_MASK 0xFFFF +#define UIC_DATA_LINK_LAYER_ERROR_TCX_REP_TIMER_EXP 0x2 +#define UIC_DATA_LINK_LAYER_ERROR_AFCX_REQ_TIMER_EXP 0x4 +#define UIC_DATA_LINK_LAYER_ERROR_FCX_PRO_TIMER_EXP 0x8 +#define UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF 0x20 +#define UIC_DATA_LINK_LAYER_ERROR_PA_INIT 0x2000 +#define UIC_DATA_LINK_LAYER_ERROR_NAC_RECEIVED 0x0001 +#define UIC_DATA_LINK_LAYER_ERROR_TCx_REPLAY_TIMEOUT 0x0002 + +/* UECN - Host UIC Error Code Network Layer 40h */ +#define UIC_NETWORK_LAYER_ERROR 0x80000000 +#define UIC_NETWORK_LAYER_ERROR_CODE_MASK 0x7 +#define UIC_NETWORK_UNSUPPORTED_HEADER_TYPE 0x1 +#define UIC_NETWORK_BAD_DEVICEID_ENC 0x2 +#define UIC_NETWORK_LHDR_TRAP_PACKET_DROPPING 0x4 + +/* UECT - Host UIC Error Code Transport Layer 44h */ +#define UIC_TRANSPORT_LAYER_ERROR 0x80000000 +#define UIC_TRANSPORT_LAYER_ERROR_CODE_MASK 0x7F +#define UIC_TRANSPORT_UNSUPPORTED_HEADER_TYPE 0x1 +#define UIC_TRANSPORT_UNKNOWN_CPORTID 0x2 +#define UIC_TRANSPORT_NO_CONNECTION_RX 0x4 +#define UIC_TRANSPORT_CONTROLLED_SEGMENT_DROPPING 0x8 +#define UIC_TRANSPORT_BAD_TC 0x10 +#define UIC_TRANSPORT_E2E_CREDIT_OVERFOW 0x20 +#define UIC_TRANSPORT_SAFETY_VALUE_DROPPING 0x40 + +/* UECDME - Host UIC Error Code DME 48h */ +#define UIC_DME_ERROR 0x80000000 +#define UIC_DME_ERROR_CODE_MASK 0x1 + +/* UTRIACR - Interrupt Aggregation control register - 0x4Ch */ +#define INT_AGGR_TIMEOUT_VAL_MASK 0xFF +#define INT_AGGR_COUNTER_THRESHOLD_MASK UFS_MASK(0x1F, 8) +#define INT_AGGR_COUNTER_AND_TIMER_RESET 0x10000 +#define INT_AGGR_STATUS_BIT 0x100000 +#define INT_AGGR_PARAM_WRITE 0x1000000 +#define INT_AGGR_ENABLE 0x80000000 + +/* UTRLRSR - UTP Transfer Request Run-Stop Register 60h */ +#define UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT 0x1 + +/* UTMRLRSR - UTP Task Management Request Run-Stop Register 80h */ +#define UTP_TASK_REQ_LIST_RUN_STOP_BIT 0x1 + +/* REG_UFS_MEM_CFG - Global Config Registers 300h */ +#define MCQ_MODE_SELECT BIT(0) + +/* CQISy - CQ y Interrupt Status Register */ +#define UFSHCD_MCQ_CQIS_TAIL_ENT_PUSH_STS 0x1 + +/* UICCMD - UIC Command */ +#define COMMAND_OPCODE_MASK 0xFF +#define GEN_SELECTOR_INDEX_MASK 0xFFFF + +#define MIB_ATTRIBUTE_MASK UFS_MASK(0xFFFF, 16) +#define RESET_LEVEL 0xFF + +#define ATTR_SET_TYPE_MASK UFS_MASK(0xFF, 16) +#define CFG_RESULT_CODE_MASK 0xFF +#define GENERIC_ERROR_CODE_MASK 0xFF + +/* GenSelectorIndex calculation macros for M-PHY attributes */ +#define UIC_ARG_MPHY_TX_GEN_SEL_INDEX(lane) (lane) +#define UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane) (PA_MAXDATALANES + (lane)) + +#define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\ + ((sel) & 0xFFFF)) +#define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0) +#define UIC_ARG_ATTR_TYPE(t) (((t) & 0xFF) << 16) +#define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF) + +/* Link Status*/ +enum link_status { + UFSHCD_LINK_IS_DOWN = 1, + UFSHCD_LINK_IS_UP = 2, +}; + +/* UIC Commands */ +enum uic_cmd_dme { + UIC_CMD_DME_GET = 0x01, + UIC_CMD_DME_SET = 0x02, + UIC_CMD_DME_PEER_GET = 0x03, + UIC_CMD_DME_PEER_SET = 0x04, + UIC_CMD_DME_POWERON = 0x10, + UIC_CMD_DME_POWEROFF = 0x11, + UIC_CMD_DME_ENABLE = 0x12, + UIC_CMD_DME_RESET = 0x14, + UIC_CMD_DME_END_PT_RST = 0x15, + UIC_CMD_DME_LINK_STARTUP = 0x16, + UIC_CMD_DME_HIBER_ENTER = 0x17, + UIC_CMD_DME_HIBER_EXIT = 0x18, + UIC_CMD_DME_TEST_MODE = 0x1A, +}; + +/* UIC Config result code / Generic error code */ +enum { + UIC_CMD_RESULT_SUCCESS = 0x00, + UIC_CMD_RESULT_INVALID_ATTR = 0x01, + UIC_CMD_RESULT_FAILURE = 0x01, + UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02, + UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03, + UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04, + UIC_CMD_RESULT_BAD_INDEX = 0x05, + UIC_CMD_RESULT_LOCKED_ATTR = 0x06, + UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07, + UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08, + UIC_CMD_RESULT_BUSY = 0x09, + UIC_CMD_RESULT_DME_FAILURE = 0x0A, +}; + +#define MASK_UIC_COMMAND_RESULT 0xFF + +#define INT_AGGR_COUNTER_THLD_VAL(c) (((c) & 0x1F) << 8) +#define INT_AGGR_TIMEOUT_VAL(t) (((t) & 0xFF) << 0) + +/* + * Request Descriptor Definitions + */ + +/* To accommodate UFS2.0 required Command type */ +enum { + UTP_CMD_TYPE_UFS_STORAGE = 0x1, +}; + +enum { + UTP_SCSI_COMMAND = 0x00000000, + UTP_REQ_DESC_INT_CMD = 0x01000000, + UTP_NATIVE_UFS_COMMAND = 0x10000000, + UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000, +}; + +/* UTP Transfer Request Data Direction (DD) */ +enum utp_data_direction { + UTP_NO_DATA_TRANSFER = 0, + UTP_HOST_TO_DEVICE = 1, + UTP_DEVICE_TO_HOST = 2, +}; + +/* Overall command status values */ +enum utp_ocs { + OCS_SUCCESS = 0x0, + OCS_INVALID_CMD_TABLE_ATTR = 0x1, + OCS_INVALID_PRDT_ATTR = 0x2, + OCS_MISMATCH_DATA_BUF_SIZE = 0x3, + OCS_MISMATCH_RESP_UPIU_SIZE = 0x4, + OCS_PEER_COMM_FAILURE = 0x5, + OCS_ABORTED = 0x6, + OCS_FATAL_ERROR = 0x7, + OCS_DEVICE_FATAL_ERROR = 0x8, + OCS_INVALID_CRYPTO_CONFIG = 0x9, + OCS_GENERAL_CRYPTO_ERROR = 0xA, + OCS_INVALID_COMMAND_STATUS = 0x0F, +}; + +enum { + MASK_OCS = 0x0F, +}; + +/* The maximum length of the data byte count field in the PRDT is 256KB */ +#define PRDT_DATA_BYTE_COUNT_MAX SZ_256K +/* The granularity of the data byte count field in the PRDT is 32-bit */ +#define PRDT_DATA_BYTE_COUNT_PAD 4 + + +struct ufshcd_sg_entry { + __le32 base_addr; + __le32 upper_addr; + __le32 reserved; + __le32 size; +}; + +#define MAX_BUFF 128 +/** + * struct utp_transfer_cmd_desc - UFS Command Descriptor structure + * @command_upiu: Command UPIU Frame address + * @response_upiu: Response UPIU Frame address + * @prd_table: Physical Region Descriptor + */ +struct utp_transfer_cmd_desc { + u8 command_upiu[ALIGNED_UPIU_SIZE]; + u8 response_upiu[ALIGNED_UPIU_SIZE]; + struct ufshcd_sg_entry prd_table[MAX_BUFF]; +}; + +/** + * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD + * @dword0: Descriptor Header DW0 + * @dword1: Descriptor Header DW1 + * @dword2: Descriptor Header DW2 + * @dword3: Descriptor Header DW3 + */ +struct request_desc_header { + __le32 dword_0; + __le32 dword_1; + __le32 dword_2; + __le32 dword_3; +}; + +/** + * struct utp_transfer_req_desc - UTRD structure + * @header: UTRD header DW-0 to DW-3 + * @command_desc_base_addr_lo: UCD base address low DW-4 + * @command_desc_base_addr_hi: UCD base address high DW-5 + * @response_upiu_length: response UPIU length DW-6 + * @response_upiu_offset: response UPIU offset DW-6 + * @prd_table_length: Physical region descriptor length DW-7 + * @prd_table_offset: Physical region descriptor offset DW-7 + */ +struct utp_transfer_req_desc { + /* DW 0-3 */ + struct request_desc_header header; + + /* DW 4-5*/ + __le32 command_desc_base_addr_lo; + __le32 command_desc_base_addr_hi; + + /* DW 6 */ + __le16 response_upiu_length; + __le16 response_upiu_offset; + + /* DW 7 */ + __le16 prd_table_length; + __le16 prd_table_offset; +}; + +/** + * struct utp_upiu_header - UPIU header structure + * @dword_0: UPIU header DW-0 + * @dword_1: UPIU header DW-1 + * @dword_2: UPIU header DW-2 + */ +struct utp_upiu_header { + __be32 dword_0; + __be32 dword_1; + __be32 dword_2; +}; + +/* + * UTMRD structure. + */ +struct utp_task_req_desc { + /* DW 0-3 */ + struct request_desc_header header; + + /* DW 4-11 - Task request UPIU structure */ + struct utp_upiu_header req_header; + __be32 input_param1; + __be32 input_param2; + __be32 input_param3; + __be32 __reserved1[2]; + + /* DW 12-19 - Task Management Response UPIU structure */ + struct utp_upiu_header rsp_header; + __be32 output_param1; + __be32 output_param2; + __be32 __reserved2[3]; +}; + +#endif diff --git a/drivers/ufs/unipro.h b/drivers/ufs/unipro.h index 6df953e6e60..56833602b77 100644 --- a/drivers/ufs/unipro.h +++ b/drivers/ufs/unipro.h @@ -34,6 +34,18 @@ /* * M-RX Configuration Attributes */ +#define RX_HS_G1_SYNC_LENGTH_CAP 0x008B +#define RX_HS_G1_PREP_LENGTH_CAP 0x008C +#define RX_MIN_ACTIVATETIME_CAPABILITY 0x008F +#define RX_HIBERN8TIME_CAPABILITY 0x0092 +#define RX_HS_G2_SYNC_LENGTH_CAP 0x0094 +#define RX_HS_G3_SYNC_LENGTH_CAP 0x0095 +#define RX_HS_G2_PREP_LENGTH_CAP 0x0096 +#define RX_HS_G3_PREP_LENGTH_CAP 0x0097 +#define RX_ADV_GRANULARITY_CAP 0x0098 +#define RX_HIBERN8TIME_CAP 0x0092 +#define RX_ADV_HIBERN8TIME_CAP 0x0099 +#define RX_ADV_MIN_ACTIVATETIME_CAP 0x009A #define RX_MODE 0x00A1 #define RX_HSRATE_SERIES 0x00A2 #define RX_HSGEAR 0x00A3 @@ -42,24 +54,27 @@ #define RX_HS_UNTERMINATED_ENABLE 0x00A6 #define RX_ENTER_HIBERN8 0x00A7 #define RX_BYPASS_8B10B_ENABLE 0x00A8 -#define RX_TERMINATION_FORCE_ENABLE 0x0089 -#define RX_MIN_ACTIVATETIME_CAPABILITY 0x008F -#define RX_HIBERN8TIME_CAPABILITY 0x0092 +#define RX_TERMINATION_FORCE_ENABLE 0x00A9 +#define RXCALCTRL 0x00B4 +#define RXSQCTRL 0x00B5 +#define CFGRXCDR8 0x00BA +#define CFGRXOVR8 0x00BD +#define CFGRXOVR6 0x00BF +#define RXDIRECTCTRL2 0x00C7 +#define CFGRXOVR4 0x00E9 #define RX_REFCLKFREQ 0x00EB #define RX_CFGCLKFREQVAL 0x00EC #define CFGWIDEINLN 0x00F0 -#define CFGRXCDR8 0x00BA #define ENARXDIRECTCFG4 0x00F2 -#define CFGRXOVR8 0x00BD -#define RXDIRECTCTRL2 0x00C7 #define ENARXDIRECTCFG3 0x00F3 -#define RXCALCTRL 0x00B4 #define ENARXDIRECTCFG2 0x00F4 -#define CFGRXOVR4 0x00E9 -#define RXSQCTRL 0x00B5 -#define CFGRXOVR6 0x00BF -#define is_mphy_tx_attr(attr) (attr < RX_MODE) +#define is_mphy_tx_attr(attr) ((attr) < RX_MODE) +#define RX_ADV_FINE_GRAN_STEP(x) ((((x) & 0x3) << 1) | 0x1) +#define SYNC_LEN_FINE(x) ((x) & 0x3F) +#define SYNC_LEN_COARSE(x) ((1 << 6) | ((x) & 0x3F)) +#define PREP_LEN(x) ((x) & 0xF) + #define RX_MIN_ACTIVATETIME_UNIT_US 100 #define HIBERN8TIME_UNIT_US 100 @@ -77,51 +92,54 @@ #define CBPRGPLL2 UNIPRO_CB_OFFSET(0x00F8) #define CBPRGTUNING UNIPRO_CB_OFFSET(0x00FB) -#define UNIPRO_CB_OFFSET(x) (0x8000 | x) +#define UNIPRO_CB_OFFSET(x) (0x8000 | (x)) /* - * PHY Adpater attributes + * PHY Adapter attributes */ -#define PA_ACTIVETXDATALANES 0x1560 -#define PA_ACTIVERXDATALANES 0x1580 -#define PA_TXTRAILINGCLOCKS 0x1564 #define PA_PHY_TYPE 0x1500 #define PA_AVAILTXDATALANES 0x1520 -#define PA_AVAILRXDATALANES 0x1540 -#define PA_MINRXTRAILINGCLOCKS 0x1543 -#define PA_TXPWRSTATUS 0x1567 -#define PA_RXPWRSTATUS 0x1582 -#define PA_TXFORCECLOCK 0x1562 -#define PA_TXPWRMODE 0x1563 -#define PA_LEGACYDPHYESCDL 0x1570 #define PA_MAXTXSPEEDFAST 0x1521 #define PA_MAXTXSPEEDSLOW 0x1522 #define PA_MAXRXSPEEDFAST 0x1541 #define PA_MAXRXSPEEDSLOW 0x1542 #define PA_TXLINKSTARTUPHS 0x1544 +#define PA_AVAILRXDATALANES 0x1540 +#define PA_MINRXTRAILINGCLOCKS 0x1543 #define PA_LOCAL_TX_LCC_ENABLE 0x155E +#define PA_ACTIVETXDATALANES 0x1560 +#define PA_CONNECTEDTXDATALANES 0x1561 +#define PA_TXFORCECLOCK 0x1562 +#define PA_TXPWRMODE 0x1563 +#define PA_TXTRAILINGCLOCKS 0x1564 #define PA_TXSPEEDFAST 0x1565 #define PA_TXSPEEDSLOW 0x1566 -#define PA_REMOTEVERINFO 0x15A0 +#define PA_TXPWRSTATUS 0x1567 #define PA_TXGEAR 0x1568 #define PA_TXTERMINATION 0x1569 #define PA_HSSERIES 0x156A +#define PA_LEGACYDPHYESCDL 0x1570 #define PA_PWRMODE 0x1571 +#define PA_ACTIVERXDATALANES 0x1580 +#define PA_CONNECTEDRXDATALANES 0x1581 +#define PA_RXPWRSTATUS 0x1582 #define PA_RXGEAR 0x1583 #define PA_RXTERMINATION 0x1584 #define PA_MAXRXPWMGEAR 0x1586 #define PA_MAXRXHSGEAR 0x1587 -#define PA_RXHSUNTERMCAP 0x15A5 -#define PA_RXLSTERMCAP 0x15A6 -#define PA_GRANULARITY 0x15AA #define PA_PACPREQTIMEOUT 0x1590 #define PA_PACPREQEOBTIMEOUT 0x1591 +#define PA_REMOTEVERINFO 0x15A0 +#define PA_LOGICALLANEMAP 0x15A1 +#define PA_SLEEPNOCONFIGTIME 0x15A2 +#define PA_STALLNOCONFIGTIME 0x15A3 +#define PA_SAVECONFIGTIME 0x15A4 +#define PA_RXHSUNTERMCAP 0x15A5 +#define PA_RXLSTERMCAP 0x15A6 #define PA_HIBERN8TIME 0x15A7 #define PA_LOCALVERINFO 0x15A9 +#define PA_GRANULARITY 0x15AA #define PA_TACTIVATE 0x15A8 -#define PA_PACPFRAMECOUNT 0x15C0 -#define PA_PACPERRORCOUNT 0x15C1 -#define PA_PHYTESTCONTROL 0x15C2 #define PA_PWRMODEUSERDATA0 0x15B0 #define PA_PWRMODEUSERDATA1 0x15B1 #define PA_PWRMODEUSERDATA2 0x15B2 @@ -134,12 +152,9 @@ #define PA_PWRMODEUSERDATA9 0x15B9 #define PA_PWRMODEUSERDATA10 0x15BA #define PA_PWRMODEUSERDATA11 0x15BB -#define PA_CONNECTEDTXDATALANES 0x1561 -#define PA_CONNECTEDRXDATALANES 0x1581 -#define PA_LOGICALLANEMAP 0x15A1 -#define PA_SLEEPNOCONFIGTIME 0x15A2 -#define PA_STALLNOCONFIGTIME 0x15A3 -#define PA_SAVECONFIGTIME 0x15A4 +#define PA_PACPFRAMECOUNT 0x15C0 +#define PA_PACPERRORCOUNT 0x15C1 +#define PA_PHYTESTCONTROL 0x15C2 #define PA_TXHSADAPTTYPE 0x15D4 /* Adapt type for PA_TXHSADAPTTYPE attribute */ @@ -151,9 +166,9 @@ #define PA_HIBERN8_TIME_UNIT_US 100 /*Other attributes*/ +#define VS_POWERSTATE 0xD083 #define VS_MPHYCFGUPDT 0xD085 #define VS_DEBUGOMC 0xD09E -#define VS_POWERSTATE 0xD083 #define VS_MPHYDISABLE 0xD0C1 #define PA_GRANULARITY_MIN_VAL 1 @@ -163,7 +178,7 @@ #define PA_MAXDATALANES 4 /* PA power modes */ -enum { +enum ufs_pa_pwr_mode { FAST_MODE = 1, SLOW_MODE = 2, FASTAUTO_MODE = 4, @@ -171,8 +186,11 @@ enum { UNCHANGED = 7, }; +#define PWRMODE_MASK 0xF +#define PWRMODE_RX_OFFSET 4 + /* PA TX/RX Frequency Series */ -enum { +enum ufs_hs_gear_rate { PA_HS_MODE_A = 1, PA_HS_MODE_B = 2, }; @@ -193,14 +211,24 @@ enum ufs_hs_gear_tag { UFS_HS_G1, /* HS Gear 1 (default for reset) */ UFS_HS_G2, /* HS Gear 2 */ UFS_HS_G3, /* HS Gear 3 */ + UFS_HS_G4, /* HS Gear 4 */ + UFS_HS_G5 /* HS Gear 5 */ +}; + +enum ufs_lanes { + UFS_LANE_DONT_CHANGE, /* Don't change Lane */ + UFS_LANE_1, /* Lane 1 (default for reset) */ + UFS_LANE_2, /* Lane 2 */ }; enum ufs_unipro_ver { UFS_UNIPRO_VER_RESERVED = 0, UFS_UNIPRO_VER_1_40 = 1, /* UniPro version 1.40 */ UFS_UNIPRO_VER_1_41 = 2, /* UniPro version 1.41 */ - UFS_UNIPRO_VER_1_6 = 3, /* UniPro version 1.6 */ - UFS_UNIPRO_VER_MAX = 4, /* UniPro unsupported version */ + UFS_UNIPRO_VER_1_6 = 3, /* UniPro version 1.6 */ + UFS_UNIPRO_VER_1_61 = 4, /* UniPro version 1.61 */ + UFS_UNIPRO_VER_1_8 = 5, /* UniPro version 1.8 */ + UFS_UNIPRO_VER_MAX = 6, /* UniPro unsupported version */ /* UniPro version field mask in PA_LOCALVERINFO */ UFS_UNIPRO_VER_MASK = 0xF, }; @@ -208,27 +236,27 @@ enum ufs_unipro_ver { /* * Data Link Layer Attributes */ +#define DL_TXPREEMPTIONCAP 0x2000 +#define DL_TC0TXMAXSDUSIZE 0x2001 +#define DL_TC0RXINITCREDITVAL 0x2002 +#define DL_TC1TXMAXSDUSIZE 0x2003 +#define DL_TC1RXINITCREDITVAL 0x2004 +#define DL_TC0TXBUFFERSIZE 0x2005 +#define DL_TC1TXBUFFERSIZE 0x2006 #define DL_TC0TXFCTHRESHOLD 0x2040 #define DL_FC0PROTTIMEOUTVAL 0x2041 #define DL_TC0REPLAYTIMEOUTVAL 0x2042 #define DL_AFC0REQTIMEOUTVAL 0x2043 #define DL_AFC0CREDITTHRESHOLD 0x2044 #define DL_TC0OUTACKTHRESHOLD 0x2045 +#define DL_PEERTC0PRESENT 0x2046 +#define DL_PEERTC0RXINITCREVAL 0x2047 #define DL_TC1TXFCTHRESHOLD 0x2060 #define DL_FC1PROTTIMEOUTVAL 0x2061 #define DL_TC1REPLAYTIMEOUTVAL 0x2062 #define DL_AFC1REQTIMEOUTVAL 0x2063 #define DL_AFC1CREDITTHRESHOLD 0x2064 #define DL_TC1OUTACKTHRESHOLD 0x2065 -#define DL_TXPREEMPTIONCAP 0x2000 -#define DL_TC0TXMAXSDUSIZE 0x2001 -#define DL_TC0RXINITCREDITVAL 0x2002 -#define DL_TC0TXBUFFERSIZE 0x2005 -#define DL_PEERTC0PRESENT 0x2046 -#define DL_PEERTC0RXINITCREVAL 0x2047 -#define DL_TC1TXMAXSDUSIZE 0x2003 -#define DL_TC1RXINITCREDITVAL 0x2004 -#define DL_TC1TXBUFFERSIZE 0x2006 #define DL_PEERTC1PRESENT 0x2066 #define DL_PEERTC1RXINITCREVAL 0x2067 diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index 35b61497d9c..1d5e4afac6c 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -49,6 +49,13 @@ config SPL_USB_CDNS3_HOST Host controller is compliant with XHCI so it will use standard XHCI driver. +config USB_CDNS3_STARFIVE + tristate "Cadence USB3 support on Starfive platforms" + default y if STARFIVE_JH7110 + help + Say 'Y' here if you are building for Starfive platforms + that contain Cadence USB3 controller core. E.g.: JH7110. + config USB_CDNS3_TI tristate "Cadence USB3 support on TI platforms" default USB_CDNS3 diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index d6047856091..dea5316599b 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -4,8 +4,9 @@ cdns3-y := core.o drd.o obj-$(CONFIG_USB_CDNS3) += cdns3.o -cdns3-$(CONFIG_$(XPL_)USB_CDNS3_GADGET) += gadget.o ep0.o +cdns3-$(CONFIG_$(PHASE_)USB_CDNS3_GADGET) += gadget.o ep0.o -cdns3-$(CONFIG_$(XPL_)USB_CDNS3_HOST) += host.o +cdns3-$(CONFIG_$(PHASE_)USB_CDNS3_HOST) += host.o +obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c new file mode 100644 index 00000000000..78ceb831b19 --- /dev/null +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller + * + * Copyright (C) 2024 StarFive Technology Co., Ltd. + * + * Author: Minda Chen <minda.chen@starfivetech.com> + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/usb/otg.h> +#include <malloc.h> +#include <reset.h> +#include <regmap.h> +#include <syscon.h> + +#include "core.h" + +#define USB_STRAP_HOST BIT(17) +#define USB_STRAP_DEVICE BIT(18) +#define USB_STRAP_MASK GENMASK(18, 16) + +#define USB_SUSPENDM_HOST BIT(19) +#define USB_SUSPENDM_MASK BIT(19) + +#define USB_MISC_CFG_MASK GENMASK(23, 20) +#define USB_SUSPENDM_BYPS BIT(20) +#define USB_PLL_EN BIT(22) +#define USB_REFCLK_MODE BIT(23) + +struct cdns_starfive { + struct udevice *dev; + struct regmap *stg_syscon; + struct reset_ctl_bulk resets; + struct clk_bulk clks; + u32 stg_usb_mode; + enum usb_dr_mode mode; +}; + +static void cdns_mode_init(struct cdns_starfive *data, enum usb_dr_mode mode) +{ + unsigned int strap, suspendm; + + regmap_update_bits(data->stg_syscon, data->stg_usb_mode, + USB_MISC_CFG_MASK, + USB_SUSPENDM_BYPS | USB_PLL_EN | USB_REFCLK_MODE); + + switch (mode) { + case USB_DR_MODE_HOST: + strap = USB_STRAP_HOST; + suspendm = USB_SUSPENDM_HOST; + break; + case USB_DR_MODE_PERIPHERAL: + strap = USB_STRAP_DEVICE; + suspendm = 0; + break; + default: + return; + } + + regmap_update_bits(data->stg_syscon, data->stg_usb_mode, + USB_SUSPENDM_MASK | USB_STRAP_MASK, + strap | suspendm); +} + +static void cdns_clk_rst_deinit(struct cdns_starfive *data) +{ + reset_assert_bulk(&data->resets); + clk_disable_bulk(&data->clks); +} + +static int cdns_clk_rst_init(struct cdns_starfive *data) +{ + int ret; + + ret = clk_get_bulk(data->dev, &data->clks); + if (ret) + return ret; + + ret = reset_get_bulk(data->dev, &data->resets); + if (ret) + goto err_clk; + + ret = clk_enable_bulk(&data->clks); + if (ret) { + dev_err(data->dev, "clk enable failed: %d\n", ret); + goto err_en_clk; + } + + ret = reset_deassert_bulk(&data->resets); + if (ret) { + dev_err(data->dev, "reset deassert failed: %d\n", ret); + goto err_reset; + } + + return 0; + +err_reset: + clk_disable_bulk(&data->clks); +err_en_clk: + reset_release_bulk(&data->resets); +err_clk: + clk_release_bulk(&data->clks); + + return ret; +} + +static int cdns_starfive_get_syscon(struct cdns_starfive *data) +{ + struct ofnode_phandle_args phandle; + int ret; + + ret = dev_read_phandle_with_args(data->dev, "starfive,stg-syscon", NULL, 1, 0, + &phandle); + if (ret < 0) { + dev_err(data->dev, "Can't get stg cfg phandle: %d\n", ret); + return ret; + } + + data->stg_syscon = syscon_node_to_regmap(phandle.node); + if (IS_ERR(data->stg_syscon)) { + dev_err(data->dev, "fail to get regmap: %d\n", (int)PTR_ERR(data->stg_syscon)); + return PTR_ERR(data->stg_syscon); + } + + data->stg_usb_mode = phandle.args[0]; + + return 0; +} + +static int cdns_starfive_probe(struct udevice *dev) +{ + struct cdns_starfive *data = dev_get_plat(dev); + enum usb_dr_mode dr_mode; + int ret; + + data->dev = dev; + + ret = cdns_starfive_get_syscon(data); + if (ret) + return ret; + + dr_mode = usb_get_dr_mode(dev_ofnode(dev)); + + data->mode = dr_mode; + ret = cdns_clk_rst_init(data); + if (ret) { + dev_err(data->dev, "clk reset failed: %d\n", ret); + return ret; + } + cdns_mode_init(data, dr_mode); + + return 0; +} + +static int cdns_starfive_remove(struct udevice *dev) +{ + struct cdns_starfive *data = dev_get_plat(dev); + + cdns_clk_rst_deinit(data); + return 0; +} + +static const struct udevice_id cdns_starfive_of_match[] = { + { .compatible = "starfive,jh7110-usb", }, + {}, +}; + +U_BOOT_DRIVER(cdns_starfive) = { + .name = "cdns-starfive", + .id = UCLASS_NOP, + .of_match = cdns_starfive_of_match, + .bind = cdns3_bind, + .probe = cdns_starfive_probe, + .remove = cdns_starfive_remove, + .plat_auto = sizeof(struct cdns_starfive), + .flags = DM_FLAG_OS_PREPARE, +}; diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 4cfd38ec245..4434dc15bec 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -410,6 +410,9 @@ int cdns3_bind(struct udevice *parent) name = ofnode_get_name(node); dr_mode = usb_get_dr_mode(node); + if (dr_mode == USB_DR_MODE_UNKNOWN) + dr_mode = usb_get_dr_mode(dev_ofnode(parent)); + switch (dr_mode) { #if defined(CONFIG_SPL_USB_HOST) || \ (!defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_HOST)) diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 47874fec29e..cbb13342343 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -217,15 +217,19 @@ static int cdns3_init_otg_mode(struct cdns3 *cdns) int cdns3_drd_update_mode(struct cdns3 *cdns) { int ret = 0; + int mode; switch (cdns->dr_mode) { case USB_DR_MODE_PERIPHERAL: + mode = PHY_MODE_USB_DEVICE; ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL); break; case USB_DR_MODE_HOST: + mode = PHY_MODE_USB_HOST; ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST); break; case USB_DR_MODE_OTG: + mode = PHY_MODE_USB_OTG; ret = cdns3_init_otg_mode(cdns); break; default: @@ -234,6 +238,16 @@ int cdns3_drd_update_mode(struct cdns3 *cdns) return -EINVAL; } + ret = generic_phy_set_mode(&cdns->usb2_phy, mode, 0); + if (ret) { + dev_err(cdns->dev, "Set usb 2.0 PHY mode failed %d\n", ret); + return ret; + } + + ret = generic_phy_set_mode(&cdns->usb3_phy, mode, 0); + if (ret) + dev_err(cdns->dev, "Set usb 3.0 PHY mode failed %d\n", ret); + return ret; } diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile index 11cc4657a0f..4c597c166c6 100644 --- a/drivers/usb/common/Makefile +++ b/drivers/usb/common/Makefile @@ -3,7 +3,7 @@ # (C) Copyright 2016 Freescale Semiconductor, Inc. # -obj-$(CONFIG_$(XPL_)DM_USB) += common.o +obj-$(CONFIG_$(PHASE_)DM_USB) += common.o obj-$(CONFIG_USB_ISP1760) += usb_urb.o obj-$(CONFIG_USB_MUSB_HOST) += usb_urb.o obj-$(CONFIG_USB_MUSB_GADGET) += usb_urb.o diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 985206eafe4..a619cd374fb 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -6,11 +6,11 @@ dwc3-y := core.o obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o -obj-$(CONFIG_$(XPL_)USB_DWC3_AM62) += dwc3-am62.o +obj-$(CONFIG_$(PHASE_)USB_DWC3_AM62) += dwc3-am62.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o obj-$(CONFIG_USB_DWC3_MESON_GXL) += dwc3-meson-gxl.o -obj-$(CONFIG_$(XPL_)USB_DWC3_GENERIC) += dwc3-generic.o +obj-$(CONFIG_$(PHASE_)USB_DWC3_GENERIC) += dwc3-generic.o obj-$(CONFIG_USB_DWC3_UNIPHIER) += dwc3-uniphier.o obj-$(CONFIG_USB_DWC3_LAYERSCAPE) += dwc3-layerscape.o obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 477ecd02098..2b01113d54c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1635,7 +1635,7 @@ usb_ep *dwc3_gadget_match_ep(struct usb_gadget *gadget, /* * Special workaround for NXP UUU tool in SPL. * - * The tool excepts the interrupt-in endpoint to be ep1in, + * The tool expects the interrupt-in endpoint to be ep1in, * otherwise it crashes. This is a result of the previous * hard-coded EP setup in drivers/usb/gadget/epautoconf.c * which did special-case EP allocation for SPL builds, diff --git a/drivers/usb/emul/sandbox_keyb.c b/drivers/usb/emul/sandbox_keyb.c index db769883ba3..5ed8c2c799a 100644 --- a/drivers/usb/emul/sandbox_keyb.c +++ b/drivers/usb/emul/sandbox_keyb.c @@ -38,7 +38,7 @@ enum { * */ struct sandbox_keyb_priv { - struct membuff in; + struct membuf in; }; struct sandbox_keyb_plat { @@ -167,7 +167,7 @@ int sandbox_usb_keyb_add_string(struct udevice *dev, struct sandbox_keyb_priv *priv = dev_get_priv(dev); int ret; - ret = membuff_put(&priv->in, scancode, USB_KBD_BOOT_REPORT_SIZE); + ret = membuf_put(&priv->in, scancode, USB_KBD_BOOT_REPORT_SIZE); if (ret != USB_KBD_BOOT_REPORT_SIZE) return -ENOSPC; @@ -194,7 +194,7 @@ static int sandbox_keyb_interrupt(struct udevice *dev, struct usb_device *udev, if (length < USB_KBD_BOOT_REPORT_SIZE) return 0; - membuff_get(&priv->in, buffer, USB_KBD_BOOT_REPORT_SIZE); + membuf_get(&priv->in, buffer, USB_KBD_BOOT_REPORT_SIZE); return 0; } @@ -220,7 +220,7 @@ static int sandbox_keyb_probe(struct udevice *dev) struct sandbox_keyb_priv *priv = dev_get_priv(dev); /* Provide an 80 character keyboard buffer */ - return membuff_new(&priv->in, 80 * USB_KBD_BOOT_REPORT_SIZE); + return membuf_new(&priv->in, 80 * USB_KBD_BOOT_REPORT_SIZE); } static const struct dm_usb_ops sandbox_usb_keyb_ops = { diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index c815764c2bc..46a83141481 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -85,6 +85,7 @@ config USB_GADGET_PRODUCT_NUM default 0x330e if ROCKCHIP_RK3308 default 0x350a if ROCKCHIP_RK3568 default 0x350b if ROCKCHIP_RK3588 + default 0x350c if ROCKCHIP_RK3528 default 0x0 help Product ID of the USB device emulated, reported to the host device. diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 4bda224ff1a..db5f8895a33 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -35,7 +35,3 @@ endif endif obj-$(CONFIG_CI_UDC) += ci_udc.o - -# Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE -# This is really only N900 and USBTTY now. -obj-$(CONFIG_USB_DEVICE) += core.o ep0.o diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index a77037a7094..f9326f0a7e7 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1443,6 +1443,7 @@ static const struct udevice_id usba_udc_ids[] = { { .compatible = "atmel,at91sam9rl-udc" }, { .compatible = "atmel,at91sam9g45-udc" }, { .compatible = "atmel,sama5d3-udc" }, + { .compatible = "microchip,sam9x60-udc" }, {} }; diff --git a/drivers/usb/gadget/core.c b/drivers/usb/gadget/core.c deleted file mode 100644 index bcb1ad3082c..00000000000 --- a/drivers/usb/gadget/core.c +++ /dev/null @@ -1,621 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * Based on - * linux/drivers/usbd/usbd.c.c - USB Device Core Layer - * - * Copyright (c) 2000, 2001, 2002 Lineo - * Copyright (c) 2001 Hewlett Packard - * - * By: - * Stuart Lynne <sl@lineo.com>, - * Tom Rushworth <tbr@lineo.com>, - * Bruce Balden <balden@lineo.com> - */ - -#include <log.h> -#include <malloc.h> -#include <serial.h> -#include <usbdevice.h> - -#define MAX_INTERFACES 2 - -int maxstrings = 20; - -/* Global variables ************************************************************************** */ - -struct usb_string_descriptor **usb_strings; - -int usb_devices; - -extern struct usb_function_driver ep0_driver; - -int registered_functions; -int registered_devices; - -__maybe_unused static char *usbd_device_events[] = { - "DEVICE_UNKNOWN", - "DEVICE_INIT", - "DEVICE_CREATE", - "DEVICE_HUB_CONFIGURED", - "DEVICE_RESET", - "DEVICE_ADDRESS_ASSIGNED", - "DEVICE_CONFIGURED", - "DEVICE_SET_INTERFACE", - "DEVICE_SET_FEATURE", - "DEVICE_CLEAR_FEATURE", - "DEVICE_DE_CONFIGURED", - "DEVICE_BUS_INACTIVE", - "DEVICE_BUS_ACTIVITY", - "DEVICE_POWER_INTERRUPTION", - "DEVICE_HUB_RESET", - "DEVICE_DESTROY", - "DEVICE_FUNCTION_PRIVATE", -}; - -__maybe_unused static char *usbd_device_status[] = { - "USBD_OPENING", - "USBD_OK", - "USBD_SUSPENDED", - "USBD_CLOSING", -}; - -#define USBD_DEVICE_STATUS(x) (((unsigned int)x <= USBD_CLOSING) ? usbd_device_status[x] : "UNKNOWN") - -/* Descriptor support functions ************************************************************** */ - -/** - * usbd_get_string - find and return a string descriptor - * @index: string index to return - * - * Find an indexed string and return a pointer to a it. - */ -struct usb_string_descriptor *usbd_get_string (__u8 index) -{ - if (index >= maxstrings) { - return NULL; - } - return usb_strings[index]; -} - -/* Access to device descriptor functions ***************************************************** */ - -/* * - * usbd_device_configuration_instance - find a configuration instance for this device - * @device: - * @configuration: index to configuration, 0 - N-1 - * - * Get specifed device configuration. Index should be bConfigurationValue-1. - */ -static struct usb_configuration_instance *usbd_device_configuration_instance (struct usb_device_instance *device, - unsigned int port, unsigned int configuration) -{ - if (configuration >= device->configurations) - return NULL; - - return device->configuration_instance_array + configuration; -} - -/* * - * usbd_device_interface_instance - * @device: - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * - * Return the specified interface descriptor for the specified device. - */ -struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *device, int port, int configuration, int interface) -{ - struct usb_configuration_instance *configuration_instance; - - if ((configuration_instance = usbd_device_configuration_instance (device, port, configuration)) == NULL) { - return NULL; - } - if (interface >= configuration_instance->interfaces) { - return NULL; - } - return configuration_instance->interface_instance_array + interface; -} - -/* * - * usbd_device_alternate_descriptor_list - * @device: - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @alternate: alternate setting - * - * Return the specified alternate descriptor for the specified device. - */ -struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *device, int port, int configuration, int interface, int alternate) -{ - struct usb_interface_instance *interface_instance; - - if ((interface_instance = usbd_device_interface_instance (device, port, configuration, interface)) == NULL) { - return NULL; - } - - if (alternate >= interface_instance->alternates) { - return NULL; - } - - return interface_instance->alternates_instance_array + alternate; -} - -/* * - * usbd_device_device_descriptor - * @device: which device - * @configuration: index to configuration, 0 - N-1 - * @port: which port - * - * Return the specified configuration descriptor for the specified device. - */ -struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port) -{ - return (device->device_descriptor); -} - -/** - * usbd_device_configuration_descriptor - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * - * Return the specified configuration descriptor for the specified device. - */ -struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct - usb_device_instance - *device, int port, int configuration) -{ - struct usb_configuration_instance *configuration_instance; - if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) { - return NULL; - } - return (configuration_instance->configuration_descriptor); -} - -/** - * usbd_device_interface_descriptor - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @alternate: alternate setting - * - * Return the specified interface descriptor for the specified device. - */ -struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance - *device, int port, int configuration, int interface, int alternate) -{ - struct usb_interface_instance *interface_instance; - if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) { - return NULL; - } - if ((alternate < 0) || (alternate >= interface_instance->alternates)) { - return NULL; - } - return (interface_instance->alternates_instance_array[alternate].interface_descriptor); -} - -/** - * usbd_device_endpoint_descriptor_index - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @alternate: index setting - * @index: which index - * - * Return the specified endpoint descriptor for the specified device. - */ -struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance - *device, int port, int configuration, int interface, int alternate, int index) -{ - struct usb_alternate_instance *alternate_instance; - - if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { - return NULL; - } - if (index >= alternate_instance->endpoints) { - return NULL; - } - return *(alternate_instance->endpoints_descriptor_array + index); -} - -/** - * usbd_device_endpoint_transfersize - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @index: which index - * - * Return the specified endpoint transfer size; - */ -int usbd_device_endpoint_transfersize (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index) -{ - struct usb_alternate_instance *alternate_instance; - - if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { - return 0; - } - if (index >= alternate_instance->endpoints) { - return 0; - } - return *(alternate_instance->endpoint_transfersize_array + index); -} - -/** - * usbd_device_endpoint_descriptor - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @alternate: alternate setting - * @endpoint: which endpoint - * - * Return the specified endpoint descriptor for the specified device. - */ -struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int endpoint) -{ - struct usb_endpoint_descriptor *endpoint_descriptor; - int i; - - for (i = 0; !(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, configuration, interface, alternate, i)); i++) { - if (endpoint_descriptor->bEndpointAddress == endpoint) { - return endpoint_descriptor; - } - } - return NULL; -} - -/** - * usbd_endpoint_halted - * @device: point to struct usb_device_instance - * @endpoint: endpoint to check - * - * Return non-zero if endpoint is halted. - */ -int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint) -{ - return (device->status == USB_STATUS_HALT); -} - -/** - * usbd_rcv_complete - complete a receive - * @endpoint: - * @len: - * @urb_bad: - * - * Called from rcv interrupt to complete. - */ -void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad) -{ - if (endpoint) { - struct urb *rcv_urb; - - /*usbdbg("len: %d urb: %p\n", len, endpoint->rcv_urb); */ - - /* if we had an urb then update actual_length, dispatch if neccessary */ - if ((rcv_urb = endpoint->rcv_urb)) { - - /*usbdbg("actual: %d buffer: %d\n", */ - /*rcv_urb->actual_length, rcv_urb->buffer_length); */ - - /* check the urb is ok, are we adding data less than the packetsize */ - if (!urb_bad && (len <= endpoint->rcv_packetSize)) { - /*usbdbg("updating actual_length by %d\n",len); */ - - /* increment the received data size */ - rcv_urb->actual_length += len; - - } else { - usberr(" RECV_ERROR actual: %d buffer: %d urb_bad: %d\n", - rcv_urb->actual_length, rcv_urb->buffer_length, urb_bad); - - rcv_urb->actual_length = 0; - rcv_urb->status = RECV_ERROR; - } - } else { - usberr("no rcv_urb!"); - } - } else { - usberr("no endpoint!"); - } - -} - -/** - * usbd_tx_complete - complete a transmit - * @endpoint: - * @resetart: - * - * Called from tx interrupt to complete. - */ -void usbd_tx_complete (struct usb_endpoint_instance *endpoint) -{ - if (endpoint) { - struct urb *tx_urb; - - /* if we have a tx_urb advance or reset, finish if complete */ - if ((tx_urb = endpoint->tx_urb)) { - int sent = endpoint->last; - endpoint->sent += sent; - endpoint->last -= sent; - - if( (endpoint->tx_urb->actual_length - endpoint->sent) <= 0 ) { - tx_urb->actual_length = 0; - endpoint->sent = 0; - endpoint->last = 0; - - /* Remove from active, save for re-use */ - urb_detach(tx_urb); - urb_append(&endpoint->done, tx_urb); - /*usbdbg("done->next %p, tx_urb %p, done %p", */ - /* endpoint->done.next, tx_urb, &endpoint->done); */ - - endpoint->tx_urb = first_urb_detached(&endpoint->tx); - if( endpoint->tx_urb ) { - endpoint->tx_queue--; - usbdbg("got urb from tx list"); - } - if( !endpoint->tx_urb ) { - /*usbdbg("taking urb from done list"); */ - endpoint->tx_urb = first_urb_detached(&endpoint->done); - } - if( !endpoint->tx_urb ) { - usbdbg("allocating new urb for tx_urb"); - endpoint->tx_urb = usbd_alloc_urb(tx_urb->device, endpoint); - } - } - } - } -} - -/* URB linked list functions ***************************************************** */ - -/* - * Initialize an urb_link to be a single element list. - * If the urb_link is being used as a distinguished list head - * the list is empty when the head is the only link in the list. - */ -void urb_link_init (urb_link * ul) -{ - if (ul) { - ul->prev = ul->next = ul; - } -} - -/* - * Detach an urb_link from a list, and set it - * up as a single element list, so no dangling - * pointers can be followed, and so it can be - * joined to another list if so desired. - */ -void urb_detach (struct urb *urb) -{ - if (urb) { - urb_link *ul = &urb->link; - ul->next->prev = ul->prev; - ul->prev->next = ul->next; - urb_link_init (ul); - } -} - -/* - * Return the first urb_link in a list with a distinguished - * head "hd", or NULL if the list is empty. This will also - * work as a predicate, returning NULL if empty, and non-NULL - * otherwise. - */ -urb_link *first_urb_link (urb_link * hd) -{ - urb_link *nx; - if (NULL != hd && NULL != (nx = hd->next) && nx != hd) { - /* There is at least one element in the list */ - /* (besides the distinguished head). */ - return (nx); - } - /* The list is empty */ - return (NULL); -} - -/* - * Return the first urb in a list with a distinguished - * head "hd", or NULL if the list is empty. - */ -struct urb *first_urb (urb_link * hd) -{ - urb_link *nx; - if (NULL == (nx = first_urb_link (hd))) { - /* The list is empty */ - return (NULL); - } - return (p2surround (struct urb, link, nx)); -} - -/* - * Detach and return the first urb in a list with a distinguished - * head "hd", or NULL if the list is empty. - * - */ -struct urb *first_urb_detached (urb_link * hd) -{ - struct urb *urb; - if ((urb = first_urb (hd))) { - urb_detach (urb); - } - return urb; -} - -/* - * Append an urb_link (or a whole list of - * urb_links) to the tail of another list - * of urb_links. - */ -void urb_append (urb_link * hd, struct urb *urb) -{ - if (hd && urb) { - urb_link *new = &urb->link; - - /* This allows the new urb to be a list of urbs, */ - /* with new pointing at the first, but the link */ - /* must be initialized. */ - /* Order is important here... */ - urb_link *pul = hd->prev; - new->prev->next = hd; - hd->prev = new->prev; - new->prev = pul; - pul->next = new; - } -} - -/* URB create/destroy functions ***************************************************** */ - -/** - * usbd_alloc_urb - allocate an URB appropriate for specified endpoint - * @device: device instance - * @endpoint: endpoint - * - * Allocate an urb structure. The usb device urb structure is used to - * contain all data associated with a transfer, including a setup packet for - * control transfers. - * - * NOTE: endpoint_address MUST contain a direction flag. - */ -struct urb *usbd_alloc_urb (struct usb_device_instance *device, - struct usb_endpoint_instance *endpoint) -{ - struct urb *urb; - - if (!(urb = (struct urb *) malloc (sizeof (struct urb)))) { - usberr (" F A T A L: malloc(%zu) FAILED!!!!", - sizeof (struct urb)); - return NULL; - } - - /* Fill in known fields */ - memset (urb, 0, sizeof (struct urb)); - urb->endpoint = endpoint; - urb->device = device; - urb->buffer = (u8 *) urb->buffer_data; - urb->buffer_length = sizeof (urb->buffer_data); - - urb_link_init (&urb->link); - - return urb; -} - -/** - * usbd_dealloc_urb - deallocate an URB and associated buffer - * @urb: pointer to an urb structure - * - * Deallocate an urb structure and associated data. - */ -void usbd_dealloc_urb (struct urb *urb) -{ - if (urb) { - free (urb); - } -} - -/* Event signaling functions ***************************************************** */ - -/** - * usbd_device_event - called to respond to various usb events - * @device: pointer to struct device - * @event: event to respond to - * - * Used by a Bus driver to indicate an event. - */ -void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data) -{ - usb_device_state_t state; - - if (!device || !device->bus) { - usberr("(%p,%d) NULL device or device->bus", device, event); - return; - } - - state = device->device_state; - - usbinfo("%s", usbd_device_events[event]); - - switch (event) { - case DEVICE_UNKNOWN: - break; - case DEVICE_INIT: - device->device_state = STATE_INIT; - break; - - case DEVICE_CREATE: - device->device_state = STATE_ATTACHED; - break; - - case DEVICE_HUB_CONFIGURED: - device->device_state = STATE_POWERED; - break; - - case DEVICE_RESET: - device->device_state = STATE_DEFAULT; - device->address = 0; - break; - - case DEVICE_ADDRESS_ASSIGNED: - device->device_state = STATE_ADDRESSED; - break; - - case DEVICE_CONFIGURED: - device->device_state = STATE_CONFIGURED; - break; - - case DEVICE_DE_CONFIGURED: - device->device_state = STATE_ADDRESSED; - break; - - case DEVICE_BUS_INACTIVE: - if (device->status != USBD_CLOSING) { - device->status = USBD_SUSPENDED; - } - break; - case DEVICE_BUS_ACTIVITY: - if (device->status != USBD_CLOSING) { - device->status = USBD_OK; - } - break; - - case DEVICE_SET_INTERFACE: - break; - case DEVICE_SET_FEATURE: - break; - case DEVICE_CLEAR_FEATURE: - break; - - case DEVICE_POWER_INTERRUPTION: - device->device_state = STATE_POWERED; - break; - case DEVICE_HUB_RESET: - device->device_state = STATE_ATTACHED; - break; - case DEVICE_DESTROY: - device->device_state = STATE_UNKNOWN; - break; - - case DEVICE_FUNCTION_PRIVATE: - break; - - default: - usbdbg("event %d - not handled",event); - break; - } - debug("%s event: %d oldstate: %d newstate: %d status: %d address: %d", - device->name, event, state, - device->device_state, device->status, device->address); - - /* tell the bus interface driver */ - if( device->event ) { - /* usbdbg("calling device->event"); */ - device->event(device, event, data); - } -} diff --git a/drivers/usb/gadget/ep0.c b/drivers/usb/gadget/ep0.c deleted file mode 100644 index 8c7fc17c2ea..00000000000 --- a/drivers/usb/gadget/ep0.c +++ /dev/null @@ -1,619 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * (C) Copyright 2006 - * Bryan O'Donoghue, deckard@CodeHermit.ie - * - * Based on - * linux/drivers/usbd/ep0.c - * - * Copyright (c) 2000, 2001, 2002 Lineo - * Copyright (c) 2001 Hewlett Packard - * - * By: - * Stuart Lynne <sl@lineo.com>, - * Tom Rushworth <tbr@lineo.com>, - * Bruce Balden <balden@lineo.com> - */ - -/* - * This is the builtin ep0 control function. It implements all required functionality - * for responding to control requests (SETUP packets). - * - * XXX - * - * Currently we do not pass any SETUP packets (or other) to the configured - * function driver. This may need to change. - * - * XXX - * - * As alluded to above, a simple callback cdc_recv_setup has been implemented - * in the usb_device data structure to facilicate passing - * Common Device Class packets to a function driver. - * - * XXX - */ - -#include <serial.h> -#include <usbdevice.h> - -#if 0 -#define dbg_ep0(lvl,fmt,args...) serial_printf("[%s] %s:%d: "fmt"\n",__FILE__,__FUNCTION__,__LINE__,##args) -#else -#define dbg_ep0(lvl,fmt,args...) -#endif - -__maybe_unused static char *usbd_device_descriptors[] = { - "UNKNOWN", /* 0 */ - "DEVICE", /* 1 */ - "CONFIG", /* 2 */ - "STRING", /* 3 */ - "INTERFACE", /* 4 */ - "ENDPOINT", /* 5 */ - "DEVICE QUALIFIER", /* 6 */ - "OTHER SPEED", /* 7 */ - "INTERFACE POWER", /* 8 */ -}; - -#define USBD_DEVICE_DESCRIPTORS(x) (((unsigned int)x <= USB_DESCRIPTOR_TYPE_INTERFACE_POWER) ? \ - usbd_device_descriptors[x] : "UNKNOWN") - -__maybe_unused static char *usbd_device_states[] = { - "STATE_INIT", - "STATE_CREATED", - "STATE_ATTACHED", - "STATE_POWERED", - "STATE_DEFAULT", - "STATE_ADDRESSED", - "STATE_CONFIGURED", - "STATE_UNKNOWN", -}; - -#define USBD_DEVICE_STATE(x) (((unsigned int)x <= STATE_UNKNOWN) ? usbd_device_states[x] : "UNKNOWN") - -__maybe_unused static char *usbd_device_requests[] = { - "GET STATUS", /* 0 */ - "CLEAR FEATURE", /* 1 */ - "RESERVED", /* 2 */ - "SET FEATURE", /* 3 */ - "RESERVED", /* 4 */ - "SET ADDRESS", /* 5 */ - "GET DESCRIPTOR", /* 6 */ - "SET DESCRIPTOR", /* 7 */ - "GET CONFIGURATION", /* 8 */ - "SET CONFIGURATION", /* 9 */ - "GET INTERFACE", /* 10 */ - "SET INTERFACE", /* 11 */ - "SYNC FRAME", /* 12 */ -}; - -#define USBD_DEVICE_REQUESTS(x) (((unsigned int)x <= USB_REQ_SYNCH_FRAME) ? usbd_device_requests[x] : "UNKNOWN") - -/* EP0 Configuration Set ********************************************************************* */ - -/** - * ep0_get_status - fill in URB data with appropriate status - * @device: - * @urb: - * @index: - * @requesttype: - * - */ -static int ep0_get_status (struct usb_device_instance *device, - struct urb *urb, int index, int requesttype) -{ - char *cp; - - urb->actual_length = 2; - cp = (char*)urb->buffer; - cp[0] = cp[1] = 0; - - switch (requesttype) { - case USB_REQ_RECIPIENT_DEVICE: - cp[0] = USB_STATUS_SELFPOWERED; - break; - case USB_REQ_RECIPIENT_INTERFACE: - break; - case USB_REQ_RECIPIENT_ENDPOINT: - cp[0] = usbd_endpoint_halted (device, index); - break; - case USB_REQ_RECIPIENT_OTHER: - urb->actual_length = 0; - default: - break; - } - dbg_ep0 (2, "%02x %02x", cp[0], cp[1]); - return 0; -} - -/** - * ep0_get_one - * @device: - * @urb: - * @result: - * - * Set a single byte value in the urb send buffer. Return non-zero to signal - * a request error. - */ -static int ep0_get_one (struct usb_device_instance *device, struct urb *urb, - __u8 result) -{ - urb->actual_length = 1; /* XXX 2? */ - ((char *) urb->buffer)[0] = result; - return 0; -} - -/** - * copy_config - * @urb: pointer to urb - * @data: pointer to configuration data - * @length: length of data - * - * Copy configuration data to urb transfer buffer if there is room for it. - */ -void copy_config (struct urb *urb, void *data, int max_length, - int max_buf) -{ - int available; - int length; - - /*dbg_ep0(3, "-> actual: %d buf: %d max_buf: %d max_length: %d data: %p", */ - /* urb->actual_length, urb->buffer_length, max_buf, max_length, data); */ - - if (!data) { - dbg_ep0 (1, "data is NULL"); - return; - } - length = max_length; - - if (length > max_length) { - dbg_ep0 (1, "length: %d >= max_length: %d", length, - max_length); - return; - } - /*dbg_ep0(1, " actual: %d buf: %d max_buf: %d max_length: %d length: %d", */ - /* urb->actual_length, urb->buffer_length, max_buf, max_length, length); */ - - if ((available = - /*urb->buffer_length */ max_buf - urb->actual_length) <= 0) { - return; - } - /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */ - /* urb->actual_length, urb->buffer_length, max_buf, length, available); */ - - if (length > available) { - length = available; - } - /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */ - /* urb->actual_length, urb->buffer_length, max_buf, length, available); */ - - memcpy (urb->buffer + urb->actual_length, data, length); - urb->actual_length += length; - - dbg_ep0 (3, - "copy_config: <- actual: %d buf: %d max_buf: %d max_length: %d available: %d", - urb->actual_length, urb->buffer_length, max_buf, max_length, - available); -} - -/** - * ep0_get_descriptor - * @device: - * @urb: - * @max: - * @descriptor_type: - * @index: - * - * Called by ep0_rx_process for a get descriptor device command. Determine what - * descriptor is being requested, copy to send buffer. Return zero if ok to send, - * return non-zero to signal a request error. - */ -static int ep0_get_descriptor (struct usb_device_instance *device, - struct urb *urb, int max, int descriptor_type, - int index) -{ - int port = 0; /* XXX compound device */ - - /*dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index); */ - - if (!urb || !urb->buffer || !urb->buffer_length - || (urb->buffer_length < 255)) { - dbg_ep0 (2, "invalid urb %p", urb); - return -1L; - } - - /* setup tx urb */ - urb->actual_length = 0; - - dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type)); - - switch (descriptor_type) { - case USB_DESCRIPTOR_TYPE_DEVICE: - { - struct usb_device_descriptor *device_descriptor; - if (! - (device_descriptor = - usbd_device_device_descriptor (device, port))) { - return -1; - } - /* copy descriptor for this device */ - copy_config (urb, device_descriptor, - sizeof (struct usb_device_descriptor), - max); - - /* correct the correct control endpoint 0 max packet size into the descriptor */ - device_descriptor = - (struct usb_device_descriptor *) urb->buffer; - - } - dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length); - break; - - case USB_DESCRIPTOR_TYPE_CONFIGURATION: - { - struct usb_configuration_descriptor - *configuration_descriptor; - struct usb_device_descriptor *device_descriptor; - if (! - (device_descriptor = - usbd_device_device_descriptor (device, port))) { - return -1; - } - /*dbg_ep0(2, "%d %d", index, device_descriptor->bNumConfigurations); */ - if (index >= device_descriptor->bNumConfigurations) { - dbg_ep0 (0, "index too large: %d >= %d", index, - device_descriptor-> - bNumConfigurations); - return -1; - } - - if (! - (configuration_descriptor = - usbd_device_configuration_descriptor (device, - port, - index))) { - dbg_ep0 (0, - "usbd_device_configuration_descriptor failed: %d", - index); - return -1; - } - dbg_ep0(0, "attempt to copy %d bytes to urb\n",cpu_to_le16(configuration_descriptor->wTotalLength)); - copy_config (urb, configuration_descriptor, - - cpu_to_le16(configuration_descriptor->wTotalLength), - max); - } - - break; - - case USB_DESCRIPTOR_TYPE_STRING: - { - struct usb_string_descriptor *string_descriptor; - if (!(string_descriptor = usbd_get_string (index))) { - dbg_ep0(0, "Invalid string index %d\n", index); - return -1; - } - dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength); - copy_config (urb, string_descriptor, string_descriptor->bLength, max); - } - break; - case USB_DESCRIPTOR_TYPE_INTERFACE: - dbg_ep0(2, "USB_DESCRIPTOR_TYPE_INTERFACE - error not implemented\n"); - return -1; - case USB_DESCRIPTOR_TYPE_ENDPOINT: - dbg_ep0(2, "USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n"); - return -1; - case USB_DESCRIPTOR_TYPE_HID: - { - dbg_ep0(2, "USB_DESCRIPTOR_TYPE_HID - error not implemented\n"); - return -1; /* unsupported at this time */ -#if 0 - int bNumInterface = - le16_to_cpu (urb->device_request.wIndex); - int bAlternateSetting = 0; - int class = 0; - struct usb_class_descriptor *class_descriptor; - - if (!(class_descriptor = - usbd_device_class_descriptor_index (device, - port, 0, - bNumInterface, - bAlternateSetting, - class)) - || class_descriptor->descriptor.hid.bDescriptorType != USB_DT_HID) { - dbg_ep0 (3, "[%d] interface is not HID", - bNumInterface); - return -1; - } - /* copy descriptor for this class */ - copy_config (urb, class_descriptor, - class_descriptor->descriptor.hid.bLength, - max); -#endif - } - break; - case USB_DESCRIPTOR_TYPE_REPORT: - { - dbg_ep0(2, "USB_DESCRIPTOR_TYPE_REPORT - error not implemented\n"); - return -1; /* unsupported at this time */ -#if 0 - int bNumInterface = - le16_to_cpu (urb->device_request.wIndex); - int bAlternateSetting = 0; - int class = 0; - struct usb_class_report_descriptor *report_descriptor; - - if (!(report_descriptor = - usbd_device_class_report_descriptor_index - (device, port, 0, bNumInterface, - bAlternateSetting, class)) - || report_descriptor->bDescriptorType != - USB_DT_REPORT) { - dbg_ep0 (3, "[%d] descriptor is not REPORT", - bNumInterface); - return -1; - } - /* copy report descriptor for this class */ - /*copy_config(urb, &report_descriptor->bData[0], report_descriptor->wLength, max); */ - if (max - urb->actual_length > 0) { - int length = - min(report_descriptor->wLength, - max - urb->actual_length); - memcpy (urb->buffer + urb->actual_length, - &report_descriptor->bData[0], length); - urb->actual_length += length; - } -#endif - } - break; - case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER: - return -1; - - default: - return -1; - } - - dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d tx_packetSize: %2d", - urb->buffer, urb->buffer_length, urb->actual_length, - device->bus->endpoint_array[0].tx_packetSize); -/* - if ((urb->actual_length < max) && !(urb->actual_length % device->bus->endpoint_array[0].tx_packetSize)) { - dbg_ep0(0, "adding null byte"); - urb->buffer[urb->actual_length++] = 0; - dbg_ep0(0, "urb: buffer_length: %2d actual_length: %2d packet size: %2d", - urb->buffer_length, urb->actual_length device->bus->endpoint_array[0].tx_packetSize); - } -*/ - return 0; - -} - -/** - * ep0_recv_setup - called to indicate URB has been received - * @urb: pointer to struct urb - * - * Check if this is a setup packet, process the device request, put results - * back into the urb and return zero or non-zero to indicate success (DATA) - * or failure (STALL). - * - */ -int ep0_recv_setup (struct urb *urb) -{ - /*struct usb_device_request *request = urb->buffer; */ - /*struct usb_device_instance *device = urb->device; */ - - struct usb_device_request *request; - struct usb_device_instance *device; - int address; - - dbg_ep0 (0, "entering ep0_recv_setup()"); - if (!urb || !urb->device) { - dbg_ep0 (3, "invalid URB %p", urb); - return -1; - } - - request = &urb->device_request; - device = urb->device; - - dbg_ep0 (3, "urb: %p device: %p", urb, urb->device); - - /*dbg_ep0(2, "- - - - - - - - - -"); */ - - dbg_ep0 (2, - "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %s", - request->bmRequestType, request->bRequest, - le16_to_cpu (request->wValue), le16_to_cpu (request->wIndex), - le16_to_cpu (request->wLength), - USBD_DEVICE_REQUESTS (request->bRequest)); - - /* handle USB Standard Request (c.f. USB Spec table 9-2) */ - if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) { - if(device->device_state <= STATE_CONFIGURED){ - /* Attempt to handle a CDC specific request if we are - * in the configured state. - */ - return device->cdc_recv_setup(request,urb); - } - dbg_ep0 (1, "non standard request: %x", - request->bmRequestType & USB_REQ_TYPE_MASK); - return -1; /* Stall here */ - } - - switch (device->device_state) { - case STATE_CREATED: - case STATE_ATTACHED: - case STATE_POWERED: - /* It actually is important to allow requests in these states, - * Windows will request descriptors before assigning an - * address to the client. - */ - - /*dbg_ep0 (1, "request %s not allowed in this state: %s", */ - /* USBD_DEVICE_REQUESTS(request->bRequest), */ - /* usbd_device_states[device->device_state]); */ - /*return -1; */ - break; - - case STATE_INIT: - case STATE_DEFAULT: - switch (request->bRequest) { - case USB_REQ_GET_STATUS: - case USB_REQ_GET_INTERFACE: - case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - case USB_REQ_SET_DESCRIPTOR: - /* case USB_REQ_SET_CONFIGURATION: */ - case USB_REQ_SET_INTERFACE: - dbg_ep0 (1, - "request %s not allowed in DEFAULT state: %s", - USBD_DEVICE_REQUESTS (request->bRequest), - usbd_device_states[device->device_state]); - return -1; - - case USB_REQ_SET_CONFIGURATION: - case USB_REQ_SET_ADDRESS: - case USB_REQ_GET_DESCRIPTOR: - case USB_REQ_GET_CONFIGURATION: - break; - } - case STATE_ADDRESSED: - case STATE_CONFIGURED: - break; - case STATE_UNKNOWN: - dbg_ep0 (1, "request %s not allowed in UNKNOWN state: %s", - USBD_DEVICE_REQUESTS (request->bRequest), - usbd_device_states[device->device_state]); - return -1; - } - - /* handle all requests that return data (direction bit set on bm RequestType) */ - if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) { - - dbg_ep0 (3, "Device-to-Host"); - - switch (request->bRequest) { - - case USB_REQ_GET_STATUS: - return ep0_get_status (device, urb, request->wIndex, - request->bmRequestType & - USB_REQ_RECIPIENT_MASK); - - case USB_REQ_GET_DESCRIPTOR: - return ep0_get_descriptor (device, urb, - le16_to_cpu (request->wLength), - le16_to_cpu (request->wValue) >> 8, - le16_to_cpu (request->wValue) & 0xff); - - case USB_REQ_GET_CONFIGURATION: - dbg_ep0(2, "get config %d\n", device->configuration); - return ep0_get_one (device, urb, - device->configuration); - - case USB_REQ_GET_INTERFACE: - return ep0_get_one (device, urb, device->alternate); - - case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ - return -1; - - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - case USB_REQ_SET_ADDRESS: - case USB_REQ_SET_DESCRIPTOR: - case USB_REQ_SET_CONFIGURATION: - case USB_REQ_SET_INTERFACE: - return -1; - } - } - /* handle the requests that do not return data */ - else { - - /*dbg_ep0(3, "Host-to-Device"); */ - switch (request->bRequest) { - - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - dbg_ep0 (0, "Host-to-Device"); - switch (request-> - bmRequestType & USB_REQ_RECIPIENT_MASK) { - case USB_REQ_RECIPIENT_DEVICE: - /* XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here */ - /* XXX fall through for now as we do not support either */ - case USB_REQ_RECIPIENT_INTERFACE: - case USB_REQ_RECIPIENT_OTHER: - dbg_ep0 (0, "request %s not", - USBD_DEVICE_REQUESTS (request->bRequest)); - default: - return -1; - - case USB_REQ_RECIPIENT_ENDPOINT: - dbg_ep0 (0, "ENDPOINT: %x", le16_to_cpu (request->wValue)); - if (le16_to_cpu (request->wValue) == USB_ENDPOINT_HALT) { - /*return usbd_device_feature (device, le16_to_cpu (request->wIndex), */ - /* request->bRequest == USB_REQ_SET_FEATURE); */ - /* NEED TO IMPLEMENT THIS!!! */ - return -1; - } else { - dbg_ep0 (1, "request %s bad wValue: %04x", - USBD_DEVICE_REQUESTS - (request->bRequest), - le16_to_cpu (request->wValue)); - return -1; - } - } - - case USB_REQ_SET_ADDRESS: - /* check if this is a re-address, reset first if it is (this shouldn't be possible) */ - if (device->device_state != STATE_DEFAULT) { - dbg_ep0 (1, "set_address: %02x state: %s", - le16_to_cpu (request->wValue), - usbd_device_states[device->device_state]); - return -1; - } - address = le16_to_cpu (request->wValue); - if ((address & 0x7f) != address) { - dbg_ep0 (1, "invalid address %04x %04x", - address, address & 0x7f); - return -1; - } - device->address = address; - - /*dbg_ep0(2, "address: %d %d %d", */ - /* request->wValue, le16_to_cpu(request->wValue), device->address); */ - - return 0; - - case USB_REQ_SET_DESCRIPTOR: /* XXX should we support this? */ - dbg_ep0 (0, "set descriptor: NOT SUPPORTED"); - return -1; - - case USB_REQ_SET_CONFIGURATION: - /* c.f. 9.4.7 - the top half of wValue is reserved */ - device->configuration = le16_to_cpu(request->wValue) & 0xff; - - /* reset interface and alternate settings */ - device->interface = device->alternate = 0; - - /*dbg_ep0(2, "set configuration: %d", device->configuration); */ - /*dbg_ep0(2, "DEVICE_CONFIGURED.. event?\n"); */ - return 0; - - case USB_REQ_SET_INTERFACE: - device->interface = le16_to_cpu (request->wIndex); - device->alternate = le16_to_cpu (request->wValue); - /*dbg_ep0(2, "set interface: %d alternate: %d", device->interface, device->alternate); */ - dbg_ep0(2, "DEVICE_SET_INTERFACE.. event?\n"); - return 0; - - case USB_REQ_GET_STATUS: - case USB_REQ_GET_DESCRIPTOR: - case USB_REQ_GET_CONFIGURATION: - case USB_REQ_GET_INTERFACE: - case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ - return -1; - } - } - return -1; -} diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index f18c6a0a761..8f7256069f5 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -238,18 +238,21 @@ static int acm_bind(struct usb_configuration *c, struct usb_function *f) return -ENODEV; f_acm->ep_in = ep; + ep->driver_data = c->cdev; /* claim */ ep = usb_ep_autoconfig(gadget, &acm_fs_out_desc); if (!ep) return -ENODEV; f_acm->ep_out = ep; + ep->driver_data = c->cdev; /* claim */ ep = usb_ep_autoconfig(gadget, &acm_fs_notify_desc); if (!ep) return -ENODEV; f_acm->ep_notify = ep; + ep->driver_data = c->cdev; /* claim */ if (gadget_is_dualspeed(gadget)) { /* Assume endpoint addresses are the same for both speeds */ @@ -660,6 +663,7 @@ static int acm_stdio_stop(struct stdio_dev *dev) { g_dnl_unregister(); g_dnl_clear_detach(); + dev->priv = NULL; return 0; } diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index d3fc4acb401..71dc58da3f0 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -284,7 +284,6 @@ static const char fsg_string_interface[] = "Mass Storage"; #define kthread_create(...) __builtin_return_address(0) #define wait_for_completion(...) do {} while (0) -struct kref {int x; }; struct completion {int x; }; struct fsg_dev; @@ -345,8 +344,6 @@ struct fsg_common { /* Vendor (8 chars), product (16 chars), release (4 * hexadecimal digits) and NUL byte */ char inquiry_string[8 + 16 + 4 + 1]; - - struct kref ref; }; struct fsg_config { @@ -2436,7 +2433,7 @@ int fsg_main_thread(void *common_) return 0; } -static void fsg_common_release(struct kref *ref); +static void fsg_common_release(struct fsg_common *common); static struct fsg_common *fsg_common_init(struct fsg_common *common, struct usb_composite_dev *cdev) @@ -2548,16 +2545,12 @@ error_luns: common->nluns = i + 1; error_release: common->state = FSG_STATE_TERMINATED; /* The thread is dead */ - /* Call fsg_common_release() directly, ref might be not - * initialised */ - fsg_common_release(&common->ref); + fsg_common_release(common); return ERR_PTR(rc); } -static void fsg_common_release(struct kref *ref) +static void fsg_common_release(struct fsg_common *common) { - struct fsg_common *common = container_of(ref, struct fsg_common, ref); - /* If the thread isn't already dead, tell it to exit now */ if (common->state != FSG_STATE_TERMINATED) { raise_exception(common, FSG_STATE_EXIT); @@ -2571,8 +2564,6 @@ static void fsg_common_release(struct kref *ref) /* In error recovery common->nluns may be zero. */ for (; i; --i, ++lun) fsg_lun_close(lun); - - kfree(common->luns); } { @@ -2648,6 +2639,7 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); } + fsg_common_release(fsg->common); free(fsg->function.descriptors); free(fsg->function.hs_descriptors); kfree(fsg); @@ -2751,6 +2743,8 @@ int fsg_add(struct usb_configuration *c) struct fsg_common *fsg_common; fsg_common = fsg_common_init(NULL, c->cdev); + if (IS_ERR(fsg_common)) + return PTR_ERR(fsg_common); fsg_common->vendor_name = 0; fsg_common->product_name = 0; diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c index 54372118348..540b9f88237 100644 --- a/drivers/usb/gadget/f_thor.c +++ b/drivers/usb/gadget/f_thor.c @@ -138,6 +138,7 @@ static int process_rqt_cmd(struct udevice *udc, const struct rqt_box *rqt) case RQT_CMD_POWEROFF: case RQT_CMD_EFSCLEAR: send_rsp(udc, rsp); + fallthrough; default: printf("Command not supported -> cmd: %d\n", rqt->rqt_data); return -EINVAL; diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index 4b6a8fdfeee..56b9b123fa1 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -2,9 +2,9 @@ # # USB peripheral controller drivers -ifndef CONFIG_$(XPL_)DM_USB_GADGET +ifndef CONFIG_$(PHASE_)DM_USB_GADGET obj-$(CONFIG_USB_DWC3_GADGET) += udc-core.o endif -obj-$(CONFIG_$(XPL_)DM_USB_GADGET) += udc-core.o +obj-$(CONFIG_$(PHASE_)DM_USB_GADGET) += udc-core.o obj-y += udc-uclass.o diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 902d68d0378..ef4ce62a680 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -3,7 +3,7 @@ # (C) Copyright 2000-2007 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. -ifdef CONFIG_$(XPL_)DM_USB +ifdef CONFIG_$(PHASE_)DM_USB obj-y += usb-uclass.o obj-$(CONFIG_SANDBOX) += usb-sandbox.o endif diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index c020d13c43d..234a6f3645d 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1361,7 +1361,8 @@ pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len, wLength)); databuf = root_hub_str_index1; OK(len); - } + } + fallthrough; default: stat = USB_ST_STALLED; } diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index bfec303e7af..7247245a702 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -9,6 +9,7 @@ #define LOG_CATEGORY UCLASS_USB #include <bootdev.h> +#include <uthread.h> #include <dm.h> #include <errno.h> #include <log.h> @@ -17,6 +18,7 @@ #include <dm/device-internal.h> #include <dm/lists.h> #include <dm/uclass-internal.h> +#include <time.h> static bool asynch_allowed; @@ -172,6 +174,10 @@ int usb_get_max_xfer_size(struct usb_device *udev, size_t *size) return ops->get_max_xfer_size(bus, size); } +#if CONFIG_IS_ENABLED(UTHREAD) +static struct uthread_mutex mutex = UTHREAD_MUTEX_INITIALIZER; +#endif + int usb_stop(void) { struct udevice *bus; @@ -180,10 +186,14 @@ int usb_stop(void) struct usb_uclass_priv *uc_priv; int err = 0, ret; + uthread_mutex_lock(&mutex); + /* De-activate any devices that have been activated */ ret = uclass_get(UCLASS_USB, &uc); - if (ret) + if (ret) { + uthread_mutex_unlock(&mutex); return ret; + } uc_priv = uclass_get_priv(uc); @@ -218,28 +228,23 @@ int usb_stop(void) uc_priv->companion_device_count = 0; usb_started = 0; + uthread_mutex_unlock(&mutex); + return err; } -static void usb_scan_bus(struct udevice *bus, bool recurse) +static void _usb_scan_bus(void *arg) { + struct udevice *bus = (struct udevice *)arg; struct usb_bus_priv *priv; struct udevice *dev; int ret; priv = dev_get_uclass_priv(bus); - assert(recurse); /* TODO: Support non-recusive */ - - printf("scanning bus %s for devices... ", bus->name); - debug("\n"); ret = usb_scan_device(bus, 0, USB_SPEED_FULL, &dev); if (ret) - printf("failed, error %d\n", ret); - else if (priv->next_addr == 0) - printf("No USB Device found\n"); - else - printf("%d USB Device(s) found\n", priv->next_addr); + printf("Scanning bus %s failed, error %d\n", bus->name, ret); } static void remove_inactive_children(struct uclass *uc, struct udevice *bus) @@ -287,64 +292,127 @@ static int usb_probe_companion(struct udevice *bus) return 0; } +static void _usb_init_bus(void *arg) +{ + struct udevice *bus = (struct udevice *)arg; + int ret; + + /* init low_level USB */ + + /* + * For Sandbox, we need scan the device tree each time when we + * start the USB stack, in order to re-create the emulated USB + * devices and bind drivers for them before we actually do the + * driver probe. + * + * For USB onboard HUB, we need to do some non-trivial init + * like enabling a power regulator, before enumeration. + */ + if (IS_ENABLED(CONFIG_SANDBOX) || + IS_ENABLED(CONFIG_USB_ONBOARD_HUB)) { + ret = dm_scan_fdt_dev(bus); + if (ret) { + printf("Bus %s: USB device scan from fdt failed (%d)\n", + bus->name, ret); + return; + } + } + + ret = device_probe(bus); + if (ret == -ENODEV) { /* No such device. */ + printf("Bus %s: Port not available.\n", bus->name); + return; + } + + if (ret) { /* Other error. */ + printf("Bus %s: probe failed, error %d\n", bus->name, ret); + return; + } + + usb_probe_companion(bus); +} + +static int nthr; +static int grp_id; + +static void usb_init_bus(struct udevice *bus) +{ + if (!grp_id) + grp_id = uthread_grp_new_id(); + if (!uthread_create(NULL, _usb_init_bus, (void *)bus, 0, grp_id)) + nthr++; +} + +static void usb_scan_bus(struct udevice *bus, bool recurse) +{ + if (!grp_id) + grp_id = uthread_grp_new_id(); + if (!uthread_create(NULL, _usb_scan_bus, (void *)bus, 0, grp_id)) + nthr++; +} + +static void usb_report_devices(struct uclass *uc) +{ + struct usb_bus_priv *priv; + struct udevice *bus; + + uclass_foreach_dev(bus, uc) { + if (!device_active(bus)) + continue; + priv = dev_get_uclass_priv(bus); + printf("Bus %s: ", bus->name); + if (priv->next_addr == 0) + printf("No USB Device found\n"); + else + printf("%d USB Device(s) found\n", priv->next_addr); + } +} + +static void run_threads(void) +{ +#if CONFIG_IS_ENABLED(UTHREAD) + if (!nthr) + return; + while (!uthread_grp_done(grp_id)) + uthread_schedule(); + nthr = 0; + grp_id = 0; +#endif +} + int usb_init(void) { int controllers_initialized = 0; + unsigned long t0 = timer_get_us(); struct usb_uclass_priv *uc_priv; struct usb_bus_priv *priv; struct udevice *bus; struct uclass *uc; int ret; + uthread_mutex_lock(&mutex); + + if (usb_started) { + ret = 0; + goto out; + } + asynch_allowed = 1; ret = uclass_get(UCLASS_USB, &uc); if (ret) - return ret; + goto out; uc_priv = uclass_get_priv(uc); uclass_foreach_dev(bus, uc) { - /* init low_level USB */ - printf("Bus %s: ", bus->name); - - /* - * For Sandbox, we need scan the device tree each time when we - * start the USB stack, in order to re-create the emulated USB - * devices and bind drivers for them before we actually do the - * driver probe. - * - * For USB onboard HUB, we need to do some non-trivial init - * like enabling a power regulator, before enumeration. - */ - if (IS_ENABLED(CONFIG_SANDBOX) || - IS_ENABLED(CONFIG_USB_ONBOARD_HUB)) { - ret = dm_scan_fdt_dev(bus); - if (ret) { - printf("USB device scan from fdt failed (%d)", ret); - continue; - } - } - - ret = device_probe(bus); - if (ret == -ENODEV) { /* No such device. */ - puts("Port not available.\n"); - controllers_initialized++; - continue; - } - - if (ret) { /* Other error. */ - printf("probe failed, error %d\n", ret); - continue; - } + usb_init_bus(bus); + } - ret = usb_probe_companion(bus); - if (ret) - continue; + if (CONFIG_IS_ENABLED(UTHREAD)) + run_threads(); - controllers_initialized++; - usb_started = true; - } + usb_started = true; /* * lowlevel init done, now scan the bus for devices i.e. search HUBs @@ -354,11 +422,16 @@ int usb_init(void) if (!device_active(bus)) continue; + controllers_initialized++; + priv = dev_get_uclass_priv(bus); if (!priv->companion) usb_scan_bus(bus, true); } + if (CONFIG_IS_ENABLED(UTHREAD)) + run_threads(); + /* * Now that the primary controllers have been scanned and have handed * over any devices they do not understand to their companions, scan @@ -375,21 +448,34 @@ int usb_init(void) } } - debug("scan end\n"); + if (CONFIG_IS_ENABLED(UTHREAD)) + run_threads(); + + usb_report_devices(uc); /* Remove any devices that were not found on this scan */ remove_inactive_children(uc, bus); ret = uclass_get(UCLASS_USB_HUB, &uc); if (ret) - return ret; + goto out; + remove_inactive_children(uc, bus); /* if we were not able to find at least one working bus, bail out */ if (controllers_initialized == 0) printf("No USB controllers found\n"); + debug("USB initialized in %ld ms\n", + (timer_get_us() - t0) / 1000); + + uthread_mutex_unlock(&mutex); + return usb_started ? 0 : -ENOENT; +out: + uthread_mutex_unlock(&mutex); + + return ret; } int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index d30725d3fca..3ee1f67190f 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -352,7 +352,7 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, * since it uses the same rules as low speed interrupt * endpoints. */ - + fallthrough; case USB_SPEED_LOW: if (usb_endpoint_xfer_int(endpt_desc) || usb_endpoint_xfer_isoc(endpt_desc)) { diff --git a/drivers/usb/musb-new/Kconfig b/drivers/usb/musb-new/Kconfig index c52afd41a75..ad9072a5327 100644 --- a/drivers/usb/musb-new/Kconfig +++ b/drivers/usb/musb-new/Kconfig @@ -22,6 +22,13 @@ config USB_MUSB_GADGET Enables the MUSB USB dual-role controller in gadget mode. if USB_MUSB_HOST || USB_MUSB_GADGET +config USB_MUSB_SC5XX + bool "Analog Devices MUSB support" + depends on (SC57X || SC58X) + help + Say y here to enable support for the USB controller on + ADI SC57X/SC58X processors. + config USB_MUSB_DA8XX bool "Enable DA8xx MUSB Controller" depends on ARCH_DAVINCI diff --git a/drivers/usb/musb-new/Makefile b/drivers/usb/musb-new/Makefile index 396ff02654b..6638772daca 100644 --- a/drivers/usb/musb-new/Makefile +++ b/drivers/usb/musb-new/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_USB_MUSB_PIC32) += pic32.o obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o obj-$(CONFIG_USB_MUSB_TI) += ti-musb.o obj-$(CONFIG_USB_MUSB_UX500) += ux500.o +obj-$(CONFIG_USB_MUSB_SC5XX) += sc5xx.o ccflags-y := $(call cc-option,-Wno-unused-variable) \ $(call cc-option,-Wno-unused-but-set-variable) \ diff --git a/drivers/usb/musb-new/sc5xx.c b/drivers/usb/musb-new/sc5xx.c new file mode 100644 index 00000000000..16201480b43 --- /dev/null +++ b/drivers/usb/musb-new/sc5xx.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * ADI SC5XX MUSB "glue layer" + * + * Written and/or maintained by Timesys Corporation + * + * Loosely ported from Linux driver: + * Author: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * + */ + +#include <dm.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/usb/musb.h> +#include "linux-compat.h" +#include "musb_core.h" +#include "musb_uboot.h" + +#define MUSB_SOFTRST 0x7f +#define MUSB_SOFTRST_NRST BIT(0) +#define MUSB_SOFTRST_NRSTX BIT(1) + +#define REG_USB_VBUS_CTL 0x380 +#define REG_USB_ID_CTL 0x382 +#define REG_USB_PHY_CTL 0x394 +#define REG_USB_PLL_OSC 0x398 +#define REG_USB_UTMI_CTL 0x39c + +/* controller data */ +struct sc5xx_musb_data { + struct musb_host_data mdata; + struct device dev; +}; + +#define to_sc5xx_musb_data(d) \ + container_of(d, struct sc5xx_musb_data, dev) + +static void sc5xx_musb_disable(struct musb *musb) +{ + /* no way to shut the controller */ +} + +static int sc5xx_musb_enable(struct musb *musb) +{ + /* soft reset by NRSTx */ + musb_writeb(musb->mregs, MUSB_SOFTRST, MUSB_SOFTRST_NRSTX); + /* set mode */ + musb_platform_set_mode(musb, musb->board_mode); + + return 0; +} + +static irqreturn_t sc5xx_interrupt(int irq, void *hci) +{ + struct musb *musb = hci; + irqreturn_t ret = IRQ_NONE; + u8 devctl; + + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); + + if (musb->int_usb & MUSB_INTR_VBUSERROR) { + musb->int_usb &= ~MUSB_INTR_VBUSERROR; + devctl = musb_readw(musb->mregs, MUSB_DEVCTL); + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + } + if (musb->int_usb || musb->int_tx || musb->int_rx) { + musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); + musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); + ret = musb_interrupt(musb); + } + + if (musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb)) + musb_writeb(musb->mregs, REG_USB_VBUS_CTL, 0x0); + + return ret; +} + +static int sc5xx_musb_set_mode(struct musb *musb, u8 mode) +{ + struct device *dev = musb->controller; + struct sc5xx_musb_data *pdata = to_sc5xx_musb_data(dev); + + switch (mode) { + case MUSB_HOST: + musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x1); + break; + case MUSB_PERIPHERAL: + musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x3); + break; + case MUSB_OTG: + musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x0); + break; + default: + dev_err(dev, "unsupported mode %d\n", mode); + return -EINVAL; + } + + return 0; +} + +static int sc5xx_musb_init(struct musb *musb) +{ + struct sc5xx_musb_data *pdata = to_sc5xx_musb_data(musb->controller); + + musb->isr = sc5xx_interrupt; + + musb_writel(musb->mregs, REG_USB_PLL_OSC, 20 << 1); + musb_writeb(musb->mregs, REG_USB_VBUS_CTL, 0x0); + musb_writeb(musb->mregs, REG_USB_PHY_CTL, 0x80); + musb_writel(musb->mregs, REG_USB_UTMI_CTL, + 0x40 | musb_readl(musb->mregs, REG_USB_UTMI_CTL)); + + return 0; +} + +const struct musb_platform_ops sc5xx_musb_ops = { + .init = sc5xx_musb_init, + .set_mode = sc5xx_musb_set_mode, + .disable = sc5xx_musb_disable, + .enable = sc5xx_musb_enable, +}; + +static struct musb_hdrc_config sc5xx_musb_config = { + .multipoint = 1, + .dyn_fifo = 1, + .num_eps = 16, + .ram_bits = 12, +}; + +/* has one MUSB controller which can be host or gadget */ +static struct musb_hdrc_platform_data sc5xx_musb_plat = { + .mode = MUSB_HOST, + .config = &sc5xx_musb_config, + .power = 100, + .platform_ops = &sc5xx_musb_ops, +}; + +static int musb_usb_probe(struct udevice *dev) +{ + struct usb_bus_priv *priv = dev_get_uclass_priv(dev); + struct sc5xx_musb_data *pdata = dev_get_priv(dev); + struct musb_host_data *mdata = &pdata->mdata; + void __iomem *mregs; + int ret; + + priv->desc_before_addr = true; + + mregs = dev_remap_addr(dev); + if (!mregs) + return -EINVAL; + + /* init controller */ + if (IS_ENABLED(CONFIG_USB_MUSB_HOST)) { + mdata->host = musb_init_controller(&sc5xx_musb_plat, + &pdata->dev, mregs); + if (!mdata->host) + return -EIO; + + ret = musb_lowlevel_init(mdata); + } else { + sc5xx_musb_plat.mode = MUSB_PERIPHERAL; + mdata->host = musb_register(&sc5xx_musb_plat, &pdata->dev, mregs); + if (!mdata->host) + return -EIO; + } + return ret; +} + +static int musb_usb_remove(struct udevice *dev) +{ + struct sc5xx_musb_data *pdata = dev_get_priv(dev); + + musb_stop(pdata->mdata.host); + + return 0; +} + +static const struct udevice_id sc5xx_musb_ids[] = { + { .compatible = "adi,sc5xx-musb" }, + { } +}; + +U_BOOT_DRIVER(usb_musb) = { + .name = "sc5xx-musb", + .id = UCLASS_USB, + .of_match = sc5xx_musb_ids, + .probe = musb_usb_probe, + .remove = musb_usb_remove, +#ifdef CONFIG_USB_MUSB_HOST + .ops = &musb_usb_ops, +#endif + .plat_auto = sizeof(struct usb_plat), + .priv_auto = sizeof(struct sc5xx_musb_data), +}; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3c3cebaacd0..73353944971 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -293,27 +293,6 @@ config PANEL_HX8238D source "drivers/video/fonts/Kconfig" -config VIDCONSOLE_AS_LCD - bool "Use 'vidconsole' when CONFIG_VIDCONSOLE_AS_NAME string is seen in stdout" - help - This is a work-around for boards which have 'lcd' or 'vga' in their - stdout environment variable, but have moved to use driver model for - video. In this case the console will no-longer work. While it is - possible to update the environment, the breakage may be confusing for - users. This option will be removed around the end of 2020. - -config VIDCONSOLE_AS_NAME - string "Use 'vidconsole' when string defined here is seen in stdout" - depends on VIDCONSOLE_AS_LCD - default "lcd" if LCD || TEGRA_COMMON - default "vga" if !LCD - help - This is a work-around for boards which have 'lcd' or 'vga' in their - stdout environment variable, but have moved to use driver model for - video. In this case the console will no-longer work. While it is - possible to update the environment, the breakage may be confusing for - users. This option will be removed around the end of 2020. - config VIDEO_BOCHS bool "Enable Bochs video emulation for QEMU" help @@ -550,6 +529,16 @@ config VIDEO_LCD_HIMAX_HX8394 Say Y here if you want to enable support for Himax HX8394 dsi 4dl panel. +config VIDEO_LCD_MOT + tristate "Atrix 4G and Droid X2 540x960 DSI video mode panel" + depends on PANEL && BACKLIGHT + select VIDEO_MIPI_DSI + help + Say Y here if you want to enable support for the LCD panel module for + Motorola Atrix 4G or Droid X2. Exact panel vendor and model are + unknown. The panel has a 540x960 resolution and uses 24 bit RGB per + pixel. + config VIDEO_LCD_ORISETECH_OTM8009A bool "OTM8009A DSI LCD panel support" select VIDEO_MIPI_DSI @@ -599,6 +588,15 @@ config VIDEO_LCD_SAMSUNG_LTL106HL02 LCD module found in Microsoft Surface 2. The panel has a FullHD resolution (1920x1080). +config VIDEO_LCD_SHARP_LQ079L1SX01 + tristate "Sharp LQ079L1SX01 1536x2048 DSI video mode panel" + depends on PANEL && BACKLIGHT + select VIDEO_MIPI_DSI + help + Say Y here if you want to enable support for Sharp LQ079L1SX01 + LCD module found in Xiaomi Mi Pad tablet. The panel has a QXGA + resolution (1536x2048). + config VIDEO_LCD_SHARP_LQ101R1SX01 tristate "Sharp LQ101R1SX01 2560x1600 DSI video mode panel" depends on PANEL && BACKLIGHT @@ -712,6 +710,12 @@ config I2C_EDID help This enables library for accessing EDID data from an LCD panel. +config I2C_EDID_STANDARD + bool "Enable standard timings EDID library expansion" + depends on I2C_EDID + help + This enables standard timings expansion for EDID data from an LCD panel. + config DISPLAY bool "Enable Display support" depends on DM @@ -734,6 +738,15 @@ config ATMEL_HLCD help HLCDC supports video output to an attached LCD panel. +config BACKLIGHT_LM3532 + bool "Backlight Driver for LM3532" + depends on BACKLIGHT + select DM_I2C + help + Say Y to enable the backlight driver for National Semiconductor / TI + LM3532 Lighting Power chip. Only backlight functions is supported as + for now. Supported backlight level range is from 1 to 255. + config BACKLIGHT_LM3533 bool "Backlight Driver for LM3533" depends on BACKLIGHT @@ -743,6 +756,16 @@ config BACKLIGHT_LM3533 LM3533 Lighting Power chip. Only Bank A is supported as for now. Supported backlight level range is from 2 to 255 with step of 1. +config BACKLIGHT_LP855x + bool "Backlight Driver for LP855x" + depends on BACKLIGHT + select DM_I2C + help + Say Y to enable the backlight driver for National Semiconductor / TI + LP8550/1/2/3/5/6/7 LED Backlight Driver. Only register driven mode is + supported for now, PWM mode can be added if there will be any need in + it. Supported backlight level range is from 0 to 255 with step of 1. + source "drivers/video/ti/Kconfig" source "drivers/video/exynos/Kconfig" @@ -805,17 +828,9 @@ source "drivers/video/stm32/Kconfig" source "drivers/video/tidss/Kconfig" -config VIDEO_TEGRA124 - bool "Enable video support on Tegra124" - help - Tegra124 supports many video output options including eDP and - HDMI. At present only eDP is supported by U-Boot. This option - enables this support which can be used on devices which - have an eDP display connected. - source "drivers/video/bridge/Kconfig" -source "drivers/video/tegra20/Kconfig" +source "drivers/video/tegra/Kconfig" source "drivers/video/imx/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 5a00438ce06..fbdb058647a 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -29,12 +29,13 @@ obj-$(CONFIG_$(PHASE_)BMP) += bmp.o endif +obj-$(CONFIG_BACKLIGHT_LM3532) += lm3532_backlight.o obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_backlight.o +obj-$(CONFIG_BACKLIGHT_LP855x) += lp855x_backlight.o obj-${CONFIG_EXYNOS_FB} += exynos/ obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/ obj-${CONFIG_VIDEO_STM32} += stm32/ -obj-${CONFIG_VIDEO_TEGRA124} += tegra124/ -obj-${CONFIG_$(XPL_)VIDEO_TIDSS} += tidss/ +obj-${CONFIG_$(PHASE_)VIDEO_TIDSS} += tidss/ obj-y += ti/ obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o @@ -52,18 +53,20 @@ obj-$(CONFIG_VIDEO_COREBOOT) += coreboot.o obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o obj-$(CONFIG_VIDEO_DW_MIPI_DSI) += dw_mipi_dsi.o obj-$(CONFIG_VIDEO_EFI) += efi.o -obj-$(CONFIG_VIDEO_IPUV3) += imx/ +obj-y += imx/ obj-$(CONFIG_VIDEO_IVYBRIDGE_IGD) += ivybridge_igd.o obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o obj-$(CONFIG_VIDEO_LCD_ENDEAVORU) += endeavoru-panel.o obj-$(CONFIG_VIDEO_LCD_HIMAX_HX8394) += himax-hx8394.o obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o obj-$(CONFIG_VIDEO_LCD_LG_LD070WX3) += lg-ld070wx3.o +obj-$(CONFIG_VIDEO_LCD_MOT) += mot-panel.o obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += orisetech_otm8009a.o obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o obj-$(CONFIG_VIDEO_LCD_RENESAS_R61307) += renesas-r61307.o obj-$(CONFIG_VIDEO_LCD_RENESAS_R69328) += renesas-r69328.o obj-$(CONFIG_VIDEO_LCD_SAMSUNG_LTL106HL02) += samsung-ltl106hl02.o +obj-$(CONFIG_VIDEO_LCD_SHARP_LQ079L1SX01) += sharp-lq079l1sx01.o obj-$(CONFIG_VIDEO_LCD_SHARP_LQ101R1SX01) += sharp-lq101r1sx01.o obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o obj-$(CONFIG_VIDEO_LCD_TDO_TL070WSH30) += tdo-tl070wsh30.o @@ -83,4 +86,4 @@ obj-$(CONFIG_VIDEO_ZYNQMP_DPSUB) += zynqmp/ obj-y += bridge/ obj-y += sunxi/ -obj-y += tegra20/ +obj-y += tegra/ diff --git a/drivers/video/bridge/Kconfig b/drivers/video/bridge/Kconfig index ab917273720..be53034bd3d 100644 --- a/drivers/video/bridge/Kconfig +++ b/drivers/video/bridge/Kconfig @@ -9,7 +9,7 @@ config VIDEO_BRIDGE config VIDEO_BRIDGE_PARADE_DP501 bool "Support Parade DP501 DP & DVI/HDMI dual mode transmitter" - depends on PANEL && DM_GPIO + depends on VIDEO_BRIDGE && PANEL && DM_GPIO select DM_I2C help The Parade DP501 is a DP & DVI/HDMI dual-mode transmitter. It @@ -46,16 +46,23 @@ config VIDEO_BRIDGE_ANALOGIX_ANX6345 config VIDEO_BRIDGE_SOLOMON_SSD2825 bool "Solomon SSD2825 bridge driver" - depends on PANEL && DM_GPIO + depends on VIDEO_BRIDGE && PANEL && DM_GPIO select VIDEO_MIPI_DSI help - Solomon SSD2824 SPI RGB-DSI bridge driver wrapped into panel uClass. + Solomon SSD2824 SPI RGB-DSI bridge driver. config VIDEO_BRIDGE_TOSHIBA_TC358768 bool "Support Toshiba TC358768 MIPI DSI bridge" - depends on PANEL && DM_GPIO + depends on VIDEO_BRIDGE && PANEL && DM_GPIO select VIDEO_MIPI_DSI select DM_I2C help Toshiba TC358768AXBG/TC358778XBG DSI bridge chip driver. Found in Asus Transformer Infinity TF700T. + +config VIDEO_BRIDGE_LVDS_CODEC + bool "Transparent LVDS encoders and decoders support" + depends on VIDEO_BRIDGE && PANEL && DM_GPIO + help + Support for transparent LVDS encoders and decoders that don't + require any configuration. diff --git a/drivers/video/bridge/Makefile b/drivers/video/bridge/Makefile index 58697e3cbe9..63dc6e62c49 100644 --- a/drivers/video/bridge/Makefile +++ b/drivers/video/bridge/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_VIDEO_BRIDGE_NXP_PTN3460) += ptn3460.o obj-$(CONFIG_VIDEO_BRIDGE_ANALOGIX_ANX6345) += anx6345.o obj-$(CONFIG_VIDEO_BRIDGE_SOLOMON_SSD2825) += ssd2825.o obj-$(CONFIG_VIDEO_BRIDGE_TOSHIBA_TC358768) += tc358768.o +obj-$(CONFIG_VIDEO_BRIDGE_LVDS_CODEC) += lvds-codec.o diff --git a/drivers/video/bridge/dp501.c b/drivers/video/bridge/dp501.c index 095e3e71fed..9937cfe095b 100644 --- a/drivers/video/bridge/dp501.c +++ b/drivers/video/bridge/dp501.c @@ -9,6 +9,7 @@ #include <log.h> #include <backlight.h> #include <panel.h> +#include <video_bridge.h> #include <linux/delay.h> #include <linux/err.h> #include <power/regulator.h> @@ -206,7 +207,6 @@ struct dp501_priv { struct udevice *chip2; struct udevice *vdd; - struct gpio_desc reset_gpio; struct gpio_desc enable_gpio; }; @@ -484,16 +484,19 @@ static int dp501_panel_timings(struct udevice *dev, return 0; } -static void dp501_hw_init(struct dp501_priv *priv) +static void dp501_hw_init(struct udevice *dev) { - dm_gpio_set_value(&priv->reset_gpio, 1); + struct dp501_priv *priv = dev_get_priv(dev); + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); + + dm_gpio_set_value(&uc_priv->reset, 1); regulator_set_enable_if_allowed(priv->vdd, 1); dm_gpio_set_value(&priv->enable_gpio, 1); udelay(100); - dm_gpio_set_value(&priv->reset_gpio, 0); + dm_gpio_set_value(&uc_priv->reset, 0); mdelay(80); } @@ -521,14 +524,6 @@ static int dp501_setup(struct udevice *dev) } /* get gpios */ - ret = gpio_request_by_name(dev, "reset-gpios", 0, - &priv->reset_gpio, GPIOD_IS_OUT); - if (ret) { - log_debug("%s: Could not decode reset-gpios (%d)\n", - __func__, ret); - return ret; - } - ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable_gpio, GPIOD_IS_OUT); if (ret) { @@ -544,7 +539,7 @@ static int dp501_setup(struct udevice *dev) return ret; } - dp501_hw_init(priv); + dp501_hw_init(dev); /* get EDID */ return panel_get_display_timing(priv->panel, &priv->timing); @@ -558,8 +553,8 @@ static int dp501_probe(struct udevice *dev) return dp501_setup(dev); } -struct panel_ops dp501_ops = { - .enable_backlight = dp501_attach, +static const struct video_bridge_ops dp501_ops = { + .attach = dp501_attach, .set_backlight = dp501_set_backlight, .get_display_timing = dp501_panel_timings, }; @@ -571,7 +566,7 @@ static const struct udevice_id dp501_ids[] = { U_BOOT_DRIVER(dp501) = { .name = "dp501", - .id = UCLASS_PANEL, + .id = UCLASS_VIDEO_BRIDGE, .of_match = dp501_ids, .ops = &dp501_ops, .probe = dp501_probe, diff --git a/drivers/video/bridge/lvds-codec.c b/drivers/video/bridge/lvds-codec.c new file mode 100644 index 00000000000..6cd8368a39e --- /dev/null +++ b/drivers/video/bridge/lvds-codec.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + * Loosely based on Linux lvds-codec.c driver + */ + +#include <dm.h> +#include <dm/ofnode_graph.h> +#include <log.h> +#include <panel.h> +#include <video_bridge.h> +#include <asm/gpio.h> +#include <power/regulator.h> + +struct lvds_codec_priv { + struct udevice *panel; + struct display_timing timing; + + struct gpio_desc powerdown_gpio; + struct udevice *power; +}; + +static int lvds_codec_attach(struct udevice *dev) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + + regulator_set_enable_if_allowed(priv->power, 1); + dm_gpio_set_value(&priv->powerdown_gpio, 0); + + return panel_enable_backlight(priv->panel); +} + +static int lvds_codec_set_panel(struct udevice *dev, int percent) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + + return panel_set_backlight(priv->panel, percent); +} + +static int lvds_codec_panel_timings(struct udevice *dev, + struct display_timing *timing) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + + memcpy(timing, &priv->timing, sizeof(*timing)); + + return 0; +} + +/* Function is purely for sandbox testing */ +static int lvds_codec_read_edid(struct udevice *dev, u8 *buf, int buf_size) +{ + return 0; +} + +static int lvds_codec_get_panel(struct udevice *dev) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + int i, ret; + + u32 num = ofnode_graph_get_port_count(dev_ofnode(dev)); + + for (i = 0; i < num; i++) { + ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1); + + ret = uclass_get_device_by_of_offset(UCLASS_PANEL, + ofnode_to_offset(remote), + &priv->panel); + if (!ret) + return 0; + } + + /* If this point is reached, no panels were found */ + return -ENODEV; +} + +static int lvds_codec_probe(struct udevice *dev) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + int ret; + + ret = lvds_codec_get_panel(dev); + if (ret) { + log_debug("%s: cannot get panel: ret=%d\n", __func__, ret); + return log_ret(ret); + } + + panel_get_display_timing(priv->panel, &priv->timing); + + ret = gpio_request_by_name(dev, "powerdown-gpios", 0, + &priv->powerdown_gpio, GPIOD_IS_OUT); + if (ret) { + log_debug("%s: could not get powerdown-gpios (%d)\n", __func__, ret); + if (ret != -ENOENT) + return log_ret(ret); + } + + ret = device_get_supply_regulator(dev, "power-supply", &priv->power); + if (ret) { + log_debug("%s: power regulator error: %d\n", __func__, ret); + if (ret != -ENOENT) + return log_ret(ret); + } + + return 0; +} + +static const struct video_bridge_ops lvds_codec_ops = { + .attach = lvds_codec_attach, + .set_backlight = lvds_codec_set_panel, + .get_display_timing = lvds_codec_panel_timings, + .read_edid = lvds_codec_read_edid, +}; + +static const struct udevice_id lvds_codec_ids[] = { + { .compatible = "lvds-decoder" }, + { .compatible = "lvds-encoder" }, + { } +}; + +U_BOOT_DRIVER(lvds_codec) = { + .name = "lvds_codec", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = lvds_codec_ids, + .ops = &lvds_codec_ops, + .probe = lvds_codec_probe, + .priv_auto = sizeof(struct lvds_codec_priv), +}; diff --git a/drivers/video/bridge/ssd2825.c b/drivers/video/bridge/ssd2825.c index f978021c860..a307993377c 100644 --- a/drivers/video/bridge/ssd2825.c +++ b/drivers/video/bridge/ssd2825.c @@ -5,12 +5,15 @@ #include <clk.h> #include <dm.h> +#include <dm/ofnode_graph.h> #include <log.h> #include <misc.h> #include <mipi_display.h> #include <mipi_dsi.h> #include <backlight.h> +#include <video_bridge.h> #include <panel.h> +#include <power/regulator.h> #include <spi.h> #include <linux/delay.h> #include <linux/err.h> @@ -106,6 +109,10 @@ #define SSD2825_LP_MIN_CLK 5000 /* KHz */ #define SSD2825_REF_MIN_CLK 2000 /* KHz */ +static const char * const ssd2825_supplies[] = { + "dvdd-supply", "avdd-supply", "vddio-supply" +}; + struct ssd2825_bridge_priv { struct mipi_dsi_host host; struct mipi_dsi_device device; @@ -113,12 +120,16 @@ struct ssd2825_bridge_priv { struct udevice *panel; struct display_timing timing; + struct udevice *supplies[ARRAY_SIZE(ssd2825_supplies)]; + struct gpio_desc power_gpio; - struct gpio_desc reset_gpio; struct clk *tx_clk; u32 pll_freq_kbps; /* PLL in kbps */ + + u32 hzd; /* HS Zero Delay in ns */ + u32 hpd; /* HS Prepare Delay is ns */ }; static int ssd2825_spi_write(struct udevice *dev, int reg, @@ -231,7 +242,6 @@ static ssize_t ssd2825_bridge_transfer(struct mipi_dsi_host *host, const struct mipi_dsi_msg *msg) { struct udevice *dev = (struct udevice *)host->dev; - u8 buf = *(u8 *)msg->tx_buf; u16 config; int ret; @@ -260,15 +270,6 @@ static ssize_t ssd2825_bridge_transfer(struct mipi_dsi_host *host, ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); ssd2825_write_dsi(dev, msg->tx_buf, msg->tx_len); - if (buf == MIPI_DCS_SET_DISPLAY_ON) { - ssd2825_write_register(dev, SSD2825_CONFIGURATION_REG, - SSD2825_CONF_REG_HS | SSD2825_CONF_REG_VEN | - SSD2825_CONF_REG_DCS | SSD2825_CONF_REG_ECD | - SSD2825_CONF_REG_EOT); - ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0001); - ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); - } - return 0; } @@ -312,9 +313,14 @@ static void ssd2825_setup_pll(struct udevice *dev) struct mipi_dsi_device *device = &priv->device; struct display_timing *dt = &priv->timing; u16 pll_config, lp_div; + u32 nibble_delay, nibble_freq_khz; u32 pclk_mult, tx_freq_khz, pd_lines; + u8 hzd, hpd; tx_freq_khz = clk_get_rate(priv->tx_clk) / 1000; + if (!tx_freq_khz || tx_freq_khz < 0) + tx_freq_khz = SSD2825_REF_MIN_CLK; + pd_lines = mipi_dsi_pixel_format_to_bpp(device->format); pclk_mult = pd_lines / device->lanes + 1; @@ -324,12 +330,19 @@ static void ssd2825_setup_pll(struct udevice *dev) lp_div = priv->pll_freq_kbps / (SSD2825_LP_MIN_CLK * 8); + /* nibble_delay in nanoseconds */ + nibble_freq_khz = priv->pll_freq_kbps / 4; + nibble_delay = 1000 * 1000 / nibble_freq_khz; + + hzd = priv->hzd / nibble_delay; + hpd = (priv->hpd - 4 * nibble_delay) / nibble_delay; + /* Disable PLL */ ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0000); ssd2825_write_register(dev, SSD2825_LINE_CTRL_REG, 0x0001); /* Set delays */ - ssd2825_write_register(dev, SSD2825_DELAY_ADJ_REG_1, 0x2103); + ssd2825_write_register(dev, SSD2825_DELAY_ADJ_REG_1, (hzd << 8) | hpd); /* Set PLL coeficients */ ssd2825_write_register(dev, SSD2825_PLL_CONFIGURATION_REG, pll_config); @@ -343,11 +356,30 @@ static void ssd2825_setup_pll(struct udevice *dev) ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); } -static int ssd2825_bridge_enable_panel(struct udevice *dev) +static int ssd2825_bridge_attach(struct udevice *dev) { struct ssd2825_bridge_priv *priv = dev_get_priv(dev); struct mipi_dsi_device *device = &priv->device; struct display_timing *dt = &priv->timing; + u8 pixel_format; + int ret; + + /* Set pixel format */ + switch (device->format) { + case MIPI_DSI_FMT_RGB565: + pixel_format = 0x00; + break; + case MIPI_DSI_FMT_RGB666_PACKED: + pixel_format = 0x01; + break; + case MIPI_DSI_FMT_RGB666: + pixel_format = 0x02; + break; + case MIPI_DSI_FMT_RGB888: + default: + pixel_format = 0x03; + break; + } /* Perform SW reset */ ssd2825_write_register(dev, SSD2825_OPERATION_CTRL_REG, 0x0100); @@ -367,7 +399,7 @@ static int ssd2825_bridge_enable_panel(struct udevice *dev) ssd2825_write_register(dev, SSD2825_RGB_INTERFACE_CTRL_REG_6, SSD2825_HSYNC_HIGH | SSD2825_VSYNC_HIGH | SSD2825_PCKL_HIGH | SSD2825_NON_BURST | - (3 - device->format)); + pixel_format); ssd2825_write_register(dev, SSD2825_LANE_CONFIGURATION_REG, device->lanes - 1); ssd2825_write_register(dev, SSD2825_TEST_REG, 0x0004); @@ -384,7 +416,18 @@ static int ssd2825_bridge_enable_panel(struct udevice *dev) ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); /* Perform panel setup */ - return panel_enable_backlight(priv->panel); + ret = panel_enable_backlight(priv->panel); + if (ret) + return ret; + + ssd2825_write_register(dev, SSD2825_CONFIGURATION_REG, + SSD2825_CONF_REG_HS | SSD2825_CONF_REG_VEN | + SSD2825_CONF_REG_DCS | SSD2825_CONF_REG_ECD | + SSD2825_CONF_REG_EOT); + ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0001); + ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); + + return 0; } static int ssd2825_bridge_set_panel(struct udevice *dev, int percent) @@ -407,7 +450,8 @@ static int ssd2825_bridge_panel_timings(struct udevice *dev, static int ssd2825_bridge_hw_init(struct udevice *dev) { struct ssd2825_bridge_priv *priv = dev_get_priv(dev); - int ret; + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); + int i, ret; ret = clk_prepare_enable(priv->tx_clk); if (ret) { @@ -416,25 +460,28 @@ static int ssd2825_bridge_hw_init(struct udevice *dev) return ret; } - ret = dm_gpio_set_value(&priv->power_gpio, 1); - if (ret) { - log_debug("%s: error changing power-gpios (%d)\n", - __func__, ret); - return ret; + /* enable supplies */ + for (i = 0; i < ARRAY_SIZE(ssd2825_supplies); i++) { + ret = regulator_set_enable_if_allowed(priv->supplies[i], 1); + if (ret) { + log_debug("%s: cannot enable %s %d\n", __func__, + ssd2825_supplies[i], ret); + return ret; + } } mdelay(10); - ret = dm_gpio_set_value(&priv->reset_gpio, 0); + ret = dm_gpio_set_value(&uc_priv->reset, 1); if (ret) { - log_debug("%s: error changing reset-gpios (%d)\n", + log_debug("%s: error entering reset (%d)\n", __func__, ret); return ret; } mdelay(10); - ret = dm_gpio_set_value(&priv->reset_gpio, 1); + ret = dm_gpio_set_value(&uc_priv->reset, 0); if (ret) { - log_debug("%s: error changing reset-gpios (%d)\n", + log_debug("%s: error exiting reset (%d)\n", __func__, ret); return ret; } @@ -443,13 +490,33 @@ static int ssd2825_bridge_hw_init(struct udevice *dev) return 0; } +static int ssd2825_bridge_get_panel(struct udevice *dev) +{ + struct ssd2825_bridge_priv *priv = dev_get_priv(dev); + int i, ret; + + u32 num = ofnode_graph_get_port_count(dev_ofnode(dev)); + + for (i = 0; i < num; i++) { + ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1); + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, + &priv->panel); + if (!ret) + return 0; + } + + /* If this point is reached, no panels were found */ + return -ENODEV; +} + static int ssd2825_bridge_probe(struct udevice *dev) { struct ssd2825_bridge_priv *priv = dev_get_priv(dev); struct spi_slave *slave = dev_get_parent_priv(dev); struct mipi_dsi_device *device = &priv->device; struct mipi_dsi_panel_plat *mipi_plat; - int ret; + int i, ret; ret = spi_claim_bus(slave); if (ret) { @@ -457,10 +524,9 @@ static int ssd2825_bridge_probe(struct udevice *dev) return ret; } - ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, - "panel", &priv->panel); + ret = ssd2825_bridge_get_panel(dev); if (ret) { - log_err("cannot get panel: ret=%d\n", ret); + log_debug("%s: panel not found, ret %d\n", __func__, ret); return ret; } @@ -477,33 +543,33 @@ static int ssd2825_bridge_probe(struct udevice *dev) device->format = mipi_plat->format; device->mode_flags = mipi_plat->mode_flags; - /* get panel gpios */ - ret = gpio_request_by_name(dev, "power-gpios", 0, - &priv->power_gpio, GPIOD_IS_OUT); - if (ret) { - log_err("could not decode power-gpios (%d)\n", ret); - return ret; - } - - ret = gpio_request_by_name(dev, "reset-gpios", 0, - &priv->reset_gpio, GPIOD_IS_OUT); - if (ret) { - log_err("could not decode reset-gpios (%d)\n", ret); - return ret; + /* get supplies */ + for (i = 0; i < ARRAY_SIZE(ssd2825_supplies); i++) { + ret = device_get_supply_regulator(dev, ssd2825_supplies[i], + &priv->supplies[i]); + if (ret) { + log_debug("%s: cannot get %s %d\n", __func__, + ssd2825_supplies[i], ret); + if (ret != -ENOENT) + return log_ret(ret); + } } /* get clk */ - priv->tx_clk = devm_clk_get(dev, "tx_clk"); + priv->tx_clk = devm_clk_get_optional(dev, NULL); if (IS_ERR(priv->tx_clk)) { log_err("cannot get tx_clk: %ld\n", PTR_ERR(priv->tx_clk)); return PTR_ERR(priv->tx_clk); } + priv->hzd = dev_read_u32_default(dev, "solomon,hs-zero-delay-ns", 133); + priv->hpd = dev_read_u32_default(dev, "solomon,hs-prep-delay-ns", 40); + return ssd2825_bridge_hw_init(dev); } -static const struct panel_ops ssd2825_bridge_ops = { - .enable_backlight = ssd2825_bridge_enable_panel, +static const struct video_bridge_ops ssd2825_bridge_ops = { + .attach = ssd2825_bridge_attach, .set_backlight = ssd2825_bridge_set_panel, .get_display_timing = ssd2825_bridge_panel_timings, }; @@ -515,9 +581,10 @@ static const struct udevice_id ssd2825_bridge_ids[] = { U_BOOT_DRIVER(ssd2825) = { .name = "ssd2825", - .id = UCLASS_PANEL, + .id = UCLASS_VIDEO_BRIDGE, .of_match = ssd2825_bridge_ids, .ops = &ssd2825_bridge_ops, + .bind = dm_scan_fdt_dev, .probe = ssd2825_bridge_probe, .priv_auto = sizeof(struct ssd2825_bridge_priv), }; diff --git a/drivers/video/bridge/tc358768.c b/drivers/video/bridge/tc358768.c index 19b6ca29d3e..358004f30f2 100644 --- a/drivers/video/bridge/tc358768.c +++ b/drivers/video/bridge/tc358768.c @@ -6,12 +6,14 @@ #include <clk.h> #include <dm.h> +#include <dm/ofnode_graph.h> #include <i2c.h> #include <log.h> #include <mipi_display.h> #include <mipi_dsi.h> #include <backlight.h> #include <panel.h> +#include <video_bridge.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/kernel.h> @@ -122,6 +124,10 @@ #define NANO 1000000000UL #define PICO 1000000000000ULL +static const char * const tc358768_supplies[] = { + "vddc-supply", "vddmipi-supply", "vddio-supply" +}; + struct tc358768_priv { struct mipi_dsi_host host; struct mipi_dsi_device device; @@ -129,9 +135,7 @@ struct tc358768_priv { struct udevice *panel; struct display_timing timing; - struct udevice *vddc; - struct udevice *vddmipi; - struct udevice *vddio; + struct udevice *supplies[ARRAY_SIZE(tc358768_supplies)]; struct clk *refclk; @@ -265,25 +269,27 @@ static void tc358768_sw_reset(struct udevice *dev) tc358768_write(dev, TC358768_SYSCTL, 0); } -static void tc358768_hw_enable(struct tc358768_priv *priv) +static void tc358768_hw_enable(struct udevice *dev) { + struct tc358768_priv *priv = dev_get_priv(dev); + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); int ret; ret = clk_prepare_enable(priv->refclk); if (ret) log_debug("%s: error enabling refclk (%d)\n", __func__, ret); - ret = regulator_set_enable_if_allowed(priv->vddc, true); + ret = regulator_set_enable_if_allowed(priv->supplies[0], true); if (ret) log_debug("%s: error enabling vddc (%d)\n", __func__, ret); - ret = regulator_set_enable_if_allowed(priv->vddmipi, true); + ret = regulator_set_enable_if_allowed(priv->supplies[1], true); if (ret) log_debug("%s: error enabling vddmipi (%d)\n", __func__, ret); mdelay(10); - ret = regulator_set_enable_if_allowed(priv->vddio, true); + ret = regulator_set_enable_if_allowed(priv->supplies[2], true); if (ret) log_debug("%s: error enabling vddio (%d)\n", __func__, ret); @@ -293,7 +299,7 @@ static void tc358768_hw_enable(struct tc358768_priv *priv) * The RESX is active low (GPIO_ACTIVE_LOW). * DEASSERT (value = 0) the reset_gpio to enable the chip */ - ret = dm_gpio_set_value(&priv->reset_gpio, 0); + ret = dm_gpio_set_value(&uc_priv->reset, 0); if (ret) log_debug("%s: error changing reset-gpio (%d)\n", __func__, ret); @@ -477,7 +483,7 @@ static int tc358768_attach(struct udevice *dev) device->mode_flags &= ~MIPI_DSI_CLOCK_NON_CONTINUOUS; } - tc358768_hw_enable(priv); + tc358768_hw_enable(dev); tc358768_sw_reset(dev); tc358768_setup_pll(dev); @@ -874,12 +880,33 @@ static int tc358768_panel_timings(struct udevice *dev, return 0; } +static int tc358768_get_panel(struct udevice *dev) +{ + struct tc358768_priv *priv = dev_get_priv(dev); + int i, ret; + + u32 num = ofnode_graph_get_port_count(dev_ofnode(dev)); + + for (i = 0; i < num; i++) { + ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1); + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, + &priv->panel); + if (!ret) + return 0; + } + + /* If this point is reached, no panels were found */ + return -ENODEV; +} + static int tc358768_setup(struct udevice *dev) { struct tc358768_priv *priv = dev_get_priv(dev); + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); struct mipi_dsi_device *device = &priv->device; struct mipi_dsi_panel_plat *mipi_plat; - int ret; + int i, ret; /* The bridge uses 16 bit registers */ ret = i2c_set_chip_offset_len(dev, 2); @@ -889,11 +916,10 @@ static int tc358768_setup(struct udevice *dev) return ret; } - ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, - "panel", &priv->panel); + ret = tc358768_get_panel(dev); if (ret) { - log_debug("%s: Cannot get panel: ret=%d\n", __func__, ret); - return log_ret(ret); + log_debug("%s: panel not found, ret %d\n", __func__, ret); + return ret; } panel_get_display_timing(priv->panel, &priv->timing); @@ -913,44 +939,26 @@ static int tc358768_setup(struct udevice *dev) priv->dsi_lanes = device->lanes; /* get regulators */ - ret = device_get_supply_regulator(dev, "vddc-supply", &priv->vddc); - if (ret) { - log_debug("%s: vddc regulator error: %d\n", __func__, ret); - if (ret != -ENOENT) - return log_ret(ret); - } - - ret = device_get_supply_regulator(dev, "vddmipi-supply", &priv->vddmipi); - if (ret) { - log_debug("%s: vddmipi regulator error: %d\n", __func__, ret); - if (ret != -ENOENT) - return log_ret(ret); - } - - ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio); - if (ret) { - log_debug("%s: vddio regulator error: %d\n", __func__, ret); - if (ret != -ENOENT) - return log_ret(ret); + for (i = 0; i < ARRAY_SIZE(tc358768_supplies); i++) { + ret = device_get_supply_regulator(dev, tc358768_supplies[i], + &priv->supplies[i]); + if (ret) { + log_debug("%s: cannot get %s %d\n", __func__, + tc358768_supplies[i], ret); + if (ret != -ENOENT) + return log_ret(ret); + } } /* get clk */ - priv->refclk = devm_clk_get(dev, "refclk"); + priv->refclk = devm_clk_get(dev, NULL); if (IS_ERR(priv->refclk)) { log_debug("%s: Could not get refclk: %ld\n", __func__, PTR_ERR(priv->refclk)); return PTR_ERR(priv->refclk); } - /* get gpios */ - ret = gpio_request_by_name(dev, "reset-gpios", 0, - &priv->reset_gpio, GPIOD_IS_OUT); - if (ret) { - log_debug("%s: Could not decode reset-gpios (%d)\n", __func__, ret); - return ret; - } - - dm_gpio_set_value(&priv->reset_gpio, 1); + dm_gpio_set_value(&uc_priv->reset, 1); return 0; } @@ -963,8 +971,8 @@ static int tc358768_probe(struct udevice *dev) return tc358768_setup(dev); } -struct panel_ops tc358768_ops = { - .enable_backlight = tc358768_attach, +static const struct video_bridge_ops tc358768_ops = { + .attach = tc358768_attach, .set_backlight = tc358768_set_backlight, .get_display_timing = tc358768_panel_timings, }; @@ -977,9 +985,10 @@ static const struct udevice_id tc358768_ids[] = { U_BOOT_DRIVER(tc358768) = { .name = "tc358768", - .id = UCLASS_PANEL, + .id = UCLASS_VIDEO_BRIDGE, .of_match = tc358768_ids, .ops = &tc358768_ops, + .bind = dm_scan_fdt_dev, .probe = tc358768_probe, .priv_auto = sizeof(struct tc358768_priv), }; diff --git a/drivers/video/bridge/video-bridge-uclass.c b/drivers/video/bridge/video-bridge-uclass.c index 2084a2e03ee..1b8aa12b9e8 100644 --- a/drivers/video/bridge/video-bridge-uclass.c +++ b/drivers/video/bridge/video-bridge-uclass.c @@ -33,6 +33,17 @@ int video_bridge_attach(struct udevice *dev) return ops->attach(dev); } +int video_bridge_get_display_timing(struct udevice *dev, + struct display_timing *timings) +{ + struct video_bridge_ops *ops = video_bridge_get_ops(dev); + + if (!ops->get_display_timing) + return -ENOSYS; + + return ops->get_display_timing(dev, timings); +} + int video_bridge_check_attached(struct udevice *dev) { struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); diff --git a/drivers/video/endeavoru-panel.c b/drivers/video/endeavoru-panel.c index d4ba4d8b6da..9950ff8bb05 100644 --- a/drivers/video/endeavoru-panel.c +++ b/drivers/video/endeavoru-panel.c @@ -117,6 +117,18 @@ static int endeavoru_panel_set_backlight(struct udevice *dev, int percent) struct endeavoru_panel_priv *priv = dev_get_priv(dev); int ret; + /* + * Due to the use of the Tegra DC backlight feature, backlight + * requests MUST NOT be made during probe or earlier. This is + * because it creates a loop, as the backlight is a DC child. + */ + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + log_err("cannot get backlight: ret = %d\n", ret); + return ret; + } + ret = backlight_enable(priv->backlight); if (ret) return ret; @@ -136,13 +148,6 @@ static int endeavoru_panel_of_to_plat(struct udevice *dev) struct endeavoru_panel_priv *priv = dev_get_priv(dev); int ret; - ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, - "backlight", &priv->backlight); - if (ret) { - log_err("cannot get backlight: ret = %d\n", ret); - return ret; - } - ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, "vdd-supply", &priv->vdd); if (ret) { @@ -231,7 +236,7 @@ static int endeavoru_panel_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 2; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; return endeavoru_panel_hw_init(dev); } diff --git a/drivers/video/imx/Kconfig b/drivers/video/imx/Kconfig index 34e8b640595..12f11c2eea8 100644 --- a/drivers/video/imx/Kconfig +++ b/drivers/video/imx/Kconfig @@ -13,3 +13,12 @@ config IMX_VIDEO_SKIP config IMX_HDMI bool "Enable HDMI support in IPUv3" depends on VIDEO_IPUV3 + +config IMX_LDB + bool "Freescale i.MX8MP LDB bridge" + depends on VIDEO_BRIDGE + help + Support for i.MX8MP DPI-to-LVDS on-SoC encoder. + +config IMX_LCDIF + bool "i.MX LCDIFv3 LCD controller" diff --git a/drivers/video/imx/Makefile b/drivers/video/imx/Makefile index 179ea651fe8..1edf5a6bdf0 100644 --- a/drivers/video/imx/Makefile +++ b/drivers/video/imx/Makefile @@ -3,4 +3,6 @@ # (C) Copyright 2000-2007 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. -obj-y += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o +obj-$(CONFIG_VIDEO_IPUV3) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o +obj-$(CONFIG_IMX_LDB) += ldb.o +obj-$(CONFIG_IMX_LCDIF) += lcdif.o diff --git a/drivers/video/imx/lcdif.c b/drivers/video/imx/lcdif.c new file mode 100644 index 00000000000..9f4fc7f5152 --- /dev/null +++ b/drivers/video/imx/lcdif.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * i.MX8 LCD interface driver inspired from the Linux driver + * Copyright 2019 NXP + * Copyright 2024 Bootlin + * Adapted by Miquel Raynal <miquel.raynal@bootlin.com> + */ + +#include <asm/io.h> +#include <asm/mach-imx/dma.h> +#include <clk.h> +#include <dm.h> +#include <panel.h> +#include <power-domain.h> +#include <video.h> +#include <video_bridge.h> +#include <linux/delay.h> + +#include "../videomodes.h" + +#define LCDIFV3_CTRL 0x0 +#define LCDIFV3_CTRL_SET 0x4 +#define LCDIFV3_CTRL_CLR 0x8 +#define CTRL_INV_HS BIT(0) +#define CTRL_INV_VS BIT(1) +#define CTRL_INV_DE BIT(2) +#define CTRL_INV_PXCK BIT(3) +#define CTRL_CLK_GATE BIT(30) +#define CTRL_SW_RESET BIT(31) + +#define LCDIFV3_DISP_PARA 0x10 +#define DISP_PARA_DISP_MODE_NORMAL 0 +#define DISP_PARA_LINE_PATTERN_RGB_YUV 0 +#define DISP_PARA_DISP_ON BIT(31) + +#define LCDIFV3_DISP_SIZE 0x14 +#define DISP_SIZE_DELTA_X(x) ((x) & 0xffff) +#define DISP_SIZE_DELTA_Y(x) ((x) << 16) + +#define LCDIFV3_HSYN_PARA 0x18 +#define HSYN_PARA_FP_H(x) ((x) & 0xffff) +#define HSYN_PARA_BP_H(x) ((x) << 16) + +#define LCDIFV3_VSYN_PARA 0x1C +#define VSYN_PARA_FP_V(x) ((x) & 0xffff) +#define VSYN_PARA_BP_V(x) ((x) << 16) + +#define LCDIFV3_VSYN_HSYN_WIDTH 0x20 +#define VSYN_HSYN_PW_H(x) ((x) & 0xffff) +#define VSYN_HSYN_PW_V(x) ((x) << 16) + +#define LCDIFV3_CTRLDESCL0_1 0x200 +#define CTRLDESCL0_1_WIDTH(x) ((x) & 0xffff) +#define CTRLDESCL0_1_HEIGHT(x) ((x) << 16) + +#define LCDIFV3_CTRLDESCL0_3 0x208 +#define CTRLDESCL0_3_PITCH(x) ((x) & 0xFFFF) + +#define LCDIFV3_CTRLDESCL_LOW0_4 0x20C +#define LCDIFV3_CTRLDESCL_HIGH0_4 0x210 + +#define LCDIFV3_CTRLDESCL0_5 0x214 +#define CTRLDESCL0_5_YUV_FORMAT(x) (((x) & 0x3) << 14) +#define CTRLDESCL0_5_BPP(x) (((x) & 0xf) << 24) +#define BPP32_ARGB8888 0x9 +#define CTRLDESCL0_5_SHADOW_LOAD_EN BIT(30) +#define CTRLDESCL0_5_EN BIT(31) + +struct lcdifv3_priv { + void __iomem *base; + struct clk pix_clk; + struct power_domain pd; + struct udevice *panel; + struct udevice *bridge; +}; + +static void lcdifv3_set_mode(struct lcdifv3_priv *priv, + struct display_timing *timings) +{ + u32 reg; + + writel(DISP_SIZE_DELTA_X(timings->hactive.typ) | + DISP_SIZE_DELTA_Y(timings->vactive.typ), + priv->base + LCDIFV3_DISP_SIZE); + + writel(HSYN_PARA_FP_H(timings->hfront_porch.typ) | + HSYN_PARA_BP_H(timings->hback_porch.typ), + priv->base + LCDIFV3_HSYN_PARA); + + writel(VSYN_PARA_BP_V(timings->vback_porch.typ) | + VSYN_PARA_FP_V(timings->vfront_porch.typ), + priv->base + LCDIFV3_VSYN_PARA); + + writel(VSYN_HSYN_PW_H(timings->hsync_len.typ) | + VSYN_HSYN_PW_V(timings->vsync_len.typ), + priv->base + LCDIFV3_VSYN_HSYN_WIDTH); + + writel(CTRLDESCL0_1_WIDTH(timings->hactive.typ) | + CTRLDESCL0_1_HEIGHT(timings->vactive.typ), + priv->base + LCDIFV3_CTRLDESCL0_1); + + if (timings->flags & DISPLAY_FLAGS_HSYNC_LOW) + writel(CTRL_INV_HS, priv->base + LCDIFV3_CTRL_SET); + else + writel(CTRL_INV_HS, priv->base + LCDIFV3_CTRL_CLR); + + if (timings->flags & DISPLAY_FLAGS_VSYNC_LOW) + writel(CTRL_INV_VS, priv->base + LCDIFV3_CTRL_SET); + else + writel(CTRL_INV_VS, priv->base + LCDIFV3_CTRL_CLR); + + if (timings->flags & DISPLAY_FLAGS_DE_LOW) + writel(CTRL_INV_DE, priv->base + LCDIFV3_CTRL_SET); + else + writel(CTRL_INV_DE, priv->base + LCDIFV3_CTRL_CLR); + + if (timings->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) + writel(CTRL_INV_PXCK, priv->base + LCDIFV3_CTRL_SET); + else + writel(CTRL_INV_PXCK, priv->base + LCDIFV3_CTRL_CLR); + + writel(0, priv->base + LCDIFV3_DISP_PARA); + + reg = readl(priv->base + LCDIFV3_CTRLDESCL0_5); + reg &= ~(CTRLDESCL0_5_BPP(0xf) | CTRLDESCL0_5_YUV_FORMAT(0x3)); + reg |= CTRLDESCL0_5_BPP(BPP32_ARGB8888); + writel(reg, priv->base + LCDIFV3_CTRLDESCL0_5); +} + +static void lcdifv3_enable_controller(struct lcdifv3_priv *priv) +{ + u32 reg; + + reg = readl(priv->base + LCDIFV3_DISP_PARA); + reg |= DISP_PARA_DISP_ON; + writel(reg, priv->base + LCDIFV3_DISP_PARA); + + reg = readl(priv->base + LCDIFV3_CTRLDESCL0_5); + reg |= CTRLDESCL0_5_EN; + writel(reg, priv->base + LCDIFV3_CTRLDESCL0_5); +} + +static int lcdifv3_video_sync(struct udevice *dev) +{ + struct lcdifv3_priv *priv = dev_get_priv(dev); + u32 reg; + + reg = readl(priv->base + LCDIFV3_CTRLDESCL0_5); + reg |= CTRLDESCL0_5_SHADOW_LOAD_EN; + writel(reg, priv->base + LCDIFV3_CTRLDESCL0_5); + + return 0; +} + +static void lcdifv3_init(struct udevice *dev, struct display_timing *timings) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + struct lcdifv3_priv *priv = dev_get_priv(dev); + + clk_set_rate(&priv->pix_clk, timings->pixelclock.typ); + + writel(CTRL_SW_RESET | CTRL_CLK_GATE, priv->base + LCDIFV3_CTRL_CLR); + udelay(10); + + lcdifv3_set_mode(priv, timings); + + writel(plat->base & 0xFFFFFFFF, priv->base + LCDIFV3_CTRLDESCL_LOW0_4); + writel(plat->base >> 32, priv->base + LCDIFV3_CTRLDESCL_HIGH0_4); + + writel(CTRLDESCL0_3_PITCH(timings->hactive.typ * 4), /* 32bpp */ + priv->base + LCDIFV3_CTRLDESCL0_3); + + lcdifv3_enable_controller(priv); +} + +static int lcdifv3_video_probe(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct lcdifv3_priv *priv = dev_get_priv(dev); + struct clk axi_clk, disp_axi_clk; + struct display_timing timings; + u32 fb_start, fb_end; + int ret; + + ret = power_domain_get(dev, &priv->pd); + if (ret < 0) + return ret; + + ret = clk_get_by_name(dev, "pix", &priv->pix_clk); + if (ret < 0) + return ret; + + ret = clk_get_by_name(dev, "axi", &axi_clk); + if (ret < 0) + return ret; + + ret = clk_get_by_name(dev, "disp_axi", &disp_axi_clk); + if (ret < 0) + return ret; + + ret = power_domain_on(&priv->pd); + if (ret) + return ret; + + ret = clk_enable(&priv->pix_clk); + if (ret) + goto dis_pd; + + ret = clk_enable(&axi_clk); + if (ret) + goto dis_pix_clk; + + ret = clk_enable(&disp_axi_clk); + if (ret) + goto dis_axi_clk; + + priv->base = dev_remap_addr(dev); + if (!priv->base) { + ret = -EINVAL; + goto dis_clks; + } + + /* Attach bridge */ + ret = uclass_get_device_by_endpoint(UCLASS_VIDEO_BRIDGE, dev, + -1, -1, &priv->bridge); + if (ret) + goto dis_clks; + + ret = video_bridge_attach(priv->bridge); + if (ret) + goto dis_clks; + + ret = video_bridge_set_backlight(priv->bridge, 80); + if (ret) + goto dis_clks; + + /* Attach panels */ + ret = uclass_get_device_by_endpoint(UCLASS_PANEL, priv->bridge, + 1, -1, &priv->panel); + if (ret) { + ret = uclass_get_device_by_endpoint(UCLASS_PANEL, priv->bridge, + 2, -1, &priv->panel); + if (ret) + goto dis_clks; + } + + ret = panel_get_display_timing(priv->panel, &timings); + if (ret) { + ret = ofnode_decode_display_timing(dev_ofnode(priv->panel), + 0, &timings); + if (ret) { + printf("Cannot decode panel timings (%d)\n", ret); + goto dis_clks; + } + } + + lcdifv3_init(dev, &timings); + + /* Only support 32bpp for now */ + uc_priv->bpix = VIDEO_BPP32; + uc_priv->xsize = timings.hactive.typ; + uc_priv->ysize = timings.vactive.typ; + + /* Enable dcache for the frame buffer */ + fb_start = plat->base & ~(MMU_SECTION_SIZE - 1); + fb_end = ALIGN(plat->base + plat->size, 1 << MMU_SECTION_SHIFT); + mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start, + DCACHE_WRITEBACK); + video_set_flush_dcache(dev, true); + + return 0; + +dis_clks: + clk_disable(&disp_axi_clk); +dis_axi_clk: + clk_disable(&axi_clk); +dis_pix_clk: + clk_disable(&priv->pix_clk); +dis_pd: + power_domain_off(&priv->pd); + + return ret; +} + +static int lcdifv3_video_bind(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + + /* Max size supported by LCDIF */ + plat->size = 1920 * 1080 * VNBYTES(VIDEO_BPP32); + + return 0; +} + +static const struct udevice_id lcdifv3_video_ids[] = { + { .compatible = "fsl,imx8mp-lcdif" }, + { } +}; + +static struct video_ops lcdifv3_video_ops = { + .video_sync = lcdifv3_video_sync, +}; + +U_BOOT_DRIVER(lcdifv3_video) = { + .name = "lcdif", + .id = UCLASS_VIDEO, + .of_match = lcdifv3_video_ids, + .bind = lcdifv3_video_bind, + .ops = &lcdifv3_video_ops, + .probe = lcdifv3_video_probe, + .priv_auto = sizeof(struct lcdifv3_priv), + .flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE, +}; diff --git a/drivers/video/imx/ldb.c b/drivers/video/imx/ldb.c new file mode 100644 index 00000000000..e918341c0a3 --- /dev/null +++ b/drivers/video/imx/ldb.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Derived work from: + * Philippe Cornu <philippe.cornu@st.com> + * Yannick Fertre <yannick.fertre@st.com> + * Adapted by Miquel Raynal <miquel.raynal@bootlin.com> + */ + +#define LOG_CATEGORY UCLASS_VIDEO_BRIDGE + +#include <clk.h> +#include <dm.h> +#include <log.h> +#include <panel.h> +#include <video_bridge.h> +#include <asm/io.h> +#include <linux/delay.h> + +#define LDB_CTRL_CH0_ENABLE BIT(0) +#define LDB_CTRL_CH1_ENABLE BIT(2) +#define LDB_CTRL_CH0_DATA_WIDTH BIT(5) +#define LDB_CTRL_CH0_BIT_MAPPING BIT(6) +#define LDB_CTRL_CH1_DATA_WIDTH BIT(7) +#define LDB_CTRL_CH1_BIT_MAPPING BIT(8) +#define LDB_CTRL_DI0_VSYNC_POLARITY BIT(9) +#define LDB_CTRL_DI1_VSYNC_POLARITY BIT(10) + +#define LVDS_CTRL_CH0_EN BIT(0) +#define LVDS_CTRL_CH1_EN BIT(1) +#define LVDS_CTRL_VBG_EN BIT(2) +#define LVDS_CTRL_PRE_EMPH_EN BIT(4) +#define LVDS_CTRL_PRE_EMPH_ADJ(n) (((n) & 0x7) << 5) +#define LVDS_CTRL_CC_ADJ(n) (((n) & 0x7) << 11) + +struct imx_ldb_priv { + struct clk ldb_clk; + void __iomem *ldb_ctrl; + void __iomem *lvds_ctrl; + struct udevice *lvds1; + struct udevice *lvds2; +}; + +static int imx_ldb_set_backlight(struct udevice *dev, int percent) +{ + struct imx_ldb_priv *priv = dev_get_priv(dev); + int ret; + + if (priv->lvds1) { + ret = panel_enable_backlight(priv->lvds1); + if (ret) { + debug("ldb: Cannot enable lvds1 backlight\n"); + return ret; + } + + ret = panel_set_backlight(priv->lvds1, percent); + if (ret) + return ret; + } + + if (priv->lvds2) { + ret = panel_enable_backlight(priv->lvds2); + if (ret) { + debug("ldb: Cannot enable lvds2 backlight\n"); + return ret; + } + + ret = panel_set_backlight(priv->lvds2, percent); + if (ret) + return ret; + } + + return 0; +} + +static int imx_ldb_of_to_plat(struct udevice *dev) +{ + struct imx_ldb_priv *priv = dev_get_priv(dev); + int ret; + + uclass_get_device_by_endpoint(UCLASS_PANEL, dev, 1, -1, &priv->lvds1); + uclass_get_device_by_endpoint(UCLASS_PANEL, dev, 2, -1, &priv->lvds2); + if (!priv->lvds1 && !priv->lvds2) { + debug("ldb: No remote panel for '%s' (ret=%d)\n", + dev_read_name(dev), ret); + return ret; + } + + return 0; +} + +/* The block has a mysterious x7 internal divisor (x3.5 in dual configuration) */ +#define IMX_LDB_INTERNAL_DIVISOR(x) (((x) * 70) / 10) +#define IMX_LDB_INTERNAL_DIVISOR_DUAL(x) (((x) * 35) / 10) + +static ulong imx_ldb_input_rate(struct imx_ldb_priv *priv, + struct display_timing *timings) +{ + ulong target_rate = timings->pixelclock.typ; + + if (priv->lvds1 && priv->lvds2) + return IMX_LDB_INTERNAL_DIVISOR_DUAL(target_rate); + + return IMX_LDB_INTERNAL_DIVISOR(target_rate); +} + +static int imx_ldb_attach(struct udevice *dev) +{ + struct imx_ldb_priv *priv = dev_get_priv(dev); + struct display_timing timings; + bool format_jeida = false; + bool format_24bpp = true; + u32 ldb_ctrl = 0, lvds_ctrl; + ulong ldb_rate; + int ret; + + /* TODO: update the 24bpp/jeida booleans with proper checks when they + * will be supported. + */ + if (priv->lvds1) { + ret = panel_get_display_timing(priv->lvds1, &timings); + if (ret) { + ret = ofnode_decode_display_timing(dev_ofnode(priv->lvds1), + 0, &timings); + if (ret) { + printf("Cannot decode lvds1 timings (%d)\n", ret); + return ret; + } + } + + ldb_ctrl |= LDB_CTRL_CH0_ENABLE; + if (format_24bpp) + ldb_ctrl |= LDB_CTRL_CH0_DATA_WIDTH; + if (format_jeida) + ldb_ctrl |= LDB_CTRL_CH0_BIT_MAPPING; + if (timings.flags & DISPLAY_FLAGS_VSYNC_HIGH) + ldb_ctrl |= LDB_CTRL_DI0_VSYNC_POLARITY; + } + + if (priv->lvds2) { + ret = panel_get_display_timing(priv->lvds2, &timings); + if (ret) { + ret = ofnode_decode_display_timing(dev_ofnode(priv->lvds2), + 0, &timings); + if (ret) { + printf("Cannot decode lvds2 timings (%d)\n", ret); + return ret; + } + } + + ldb_ctrl |= LDB_CTRL_CH1_ENABLE; + if (format_24bpp) + ldb_ctrl |= LDB_CTRL_CH1_DATA_WIDTH; + if (format_jeida) + ldb_ctrl |= LDB_CTRL_CH1_BIT_MAPPING; + if (timings.flags & DISPLAY_FLAGS_VSYNC_HIGH) + ldb_ctrl |= LDB_CTRL_DI1_VSYNC_POLARITY; + } + + /* + * Not all pixel clocks will work, as the final rate (after internal + * integer division) should be identical to the LCDIF clock, otherwise + * the rendering will appear resized/shimmering. + */ + ldb_rate = imx_ldb_input_rate(priv, &timings); + clk_set_rate(&priv->ldb_clk, ldb_rate); + + writel(ldb_ctrl, priv->ldb_ctrl); + + lvds_ctrl = LVDS_CTRL_CC_ADJ(2) | LVDS_CTRL_PRE_EMPH_EN | + LVDS_CTRL_PRE_EMPH_ADJ(3) | LVDS_CTRL_VBG_EN; + writel(lvds_ctrl, priv->lvds_ctrl); + + /* Wait for VBG to stabilize. */ + udelay(15); + + if (priv->lvds1) + lvds_ctrl |= LVDS_CTRL_CH0_EN; + if (priv->lvds2) + lvds_ctrl |= LVDS_CTRL_CH1_EN; + + writel(lvds_ctrl, priv->lvds_ctrl); + + return 0; +} + +static int imx_ldb_probe(struct udevice *dev) +{ + struct imx_ldb_priv *priv = dev_get_priv(dev); + struct udevice *parent = dev_get_parent(dev); + fdt_addr_t parent_addr, child_addr; + int ret; + + ret = clk_get_by_name(dev, "ldb", &priv->ldb_clk); + if (ret < 0) + return ret; + + parent_addr = dev_read_addr(parent); + if (parent_addr == FDT_ADDR_T_NONE) + return -EINVAL; + + child_addr = dev_read_addr_name(dev, "ldb"); + if (child_addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->ldb_ctrl = map_physmem(parent_addr + child_addr, 0, MAP_NOCACHE); + if (!priv->ldb_ctrl) + return -EINVAL; + + child_addr = dev_read_addr_name(dev, "lvds"); + if (child_addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->lvds_ctrl = map_physmem(parent_addr + child_addr, 0, MAP_NOCACHE); + if (!priv->lvds_ctrl) + return -EINVAL; + + ret = clk_enable(&priv->ldb_clk); + if (ret) + return ret; + + ret = video_bridge_set_active(dev, true); + if (ret) + goto dis_clk; + + return 0; + +dis_clk: + clk_disable(&priv->ldb_clk); + + return ret; +} + +struct video_bridge_ops imx_ldb_ops = { + .attach = imx_ldb_attach, + .set_backlight = imx_ldb_set_backlight, +}; + +static const struct udevice_id imx_ldb_ids[] = { + { .compatible = "fsl,imx8mp-ldb"}, + { } +}; + +U_BOOT_DRIVER(imx_ldb) = { + .name = "imx-lvds-display-bridge", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = imx_ldb_ids, + .probe = imx_ldb_probe, + .of_to_plat = imx_ldb_of_to_plat, + .ops = &imx_ldb_ops, + .priv_auto = sizeof(struct imx_ldb_priv), +}; diff --git a/drivers/video/lg-ld070wx3.c b/drivers/video/lg-ld070wx3.c index 610a06ffe7b..3676e45bc65 100644 --- a/drivers/video/lg-ld070wx3.c +++ b/drivers/video/lg-ld070wx3.c @@ -158,7 +158,7 @@ static int lg_ld070wx3_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; return lg_ld070wx3_hw_init(dev); } diff --git a/drivers/video/lm3532_backlight.c b/drivers/video/lm3532_backlight.c new file mode 100644 index 00000000000..81b3b910196 --- /dev/null +++ b/drivers/video/lm3532_backlight.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TI LM3532 LED driver + * + * Copyright (c) 2019 Texas Instruments Incorporated + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT + +#include <backlight.h> +#include <dm.h> +#include <dm/ofnode.h> +#include <i2c.h> +#include <log.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <asm/gpio.h> +#include <power/regulator.h> + +#define LM3532_BL_MODE_MANUAL 0x00 +#define LM3532_BL_MODE_ALS 0x01 + +#define LM3532_REG_OUTPUT_CFG 0x10 +#define LM3532_REG_STARTSHUT_RAMP 0x11 +#define LM3532_REG_RT_RAMP 0x12 +#define LM3532_REG_PWM_A_CFG 0x13 +#define LM3532_REG_PWM_B_CFG 0x14 +#define LM3532_REG_PWM_C_CFG 0x15 +#define LM3532_REG_ZONE_CFG_A 0x16 +#define LM3532_REG_CTRL_A_FS_CURR 0x17 +#define LM3532_REG_ZONE_CFG_B 0x18 +#define LM3532_REG_CTRL_B_FS_CURR 0x19 +#define LM3532_REG_ZONE_CFG_C 0x1a +#define LM3532_REG_CTRL_C_FS_CURR 0x1b +#define LM3532_REG_ENABLE 0x1d +#define LM3532_ALS_CONFIG 0x23 +#define LM3532_REG_ZN_0_HI 0x60 +#define LM3532_REG_ZN_0_LO 0x61 +#define LM3532_REG_ZN_1_HI 0x62 +#define LM3532_REG_ZN_1_LO 0x63 +#define LM3532_REG_ZN_2_HI 0x64 +#define LM3532_REG_ZN_2_LO 0x65 +#define LM3532_REG_ZN_3_HI 0x66 +#define LM3532_REG_ZN_3_LO 0x67 +#define LM3532_REG_ZONE_TRGT_A 0x70 +#define LM3532_REG_ZONE_TRGT_B 0x75 +#define LM3532_REG_ZONE_TRGT_C 0x7a +#define LM3532_REG_MAX 0x7e + +/* Control Enable */ +#define LM3532_CTRL_A_ENABLE BIT(0) +#define LM3532_CTRL_B_ENABLE BIT(1) +#define LM3532_CTRL_C_ENABLE BIT(2) + +/* PWM Zone Control */ +#define LM3532_PWM_ZONE_MASK 0x7c +#define LM3532_PWM_ZONE_0_EN BIT(2) +#define LM3532_PWM_ZONE_1_EN BIT(3) +#define LM3532_PWM_ZONE_2_EN BIT(4) +#define LM3532_PWM_ZONE_3_EN BIT(5) +#define LM3532_PWM_ZONE_4_EN BIT(6) + +/* Brightness Configuration */ +#define LM3532_I2C_CTRL BIT(0) +#define LM3532_ALS_CTRL 0 +#define LM3532_LINEAR_MAP BIT(1) +#define LM3532_ZONE_MASK (BIT(2) | BIT(3) | BIT(4)) +#define LM3532_ZONE_0 0 +#define LM3532_ZONE_1 BIT(2) +#define LM3532_ZONE_2 BIT(3) +#define LM3532_ZONE_3 (BIT(2) | BIT(3)) +#define LM3532_ZONE_4 BIT(4) + +#define LM3532_ENABLE_ALS BIT(3) +#define LM3532_ALS_SEL_SHIFT 6 + +/* Zone Boundary Register */ +#define LM3532_ALS_WINDOW_mV 2000 +#define LM3532_ALS_ZB_MAX 4 +#define LM3532_ALS_OFFSET_mV 2 + +#define LM3532_CONTROL_A 0 +#define LM3532_CONTROL_B 1 +#define LM3532_CONTROL_C 2 +#define LM3532_MAX_CONTROL_BANKS 3 +#define LM3532_MAX_LED_STRINGS 3 + +#define LM3532_OUTPUT_CFG_MASK 0x3 +#define LM3532_BRT_VAL_ADJUST 8 +#define LM3532_RAMP_DOWN_SHIFT 3 + +#define LM3532_NUM_RAMP_VALS 8 +#define LM3532_NUM_AVG_VALS 8 +#define LM3532_NUM_IMP_VALS 32 + +#define LM3532_FS_CURR_MIN 5000 +#define LM3532_FS_CURR_MAX 29800 +#define LM3532_FS_CURR_STEP 800 + +struct lm3532_bank_data { + int control_bank; + int mode; + int ctrl_brt_pointer; + int num_leds; + int full_scale_current; + u32 present:1; + u32 led_strings[LM3532_MAX_CONTROL_BANKS]; +}; + +struct lm3532_backlight_priv { + struct gpio_desc enable_gpio; + struct udevice *regulator; + + u32 runtime_ramp_up; + u32 runtime_ramp_down; + + struct lm3532_bank_data bank[LM3532_MAX_CONTROL_BANKS]; +}; + +/* This device does not like i2c md so use this instead */ +static void __maybe_unused dump_i2c(struct udevice *dev) +{ + int i, c; + + for (i = 0; i < 0x10; i++) { + printf("00%02x: %02x", i * 0x10, dm_i2c_reg_read(dev, i * 0x10)); + for (c = 1; c < 0xf; c++) + printf(" %02x", dm_i2c_reg_read(dev, i * 0x10 + c)); + printf(" %02x\n", dm_i2c_reg_read(dev, i * 0x10 + 0xf)); + } +} + +static int lm3532_backlight_enable(struct udevice *dev) +{ + struct lm3532_backlight_priv *priv = dev_get_priv(dev); + int ret, i; + + for (i = 0; i < LM3532_MAX_CONTROL_BANKS; i++) { + if (priv->bank[i].present) { + u32 ctrl_en_val = BIT(priv->bank[i].control_bank); + + ret = dm_i2c_reg_clrset(dev, LM3532_REG_ENABLE, + ctrl_en_val, ctrl_en_val); + if (ret) { + log_debug("%s: failed to set ctrl: %d\n", + __func__, ret); + return ret; + } + } + } + + regulator_set_enable_if_allowed(priv->regulator, 1); + + return 0; +} + +static int lm3532_backlight_set_brightness(struct udevice *dev, int percent) +{ + struct lm3532_backlight_priv *priv = dev_get_priv(dev); + struct lm3532_bank_data *bank; + int ret, i; + + for (i = 0; i < LM3532_MAX_CONTROL_BANKS; i++) { + if (priv->bank[i].present) { + bank = &priv->bank[i]; + u32 brightness_reg = LM3532_REG_ZONE_TRGT_A + + bank->control_bank * 5 + + (bank->ctrl_brt_pointer >> 2); + + ret = dm_i2c_reg_write(dev, brightness_reg, percent); + if (ret) { + log_debug("%s: failed to set brightness: %d\n", + __func__, ret); + return ret; + } + } + } + + return 0; +} + +static const int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192, + 16384, 32768, 65536 }; +static int lm3532_get_ramp_index(int ramp_time) +{ + int i; + + if (ramp_time <= ramp_table[0]) + return 0; + + if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1]) + return LM3532_NUM_RAMP_VALS - 1; + + for (i = 1; i < LM3532_NUM_RAMP_VALS; i++) { + if (ramp_time == ramp_table[i]) + return i; + + /* Find an approximate index by looking up the table */ + if (ramp_time > ramp_table[i - 1] && + ramp_time < ramp_table[i]) { + if (ramp_time - ramp_table[i - 1] < ramp_table[i] - ramp_time) + return i - 1; + else + return i; + } + } + + return 0; +} + +static int lm3532_backlight_of_to_plat(struct udevice *dev) +{ + struct lm3532_backlight_priv *priv = dev_get_priv(dev); + u32 ramp_time, reg; + ofnode child; + int ret; + + ret = gpio_request_by_name(dev, "enable-gpios", 0, + &priv->enable_gpio, GPIOD_IS_OUT); + if (ret) { + log_debug("%s: could not decode enable-gpios (%d)\n", __func__, ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vin-supply", &priv->regulator); + if (ret) { + log_debug("%s: vin regulator not defined: %d\n", __func__, ret); + if (ret != -ENOENT) + return log_ret(ret); + } + + ramp_time = dev_read_u32_default(dev, "ramp-up-us", 0); + priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time); + + ramp_time = dev_read_u32_default(dev, "ramp-down-us", 0); + priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time); + + /* Backlight is one of children but has no dedicated driver */ + ofnode_for_each_subnode(child, dev_ofnode(dev)) { + ret = ofnode_read_u32(child, "reg", ®); + if (ret || reg > LM3532_CONTROL_C) { + log_debug("%s: control bank invalid %d\n", __func__, reg); + continue; + } + + struct lm3532_bank_data *bank = &priv->bank[reg]; + + bank->control_bank = reg; + bank->present = 1; + bank->mode = ofnode_read_u32_default(child, "ti,led-mode", + LM3532_BL_MODE_MANUAL); + bank->mode = LM3532_BL_MODE_ALS ? LM3532_ALS_CTRL : LM3532_I2C_CTRL; + + if (ofnode_read_bool(child, "ti,linear-mapping-mode")) + bank->mode |= LM3532_LINEAR_MAP; + + bank->num_leds = ofnode_read_size(child, "led-sources"); + bank->num_leds /= sizeof(u32); + if (bank->num_leds > LM3532_MAX_LED_STRINGS) { + log_debug("%s: too many LED string defined %d\n", + __func__, bank->num_leds); + continue; + } + + ret = ofnode_read_u32_array(child, "led-sources", + bank->led_strings, + bank->num_leds); + if (ret) { + log_debug("%s: led-sources property missing %d\n", + __func__, ret); + continue; + } + + ret = ofnode_read_u32(child, "led-max-microamp", + &bank->full_scale_current); + if (ret) + log_debug("%s: failed getting led-max-microamp %d\n", + __func__, ret); + else + bank->full_scale_current = min(bank->full_scale_current, + LM3532_FS_CURR_MAX); + } + + return 0; +} + +static int lm3532_backlight_init_registers(struct udevice *dev, + struct lm3532_bank_data *bank) +{ + struct lm3532_backlight_priv *priv = dev_get_priv(dev); + u32 brightness_config_val, runtime_ramp_val; + u32 output_cfg_val = 0, output_cfg_shift = 0, output_cfg_mask = 0; + int fs_current_reg, fs_current_val; + int ret, i; + + if (!bank->present) + return 0; + + u32 brightness_config_reg = LM3532_REG_ZONE_CFG_A + bank->control_bank * 2; + /* + * This could be hard coded to the default value but the control + * brightness register may have changed during boot. + */ + ret = dm_i2c_reg_read(dev, brightness_config_reg); + if (ret < 0) + return ret; + + bank->ctrl_brt_pointer = ret & ~LM3532_ZONE_MASK; + brightness_config_val = bank->ctrl_brt_pointer | bank->mode; + + ret = dm_i2c_reg_write(dev, brightness_config_reg, brightness_config_val); + if (ret) + return ret; + + if (bank->full_scale_current) { + fs_current_reg = LM3532_REG_CTRL_A_FS_CURR + bank->control_bank * 2; + fs_current_val = (bank->full_scale_current - LM3532_FS_CURR_MIN) / + LM3532_FS_CURR_STEP; + + ret = dm_i2c_reg_write(dev, fs_current_reg, fs_current_val); + if (ret) + return ret; + } + + for (i = 0; i < bank->num_leds; i++) { + output_cfg_shift = bank->led_strings[i] * 2; + output_cfg_val |= (bank->control_bank << output_cfg_shift); + output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift; + } + + ret = dm_i2c_reg_clrset(dev, LM3532_REG_OUTPUT_CFG, output_cfg_mask, + output_cfg_val); + if (ret) + return ret; + + runtime_ramp_val = priv->runtime_ramp_up | + (priv->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT); + + return dm_i2c_reg_write(dev, LM3532_REG_RT_RAMP, runtime_ramp_val); +} + +static int lm3532_backlight_probe(struct udevice *dev) +{ + struct lm3532_backlight_priv *priv = dev_get_priv(dev); + int ret, i; + + if (device_get_uclass_id(dev->parent) != UCLASS_I2C) + return -EPROTONOSUPPORT; + + dm_gpio_set_value(&priv->enable_gpio, 1); + + for (i = 0; i < LM3532_MAX_CONTROL_BANKS; i++) { + ret = lm3532_backlight_init_registers(dev, &priv->bank[i]); + if (ret) + return ret; + } + + return 0; +} + +static const struct backlight_ops lm3532_backlight_ops = { + .enable = lm3532_backlight_enable, + .set_brightness = lm3532_backlight_set_brightness, +}; + +static const struct udevice_id lm3532_backlight_ids[] = { + { .compatible = "ti,lm3532" }, + { } +}; + +U_BOOT_DRIVER(lm3532_backlight) = { + .name = "lm3532_backlight", + .id = UCLASS_PANEL_BACKLIGHT, + .of_match = lm3532_backlight_ids, + .of_to_plat = lm3532_backlight_of_to_plat, + .probe = lm3532_backlight_probe, + .ops = &lm3532_backlight_ops, + .priv_auto = sizeof(struct lm3532_backlight_priv), +}; diff --git a/drivers/video/lm3533_backlight.c b/drivers/video/lm3533_backlight.c index 6b51fa0628e..7b87b6bd40b 100644 --- a/drivers/video/lm3533_backlight.c +++ b/drivers/video/lm3533_backlight.c @@ -7,6 +7,7 @@ #include <backlight.h> #include <dm.h> +#include <dm/ofnode.h> #include <i2c.h> #include <log.h> #include <linux/delay.h> @@ -17,56 +18,79 @@ #define LM3533_BL_MAX_BRIGHTNESS 0xFF #define LM3533_SINK_OUTPUT_CONFIG_1 0x10 -#define LM3533_CONTROL_BANK_A_PWM 0x14 +#define LM3533_CONTROL_PWM_BASE 0x14 +#define PWM_MAX GENMASK(5, 0) #define LM3533_CONTROL_BANK_AB_BRIGHTNESS 0x1A -#define LM3533_CONTROL_BANK_A_FULLSCALE_CURRENT 0x1F +#define LM3533_CONTROL_FULLSCALE_CURRENT_BASE 0x1F +#define MAX_CURRENT_MIN 5000 +#define MAX_CURRENT_MAX 29800 +#define MAX_CURRENT_STEP 800 #define LM3533_CONTROL_BANK_ENABLE 0x27 #define LM3533_OVP_FREQUENCY_PWM_POLARITY 0x2C +#define BOOST_OVP_MASK GENMASK(2, 1) +#define BOOST_OVP_SHIFT 1 +#define BOOST_FREQ_MASK BIT(0) +#define BOOST_FREQ_SHIFT 0 #define LM3533_BRIGHTNESS_REGISTER_A 0x40 +#define LM3533_BOOST_OVP_16V 16000000UL +#define LM3533_BOOST_FREQ_500KHZ 500000UL + struct lm3533_backlight_priv { struct gpio_desc enable_gpio; u32 def_bl_lvl; + + /* Core */ + u32 boost_ovp; + u32 boost_freq; + + /* Backlight */ + u32 reg; + u16 max_current; /* 5000 - 29800 uA (800 uA step) */ + u8 pwm; /* 0 - 0x3f */ + bool linear; + bool hvled; }; static int lm3533_backlight_enable(struct udevice *dev) { struct lm3533_backlight_priv *priv = dev_get_priv(dev); + u8 val, id = priv->reg; int ret; - dm_gpio_set_value(&priv->enable_gpio, 1); - mdelay(5); + if (priv->linear) { + ret = dm_i2c_reg_clrset(dev, LM3533_CONTROL_BANK_AB_BRIGHTNESS, + BIT(2 * id + 1), BIT(2 * id + 1)); + if (ret) + return ret; + } - /* HVLED 1 & 2 are controlled by Bank A */ - ret = dm_i2c_reg_write(dev, LM3533_SINK_OUTPUT_CONFIG_1, 0x00); - if (ret) - return ret; + if (priv->hvled) { + ret = dm_i2c_reg_clrset(dev, LM3533_SINK_OUTPUT_CONFIG_1, + BIT(0) | BIT(1), id | id << 1); + if (ret) + return ret; + } - /* PWM input is disabled for CABC */ - ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_A_PWM, 0x00); - if (ret) - return ret; + /* Set current */ + if (priv->max_current < MAX_CURRENT_MIN || priv->max_current > MAX_CURRENT_MAX) + return -EINVAL; - /* Linear & Control Bank A is configured for register Current control */ - ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_AB_BRIGHTNESS, 0x02); + val = (priv->max_current - MAX_CURRENT_MIN) / MAX_CURRENT_STEP; + ret = dm_i2c_reg_write(dev, LM3533_CONTROL_FULLSCALE_CURRENT_BASE + id, val); if (ret) return ret; - /* Full-Scale Current (20.2mA) */ - ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_A_FULLSCALE_CURRENT, 0x13); - if (ret) - return ret; + /* Set PWM mask */ + if (priv->pwm > PWM_MAX) + return -EINVAL; - /* Control Bank A is enable */ - ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_ENABLE, 0x01); + ret = dm_i2c_reg_write(dev, LM3533_CONTROL_PWM_BASE + id, priv->pwm); if (ret) return ret; - ret = dm_i2c_reg_write(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY, 0x0A); - if (ret) - return ret; - - return 0; + /* Enable Control Bank */ + return dm_i2c_reg_clrset(dev, LM3533_CONTROL_BANK_ENABLE, BIT(id), BIT(id)); } static int lm3533_backlight_set_brightness(struct udevice *dev, int percent) @@ -92,6 +116,56 @@ static int lm3533_backlight_set_brightness(struct udevice *dev, int percent) return 0; } +static int lm3533_backlight_of_to_plat(struct udevice *dev) +{ + struct lm3533_backlight_priv *priv = dev_get_priv(dev); + ofnode child; + int ret; + + ret = gpio_request_by_name(dev, "enable-gpios", 0, + &priv->enable_gpio, GPIOD_IS_OUT); + if (ret) { + log_err("Could not decode enable-gpios (%d)\n", ret); + return ret; + } + + priv->boost_ovp = dev_read_u32_default(dev, "ti,boost-ovp-microvolt", + LM3533_BOOST_OVP_16V); + + /* boost_ovp is defined in microvolts, convert to enum value */ + priv->boost_ovp = priv->boost_ovp / (8 * 1000 * 1000) - 2; + + priv->boost_freq = dev_read_u32_default(dev, "ti,boost-freq-hz", + LM3533_BOOST_FREQ_500KHZ); + + /* boost_freq is defined in Hz, convert to enum value */ + priv->boost_freq = priv->boost_freq / (500 * 1000) - 1; + + /* Backlight is one of children but has no dedicated driver */ + ofnode_for_each_subnode(child, dev_ofnode(dev)) { + if (ofnode_device_is_compatible(child, "ti,lm3533-backlight")) { + const char *node_name = ofnode_get_name(child); + + if (!strcmp(&node_name[10], "1")) + priv->reg = 1; + else + priv->reg = 0; + + priv->max_current = ofnode_read_u32_default(child, "ti,max-current-microamp", + 5000); + priv->pwm = ofnode_read_u32_default(child, "ti,pwm-config-mask", 0); + + priv->def_bl_lvl = ofnode_read_u32_default(child, "default-brightness", + LM3533_BL_MAX_BRIGHTNESS); + + priv->linear = ofnode_read_bool(child, "ti,linear-mapping-mode"); + priv->hvled = ofnode_read_bool(child, "ti,hardware-controlled"); + } + } + + return 0; +} + static int lm3533_backlight_probe(struct udevice *dev) { struct lm3533_backlight_priv *priv = dev_get_priv(dev); @@ -100,15 +174,22 @@ static int lm3533_backlight_probe(struct udevice *dev) if (device_get_uclass_id(dev->parent) != UCLASS_I2C) return -EPROTONOSUPPORT; - ret = gpio_request_by_name(dev, "enable-gpios", 0, - &priv->enable_gpio, GPIOD_IS_OUT); + dm_gpio_set_value(&priv->enable_gpio, 1); + mdelay(5); + + ret = dm_i2c_reg_clrset(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY, + BOOST_FREQ_MASK, priv->boost_freq << BOOST_FREQ_SHIFT); if (ret) { - log_err("Could not decode enable-gpios (%d)\n", ret); + log_debug("%s: freq config failed %d\n", __func__, ret); return ret; } - priv->def_bl_lvl = dev_read_u32_default(dev, "default-brightness-level", - LM3533_BL_MAX_BRIGHTNESS); + ret = dm_i2c_reg_clrset(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY, + BOOST_OVP_MASK, priv->boost_ovp << BOOST_OVP_SHIFT); + if (ret) { + log_debug("%s: ovp config failed %d\n", __func__, ret); + return ret; + } return 0; } @@ -127,6 +208,7 @@ U_BOOT_DRIVER(lm3533_backlight) = { .name = "lm3533_backlight", .id = UCLASS_PANEL_BACKLIGHT, .of_match = lm3533_backlight_ids, + .of_to_plat = lm3533_backlight_of_to_plat, .probe = lm3533_backlight_probe, .ops = &lm3533_backlight_ops, .priv_auto = sizeof(struct lm3533_backlight_priv), diff --git a/drivers/video/lp855x_backlight.c b/drivers/video/lp855x_backlight.c new file mode 100644 index 00000000000..5debc0aa453 --- /dev/null +++ b/drivers/video/lp855x_backlight.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2011 Texas Instruments + * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT + +#include <malloc.h> +#include <backlight.h> +#include <dm.h> +#include <dm/devres.h> +#include <i2c.h> +#include <log.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <asm/gpio.h> +#include <power/regulator.h> + +#define LP855x_MIN_BRIGHTNESS 0x00 +#define LP855x_MAX_BRIGHTNESS 0xff + +/* LP8550/1/2/3/6 Registers */ +#define LP855X_BRIGHTNESS_CTRL 0x00 +#define LP855X_DEVICE_CTRL 0x01 +#define LP855X_EEPROM_START 0xa0 +#define LP855X_EEPROM_END 0xa7 +#define LP8556_EPROM_START 0x98 +#define LP8556_EPROM_END 0xaf + +/* LP8555/7 Registers */ +#define LP8557_BL_CMD 0x00 +#define LP8557_BL_MASK 0x01 +#define LP8557_BL_ON 0x01 +#define LP8557_BL_OFF 0x00 +#define LP8557_BRIGHTNESS_CTRL 0x04 +#define LP8557_CONFIG 0x10 +#define LP8555_EPROM_START 0x10 +#define LP8555_EPROM_END 0x7a +#define LP8557_EPROM_START 0x10 +#define LP8557_EPROM_END 0x1e + +struct lp855x_rom_data { + u8 addr; + u8 val; +}; + +struct lp855x_backlight_priv; + +/* + * struct lp855x_device_config + * @pre_init_device: init device function call before updating the brightness + * @reg_brightness: register address for brigthenss control + * @reg_devicectrl: register address for device control + * @post_init_device: late init device function call + */ +struct lp855x_device_config { + int (*pre_init_device)(struct udevice *dev); + u8 reg_brightness; + u8 reg_devicectrl; + u8 reg_eepromstart; + u8 reg_eepromend; + int (*post_init_device)(struct udevice *dev); +}; + +struct lp855x_backlight_priv { + struct udevice *supply; /* regulator for VDD input */ + struct udevice *enable; /* regulator for EN/VDDIO input */ + + u8 device_control; + u8 initial_brightness; + + int size_program; + struct lp855x_rom_data *rom_data; + struct lp855x_device_config *cfg; +}; + +static int lp855x_backlight_enable(struct udevice *dev) +{ + struct lp855x_backlight_priv *priv = dev_get_priv(dev); + int ret; + + ret = regulator_set_enable_if_allowed(priv->supply, 1); + if (ret) { + log_debug("%s: enabling power-supply failed (%d)\n", + __func__, ret); + return ret; + } + + ret = regulator_set_enable_if_allowed(priv->enable, 1); + if (ret) { + log_debug("%s: enabling enable-supply failed (%d)\n", + __func__, ret); + return ret; + } + mdelay(2); + + if (priv->cfg->pre_init_device) { + ret = priv->cfg->pre_init_device(dev); + if (ret) { + log_debug("%s: pre init device err: %d\n", + __func__, ret); + return ret; + } + } + + ret = dm_i2c_reg_write(dev, priv->cfg->reg_brightness, + priv->initial_brightness); + if (ret) + return ret; + + ret = dm_i2c_reg_write(dev, priv->cfg->reg_devicectrl, + priv->device_control); + if (ret) + return ret; + + if (priv->size_program > 0) { + int i; + u8 val, addr; + + for (i = 0; i < priv->size_program; i++) { + addr = priv->rom_data[i].addr; + val = priv->rom_data[i].val; + + if (addr < priv->cfg->reg_eepromstart && + addr > priv->cfg->reg_eepromend) + continue; + + ret = dm_i2c_reg_write(dev, addr, val); + if (ret) + return ret; + } + } + + if (priv->cfg->post_init_device) { + ret = priv->cfg->post_init_device(dev); + if (ret) { + log_debug("%s: post init device err: %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +static int lp855x_backlight_set_brightness(struct udevice *dev, int percent) +{ + struct lp855x_backlight_priv *priv = dev_get_priv(dev); + + if (percent == BACKLIGHT_DEFAULT) + percent = priv->initial_brightness; + + if (percent < LP855x_MIN_BRIGHTNESS) + percent = LP855x_MIN_BRIGHTNESS; + + if (percent > LP855x_MAX_BRIGHTNESS) + percent = LP855x_MAX_BRIGHTNESS; + + /* Set brightness level */ + return dm_i2c_reg_write(dev, priv->cfg->reg_brightness, + percent); +} + +static int lp855x_backlight_probe(struct udevice *dev) +{ + struct lp855x_backlight_priv *priv = dev_get_priv(dev); + int rom_length, ret; + + if (device_get_uclass_id(dev->parent) != UCLASS_I2C) + return -EPROTONOSUPPORT; + + priv->cfg = (struct lp855x_device_config *)dev_get_driver_data(dev); + + dev_read_u8(dev, "dev-ctrl", &priv->device_control); + dev_read_u8(dev, "init-brt", &priv->initial_brightness); + + /* Fill ROM platform data if defined */ + rom_length = dev_get_child_count(dev); + if (rom_length > 0) { + struct lp855x_rom_data *rom; + ofnode child; + int i = 0; + + rom = devm_kcalloc(dev, rom_length, sizeof(*rom), GFP_KERNEL); + if (!rom) + return -ENOMEM; + + dev_for_each_subnode(child, dev) { + ofnode_read_u8(child, "rom-addr", &rom[i].addr); + ofnode_read_u8(child, "rom-val", &rom[i].val); + i++; + } + + priv->size_program = rom_length; + priv->rom_data = &rom[0]; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "power-supply", &priv->supply); + if (ret) { + log_err("%s: cannot get power-supply: ret = %d\n", __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "enable-supply", &priv->enable); + if (ret) { + log_err("%s: cannot get enable-supply: ret = %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static const struct backlight_ops lp855x_backlight_ops = { + .enable = lp855x_backlight_enable, + .set_brightness = lp855x_backlight_set_brightness, +}; + +static int lp8556_bl_rst(struct udevice *dev) +{ + int ret; + + /* Reset backlight after updating EPROM settings */ + ret = dm_i2c_reg_clrset(dev, LP855X_DEVICE_CTRL, LP8557_BL_MASK, + LP8557_BL_OFF); + if (ret) + return ret; + + mdelay(10); + + return dm_i2c_reg_clrset(dev, LP855X_DEVICE_CTRL, LP8557_BL_MASK, + LP8557_BL_ON); +} + +static int lp8557_bl_off(struct udevice *dev) +{ + /* BL_ON = 0 before updating EPROM settings */ + return dm_i2c_reg_clrset(dev, LP8557_BL_CMD, LP8557_BL_MASK, + LP8557_BL_OFF); +} + +static int lp8557_bl_on(struct udevice *dev) +{ + /* BL_ON = 1 after updating EPROM settings */ + return dm_i2c_reg_clrset(dev, LP8557_BL_CMD, LP8557_BL_MASK, + LP8557_BL_ON); +} + +static struct lp855x_device_config lp855x_dev_cfg = { + .reg_brightness = LP855X_BRIGHTNESS_CTRL, + .reg_devicectrl = LP855X_DEVICE_CTRL, + .reg_eepromstart = LP855X_EEPROM_START, + .reg_eepromend = LP855X_EEPROM_END, +}; + +static struct lp855x_device_config lp8555_dev_cfg = { + .reg_brightness = LP8557_BRIGHTNESS_CTRL, + .reg_devicectrl = LP8557_CONFIG, + .reg_eepromstart = LP8555_EPROM_START, + .reg_eepromend = LP8555_EPROM_END, + .pre_init_device = lp8557_bl_off, + .post_init_device = lp8557_bl_on, +}; + +static struct lp855x_device_config lp8556_dev_cfg = { + .reg_brightness = LP855X_BRIGHTNESS_CTRL, + .reg_devicectrl = LP855X_DEVICE_CTRL, + .reg_eepromstart = LP8556_EPROM_START, + .reg_eepromend = LP8556_EPROM_END, + .post_init_device = lp8556_bl_rst, +}; + +static struct lp855x_device_config lp8557_dev_cfg = { + .reg_brightness = LP8557_BRIGHTNESS_CTRL, + .reg_devicectrl = LP8557_CONFIG, + .reg_eepromstart = LP8557_EPROM_START, + .reg_eepromend = LP8557_EPROM_END, + .pre_init_device = lp8557_bl_off, + .post_init_device = lp8557_bl_on, +}; + +static const struct udevice_id lp855x_backlight_ids[] = { + { .compatible = "ti,lp8550", .data = (ulong)&lp855x_dev_cfg }, + { .compatible = "ti,lp8551", .data = (ulong)&lp855x_dev_cfg }, + { .compatible = "ti,lp8552", .data = (ulong)&lp855x_dev_cfg }, + { .compatible = "ti,lp8553", .data = (ulong)&lp855x_dev_cfg }, + { .compatible = "ti,lp8555", .data = (ulong)&lp8555_dev_cfg }, + { .compatible = "ti,lp8556", .data = (ulong)&lp8556_dev_cfg }, + { .compatible = "ti,lp8557", .data = (ulong)&lp8557_dev_cfg }, + { } +}; + +U_BOOT_DRIVER(lp855x_backlight) = { + .name = "lp855x_backlight", + .id = UCLASS_PANEL_BACKLIGHT, + .of_match = lp855x_backlight_ids, + .probe = lp855x_backlight_probe, + .ops = &lp855x_backlight_ops, + .priv_auto = sizeof(struct lp855x_backlight_priv), +}; diff --git a/drivers/video/mot-panel.c b/drivers/video/mot-panel.c new file mode 100644 index 00000000000..a9114957867 --- /dev/null +++ b/drivers/video/mot-panel.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Motorola ATRIX 4G and DROID X2 DSI panel driver + * Exact manufacturer and model unknown + * + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <backlight.h> +#include <dm.h> +#include <panel.h> +#include <log.h> +#include <misc.h> +#include <mipi_display.h> +#include <mipi_dsi.h> +#include <asm/gpio.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <power/regulator.h> + +struct mot_panel_priv { + struct udevice *vdd; + struct udevice *vddio; + + struct udevice *backlight; + + struct gpio_desc reset_gpio; +}; + +#define dsi_generic_write_seq(dsi, cmd, seq...) do { \ + static const u8 b[] = { cmd, seq }; \ + int ret; \ + ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \ + if (ret < 0) \ + return ret; \ + } while (0) + +static struct display_timing default_timing = { + .pixelclock.typ = 38250000, + .hactive.typ = 540, + .hfront_porch.typ = 32, + .hback_porch.typ = 32, + .hsync_len.typ = 16, + .vactive.typ = 960, + .vfront_porch.typ = 12, + .vback_porch.typ = 12, + .vsync_len.typ = 8, +}; + +static int mot_es2(struct mipi_dsi_device *dsi) +{ + int ret; + + dsi_generic_write_seq(dsi, 0x55, 0x01); + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + log_debug("%s: failed to exit sleep mode: %d\n", __func__, ret); + return ret; + } + mdelay(120); + + dsi_generic_write_seq(dsi, 0xf4, 0x00, 0xbb, 0x46, 0x53, 0x0c, 0x49, + 0x74, 0x29, 0x12, 0x15, 0x2f, 0x2f, 0x04); + dsi_generic_write_seq(dsi, 0xf8, 0x4b, 0x04, 0x10, 0x1a, 0x2c, 0x2c, + 0x2c, 0x2c, 0x14, 0x12); + + dsi_generic_write_seq(dsi, 0xb5, 0x03, 0x7f, 0x00, 0x80, 0xc7, 0x00); + dsi_generic_write_seq(dsi, 0xb7, 0x66, 0xf6, 0x46, 0x9f, 0x90, 0x99, + 0xff, 0x80, 0x6d, 0x01); + + /* Gamma R */ + dsi_generic_write_seq(dsi, 0xf9, 0x04); + dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x30, 0x12, 0x0e, 0x0c, + 0x22, 0x27, 0x31, 0x2e, 0x07, 0x0f); + dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x30, 0x12, 0x0e, 0x0c, + 0x22, 0x27, 0x31, 0x2e, 0x07, 0x0f); + + /* Gamma G */ + dsi_generic_write_seq(dsi, 0xf9, 0x02); + dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x37, 0x15, 0x15, 0x11, + 0x1f, 0x25, 0x2d, 0x2a, 0x05, 0x0f); + dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x37, 0x15, 0x15, 0x11, + 0x1f, 0x25, 0x2d, 0x2a, 0x05, 0x0f); + + /* Gamma B */ + dsi_generic_write_seq(dsi, 0xf9, 0x01); + dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x3f, 0x16, 0x1f, 0x15, + 0x1f, 0x25, 0x2d, 0x2b, 0x06, 0x0b); + dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x3f, 0x16, 0x1f, 0x15, + 0x1f, 0x25, 0x2d, 0x2b, 0x06, 0x0b); + + /* Gamma W */ + dsi_generic_write_seq(dsi, 0xf9, 0x20); + dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x34, 0x15, 0x1a, 0x11, + 0x1f, 0x23, 0x2d, 0x29, 0x02, 0x08); + dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x34, 0x15, 0x1a, 0x11, + 0x1f, 0x23, 0x2d, 0x29, 0x02, 0x08); + + dsi_generic_write_seq(dsi, 0x53, 0x2c); + dsi_generic_write_seq(dsi, 0x35, 0x00); + + return 0; +} + +static int __maybe_unused mot_es4(struct mipi_dsi_device *dsi) +{ + int ret; + + dsi_generic_write_seq(dsi, 0xd2, 0x04, 0x53); + dsi_generic_write_seq(dsi, 0xd2, 0x05, 0x53); + dsi_generic_write_seq(dsi, 0x55, 0x01); + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + log_debug("%s: failed to exit sleep mode: %d\n", __func__, ret); + return ret; + } + mdelay(120); + + dsi_generic_write_seq(dsi, 0xb5, 0x03, 0x7f, 0x0a, 0x80, 0xff, 0x00); + dsi_generic_write_seq(dsi, 0xb7, 0x7a, 0xf7, 0x4d, 0x91, 0x90, 0xb3, + 0xff, 0x80, 0x6d, 0x01); + dsi_generic_write_seq(dsi, 0xf4, 0x00, 0xbb, 0x46, 0x53, 0x0c, 0x49, + 0x74, 0x29, 0x12, 0x15, 0x37, 0x37, 0x04); + dsi_generic_write_seq(dsi, 0xf8, 0x0a, 0x04, 0x10, 0x2a, 0x35, 0x35, + 0x35, 0x35, 0x21, 0x1a); + + /* Gamma R */ + dsi_generic_write_seq(dsi, 0xf9, 0x04); + dsi_generic_write_seq(dsi, 0xfa, 0x08, 0x1c, 0x1b, 0x0f, 0x0f, 0x0a, + 0x1e, 0x22, 0x27, 0x26, 0x07, 0x0d); + dsi_generic_write_seq(dsi, 0xfb, 0x08, 0x3c, 0x27, 0x0f, 0x0f, 0x0a, + 0x1e, 0x26, 0x31, 0x2f, 0x07, 0x0b); + + /* Gamma G */ + dsi_generic_write_seq(dsi, 0xf9, 0x02); + dsi_generic_write_seq(dsi, 0xfa, 0x30, 0x14, 0x0f, 0x00, 0x06, 0x02, + 0x1e, 0x22, 0x27, 0x27, 0x08, 0x10); + dsi_generic_write_seq(dsi, 0xfb, 0x30, 0x35, 0x0f, 0x00, 0x0a, 0x02, + 0x1c, 0x23, 0x31, 0x2f, 0x08, 0x0e); + + /* Gamma B */ + dsi_generic_write_seq(dsi, 0xf9, 0x01); + dsi_generic_write_seq(dsi, 0xfa, 0x12, 0x1b, 0x26, 0x0e, 0x12, 0x0b, + 0x1e, 0x22, 0x27, 0x27, 0x06, 0x0c); + dsi_generic_write_seq(dsi, 0xfb, 0x12, 0x3b, 0x2c, 0x12, 0x12, 0x0e, + 0x1e, 0x26, 0x31, 0x2f, 0x06, 0x0d); + + /* Gamma W */ + dsi_generic_write_seq(dsi, 0xf9, 0x20); + dsi_generic_write_seq(dsi, 0xfa, 0x37, 0x1b, 0x09, 0x01, 0x06, 0x04, + 0x19, 0x19, 0x22, 0x24, 0x04, 0x15); + dsi_generic_write_seq(dsi, 0xfb, 0x37, 0x3b, 0x17, 0x01, 0x0a, 0x04, + 0x19, 0x1d, 0x2c, 0x2c, 0x04, 0x13); + + dsi_generic_write_seq(dsi, 0x53, 0x2c); + dsi_generic_write_seq(dsi, 0x35, 0x00); + dsi_generic_write_seq(dsi, 0xc3, 0x01, 0x4e); + + return 0; +} + +static int mot_panel_enable_backlight(struct udevice *dev) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + struct mipi_dsi_device *dsi = plat->device; + int ret; + + dsi_generic_write_seq(dsi, 0xf0, 0x5a, 0x5a); + dsi_generic_write_seq(dsi, 0xf1, 0x5a, 0x5a); + dsi_generic_write_seq(dsi, 0xd0, 0x8e); + + ret = mot_es2(dsi); + if (ret) + return ret; + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + log_debug("%s: failed to set display on: %d\n", __func__, ret); + return ret; + } + mdelay(20); + + return 0; +} + +static int mot_panel_set_backlight(struct udevice *dev, int percent) +{ + struct mot_panel_priv *priv = dev_get_priv(dev); + int ret; + + ret = backlight_enable(priv->backlight); + if (ret) + return ret; + + mdelay(5); + + return backlight_set_brightness(priv->backlight, percent); +} + +static int mot_panel_timings(struct udevice *dev, struct display_timing *timing) +{ + memcpy(timing, &default_timing, sizeof(*timing)); + return 0; +} + +static int mot_panel_of_to_plat(struct udevice *dev) +{ + struct mot_panel_priv *priv = dev_get_priv(dev); + int ret; + + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + log_debug("%s: cannot get backlight: ret = %d\n", __func__, ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vdd-supply", &priv->vdd); + if (ret) { + log_debug("%s: cannot get vdd-supply: ret = %d\n", __func__, ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio); + if (ret) { + log_debug("%s: cannot get vddio-supply: ret = %d\n", __func__, ret); + return ret; + } + + ret = gpio_request_by_name(dev, "reset-gpios", 0, + &priv->reset_gpio, GPIOD_IS_OUT); + if (ret) { + log_debug("%s: could not decode reser-gpios (%d)\n", __func__, ret); + return ret; + } + + return 0; +} + +static int mot_panel_hw_init(struct udevice *dev) +{ + struct mot_panel_priv *priv = dev_get_priv(dev); + int ret; + + ret = regulator_set_enable_if_allowed(priv->vddio, 1); + if (ret) { + log_debug("%s: enabling vddio-supply failed (%d)\n", __func__, ret); + return ret; + } + + ret = regulator_set_enable_if_allowed(priv->vdd, 1); + if (ret) { + log_debug("%s: enabling vdd-supply failed (%d)\n", __func__, ret); + return ret; + } + + ret = dm_gpio_set_value(&priv->reset_gpio, 1); + if (ret) { + log_debug("%s: error entering reset (%d)\n", __func__, ret); + return ret; + } + mdelay(50); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_debug("%s: error exiting reset (%d)\n", __func__, ret); + return ret; + } + mdelay(10); + + return 0; +} + +static int mot_panel_probe(struct udevice *dev) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + + /* fill characteristics of DSI data link */ + plat->lanes = 2; + plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_LPM; + + return mot_panel_hw_init(dev); +} + +static const struct panel_ops mot_panel_ops = { + .enable_backlight = mot_panel_enable_backlight, + .set_backlight = mot_panel_set_backlight, + .get_display_timing = mot_panel_timings, +}; + +static const struct udevice_id mot_panel_ids[] = { + { .compatible = "motorola,mot-panel" }, + { } +}; + +U_BOOT_DRIVER(mot_panel) = { + .name = "mot_panel", + .id = UCLASS_PANEL, + .of_match = mot_panel_ids, + .ops = &mot_panel_ops, + .of_to_plat = mot_panel_of_to_plat, + .probe = mot_panel_probe, + .plat_auto = sizeof(struct mipi_dsi_panel_plat), + .priv_auto = sizeof(struct mot_panel_priv), +}; diff --git a/drivers/video/renesas-r61307.c b/drivers/video/renesas-r61307.c index a3697bce5ee..ef6fab1e953 100644 --- a/drivers/video/renesas-r61307.c +++ b/drivers/video/renesas-r61307.c @@ -254,17 +254,17 @@ static int renesas_r61307_hw_init(struct udevice *dev) return ret; } - ret = dm_gpio_set_value(&priv->reset_gpio, 0); + ret = dm_gpio_set_value(&priv->reset_gpio, 1); if (ret) { - log_debug("%s: changing reset-gpio failed (%d)\n", + log_debug("%s: entering reset failed (%d)\n", __func__, ret); return ret; } mdelay(5); - ret = dm_gpio_set_value(&priv->reset_gpio, 1); + ret = dm_gpio_set_value(&priv->reset_gpio, 0); if (ret) { - log_debug("%s: changing reset-gpio failed (%d)\n", + log_debug("%s: exiting reset failed (%d)\n", __func__, ret); return ret; } @@ -281,7 +281,8 @@ static int renesas_r61307_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; return renesas_r61307_hw_init(dev); } @@ -294,7 +295,7 @@ static const struct panel_ops renesas_r61307_ops = { static const struct udevice_id renesas_r61307_ids[] = { { .compatible = "koe,tx13d100vm0eaa" }, - { .compatible = "hitachi,tx13d100vm0eaa" }, + { .compatible = "hit,tx13d100vm0eaa" }, { } }; diff --git a/drivers/video/renesas-r69328.c b/drivers/video/renesas-r69328.c index 9861c3fef11..0954b04b62e 100644 --- a/drivers/video/renesas-r69328.c +++ b/drivers/video/renesas-r69328.c @@ -32,9 +32,11 @@ #define R69328_POWER_SET 0xD1 struct renesas_r69328_priv { + struct udevice *vdd; + struct udevice *vddio; + struct udevice *backlight; - struct gpio_desc enable_gpio; struct gpio_desc reset_gpio; }; @@ -159,10 +161,15 @@ static int renesas_r69328_of_to_plat(struct udevice *dev) return ret; } - ret = gpio_request_by_name(dev, "enable-gpios", 0, - &priv->enable_gpio, GPIOD_IS_OUT); + ret = device_get_supply_regulator(dev, "vdd-supply", &priv->vdd); if (ret) { - log_err("could not decode enable-gpios (%d)\n", ret); + log_err("Cannot get vdd-supply: ret = %d\n", ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio); + if (ret) { + log_err("Cannot get vddio-supply: ret = %d\n", ret); return ret; } @@ -181,25 +188,32 @@ static int renesas_r69328_hw_init(struct udevice *dev) struct renesas_r69328_priv *priv = dev_get_priv(dev); int ret; - ret = dm_gpio_set_value(&priv->enable_gpio, 1); + ret = regulator_set_enable_if_allowed(priv->vddio, 1); if (ret) { - log_debug("%s: error changing enable-gpios (%d)\n", + log_debug("%s: enabling vddio-supply failed (%d)\n", __func__, ret); return ret; } mdelay(5); - ret = dm_gpio_set_value(&priv->reset_gpio, 0); + ret = regulator_set_enable_if_allowed(priv->vdd, 1); if (ret) { - log_debug("%s: error changing reset-gpios (%d)\n", + log_debug("%s: enabling vdd-supply failed (%d)\n", __func__, ret); return ret; } - mdelay(5); ret = dm_gpio_set_value(&priv->reset_gpio, 1); if (ret) { - log_debug("%s: error changing reset-gpios (%d)\n", + log_debug("%s: error entering reset (%d)\n", + __func__, ret); + return ret; + } + mdelay(5); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_debug("%s: error exiting reset (%d)\n", __func__, ret); return ret; } @@ -216,7 +230,8 @@ static int renesas_r69328_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; return renesas_r69328_hw_init(dev); } diff --git a/drivers/video/samsung-ltl106hl02.c b/drivers/video/samsung-ltl106hl02.c index 5e6c11c4be3..1efc9fca610 100644 --- a/drivers/video/samsung-ltl106hl02.c +++ b/drivers/video/samsung-ltl106hl02.c @@ -129,7 +129,7 @@ static int samsung_ltl106hl02_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; return samsung_ltl106hl02_hw_init(dev); } diff --git a/drivers/video/sharp-lq079l1sx01.c b/drivers/video/sharp-lq079l1sx01.c new file mode 100644 index 00000000000..a8197f40fc7 --- /dev/null +++ b/drivers/video/sharp-lq079l1sx01.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sharp LQ079L1SX01 DSI panel driver + * + * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <backlight.h> +#include <dm.h> +#include <panel.h> +#include <log.h> +#include <mipi_dsi.h> +#include <linux/delay.h> +#include <asm/gpio.h> +#include <power/regulator.h> + +struct sharp_lq079l1sx01_priv { + struct udevice *backlight; + struct udevice *panel_sec; + + struct udevice *avdd; + struct udevice *vddio; + struct udevice *vsp; + struct udevice *vsn; + + struct gpio_desc reset_gpio; +}; + +static struct display_timing default_timing = { + .pixelclock.typ = 215000000, + .hactive.typ = 1536, + .hfront_porch.typ = 136, + .hback_porch.typ = 28, + .hsync_len.typ = 28, + .vactive.typ = 2048, + .vfront_porch.typ = 14, + .vback_porch.typ = 8, + .vsync_len.typ = 2, +}; + +static int dcs_write_one(struct mipi_dsi_device *dsi, u8 cmd, u8 data) +{ + return mipi_dsi_dcs_write(dsi, cmd, &data, 1); +} + +static int sharp_lq079l1sx01_configure_link(struct udevice *dev) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + struct mipi_dsi_device *dsi = plat->device; + int ret; + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + log_debug("%s: failed to exit sleep mode %s: %d\n", + __func__, dev->parent->name, ret); + } + mdelay(120); + + ret = dcs_write_one(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff); + if (ret < 0) { + log_debug("%s: failed to SET_DISPLAY_BRIGHTNESS %s: %d\n", + __func__, dev->parent->name, ret); + } + ret = dcs_write_one(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x01); + if (ret < 0) { + log_debug("%s: failed to WRITE_POWER_SAVE %s: %d\n", + __func__, dev->parent->name, ret); + } + ret = dcs_write_one(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x2c); + if (ret < 0) { + log_debug("%s: failed to WRITE_CONTROL_DISPLAY %s: %d\n", + __func__, dev->parent->name, ret); + } + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + log_debug("%s: failed to set panel on %s: %d\n", + __func__, dev->parent->name, ret); + } + + return 0; +} + +static int sharp_lq079l1sx01_enable_backlight(struct udevice *dev) +{ + struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev); + + if (!priv->panel_sec) + return 0; + + sharp_lq079l1sx01_configure_link(dev); + sharp_lq079l1sx01_configure_link(priv->panel_sec); + + return 0; +} + +static int sharp_lq079l1sx01_set_backlight(struct udevice *dev, int percent) +{ + struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev); + int ret; + + if (!priv->panel_sec) + return 0; + + ret = backlight_enable(priv->backlight); + if (ret) + return ret; + + return backlight_set_brightness(priv->backlight, percent); +} + +static int sharp_lq079l1sx01_timings(struct udevice *dev, + struct display_timing *timing) +{ + memcpy(timing, &default_timing, sizeof(*timing)); + return 0; +} + +static int sharp_lq079l1sx01_of_to_plat(struct udevice *dev) +{ + struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev); + int ret; + + /* If node has no link2 it is secondary panel */ + if (!dev_read_bool(dev, "link2")) + return 0; + + ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, + "link2", &priv->panel_sec); + if (ret) { + log_debug("%s: cannot get secondary panel: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + log_debug("%s: cannot get backlight: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "avdd-supply", &priv->avdd); + if (ret) { + log_debug("%s: cannot get avdd-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "vddio-supply", &priv->vddio); + if (ret) { + log_debug("%s: cannot get vddio-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "vsp-supply", &priv->vsp); + if (ret) { + log_debug("%s: cannot get vsp-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "vsn-supply", &priv->vsn); + if (ret) { + log_debug("%s: cannot get vsn-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = gpio_request_by_name(dev, "reset-gpios", 0, + &priv->reset_gpio, GPIOD_IS_OUT); + if (ret) { + log_debug("%s: cannot get reser-gpios (%d)\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int sharp_lq079l1sx01_hw_init(struct udevice *dev) +{ + struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev); + int ret; + + if (!priv->panel_sec) + return 0; + + ret = regulator_set_enable_if_allowed(priv->vddio, 1); + if (ret) { + log_debug("%s: enabling vddio-supply failed (%d)\n", + __func__, ret); + return ret; + } + + ret = regulator_set_enable_if_allowed(priv->avdd, 1); + if (ret) { + log_debug("%s: enabling avdd-supply failed (%d)\n", + __func__, ret); + return ret; + } + + mdelay(12); + + ret = regulator_set_enable_if_allowed(priv->vsp, 1); + if (ret) { + log_debug("%s: enabling vsp-supply failed (%d)\n", + __func__, ret); + return ret; + } + + mdelay(12); + + ret = regulator_set_enable_if_allowed(priv->vsn, 1); + if (ret) { + log_debug("%s: enabling vsn-supply failed (%d)\n", + __func__, ret); + return ret; + } + + mdelay(24); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_debug("%s: error disabling reset-gpios (%d)\n", + __func__, ret); + return ret; + } + udelay(3); + + ret = dm_gpio_set_value(&priv->reset_gpio, 1); + if (ret) { + log_debug("%s: error enabling reset-gpios (%d)\n", + __func__, ret); + return ret; + } + udelay(3); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_debug("%s: error disabling reset-gpios (%d)\n", + __func__, ret); + return ret; + } + mdelay(32); + + return 0; +} + +static int sharp_lq079l1sx01_probe(struct udevice *dev) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + + /* fill characteristics of DSI data link */ + plat->lanes = 4; + plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; + + return sharp_lq079l1sx01_hw_init(dev); +} + +static const struct panel_ops sharp_lq079l1sx01_ops = { + .enable_backlight = sharp_lq079l1sx01_enable_backlight, + .set_backlight = sharp_lq079l1sx01_set_backlight, + .get_display_timing = sharp_lq079l1sx01_timings, +}; + +static const struct udevice_id sharp_lq079l1sx01_ids[] = { + { .compatible = "sharp,lq079l1sx01" }, + { } +}; + +U_BOOT_DRIVER(sharp_lq079l1sx01) = { + .name = "sharp_lq079l1sx01", + .id = UCLASS_PANEL, + .of_match = sharp_lq079l1sx01_ids, + .ops = &sharp_lq079l1sx01_ops, + .of_to_plat = sharp_lq079l1sx01_of_to_plat, + .probe = sharp_lq079l1sx01_probe, + .plat_auto = sizeof(struct mipi_dsi_panel_plat), + .priv_auto = sizeof(struct sharp_lq079l1sx01_priv), +}; diff --git a/drivers/video/sharp-lq101r1sx01.c b/drivers/video/sharp-lq101r1sx01.c index 5d8453fd796..4fdf0da8a94 100644 --- a/drivers/video/sharp-lq101r1sx01.c +++ b/drivers/video/sharp-lq101r1sx01.c @@ -255,6 +255,7 @@ static int sharp_lq101r1sx01_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_LPM; return sharp_lq101r1sx01_hw_init(dev); } diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig new file mode 100644 index 00000000000..d3b8dbb2826 --- /dev/null +++ b/drivers/video/tegra/Kconfig @@ -0,0 +1,51 @@ +config HOST1X_TEGRA + bool "NVIDIA Tegra host1x BUS support" + depends on SIMPLE_BUS + +config VIDEO_TEGRA + bool "Enable Display Controller support on Tegra devices" + depends on OF_CONTROL + select HOST1X_TEGRA + help + Enable support for Display Controller found in Tegra SoC. The + Display Controller Complex integrates two independent display + controllers. Each display controller is capable of interfacing + to an external display device, which can be a parallel interface + or SPI LCD, DVI, an HDMI HDTV, RGB monitor or a MIPI DSI LCD. + Direct interface is supported directly to most LCD displays with + TFT or TFT-like interface. + +config VIDEO_DSI_TEGRA + bool "Enable DSI controller support on Tegra devices" + depends on VIDEO_BRIDGE && PANEL && DM_GPIO + select VIDEO_TEGRA + select VIDEO_MIPI_DSI + help + Enable support for the Display Serial Interface (DSI) found in + Tegra SoC. It is a MIPI standard serial bitstream, intended to + provide a low pin count interface to a display panel. + +config VIDEO_HDMI_TEGRA + bool "Enable HDMI support on Tegra devices" + depends on VIDEO_BRIDGE && DM_I2C + select I2C_EDID + select VIDEO_TEGRA + help + Enable support for the High-Definition Multimedia Interface (HDMI) + found in Tegra SoC. + +config TEGRA_BACKLIGHT_PWM + bool "Enable Tegra DC PWM backlight support" + depends on BACKLIGHT + select VIDEO_TEGRA + help + Enable support for the Display Controller dependent PWM backlight + found in the Tegra SoC and usually used with DSI panels. + +config VIDEO_TEGRA124 + bool "Enable video support on Tegra124" + help + Tegra124 supports many video output options including eDP and + HDMI. At present only eDP is supported by U-Boot. This option + enables this support which can be used on devices which + have an eDP display connected. diff --git a/drivers/video/tegra/Makefile b/drivers/video/tegra/Makefile new file mode 100644 index 00000000000..3c50a0ba3c3 --- /dev/null +++ b/drivers/video/tegra/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_HOST1X_TEGRA) += host1x.o +obj-$(CONFIG_VIDEO_TEGRA) += dc.o +obj-$(CONFIG_VIDEO_DSI_TEGRA) += dsi.o mipi.o mipi-phy.o +obj-$(CONFIG_VIDEO_HDMI_TEGRA) += hdmi.o +obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += dc-pwm-backlight.o + +obj-${CONFIG_VIDEO_TEGRA124} += tegra124/ diff --git a/drivers/video/tegra/TODO b/drivers/video/tegra/TODO new file mode 100644 index 00000000000..1c8c2389a18 --- /dev/null +++ b/drivers/video/tegra/TODO @@ -0,0 +1,5 @@ +Existence of separate Tegra124 video implementations is not an ideal solution +since generic video setup for Tegra already has Tegra124 support of some degree. +It is not possible at the time of this note is written to integrate T124 SOR +and DP without possible regressions. Tegra124 setup for SOR and DP should be +incorporated into existing setup once such opportunity occurs. diff --git a/drivers/video/tegra20/tegra-pwm-backlight.c b/drivers/video/tegra/dc-pwm-backlight.c index 79d8a021a3a..eff10b5563e 100644 --- a/drivers/video/tegra20/tegra-pwm-backlight.c +++ b/drivers/video/tegra/dc-pwm-backlight.c @@ -15,10 +15,7 @@ #include <asm/io.h> #include <asm/gpio.h> -#include "tegra-dc.h" - -#define TEGRA_DISPLAY_A_BASE 0x54200000 -#define TEGRA_DISPLAY_B_BASE 0x54240000 +#include "dc.h" #define TEGRA_PWM_BL_MIN_BRIGHTNESS 0x10 #define TEGRA_PWM_BL_MAX_BRIGHTNESS 0xFF @@ -106,14 +103,11 @@ static int tegra_pwm_backlight_enable(struct udevice *dev) static int tegra_pwm_backlight_probe(struct udevice *dev) { struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev); + ofnode dc = ofnode_get_parent(dev_ofnode(dev)); - if (dev_read_bool(dev, "nvidia,display-b-base")) - priv->dc = (struct dc_ctlr *)TEGRA_DISPLAY_B_BASE; - else - priv->dc = (struct dc_ctlr *)TEGRA_DISPLAY_A_BASE; - + priv->dc = (struct dc_ctlr *)ofnode_get_addr(dc); if (!priv->dc) { - log_err("no display controller address\n"); + log_err("%s: failed to get DC controller\n", __func__); return -EINVAL; } diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra/dc.c index d24aa375b39..f0e3d2c993f 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra/dc.c @@ -1,30 +1,25 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2011 The Chromium OS Authors. + * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com> */ #include <backlight.h> #include <cpu_func.h> +#include <clk.h> #include <dm.h> +#include <dm/ofnode_graph.h> #include <fdtdec.h> #include <log.h> #include <panel.h> -#include <part.h> -#include <pwm.h> #include <video.h> -#include <asm/cache.h> -#include <asm/global_data.h> +#include <video_bridge.h> #include <asm/system.h> -#include <asm/gpio.h> #include <asm/io.h> - #include <asm/arch/clock.h> -#include <asm/arch/funcmux.h> -#include <asm/arch/pinmux.h> #include <asm/arch/powergate.h> -#include <asm/arch/pwm.h> -#include "tegra-dc.h" +#include "dc.h" /* Holder of Tegra per-SOC DC differences */ struct tegra_dc_soc_info { @@ -39,12 +34,14 @@ struct tegra_lcd_priv { int height; /* height in pixels */ enum video_log2_bpp log2_bpp; /* colour depth */ struct display_timing timing; - struct udevice *panel; + struct udevice *panel; /* Panels attached to RGB */ + struct udevice *bridge; /* Bridge linked with DC */ struct dc_ctlr *dc; /* Display controller regmap */ const struct tegra_dc_soc_info *soc; fdt_addr_t frame_buffer; /* Address of frame buffer */ unsigned pixel_clock; /* Pixel clock in Hz */ - int dc_clk[2]; /* Contains clk and its parent */ + struct clk *clk; + struct clk *clk_parent; ulong scdiv; /* Clock divider used by disp_clk_ctrl */ bool rotation; /* 180 degree panel turn */ int pipe; /* DC controller: 0 for A, 1 for B */ @@ -144,10 +141,9 @@ static int update_display_mode(struct tegra_lcd_priv *priv) val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT; val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT; writel(val, &disp->disp_interface_ctrl); - } - if (priv->soc->has_rgb) writel(0x00010001, &disp->shift_clk_opt); + } val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; val |= priv->scdiv << SHIFT_CLK_DIVIDER_SHIFT; @@ -267,7 +263,9 @@ static int setup_window(struct tegra_lcd_priv *priv, win->out_h = priv->height; win->phys_addr = priv->frame_buffer; win->stride = priv->width * (1 << priv->log2_bpp) / 8; - debug("%s: depth = %d\n", __func__, priv->log2_bpp); + + log_debug("%s: depth = %d\n", __func__, priv->log2_bpp); + switch (priv->log2_bpp) { case VIDEO_BPP32: win->fmt = COLOR_DEPTH_R8G8B8A8; @@ -279,7 +277,7 @@ static int setup_window(struct tegra_lcd_priv *priv, break; default: - debug("Unsupported LCD bit depth"); + log_debug("Unsupported LCD bit depth\n"); return -1; } @@ -301,7 +299,8 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, void *default_lcd_base) { struct disp_ctl_win window; - unsigned long rate = clock_get_rate(priv->dc_clk[1]); + unsigned long rate = clk_get_rate(priv->clk_parent); + int ret; priv->frame_buffer = (u32)default_lcd_base; @@ -309,15 +308,10 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, * We halve the rate if DISP1 parent is PLLD, since actual parent * is plld_out0 which is PLLD divided by 2. */ - if (priv->dc_clk[1] == CLOCK_ID_DISPLAY) + if (priv->clk_parent->id == CLOCK_ID_DISPLAY || + priv->clk_parent->id == CLOCK_ID_DISPLAY2) rate /= 2; -#ifndef CONFIG_TEGRA20 - /* PLLD2 obeys same rules as PLLD but it is present only on T30+ */ - if (priv->dc_clk[1] == CLOCK_ID_DISPLAY2) - rate /= 2; -#endif - /* * The pixel clock divider is in 7.1 format (where the bottom bit * represents 0.5). Here we calculate the divider needed to get from @@ -327,14 +321,9 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, if (!priv->scdiv) priv->scdiv = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2; - debug("Display clock %lu, divider %lu\n", rate, priv->scdiv); + log_debug("Display clock %lu, divider %lu\n", rate, priv->scdiv); - /* - * HOST1X is init by default at 150MHz with PLLC as parent - */ - clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_CGENERAL, - 150 * 1000000); - clock_start_periph_pll(priv->dc_clk[0], priv->dc_clk[1], + clock_start_periph_pll(priv->clk->id, priv->clk_parent->id, rate); basic_init(&priv->dc->cmd); @@ -348,8 +337,9 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, if (priv->pixel_clock) update_display_mode(priv); - if (setup_window(priv, &window)) - return -1; + ret = setup_window(priv, &window); + if (ret) + return ret; update_window(priv, &window); @@ -364,10 +354,6 @@ static int tegra_lcd_probe(struct udevice *dev) int ret; /* Initialize the Tegra display controller */ -#ifdef CONFIG_TEGRA20 - funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); -#endif - if (priv->soc->has_pgate) { uint powergate; @@ -378,44 +364,51 @@ static int tegra_lcd_probe(struct udevice *dev) ret = tegra_powergate_power_off(powergate); if (ret < 0) { - log_err("failed to power off DISP gate: %d", ret); + log_debug("failed to power off DISP gate: %d", ret); return ret; } ret = tegra_powergate_sequence_power_up(powergate, - priv->dc_clk[0]); + priv->clk->id); if (ret < 0) { - log_err("failed to power up DISP gate: %d", ret); + log_debug("failed to power up DISP gate: %d", ret); return ret; } } /* Get shift clock divider from Tegra DSI if used */ - if (!strcmp(priv->panel->name, TEGRA_DSI_A) || - !strcmp(priv->panel->name, TEGRA_DSI_B)) { - struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel); + if (priv->bridge) { + if (!strcmp(priv->bridge->driver->name, "tegra_dsi")) { + struct tegra_dc_plat *dc_plat = dev_get_plat(priv->bridge); - priv->scdiv = dc_plat->scdiv; + priv->scdiv = dc_plat->scdiv; + } } /* Clean the framebuffer area */ memset((u8 *)plat->base, 0, plat->size); flush_dcache_all(); - if (tegra_display_probe(priv, (void *)plat->base)) { - debug("%s: Failed to probe display driver\n", __func__); - return -1; + ret = tegra_display_probe(priv, (void *)plat->base); + if (ret) { + log_debug("%s: Failed to probe display driver\n", __func__); + return ret; } -#ifdef CONFIG_TEGRA20 - pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM); - pinmux_tristate_disable(PMUX_PINGRP_GPU); -#endif + if (priv->panel) { + ret = panel_enable_backlight(priv->panel); + if (ret) { + log_debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret); + return ret; + } + } - ret = panel_enable_backlight(priv->panel); - if (ret) { - debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret); - return ret; + if (priv->bridge) { + ret = video_bridge_attach(priv->bridge); + if (ret) { + log_debug("%s: Cannot attach bridge, ret=%d\n", __func__, ret); + return ret; + } } mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size, @@ -427,81 +420,198 @@ static int tegra_lcd_probe(struct udevice *dev) uc_priv->xsize = priv->width; uc_priv->ysize = priv->height; uc_priv->bpix = priv->log2_bpp; - debug("LCD frame buffer at %08x, size %x\n", priv->frame_buffer, - plat->size); + log_debug("LCD frame buffer at %08x, size %x\n", priv->frame_buffer, + plat->size); - return panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT); + if (priv->panel) { + ret = panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT); + if (ret) + return ret; + } + + if (priv->bridge) { + ret = video_bridge_set_backlight(priv->bridge, BACKLIGHT_DEFAULT); + if (ret) + return ret; + } + + return 0; +} + +static int tegra_lcd_configure_rgb(struct udevice *dev, ofnode rgb) +{ + struct tegra_lcd_priv *priv = dev_get_priv(dev); + ofnode remote; + int ret; + + /* DC can have only 1 port */ + remote = ofnode_graph_get_remote_node(rgb, -1, -1); + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, &priv->panel); + if (!ret) + return 0; + + ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, remote, &priv->bridge); + if (!ret) + return 0; + + /* Try legacy method if graph did not work */ + remote = ofnode_parse_phandle(rgb, "nvidia,panel", 0); + if (!ofnode_valid(remote)) + return -EINVAL; + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, &priv->panel); + if (ret) { + log_debug("%s: Cannot find panel for '%s' (ret=%d)\n", + __func__, dev->name, ret); + + ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, remote, + &priv->bridge); + if (ret) { + log_err("%s: Cannot find panel or bridge for '%s' (ret=%d)\n", + __func__, dev->name, ret); + return ret; + } + } + + return 0; +} + +static int tegra_lcd_configure_internal(struct udevice *dev) +{ + struct tegra_lcd_priv *priv = dev_get_priv(dev); + struct tegra_dc_plat *dc_plat; + ofnode host1x = ofnode_get_parent(dev_ofnode(dev)); + ofnode node; + int ret; + + switch (priv->pipe) { + case 0: /* DC0 is usually used for DSI */ + /* Check for ganged DSI configuration */ + ofnode_for_each_subnode(node, host1x) + if (ofnode_name_eq(node, "dsi") && ofnode_is_enabled(node) && + ofnode_read_bool(node, "nvidia,ganged-mode")) + goto exit; + + /* If no master DSI found loop for any active DSI */ + ofnode_for_each_subnode(node, host1x) + if (ofnode_name_eq(node, "dsi") && ofnode_is_enabled(node)) + goto exit; + + log_err("%s: failed to find DSI device for '%s'\n", + __func__, dev->name); + + return -ENODEV; + case 1: /* DC1 is usually used for HDMI */ + ofnode_for_each_subnode(node, host1x) + if (ofnode_name_eq(node, "hdmi")) + goto exit; + + log_err("%s: failed to find HDMI device for '%s'\n", + __func__, dev->name); + + return -ENODEV; + default: + log_debug("Unsupported DC selection\n"); + return -EINVAL; + } + +exit: + ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, node, &priv->bridge); + if (ret) { + log_err("%s: failed to get DSI/HDMI device for '%s' (ret %d)\n", + __func__, dev->name, ret); + return ret; + } + + priv->clk_parent = devm_clk_get(priv->bridge, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get DC clock parent from DSI/HDMI: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); + } + + dc_plat = dev_get_plat(priv->bridge); + + /* Fill the platform data for internal devices */ + dc_plat->dev = dev; + dc_plat->dc = priv->dc; + dc_plat->pipe = priv->pipe; + + return 0; } static int tegra_lcd_of_to_plat(struct udevice *dev) { struct tegra_lcd_priv *priv = dev_get_priv(dev); - const void *blob = gd->fdt_blob; struct display_timing *timing; - int node = dev_of_offset(dev); - int panel_node; - int rgb; + ofnode rgb; int ret; priv->dc = (struct dc_ctlr *)dev_read_addr_ptr(dev); if (!priv->dc) { - debug("%s: No display controller address\n", __func__); + log_debug("%s: No display controller address\n", __func__); return -EINVAL; } priv->soc = (struct tegra_dc_soc_info *)dev_get_driver_data(dev); - ret = clock_decode_pair(dev, priv->dc_clk); - if (ret < 0) { - debug("%s: Cannot decode clocks for '%s' (ret = %d)\n", - __func__, dev->name, ret); - return -EINVAL; + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + log_debug("%s: Could not get DC clock: %ld\n", + __func__, PTR_ERR(priv->clk)); + return PTR_ERR(priv->clk); + } + + priv->clk_parent = devm_clk_get(dev, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get DC clock parent: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); } priv->rotation = dev_read_bool(dev, "nvidia,180-rotation"); priv->pipe = dev_read_u32_default(dev, "nvidia,head", 0); - rgb = fdt_subnode_offset(blob, node, "rgb"); - if (rgb < 0) { - debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n", - __func__, dev->name, rgb); - return -EINVAL; - } - /* - * Sadly the panel phandle is in an rgb subnode so we cannot use - * uclass_get_device_by_phandle(). + * Usual logic of Tegra video routing should be next: + * 1. Check rgb subnode for RGB/LVDS panels or bridges + * 2. If none found, then iterate through bridges bound, + * looking for DSIA or DSIB for DC0 and HDMI for DC1. + * If none of above is valid, then configuration is not + * valid. */ - panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); - if (panel_node < 0) { - debug("%s: Cannot find panel information\n", __func__); - return -EINVAL; - } - ret = uclass_get_device_by_of_offset(UCLASS_PANEL, panel_node, - &priv->panel); - if (ret) { - debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__, - dev->name, ret); - return ret; + rgb = dev_read_subnode(dev, "rgb"); + if (ofnode_valid(rgb) && ofnode_is_enabled(rgb)) { + /* RGB is available, use it */ + ret = tegra_lcd_configure_rgb(dev, rgb); + if (ret) + return ret; + } else { + /* RGB is not available, check for internal devices */ + ret = tegra_lcd_configure_internal(dev); + if (ret) + return ret; } - /* Fill the platform data for internal devices */ - if (!strcmp(priv->panel->name, TEGRA_DSI_A) || - !strcmp(priv->panel->name, TEGRA_DSI_B)) { - struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel); - - dc_plat->dev = dev; - dc_plat->dc = priv->dc; - dc_plat->pipe = priv->pipe; + if (priv->panel) { + ret = panel_get_display_timing(priv->panel, &priv->timing); + if (ret) { + ret = ofnode_decode_display_timing(rgb, 0, &priv->timing); + if (ret) { + log_debug("%s: Cannot read display timing for '%s' (ret=%d)\n", + __func__, dev->name, ret); + return -EINVAL; + } + } } - ret = panel_get_display_timing(priv->panel, &priv->timing); - if (ret) { - ret = fdtdec_decode_display_timing(blob, rgb, 0, &priv->timing); + if (priv->bridge) { + ret = video_bridge_get_display_timing(priv->bridge, &priv->timing); if (ret) { - debug("%s: Cannot read display timing for '%s' (ret=%d)\n", - __func__, dev->name, ret); + log_debug("%s: Cannot read display timing for '%s' (ret=%d)\n", + __func__, dev->name, ret); return -EINVAL; } } @@ -518,23 +628,13 @@ static int tegra_lcd_of_to_plat(struct udevice *dev) static int tegra_lcd_bind(struct udevice *dev) { struct video_uc_plat *plat = dev_get_uclass_plat(dev); - const void *blob = gd->fdt_blob; - int node = dev_of_offset(dev); - int rgb; - - rgb = fdt_subnode_offset(blob, node, "rgb"); - if ((rgb < 0) || !fdtdec_get_is_enabled(blob, rgb)) - return -ENODEV; plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * (1 << LCD_MAX_LOG2_BPP) / 8; - return 0; + return dm_scan_fdt_dev(dev); } -static const struct video_ops tegra_lcd_ops = { -}; - static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .has_timer = true, .has_rgb = true, @@ -564,6 +664,9 @@ static const struct udevice_id tegra_lcd_ids[] = { .compatible = "nvidia,tegra114-dc", .data = (ulong)&tegra114_dc_soc_info }, { + .compatible = "nvidia,tegra124-dc", + .data = (ulong)&tegra114_dc_soc_info + }, { /* sentinel */ } }; @@ -572,7 +675,6 @@ U_BOOT_DRIVER(tegra_lcd) = { .name = "tegra_lcd", .id = UCLASS_VIDEO, .of_match = tegra_lcd_ids, - .ops = &tegra_lcd_ops, .bind = tegra_lcd_bind, .probe = tegra_lcd_probe, .of_to_plat = tegra_lcd_of_to_plat, diff --git a/drivers/video/tegra20/tegra-dc.h b/drivers/video/tegra/dc.h index 7d0c189ec80..2a4013b3355 100644 --- a/drivers/video/tegra20/tegra-dc.h +++ b/drivers/video/tegra/dc.h @@ -14,9 +14,6 @@ /* arch-tegra/dc exists only because T124 uses it */ #include <asm/arch-tegra/dc.h> -#define TEGRA_DSI_A "dsi@54300000" -#define TEGRA_DSI_B "dsi@54400000" - struct tegra_dc_plat { struct udevice *dev; /* Display controller device */ struct dc_ctlr *dc; /* Display controller regmap */ diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra/dsi.c index 6327266dd22..bc308869f4e 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra/dsi.c @@ -5,11 +5,13 @@ */ #include <dm.h> +#include <clk.h> #include <log.h> #include <misc.h> #include <mipi_display.h> #include <mipi_dsi.h> #include <backlight.h> +#include <video_bridge.h> #include <panel.h> #include <reset.h> #include <linux/delay.h> @@ -22,8 +24,8 @@ #include <asm/arch/clock.h> #include <asm/arch-tegra/clk_rst.h> -#include "tegra-dc.h" -#include "tegra-dsi.h" +#include "dc.h" +#include "dsi.h" #include "mipi-phy.h" /* List of supported DSI bridges */ @@ -46,10 +48,13 @@ struct tegra_dsi_priv { enum tegra_dsi_format format; - int dsi_clk; + struct clk *clk; + struct clk *clk_parent; + int video_fifo_depth; int host_fifo_depth; + u32 calibration_pads; u32 version; /* for ganged-mode support */ @@ -246,6 +251,9 @@ static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host, value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC; + if ((msg->flags & MIPI_DSI_MSG_USE_LPM) == 0) + value |= DSI_HOST_CONTROL_HS; + /* * The host FIFO has a maximum of 64 words, so larger transmissions * need to use the video FIFO. @@ -515,8 +523,9 @@ static void tegra_dsi_pad_calibrate(struct dsi_pad_ctrl_reg *pad) writel(value, TEGRA_VI_BASE + (CSI_CIL_PAD_CONFIG << 2)); } -static void tegra_dsi_mipi_calibrate(struct tegra_dsi_priv *priv) +static void tegra_dsi_mipi_calibrate(struct udevice *dev) { + struct tegra_dsi_priv *priv = dev_get_priv(dev); struct dsi_pad_ctrl_reg *pad = &priv->dsi->pad; u32 value; int ret; @@ -545,15 +554,20 @@ static void tegra_dsi_mipi_calibrate(struct tegra_dsi_priv *priv) DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3); writel(value, &pad->pad_ctrl_3); - ret = misc_write(priv->mipi, 0, NULL, 0); + ret = misc_write(priv->mipi, priv->calibration_pads, NULL, 0); if (ret) log_debug("%s: MIPI calibration failed %d\n", __func__, ret); + + if (priv->slave) + tegra_dsi_mipi_calibrate(priv->slave); } -static void tegra_dsi_set_timeout(struct dsi_timeout_reg *rtimeout, +static void tegra_dsi_set_timeout(struct udevice *dev, unsigned long bclk, unsigned int vrefresh) { + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_timeout_reg *rtimeout = &priv->dsi->timeout; unsigned int timeout; u32 value; @@ -569,12 +583,17 @@ static void tegra_dsi_set_timeout(struct dsi_timeout_reg *rtimeout, value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); writel(value, &rtimeout->dsi_to_tally); + + if (priv->slave) + tegra_dsi_set_timeout(priv->slave, bclk, vrefresh); } -static void tegra_dsi_set_phy_timing(struct dsi_timing_reg *ptiming, +static void tegra_dsi_set_phy_timing(struct udevice *dev, unsigned long period, const struct mipi_dphy_timing *dphy_timing) { + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_timing_reg *ptiming = &priv->dsi->ptiming; u32 value; value = DSI_TIMING_FIELD(dphy_timing->hsexit, period, 1) << 24 | @@ -598,6 +617,31 @@ static void tegra_dsi_set_phy_timing(struct dsi_timing_reg *ptiming, DSI_TIMING_FIELD(dphy_timing->tasure, period, 1) << 8 | DSI_TIMING_FIELD(dphy_timing->tago, period, 1); writel(value, &ptiming->dsi_bta_timing); + + if (priv->slave) + tegra_dsi_set_phy_timing(priv->slave, period, dphy_timing); +} + +static u32 tegra_dsi_get_lanes(struct udevice *dev) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + + if (priv->master) { + struct tegra_dsi_priv *mpriv = dev_get_priv(priv->master); + struct mipi_dsi_device *mdevice = &mpriv->device; + + return mdevice->lanes + device->lanes; + } + + if (priv->slave) { + struct tegra_dsi_priv *spriv = dev_get_priv(priv->slave); + struct mipi_dsi_device *sdevice = &spriv->device; + + return device->lanes + sdevice->lanes; + } + + return device->lanes; } static void tegra_dsi_ganged_enable(struct udevice *dev, unsigned int start, @@ -611,7 +655,7 @@ static void tegra_dsi_ganged_enable(struct udevice *dev, unsigned int start, writel(DSI_GANGED_MODE_CONTROL_ENABLE, &ganged->ganged_mode_ctrl); } -static void tegra_dsi_configure(struct udevice *dev, +static void tegra_dsi_configure(struct udevice *dev, unsigned int pipe, unsigned long mode_flags) { struct tegra_dsi_priv *priv = dev_get_priv(dev); @@ -642,7 +686,7 @@ static void tegra_dsi_configure(struct udevice *dev, value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(priv->format) | DSI_CONTROL_LANES(device->lanes - 1) | - DSI_CONTROL_SOURCE(0); + DSI_CONTROL_SOURCE(pipe); writel(value, &misc->dsi_ctrl); writel(priv->video_fifo_depth, &misc->dsi_max_threshold); @@ -680,12 +724,19 @@ static void tegra_dsi_configure(struct udevice *dev, /* horizontal back porch */ hbp = timing->hback_porch.typ * mul / div; - if ((mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0) - hbp += hsw; - /* horizontal front porch */ hfp = timing->hfront_porch.typ * mul / div; + if (priv->master || priv->slave) { + hact /= 2; + hsw /= 2; + hbp = hbp / 2 - 1; + hfp /= 2; + } + + if ((mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0) + hbp += hsw; + /* subtract packet overhead */ hsw -= 10; hbp -= 14; @@ -695,9 +746,6 @@ static void tegra_dsi_configure(struct udevice *dev, writel(hact << 16 | hbp, &len->dsi_pkt_len_2_3); writel(hfp, &len->dsi_pkt_len_4_5); writel(0x0f0f << 16, &len->dsi_pkt_len_6_7); - - /* set SOL delay (for non-burst mode only) */ - writel(8 * mul / div, &misc->dsi_sol_delay); } else { if (priv->master || priv->slave) { /* @@ -717,32 +765,33 @@ static void tegra_dsi_configure(struct udevice *dev, value = MIPI_DCS_WRITE_MEMORY_START << 8 | MIPI_DCS_WRITE_MEMORY_CONTINUE; writel(value, &len->dsi_dcs_cmds); + } - /* set SOL delay */ - if (priv->master || priv->slave) { - unsigned long delay, bclk, bclk_ganged; - unsigned int lanes = device->lanes; - unsigned long htotal = timing->hactive.typ + timing->hfront_porch.typ + - timing->hback_porch.typ + timing->hsync_len.typ; - - /* SOL to valid, valid to FIFO and FIFO write delay */ - delay = 4 + 4 + 2; - delay = DIV_ROUND_UP(delay * mul, div * lanes); - /* FIFO read delay */ - delay = delay + 6; - - bclk = DIV_ROUND_UP(htotal * mul, div * lanes); - bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes); - value = bclk - bclk_ganged + delay + 20; - } else { - /* TODO: revisit for non-ganged mode */ - value = 8 * mul / div; - } - - writel(value, &misc->dsi_sol_delay); + /* set SOL delay */ + if (priv->master || priv->slave) { + unsigned long delay, bclk, bclk_ganged; + unsigned int lanes = tegra_dsi_get_lanes(dev); + unsigned long htotal = timing->hactive.typ + timing->hfront_porch.typ + + timing->hback_porch.typ + timing->hsync_len.typ; + + /* SOL to valid, valid to FIFO and FIFO write delay */ + delay = 4 + 4 + 2; + delay = DIV_ROUND_UP(delay * mul, div * lanes); + /* FIFO read delay */ + delay = delay + 6; + + bclk = DIV_ROUND_UP(htotal * mul, div * lanes); + bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes); + value = bclk - bclk_ganged + delay + 20; + } else { + /* set SOL delay (for non-burst mode only) */ + value = 8 * mul / div; } + writel(value, &misc->dsi_sol_delay); + if (priv->slave) { + tegra_dsi_configure(priv->slave, pipe, mode_flags); /* * TODO: Support modes other than symmetrical left-right * split. @@ -753,15 +802,31 @@ static void tegra_dsi_configure(struct udevice *dev, } } +static void tegra_dsi_enable(struct udevice *dev) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_misc_reg *misc = &priv->dsi->misc; + u32 value; + + /* enable DSI controller */ + value = readl(&misc->dsi_pwr_ctrl); + value |= DSI_POWER_CONTROL_ENABLE; + writel(value, &misc->dsi_pwr_ctrl); + + if (priv->slave) + tegra_dsi_enable(priv->slave); +} + static int tegra_dsi_encoder_enable(struct udevice *dev) { struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct tegra_dc_plat *dc_plat = dev_get_plat(dev); struct mipi_dsi_device *device = &priv->device; struct display_timing *timing = &priv->timing; struct dsi_misc_reg *misc = &priv->dsi->misc; unsigned int mul, div; unsigned long bclk, plld, period; - u32 value; + u32 value, lanes; int ret; /* If for some reasone DSI is enabled then it needs to @@ -780,16 +845,17 @@ static int tegra_dsi_encoder_enable(struct udevice *dev) writel(0, &misc->int_enable); if (priv->version) - tegra_dsi_mipi_calibrate(priv); + tegra_dsi_mipi_calibrate(dev); else tegra_dsi_pad_calibrate(&priv->dsi->pad); tegra_dsi_get_muldiv(device->format, &mul, &div); /* compute byte clock */ - bclk = (timing->pixelclock.typ * mul) / (div * device->lanes); + lanes = tegra_dsi_get_lanes(dev); + bclk = (timing->pixelclock.typ * mul) / (div * lanes); - tegra_dsi_set_timeout(&priv->dsi->timeout, bclk, 60); + tegra_dsi_set_timeout(dev, bclk, 60); /* * Compute bit clock and round up to the next MHz. @@ -813,25 +879,18 @@ static int tegra_dsi_encoder_enable(struct udevice *dev) * The D-PHY timing fields are expressed in byte-clock cycles, so * multiply the period by 8. */ - tegra_dsi_set_phy_timing(&priv->dsi->ptiming, - period * 8, &priv->dphy_timing); + tegra_dsi_set_phy_timing(dev, period * 8, &priv->dphy_timing); /* Perform panel HW setup */ ret = panel_enable_backlight(priv->panel); if (ret) return ret; - tegra_dsi_configure(dev, device->mode_flags); + tegra_dsi_configure(dev, dc_plat->pipe, device->mode_flags); tegra_dc_enable_controller(dev); - /* enable DSI controller */ - value = readl(&misc->dsi_pwr_ctrl); - value |= DSI_POWER_CONTROL_ENABLE; - writel(value, &misc->dsi_pwr_ctrl); - - if (priv->slave) - tegra_dsi_encoder_enable(priv->slave); + tegra_dsi_enable(dev); return 0; } @@ -859,21 +918,35 @@ static void tegra_dsi_init_clocks(struct udevice *dev) struct tegra_dsi_priv *priv = dev_get_priv(dev); struct tegra_dc_plat *dc_plat = dev_get_plat(dev); struct mipi_dsi_device *device = &priv->device; - unsigned int mul, div; + unsigned int mul, div, lanes; unsigned long bclk, plld; - if (!priv->slave) { + /* Switch parents of DSI clocks in case of not standard parent */ + if (priv->clk->id == PERIPH_ID_DSI && + priv->clk_parent->id == CLOCK_ID_DISPLAY2) { + /* Change DSIA clock parent to PLLD2 */ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + + /* DSIA_CLK_SRC */ + setbits_le32(&clkrst->crc_pll[CLOCK_ID_DISPLAY].pll_base, + BIT(25)); + } + + if (priv->clk->id == PERIPH_ID_DSIB && + priv->clk_parent->id == CLOCK_ID_DISPLAY) { /* Change DSIB clock parent to match DSIA */ struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; - clrbits_le32(&clkrst->plld2.pll_base, BIT(25)); /* DSIB_CLK_SRC */ + /* DSIB_CLK_SRC */ + clrbits_le32(&clkrst->plld2.pll_base, BIT(25)); } tegra_dsi_get_muldiv(device->format, &mul, &div); - bclk = (priv->timing.pixelclock.typ * mul) / - (div * device->lanes); + lanes = tegra_dsi_get_lanes(dev); + bclk = (priv->timing.pixelclock.typ * mul) / (div * lanes); plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC); @@ -893,16 +966,16 @@ static void tegra_dsi_init_clocks(struct udevice *dev) switch (clock_get_osc_freq()) { case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */ case CLOCK_OSC_FREQ_48_0: /* OSC is 48Mhz */ - clock_set_rate(CLOCK_ID_DISPLAY, plld, 12, 0, 8); + clock_set_rate(priv->clk_parent->id, plld, 12, 0, 8); break; case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */ - clock_set_rate(CLOCK_ID_DISPLAY, plld, 26, 0, 8); + clock_set_rate(priv->clk_parent->id, plld, 26, 0, 8); break; case CLOCK_OSC_FREQ_13_0: /* OSC is 13Mhz */ case CLOCK_OSC_FREQ_16_8: /* OSC is 16.8Mhz */ - clock_set_rate(CLOCK_ID_DISPLAY, plld, 13, 0, 8); + clock_set_rate(priv->clk_parent->id, plld, 13, 0, 8); break; case CLOCK_OSC_FREQ_19_2: @@ -914,11 +987,7 @@ static void tegra_dsi_init_clocks(struct udevice *dev) break; } - priv->dsi_clk = clock_decode_periph_id(dev); - - clock_enable(priv->dsi_clk); - udelay(2); - reset_set_enable(priv->dsi_clk, 0); + clk_enable(priv->clk); } static int tegra_dsi_ganged_probe(struct udevice *dev) @@ -926,7 +995,7 @@ static int tegra_dsi_ganged_probe(struct udevice *dev) struct tegra_dsi_priv *mpriv = dev_get_priv(dev); struct udevice *gangster; - uclass_get_device_by_phandle(UCLASS_PANEL, dev, + uclass_get_device_by_phandle(UCLASS_VIDEO_BRIDGE, dev, "nvidia,ganged-mode", &gangster); if (gangster) { /* Ganged mode is set */ @@ -955,6 +1024,20 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) return -EINVAL; } + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + log_debug("%s: Could not get DSI clock: %ld\n", + __func__, PTR_ERR(priv->clk)); + return PTR_ERR(priv->clk); + } + + priv->clk_parent = devm_clk_get(dev, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get DSI clock parent: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); + } + priv->video_fifo_depth = 1920; priv->host_fifo_depth = 64; @@ -973,10 +1056,22 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) debug("%s: Cannot get avdd-dsi-csi-supply: error %d\n", __func__, ret); - ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, - "panel", &priv->panel); + /* Check all DSI children */ + device_foreach_child(priv->panel, dev) { + if (device_get_uclass_id(priv->panel) == UCLASS_PANEL) + break; + } + + /* if loop exits without panel device return error */ + if (device_get_uclass_id(priv->panel) != UCLASS_PANEL) { + log_debug("%s: panel not found, ret %d\n", __func__, ret); + return -EINVAL; + } + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, dev_ofnode(priv->panel), + &priv->panel); if (ret) { - printf("%s: Cannot get panel: error %d\n", __func__, ret); + log_debug("%s: Cannot get panel: error %d\n", __func__, ret); return log_ret(ret); } @@ -988,6 +1083,14 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) log_debug("%s: cannot get MIPI: error %d\n", __func__, ret); return ret; } + + ret = dev_read_u32_index(dev, "nvidia,mipi-calibrate", 1, + &priv->calibration_pads); + if (ret) { + log_debug("%s: cannot get calibration pads: error %d\n", + __func__, ret); + return ret; + } } panel_get_display_timing(priv->panel, &priv->timing); @@ -1019,23 +1122,26 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) return 0; } -static const struct panel_ops tegra_dsi_bridge_ops = { - .enable_backlight = tegra_dsi_encoder_enable, +static const struct video_bridge_ops tegra_dsi_bridge_ops = { + .attach = tegra_dsi_encoder_enable, .set_backlight = tegra_dsi_bridge_set_panel, .get_display_timing = tegra_dsi_panel_timings, }; static const struct udevice_id tegra_dsi_bridge_ids[] = { + { .compatible = "nvidia,tegra20-dsi", .data = DSI_V0 }, { .compatible = "nvidia,tegra30-dsi", .data = DSI_V0 }, { .compatible = "nvidia,tegra114-dsi", .data = DSI_V1 }, + { .compatible = "nvidia,tegra124-dsi", .data = DSI_V1 }, { } }; U_BOOT_DRIVER(tegra_dsi) = { .name = "tegra_dsi", - .id = UCLASS_PANEL, + .id = UCLASS_VIDEO_BRIDGE, .of_match = tegra_dsi_bridge_ids, .ops = &tegra_dsi_bridge_ops, + .bind = dm_scan_fdt_dev, .probe = tegra_dsi_bridge_probe, .plat_auto = sizeof(struct tegra_dc_plat), .priv_auto = sizeof(struct tegra_dsi_priv), diff --git a/drivers/video/tegra20/tegra-dsi.h b/drivers/video/tegra/dsi.h index 683c5e31a34..683c5e31a34 100644 --- a/drivers/video/tegra20/tegra-dsi.h +++ b/drivers/video/tegra/dsi.h diff --git a/drivers/video/tegra/hdmi.c b/drivers/video/tegra/hdmi.c new file mode 100644 index 00000000000..bfb48b25187 --- /dev/null +++ b/drivers/video/tegra/hdmi.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 NVIDIA Corporation + * Copyright (c) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <clk.h> +#include <dm.h> +#include <edid.h> +#include <i2c.h> +#include <log.h> +#include <misc.h> +#include <panel.h> +#include <reset.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/time.h> +#include <power/regulator.h> +#include <video_bridge.h> + +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clock.h> + +#include "dc.h" +#include "hdmi.h" + +#define DDCCI_ENTRY_ADDR 0x37 +#define DDCCI_SOURSE_ADDR 0x51 +#define DDCCI_COMMAND_WRITE 0x03 +#define DDCCI_CTRL_BRIGHTNESS 0x10 + +#define HDMI_EDID_I2C_ADDR 0x50 +#define HDMI_REKEY_DEFAULT 56 + +static const char * const hdmi_supplies[] = { + "hdmi-supply", "pll-supply", "vdd-supply" +}; + +struct tmds_config { + unsigned int pclk; + u32 pll0; + u32 pll1; + u32 pe_current; + u32 drive_current; + u32 peak_current; +}; + +struct tegra_hdmi_config { + const struct tmds_config *tmds; + unsigned int num_tmds; + unsigned int max_pclk; + + /* to be filled */ +}; + +struct tegra_hdmi_priv { + struct hdmi_ctlr *hdmi_regmap; + + struct udevice *supplies[ARRAY_SIZE(hdmi_supplies)]; + struct udevice *hdmi_ddc; + + struct gpio_desc hpd; /* hotplug detection gpio */ + struct display_timing timing; + + struct clk *clk; + struct clk *clk_parent; + + int panel_bits_per_colourp; + const struct tegra_hdmi_config *config; +}; + +/* 1280x720p 60hz: EIA/CEA-861-B Format 4 */ +static struct display_timing default_720p_timing = { + .pixelclock.typ = 74250000, + .hactive.typ = 1280, + .hfront_porch.typ = 110, + .hback_porch.typ = 220, + .hsync_len.typ = 40, + .vactive.typ = 720, + .vfront_porch.typ = 5, + .vback_porch.typ = 20, + .vsync_len.typ = 5, + .flags = DISPLAY_FLAGS_HSYNC_HIGH | + DISPLAY_FLAGS_VSYNC_HIGH, +}; + +static const struct tmds_config tegra20_tmds_config[] = { + { /* slow pixel clock modes */ + .pclk = 27000000, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(0) | + SOR_PLL_TX_REG_LOAD(3), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE, + .pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) | + PE_CURRENT1(PE_CURRENT_0_0_mA) | + PE_CURRENT2(PE_CURRENT_0_0_mA) | + PE_CURRENT3(PE_CURRENT_0_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA), + }, + { /* high pixel clock modes */ + .pclk = UINT_MAX, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(1) | + SOR_PLL_TX_REG_LOAD(3), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, + .pe_current = PE_CURRENT0(PE_CURRENT_6_0_mA) | + PE_CURRENT1(PE_CURRENT_6_0_mA) | + PE_CURRENT2(PE_CURRENT_6_0_mA) | + PE_CURRENT3(PE_CURRENT_6_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA), + }, +}; + +static const struct tmds_config tegra30_tmds_config[] = { + { /* 480p modes */ + .pclk = 27000000, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(0) | + SOR_PLL_TX_REG_LOAD(0), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE, + .pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) | + PE_CURRENT1(PE_CURRENT_0_0_mA) | + PE_CURRENT2(PE_CURRENT_0_0_mA) | + PE_CURRENT3(PE_CURRENT_0_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), + }, { /* 720p modes */ + .pclk = 74250000, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(1) | + SOR_PLL_TX_REG_LOAD(0), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, + .pe_current = PE_CURRENT0(PE_CURRENT_5_0_mA) | + PE_CURRENT1(PE_CURRENT_5_0_mA) | + PE_CURRENT2(PE_CURRENT_5_0_mA) | + PE_CURRENT3(PE_CURRENT_5_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), + }, { /* 1080p modes */ + .pclk = UINT_MAX, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(3) | + SOR_PLL_TX_REG_LOAD(0), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, + .pe_current = PE_CURRENT0(PE_CURRENT_5_0_mA) | + PE_CURRENT1(PE_CURRENT_5_0_mA) | + PE_CURRENT2(PE_CURRENT_5_0_mA) | + PE_CURRENT3(PE_CURRENT_5_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), + }, +}; + +static void tegra_dc_enable_controller(struct udevice *dev) +{ + struct tegra_dc_plat *dc_plat = dev_get_plat(dev); + struct dc_ctlr *dc = dc_plat->dc; + u32 value; + + value = readl(&dc->disp.disp_win_opt); + value |= HDMI_ENABLE; + writel(value, &dc->disp.disp_win_opt); + + writel(GENERAL_UPDATE, &dc->cmd.state_ctrl); + writel(GENERAL_ACT_REQ, &dc->cmd.state_ctrl); +} + +static void tegra_hdmi_setup_tmds(struct tegra_hdmi_priv *priv, + const struct tmds_config *tmds) +{ + struct hdmi_ctlr *hdmi = priv->hdmi_regmap; + u32 value; + + writel(tmds->pll0, &hdmi->nv_pdisp_sor_pll0); + writel(tmds->pll1, &hdmi->nv_pdisp_sor_pll1); + writel(tmds->pe_current, &hdmi->nv_pdisp_pe_current); + + writel(tmds->drive_current, &hdmi->nv_pdisp_sor_lane_drive_current); + + value = readl(&hdmi->nv_pdisp_sor_lane_drive_current); + value |= BIT(31); + writel(value, &hdmi->nv_pdisp_sor_lane_drive_current); +} + +static int tegra_hdmi_encoder_enable(struct udevice *dev) +{ + struct tegra_dc_plat *dc_plat = dev_get_plat(dev); + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + struct dc_ctlr *dc = dc_plat->dc; + struct display_timing *dt = &priv->timing; + struct hdmi_ctlr *hdmi = priv->hdmi_regmap; + unsigned long rate, div82; + unsigned int pulse_start, rekey; + int retries = 1000; + u32 value; + int i; + + /* power up sequence */ + value = readl(&hdmi->nv_pdisp_sor_pll0); + value &= ~SOR_PLL_PDBG; + writel(value, &hdmi->nv_pdisp_sor_pll0); + + udelay(20); + + value = readl(&hdmi->nv_pdisp_sor_pll0); + value &= ~SOR_PLL_PWR; + writel(value, &hdmi->nv_pdisp_sor_pll0); + + writel(VSYNC_H_POSITION(1), &dc->disp.disp_timing_opt); + writel(DITHER_CONTROL_DISABLE | BASE_COLOR_SIZE_888, + &dc->disp.disp_color_ctrl); + + /* video_preamble uses h_pulse2 */ + pulse_start = 1 + dt->hsync_len.typ + dt->hback_porch.typ - 10; + + writel(H_PULSE2_ENABLE, &dc->disp.disp_signal_opt0); + + value = PULSE_MODE_NORMAL | PULSE_POLARITY_HIGH | + PULSE_QUAL_VACTIVE | PULSE_LAST_END_A; + writel(value, &dc->disp.h_pulse[H_PULSE2].h_pulse_ctrl); + + value = PULSE_START(pulse_start) | PULSE_END(pulse_start + 8); + writel(value, &dc->disp.h_pulse[H_PULSE2].h_pulse_pos[H_PULSE0_POSITION_A]); + + value = VSYNC_WINDOW_END(0x210) | VSYNC_WINDOW_START(0x200) | + VSYNC_WINDOW_ENABLE; + writel(value, &hdmi->nv_pdisp_hdmi_vsync_window); + + if (dc_plat->pipe) + value = HDMI_SRC_DISPLAYB; + else + value = HDMI_SRC_DISPLAYA; + + if (dt->hactive.typ == 720 && (dt->vactive.typ == 480 || + dt->vactive.typ == 576)) + writel(value | ARM_VIDEO_RANGE_FULL, + &hdmi->nv_pdisp_input_control); + else + writel(value | ARM_VIDEO_RANGE_LIMITED, + &hdmi->nv_pdisp_input_control); + + rate = clock_get_periph_rate(priv->clk->id, priv->clk_parent->id); + div82 = rate / USEC_PER_SEC * 4; + value = SOR_REFCLK_DIV_INT(div82 >> 2) | SOR_REFCLK_DIV_FRAC(div82); + writel(value, &hdmi->nv_pdisp_sor_refclk); + + rekey = HDMI_REKEY_DEFAULT; + value = HDMI_CTRL_REKEY(rekey); + value |= HDMI_CTRL_MAX_AC_PACKET((dt->hsync_len.typ + dt->hback_porch.typ + + dt->hfront_porch.typ - rekey - 18) / 32); + writel(value, &hdmi->nv_pdisp_hdmi_ctrl); + + /* TMDS CONFIG */ + for (i = 0; i < priv->config->num_tmds; i++) { + if (dt->pixelclock.typ <= priv->config->tmds[i].pclk) { + tegra_hdmi_setup_tmds(priv, &priv->config->tmds[i]); + break; + } + } + + writel(SOR_SEQ_PU_PC(0) | SOR_SEQ_PU_PC_ALT(0) | SOR_SEQ_PD_PC(8) | + SOR_SEQ_PD_PC_ALT(8), &hdmi->nv_pdisp_sor_seq_ctl); + + value = SOR_SEQ_INST_WAIT_TIME(1) | SOR_SEQ_INST_WAIT_UNITS_VSYNC | + SOR_SEQ_INST_HALT | SOR_SEQ_INST_PIN_A_LOW | + SOR_SEQ_INST_PIN_B_LOW | SOR_SEQ_INST_DRIVE_PWM_OUT_LO; + + writel(value, &hdmi->nv_pdisp_sor_seq_inst0); + writel(value, &hdmi->nv_pdisp_sor_seq_inst8); + + value = readl(&hdmi->nv_pdisp_sor_cstm); + + value &= ~SOR_CSTM_ROTCLK(~0); + value |= SOR_CSTM_ROTCLK(2); + value |= SOR_CSTM_PLLDIV; + value &= ~SOR_CSTM_LVDS_ENABLE; + value &= ~SOR_CSTM_MODE_MASK; + value |= SOR_CSTM_MODE_TMDS; + + writel(value, &hdmi->nv_pdisp_sor_cstm); + + /* start SOR */ + writel(SOR_PWR_NORMAL_STATE_PU | SOR_PWR_NORMAL_START_NORMAL | + SOR_PWR_SAFE_STATE_PD | SOR_PWR_SETTING_NEW_TRIGGER, + &hdmi->nv_pdisp_sor_pwr); + writel(SOR_PWR_NORMAL_STATE_PU | SOR_PWR_NORMAL_START_NORMAL | + SOR_PWR_SAFE_STATE_PD | SOR_PWR_SETTING_NEW_DONE, + &hdmi->nv_pdisp_sor_pwr); + + do { + if (--retries < 0) + return -ETIME; + value = readl(&hdmi->nv_pdisp_sor_pwr); + } while (value & SOR_PWR_SETTING_NEW_PENDING); + + value = SOR_STATE_ASY_CRCMODE_COMPLETE | + SOR_STATE_ASY_OWNER_HEAD0 | + SOR_STATE_ASY_SUBOWNER_BOTH | + SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A | + SOR_STATE_ASY_DEPOL_POS; + + /* setup sync polarities */ + if (dt->flags & DISPLAY_FLAGS_HSYNC_HIGH) + value |= SOR_STATE_ASY_HSYNCPOL_POS; + + if (dt->flags & DISPLAY_FLAGS_HSYNC_LOW) + value |= SOR_STATE_ASY_HSYNCPOL_NEG; + + if (dt->flags & DISPLAY_FLAGS_VSYNC_HIGH) + value |= SOR_STATE_ASY_VSYNCPOL_POS; + + if (dt->flags & DISPLAY_FLAGS_VSYNC_LOW) + value |= SOR_STATE_ASY_VSYNCPOL_NEG; + + writel(value, &hdmi->nv_pdisp_sor_state2); + + value = SOR_STATE_ASY_HEAD_OPMODE_AWAKE | SOR_STATE_ASY_ORMODE_NORMAL; + writel(value, &hdmi->nv_pdisp_sor_state1); + + writel(0, &hdmi->nv_pdisp_sor_state0); + writel(SOR_STATE_UPDATE, &hdmi->nv_pdisp_sor_state0); + writel(value | SOR_STATE_ATTACHED, + &hdmi->nv_pdisp_sor_state1); + writel(0, &hdmi->nv_pdisp_sor_state0); + + tegra_dc_enable_controller(dev); + + return 0; +} + +/* DDC/CI backlight control */ +static int tegra_hdmi_set_connector(struct udevice *dev, int percent) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + struct udevice *ddc_entry; + struct i2c_msg msg[1]; + u8 checksum = DDCCI_ENTRY_ADDR << 1; + int i, ret; + + ret = dm_i2c_probe(priv->hdmi_ddc, DDCCI_ENTRY_ADDR, 0, &ddc_entry); + if (ret) { + log_debug("%s: cannot probe DDC/CI entry: error %d\n", + __func__, ret); + return 0; + } + + /* + * payload[1] is length: hithest bit OR last 4 bits indicate + * the number of following bytes (excluding checksum) + */ + u8 payload[7] = { DDCCI_SOURSE_ADDR, BIT(7) | (sizeof(payload) - 3), + DDCCI_COMMAND_WRITE, DDCCI_CTRL_BRIGHTNESS, + (u8)(percent & 0xff), (u8)(percent & 0xff), 0 }; + + /* DDC/CI checksum is a simple XOR of all preceding bytes */ + for (i = 0; i < (sizeof(payload) - 1); i++) + checksum ^= payload[i]; + + payload[6] = checksum; + + msg->addr = DDCCI_ENTRY_ADDR; + msg->flags = 0; + msg->len = sizeof(payload); + msg->buf = payload; + + dm_i2c_xfer(ddc_entry, msg, 1); + + return 0; +} + +static int tegra_hdmi_timings(struct udevice *dev, + struct display_timing *timing) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + + memcpy(timing, &priv->timing, sizeof(*timing)); + + return 0; +} + +static void tegra_hdmi_init_clocks(struct udevice *dev) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + u32 n = priv->timing.pixelclock.typ * 2 / USEC_PER_SEC; + + switch (clock_get_osc_freq()) { + case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */ + case CLOCK_OSC_FREQ_48_0: /* OSC is 48Mhz */ + clock_set_rate(priv->clk_parent->id, n, 12, 0, 8); + break; + + case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */ + clock_set_rate(priv->clk_parent->id, n, 26, 0, 8); + break; + + case CLOCK_OSC_FREQ_13_0: /* OSC is 13Mhz */ + case CLOCK_OSC_FREQ_16_8: /* OSC is 16.8Mhz */ + clock_set_rate(priv->clk_parent->id, n, 13, 0, 8); + break; + + case CLOCK_OSC_FREQ_19_2: + case CLOCK_OSC_FREQ_38_4: + default: + /* + * These are not supported. + */ + break; + } + + clock_start_periph_pll(priv->clk->id, priv->clk_parent->id, + priv->timing.pixelclock.typ); +} + +static bool tegra_hdmi_mode_valid(void *hdmi_priv, const struct display_timing *timing) +{ + struct tegra_hdmi_priv *priv = hdmi_priv; + + if (timing->pixelclock.typ > priv->config->max_pclk) + return false; + + return true; +} + +static int tegra_hdmi_decode_edid(struct udevice *dev) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + struct udevice *hdmi_edid; + uchar edid_buf[EDID_SIZE] = { 0 }; + int i, ret; + + /* Poll for 1 sec in case EDID is not ready right after hpd */ + for (i = 0; i < 10; i++) { + ret = dm_i2c_probe(priv->hdmi_ddc, HDMI_EDID_I2C_ADDR, 0, + &hdmi_edid); + if (!ret) + break; + + mdelay(100); + } + if (ret) { + log_debug("%s: cannot probe EDID: error %d\n", + __func__, ret); + return ret; + } + + ret = dm_i2c_read(hdmi_edid, 0, edid_buf, sizeof(edid_buf)); + if (ret) { + log_debug("%s: cannot dump EDID buffer: error %d\n", + __func__, ret); + return ret; + } + + ret = edid_get_timing_validate(edid_buf, sizeof(edid_buf), &priv->timing, + &priv->panel_bits_per_colourp, + tegra_hdmi_mode_valid, priv); + if (ret) { + log_debug("%s: cannot decode EDID info: error %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int tegra_hdmi_wait_hpd(struct tegra_hdmi_priv *priv) +{ + int i; + + /* Poll 1 second for HPD signal */ + for (i = 0; i < 10; i++) { + if (dm_gpio_get_value(&priv->hpd)) + return 0; + + mdelay(100); + } + + return -ETIMEDOUT; +} + +static int tegra_hdmi_probe(struct udevice *dev) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + struct reset_ctl reset_ctl; + int i, ret; + + priv->hdmi_regmap = (struct hdmi_ctlr *)dev_read_addr_ptr(dev); + if (!priv->hdmi_regmap) { + log_debug("%s: no display controller address\n", __func__); + return -EINVAL; + } + + priv->config = (struct tegra_hdmi_config *)dev_get_driver_data(dev); + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + log_debug("%s: Could not get HDMI clock: %ld\n", + __func__, PTR_ERR(priv->clk)); + return PTR_ERR(priv->clk); + } + + priv->clk_parent = devm_clk_get(dev, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get HDMI clock parent: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); + } + + for (i = 0; i < ARRAY_SIZE(hdmi_supplies); i++) { + ret = device_get_supply_regulator(dev, hdmi_supplies[i], + &priv->supplies[i]); + if (ret) { + log_debug("%s: cannot get %s %d\n", __func__, + hdmi_supplies[i], ret); + if (ret != -ENOENT) + return log_ret(ret); + } + + ret = regulator_set_enable_if_allowed(priv->supplies[i], true); + if (ret && ret != -ENOSYS) { + log_debug("%s: cannot enable %s: error %d\n", + __func__, hdmi_supplies[i], ret); + return ret; + } + } + + ret = reset_get_by_name(dev, "hdmi", &reset_ctl); + if (ret) { + log_debug("%s: reset_get_by_name() failed: %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_I2C, dev, + "nvidia,ddc-i2c-bus", + &priv->hdmi_ddc); + if (ret) { + log_debug("%s: cannot get hdmi ddc i2c bus: error %d\n", + __func__, ret); + return ret; + } + + ret = gpio_request_by_name(dev, "nvidia,hpd-gpio", 0, + &priv->hpd, GPIOD_IS_IN); + if (ret) { + log_debug("%s: Could not decode hpd-gpios (%d)\n", + __func__, ret); + return ret; + } + + /* wait for connector */ + ret = tegra_hdmi_wait_hpd(priv); + if (ret) { + /* HPD failed, use default timings */ + memcpy(&priv->timing, &default_720p_timing, + sizeof(default_720p_timing)); + } else { + ret = tegra_hdmi_decode_edid(dev); + if (ret) + memcpy(&priv->timing, &default_720p_timing, + sizeof(default_720p_timing)); + } + + reset_assert(&reset_ctl); + tegra_hdmi_init_clocks(dev); + + mdelay(2); + reset_deassert(&reset_ctl); + + return 0; +} + +static const struct tegra_hdmi_config tegra20_hdmi_config = { + .tmds = tegra20_tmds_config, + .num_tmds = ARRAY_SIZE(tegra20_tmds_config), + .max_pclk = 148500000, /* 1080p */ +}; + +static const struct tegra_hdmi_config tegra30_hdmi_config = { + .tmds = tegra30_tmds_config, + .num_tmds = ARRAY_SIZE(tegra30_tmds_config), + .max_pclk = 148500000, /* 1080p */ +}; + +static const struct video_bridge_ops tegra_hdmi_ops = { + .attach = tegra_hdmi_encoder_enable, + .set_backlight = tegra_hdmi_set_connector, + .get_display_timing = tegra_hdmi_timings, +}; + +static const struct udevice_id tegra_hdmi_ids[] = { + { + .compatible = "nvidia,tegra20-hdmi", + .data = (ulong)&tegra20_hdmi_config + }, { + .compatible = "nvidia,tegra30-hdmi", + .data = (ulong)&tegra30_hdmi_config + }, { + /* sentinel */ + } +}; + +U_BOOT_DRIVER(tegra_hdmi) = { + .name = "tegra_hdmi", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = tegra_hdmi_ids, + .ops = &tegra_hdmi_ops, + .probe = tegra_hdmi_probe, + .plat_auto = sizeof(struct tegra_dc_plat), + .priv_auto = sizeof(struct tegra_hdmi_priv), +}; diff --git a/drivers/video/tegra/hdmi.h b/drivers/video/tegra/hdmi.h new file mode 100644 index 00000000000..d17655973e3 --- /dev/null +++ b/drivers/video/tegra/hdmi.h @@ -0,0 +1,648 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2010 + * NVIDIA Corporation <www.nvidia.com> + */ + +#ifndef _TEGRA_HDMI_H +#define _TEGRA_HDMI_H + +#ifndef __ASSEMBLY__ +#include <linux/bitops.h> +#endif + +/* Register definitions for the Tegra high-definition multimedia interface */ + +/* High-Definition Multimedia Interface (HDMI_) regs */ +struct hdmi_ctlr { + /* Address 0x000 ~ 0x0d2 */ + uint ctxsw; /* _CTXSW */ /* 0x00 */ + + uint nv_pdisp_sor_state0; /* _NV_PDISP_SOR_STATE0 */ + uint nv_pdisp_sor_state1; /* _NV_PDISP_SOR_STATE1 */ + uint nv_pdisp_sor_state2; /* _NV_PDISP_SOR_STATE2 */ + + uint nv_pdisp_rg_hdcp_an_msb; /* _NV_PDISP_RG_HDCP_AN_MSB */ + uint nv_pdisp_rg_hdcp_an_lsb; /* _NV_PDISP_RG_HDCP_AN_LSB */ + uint nv_pdisp_rg_hdcp_cn_msb; /* _NV_PDISP_RG_HDCP_CN_MSB */ + uint nv_pdisp_rg_hdcp_cn_lsb; /* _NV_PDISP_RG_HDCP_CN_LSB */ + uint nv_pdisp_rg_hdcp_aksv_msb; /* _NV_PDISP_RG_HDCP_AKSV_MSB */ + uint nv_pdisp_rg_hdcp_aksv_lsb; /* _NV_PDISP_RG_HDCP_AKSV_LSB */ + uint nv_pdisp_rg_hdcp_bksv_msb; /* _NV_PDISP_RG_HDCP_BKSV_MSB */ + uint nv_pdisp_rg_hdcp_bksv_lsb; /* _NV_PDISP_RG_HDCP_BKSV_LSB */ + uint nv_pdisp_rg_hdcp_cksv_msb; /* _NV_PDISP_RG_HDCP_CKSV_MSB */ + uint nv_pdisp_rg_hdcp_cksv_lsb; /* _NV_PDISP_RG_HDCP_CKSV_LSB */ + uint nv_pdisp_rg_hdcp_dksv_msb; /* _NV_PDISP_RG_HDCP_DKSV_MSB */ + uint nv_pdisp_rg_hdcp_dksv_lsb; /* _NV_PDISP_RG_HDCP_DKSV_LSB */ + uint nv_pdisp_rg_hdcp_ctrl; /* _NV_PDISP_RG_HDCP_CTRL */ /* 0x10 */ + uint nv_pdisp_rg_hdcp_cmode; /* _NV_PDISP_RG_HDCP_CMODE */ + uint nv_pdisp_rg_hdcp_mprime_msb; /* _NV_PDISP_RG_HDCP_MPRIME_MSB */ + uint nv_pdisp_rg_hdcp_mprime_lsb; /* _NV_PDISP_RG_HDCP_MPRIME_LSB */ + uint nv_pdisp_rg_hdcp_sprime_msb; /* _NV_PDISP_RG_HDCP_SPRIME_MSB */ + uint nv_pdisp_rg_hdcp_sprime_lsb2; /* _NV_PDISP_RG_HDCP_SPRIME_LSB2 */ + uint nv_pdisp_rg_hdcp_sprime_lsb1; /* _NV_PDISP_RG_HDCP_SPRIME_LSB1 */ + uint nv_pdisp_rg_hdcp_ri; /* _NV_PDISP_RG_HDCP_RI */ + uint nv_pdisp_rg_hdcp_cs_msb; /* _NV_PDISP_RG_HDCP_CS_MSB */ + uint nv_pdisp_rg_hdcp_cs_lsb; /* _NV_PDISP_RG_HDCP_CS_LSB */ + + uint nv_pdisp_hdmi_audio_emu0; /* _NV_PDISP_HDMI_AUDIO_EMU0 */ + uint nv_pdisp_hdmi_audio_emu_rdata0; /* _NV_PDISP_HDMI_AUDIO_EMU_RDATA0 */ + uint nv_pdisp_hdmi_audio_emu1; /* _NV_PDISP_HDMI_AUDIO_EMU1 */ + uint nv_pdisp_hdmi_audio_emu2; /* _NV_PDISP_HDMI_AUDIO_EMU2 */ + uint nv_pdisp_hdmi_audio_infoframe_ctrl; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL */ + uint nv_pdisp_hdmi_audio_infoframe_status; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS */ + uint nv_pdisp_hdmi_audio_infoframe_header; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER */ /* 0x20 */ + uint nv_pdisp_hdmi_audio_infoframe_subpack0_low; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW */ + uint nv_pdisp_hdmi_audio_infoframe_subpack0_high; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH */ + + uint nv_pdisp_hdmi_avi_infoframe_ctrl; /* _NV_PDISP_HDMI_AVI_INFOFRAME_CTRL */ + uint nv_pdisp_hdmi_avi_infoframe_status; /* _NV_PDISP_HDMI_AVI_INFOFRAME_STATUS */ + uint nv_pdisp_hdmi_avi_infoframe_header; /* _NV_PDISP_HDMI_AVI_INFOFRAME_HEADER */ + uint nv_pdisp_hdmi_avi_infoframe_subpack0_low; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW */ + uint nv_pdisp_hdmi_avi_infoframe_subpack0_high; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH */ + uint nv_pdisp_hdmi_avi_infoframe_subpack1_low; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW */ + uint nv_pdisp_hdmi_avi_infoframe_subpack1_high; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH */ + + uint nv_pdisp_hdmi_generic_ctrl; /* _NV_PDISP_HDMI_GENERIC_CTRL */ + uint nv_pdisp_hdmi_generic_status; /* _NV_PDISP_HDMI_GENERIC_STATUS */ + uint nv_pdisp_hdmi_generic_header; /* _NV_PDISP_HDMI_GENERIC_HEADER */ + uint nv_pdisp_hdmi_generic_subpack0_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW */ + uint nv_pdisp_hdmi_generic_subpack0_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH */ + uint nv_pdisp_hdmi_generic_subpack1_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW */ + uint nv_pdisp_hdmi_generic_subpack1_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH */ + uint nv_pdisp_hdmi_generic_subpack2_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW */ + uint nv_pdisp_hdmi_generic_subpack2_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH */ + uint nv_pdisp_hdmi_generic_subpack3_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW */ + uint nv_pdisp_hdmi_generic_subpack3_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH */ + + uint nv_pdisp_hdmi_acr_ctrl; /* _NV_PDISP_HDMI_ACR_CTRL */ + uint nv_pdisp_hdmi_acr_0320_subpack_low; /* _NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0320_subpack_high; /* _NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_0441_subpack_low; /* _NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0441_subpack_high; /* _NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_0882_subpack_low; /* _NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0882_subpack_high; /* _NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_1764_subpack_low; /* _NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_1764_subpack_high; /* _NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_0480_subpack_low; /* _NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0480_subpack_high; /* _NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_0960_subpack_low; /* _NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0960_subpack_high; /* _NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_1920_subpack_low; /* _NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_1920_subpack_high; /* _NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH */ + + uint nv_pdisp_hdmi_ctrl; /* _NV_PDISP_HDMI_CTRL */ + uint nv_pdisp_hdmi_vsync_keepout; /* _NV_PDISP_HDMI_VSYNC_KEEPOUT */ + uint nv_pdisp_hdmi_vsync_window; /* _NV_PDISP_HDMI_VSYNC_WINDOW */ + uint nv_pdisp_hdmi_gcp_ctrl; /* _NV_PDISP_HDMI_GCP_CTRL */ + uint nv_pdisp_hdmi_gcp_status; /* _NV_PDISP_HDMI_GCP_STATUS */ + uint nv_pdisp_hdmi_gcp_subpack; /* _NV_PDISP_HDMI_GCP_SUBPACK */ + uint nv_pdisp_hdmi_channel_status1; /* _NV_PDISP_HDMI_CHANNEL_STATUS1 */ + uint nv_pdisp_hdmi_channel_status2; /* _NV_PDISP_HDMI_CHANNEL_STATUS2 */ + uint nv_pdisp_hdmi_emu0; /* _NV_PDISP_HDMI_EMU0 */ + uint nv_pdisp_hdmi_emu1; /* _NV_PDISP_HDMI_EMU1 */ + uint nv_pdisp_hdmi_emu1_rdata; /* _NV_PDISP_HDMI_EMU1_RDATA */ + uint nv_pdisp_hdmi_spare; /* _NV_PDISP_HDMI_SPARE */ + uint nv_pdisp_hdmi_spdif_chn_status1; /* _NV_PDISP_HDMI_SPDIF_CHN_STATUS1 */ + uint nv_pdisp_hdmi_spdif_chn_status2; /* _NV_PDISP_HDMI_SPDIF_CHN_STATUS2 */ + + uint nv_pdisp_hdcprif_rom_ctrl; /* _NV_PDISP_HDCPRIF_ROM_CTRL */ + + uint unused; + + uint nv_pdisp_sor_cap; /* _NV_PDISP_SOR_CAP */ + uint nv_pdisp_sor_pwr; /* _NV_PDISP_SOR_PWR */ + uint nv_pdisp_sor_test; /* _NV_PDISP_SOR_TEST */ + uint nv_pdisp_sor_pll0; /* _NV_PDISP_SOR_PLL0 */ + uint nv_pdisp_sor_pll1; /* _NV_PDISP_SOR_PLL1 */ + uint nv_pdisp_sor_pll2; /* _NV_PDISP_SOR_PLL2 */ + uint nv_pdisp_sor_cstm; /* _NV_PDISP_SOR_CSTM */ + uint nv_pdisp_sor_lvds; /* _NV_PDISP_SOR_LVDS */ + uint nv_pdisp_sor_crca; /* _NV_PDISP_SOR_CRCA */ + uint nv_pdisp_sor_crcb; /* _NV_PDISP_SOR_CRCB */ + uint nv_pdisp_sor_blank; /* _NV_PDISP_SOR_BLANK */ + + uint nv_pdisp_sor_seq_ctl; /* _NV_PDISP_SOR_SEQ_CTL */ + uint nv_pdisp_sor_seq_inst0; /* _NV_PDISP_SOR_SEQ_INST0 */ + uint nv_pdisp_sor_seq_inst1; /* _NV_PDISP_SOR_SEQ_INST1 */ + uint nv_pdisp_sor_seq_inst2; /* _NV_PDISP_SOR_SEQ_INST2 */ + uint nv_pdisp_sor_seq_inst3; /* _NV_PDISP_SOR_SEQ_INST3 */ + uint nv_pdisp_sor_seq_inst4; /* _NV_PDISP_SOR_SEQ_INST4 */ + uint nv_pdisp_sor_seq_inst5; /* _NV_PDISP_SOR_SEQ_INST5 */ + uint nv_pdisp_sor_seq_inst6; /* _NV_PDISP_SOR_SEQ_INST6 */ + uint nv_pdisp_sor_seq_inst7; /* _NV_PDISP_SOR_SEQ_INST7 */ + uint nv_pdisp_sor_seq_inst8; /* _NV_PDISP_SOR_SEQ_INST8 */ + uint nv_pdisp_sor_seq_inst9; /* _NV_PDISP_SOR_SEQ_INST9 */ + uint nv_pdisp_sor_seq_insta; /* _NV_PDISP_SOR_SEQ_INSTA */ + uint nv_pdisp_sor_seq_instb; /* _NV_PDISP_SOR_SEQ_INSTB */ + uint nv_pdisp_sor_seq_instc; /* _NV_PDISP_SOR_SEQ_INSTC */ + uint nv_pdisp_sor_seq_instd; /* _NV_PDISP_SOR_SEQ_INSTD */ + uint nv_pdisp_sor_seq_inste; /* _NV_PDISP_SOR_SEQ_INSTE */ + uint nv_pdisp_sor_seq_instf; /* _NV_PDISP_SOR_SEQ_INSTF */ + + uint unused1[2]; + + uint nv_pdisp_sor_vcrca0; /* _NV_PDISP_SOR_VCRCA0 */ + uint nv_pdisp_sor_vcrca1; /* _NV_PDISP_SOR_VCRCA1 */ + uint nv_pdisp_sor_ccrca0; /* _NV_PDISP_SOR_CCRCA0 */ + uint nv_pdisp_sor_ccrca1; /* _NV_PDISP_SOR_CCRCA1 */ + + uint nv_pdisp_sor_edataa0; /* _NV_PDISP_SOR_EDATAA0 */ + uint nv_pdisp_sor_edataa1; /* _NV_PDISP_SOR_EDATAA1 */ + + uint nv_pdisp_sor_counta0; /* _NV_PDISP_SOR_COUNTA0 */ + uint nv_pdisp_sor_counta1; /* _NV_PDISP_SOR_COUNTA1 */ + + uint nv_pdisp_sor_debuga0; /* _NV_PDISP_SOR_DEBUGA0 */ + uint nv_pdisp_sor_debuga1; /* _NV_PDISP_SOR_DEBUGA1 */ + + uint nv_pdisp_sor_trig; /* _NV_PDISP_SOR_TRIG */ + uint nv_pdisp_sor_mscheck; /* _NV_PDISP_SOR_MSCHECK */ + uint nv_pdisp_sor_lane_drive_current; /* _NV_PDISP_SOR_LANE_DRIVE_CURRENT */ + + uint nv_pdisp_audio_debug0; /* _NV_PDISP_AUDIO_DEBUG0 0x7f */ + uint nv_pdisp_audio_debug1; /* _NV_PDISP_AUDIO_DEBUG1 0x80 */ + uint nv_pdisp_audio_debug2; /* _NV_PDISP_AUDIO_DEBUG2 0x81 */ + + uint nv_pdisp_audio_fs1; /* _NV_PDISP_AUDIO_FS1 0x82 */ + uint nv_pdisp_audio_fs2; /* _NV_PDISP_AUDIO_FS2 */ + uint nv_pdisp_audio_fs3; /* _NV_PDISP_AUDIO_FS3 */ + uint nv_pdisp_audio_fs4; /* _NV_PDISP_AUDIO_FS4 */ + uint nv_pdisp_audio_fs5; /* _NV_PDISP_AUDIO_FS5 */ + uint nv_pdisp_audio_fs6; /* _NV_PDISP_AUDIO_FS6 */ + uint nv_pdisp_audio_fs7; /* _NV_PDISP_AUDIO_FS7 0x88 */ + + uint nv_pdisp_audio_pulse_width; /* _NV_PDISP_AUDIO_PULSE_WIDTH */ + uint nv_pdisp_audio_threshold; /* _NV_PDISP_AUDIO_THRESHOLD */ + uint nv_pdisp_audio_cntrl0; /* _NV_PDISP_AUDIO_CNTRL0 */ + uint nv_pdisp_audio_n; /* _NV_PDISP_AUDIO_N */ + uint nv_pdisp_audio_nval[7]; /* _NV_PDISP_AUDIO_NVAL */ + + uint nv_pdisp_hdcprif_rom_timing; /* _NV_PDISP_HDCPRIF_ROM_TIMING */ + uint nv_pdisp_sor_refclk; /* _NV_PDISP_SOR_REFCLK */ + uint nv_pdisp_crc_control; /* _NV_PDISP_CRC_CONTROL */ + uint nv_pdisp_input_control; /* _NV_PDISP_INPUT_CONTROL */ + uint nv_pdisp_scratch; /* _NV_PDISP_SCRATCH */ + uint nv_pdisp_pe_current; /* _NV_PDISP_PE_CURRENT */ + + uint nv_pdisp_key_ctrl; /* _NV_PDISP_KEY_CTRL */ + uint nv_pdisp_key_debug0; /* _NV_PDISP_KEY_DEBUG0 */ + uint nv_pdisp_key_debug1; /* _NV_PDISP_KEY_DEBUG1 */ + uint nv_pdisp_key_debug2; /* _NV_PDISP_KEY_DEBUG2 */ + uint nv_pdisp_key_hdcp_key_0; /* _NV_PDISP_KEY_HDCP_KEY_0 */ + uint nv_pdisp_key_hdcp_key_1; /* _NV_PDISP_KEY_HDCP_KEY_1 */ + uint nv_pdisp_key_hdcp_key_2; /* _NV_PDISP_KEY_HDCP_KEY_2 */ + uint nv_pdisp_key_hdcp_key_3; /* _NV_PDISP_KEY_HDCP_KEY_3 */ + uint nv_pdisp_key_hdcp_key_trig; /* _NV_PDISP_KEY_HDCP_KEY_3 */ + uint nv_pdisp_key_skey_index; /* _NV_PDISP_KEY_HDCP_KEY_3 */ /* 0xa3 */ + + uint unused2[8]; + + uint nv_pdisp_sor_audio_cntrl0; /* _NV_PDISP_SOR_AUDIO_CNTRL0 */ /* 0xac */ + uint nv_pdisp_sor_audio_debug; /* _NV_PDISP_SOR_AUDIO_DEBUG */ + uint nv_pdisp_sor_audio_spare0; /* _NV_PDISP_SOR_AUDIO_SPARE0 */ + uint nv_pdisp_sor_audio_nval[7]; /* _NV_PDISP_SOR_AUDIO_NVAL 0xaf ~ 0xb5 */ + uint nv_pdisp_sor_audio_hda_scratch[4]; /* _NV_PDISP_SOR_AUDIO_HDA_SCRATCH 0xb6 ~ 0xb9 */ + uint nv_pdisp_sor_audio_hda_codec_scratch[2]; /* _NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH 0xba ~ 0xbb */ + + uint nv_pdisp_sor_audio_hda_eld_bufwr; /* _NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR */ + uint nv_pdisp_sor_audio_hda_presense; /* _NV_PDISP_SOR_AUDIO_HDA_PRESENSE */ + uint nv_pdisp_sor_audio_hda_cp; /* _NV_PDISP_SOR_AUDIO_HDA_CP */ + uint nv_pdisp_sor_audio_aval[8]; /* _NV_PDISP_SOR_AUDIO_AVAL */ + uint nv_pdisp_sor_audio_gen_ctrl; /* _NV_PDISP_SOR_AUDIO_GEN_CTRL */ + + uint unused3[4]; + + uint nv_pdisp_int_status; /* _NV_PDISP_INT_STATUS */ + uint nv_pdisp_int_mask; /* _NV_PDISP_INT_MASK */ + uint nv_pdisp_int_enable; /* _NV_PDISP_INT_ENABLE */ + + uint unused4[2]; + + uint nv_pdisp_sor_io_peak_current; /* _NV_PDISP_SOR_IO_PEAK_CURRENT */ + uint nv_pdisp_sor_pad_ctls0; /* _NV_PDISP_SOR_PAD_CTLS0 */ +}; + +/* HDMI_NV_PDISP_SOR_STATE0 0x01 */ +#define SOR_STATE_UPDATE BIT(0) + +/* HDMI_NV_PDISP_SOR_STATE1 0x02 */ +#define SOR_STATE_ASY_HEAD_OPMODE_AWAKE BIT(1) +#define SOR_STATE_ASY_ORMODE_NORMAL BIT(2) +#define SOR_STATE_ATTACHED BIT(3) + +/* HDMI_NV_PDISP_SOR_STATE2 0x03 */ +#define SOR_STATE_ASY_OWNER_NONE (0 << 0) +#define SOR_STATE_ASY_OWNER_HEAD0 (1 << 0) +#define SOR_STATE_ASY_SUBOWNER_NONE (0 << 4) +#define SOR_STATE_ASY_SUBOWNER_SUBHEAD0 (1 << 4) +#define SOR_STATE_ASY_SUBOWNER_SUBHEAD1 (2 << 4) +#define SOR_STATE_ASY_SUBOWNER_BOTH (3 << 4) +#define SOR_STATE_ASY_CRCMODE_ACTIVE (0 << 6) +#define SOR_STATE_ASY_CRCMODE_COMPLETE (1 << 6) +#define SOR_STATE_ASY_CRCMODE_NON_ACTIVE (2 << 6) +#define SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A (1 << 8) +#define SOR_STATE_ASY_PROTOCOL_CUSTOM (15 << 8) +#define SOR_STATE_ASY_HSYNCPOL_POS (0 << 12) +#define SOR_STATE_ASY_HSYNCPOL_NEG (1 << 12) +#define SOR_STATE_ASY_VSYNCPOL_POS (0 << 13) +#define SOR_STATE_ASY_VSYNCPOL_NEG (1 << 13) +#define SOR_STATE_ASY_DEPOL_POS (0 << 14) +#define SOR_STATE_ASY_DEPOL_NEG (1 << 14) + +#define INFOFRAME_CTRL_ENABLE BIT(0) +#define INFOFRAME_HEADER_TYPE(x) (((x) & 0xff) << 0) +#define INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) << 8) +#define INFOFRAME_HEADER_LEN(x) (((x) & 0x0f) << 16) + +/* HDMI_NV_PDISP_HDMI_GENERIC_CTRL 0x2a */ +#define GENERIC_CTRL_ENABLE BIT(0) +#define GENERIC_CTRL_OTHER BIT(4) +#define GENERIC_CTRL_SINGLE BIT(8) +#define GENERIC_CTRL_HBLANK BIT(12) +#define GENERIC_CTRL_AUDIO BIT(16) + +/* HDMI_NV_PDISP_HDMI_ACR_* */ +#define ACR_SUBPACK_CTS(x) (((x) & 0xffffff) << 8) +#define ACR_SUBPACK_N(x) (((x) & 0xffffff) << 0) +#define ACR_ENABLE BIT(31) + +/* HDMI_NV_PDISP_HDMI_CTRL 0x44 */ +#define HDMI_CTRL_REKEY(x) (((x) & 0x7f) << 0) +#define HDMI_CTRL_MAX_AC_PACKET(x) (((x) & 0x1f) << 16) +#define HDMI_CTRL_ENABLE BIT(30) + +/* HDMI_NV_PDISP_HDMI_VSYNC_* */ +#define VSYNC_WINDOW_END(x) (((x) & 0x3ff) << 0) +#define VSYNC_WINDOW_START(x) (((x) & 0x3ff) << 16) +#define VSYNC_WINDOW_ENABLE BIT(31) + +/* HDMI_NV_PDISP_HDMI_SPARE 0x4f */ +#define SPARE_HW_CTS BIT(0) +#define SPARE_FORCE_SW_CTS BIT(1) +#define SPARE_CTS_RESET_VAL(x) (((x) & 0x7) << 16) + +/* HDMI_NV_PDISP_SOR_PWR 0x55 */ +#define SOR_PWR_NORMAL_STATE_PD (0 << 0) +#define SOR_PWR_NORMAL_STATE_PU (1 << 0) +#define SOR_PWR_NORMAL_START_NORMAL (0 << 1) +#define SOR_PWR_NORMAL_START_ALT (1 << 1) +#define SOR_PWR_SAFE_STATE_PD (0 << 16) +#define SOR_PWR_SAFE_STATE_PU (1 << 16) +#define SOR_PWR_SETTING_NEW_DONE (0 << 31) +#define SOR_PWR_SETTING_NEW_PENDING (1 << 31) +#define SOR_PWR_SETTING_NEW_TRIGGER (1 << 31) + +/* HDMI_NV_PDISP_SOR_PLL0 0x57 */ +#define SOR_PLL_PWR BIT(0) +#define SOR_PLL_PDBG BIT(1) +#define SOR_PLL_VCAPD BIT(2) +#define SOR_PLL_PDPORT BIT(3) +#define SOR_PLL_RESISTORSEL BIT(4) +#define SOR_PLL_PULLDOWN BIT(5) +#define SOR_PLL_VCOCAP(x) (((x) & 0xf) << 8) +#define SOR_PLL_BG_V17_S(x) (((x) & 0xf) << 12) +#define SOR_PLL_FILTER(x) (((x) & 0xf) << 16) +#define SOR_PLL_ICHPMP(x) (((x) & 0xf) << 24) +#define SOR_PLL_TX_REG_LOAD(x) (((x) & 0xf) << 28) + +/* HDMI_NV_PDISP_SOR_PLL1 0x58 */ +#define SOR_PLL_TMDS_TERM_ENABLE BIT(8) +#define SOR_PLL_TMDS_TERMADJ(x) (((x) & 0xf) << 9) +#define SOR_PLL_LOADADJ(x) (((x) & 0xf) << 20) +#define SOR_PLL_PE_EN BIT(28) +#define SOR_PLL_HALF_FULL_PE BIT(29) +#define SOR_PLL_S_D_PIN_PE BIT(30) + +/* HDMI_NV_PDISP_SOR_CSTM 0x5a */ +#define SOR_CSTM_ROTCLK(x) (((x) & 0xf) << 24) +#define SOR_CSTM_PLLDIV BIT(21) +#define SOR_CSTM_LVDS_ENABLE BIT(16) +#define SOR_CSTM_MODE_LVDS (0 << 12) +#define SOR_CSTM_MODE_TMDS (1 << 12) +#define SOR_CSTM_MODE_MASK (3 << 12) + +/* HDMI_NV_PDISP_SOR_SEQ_CTL 0x5f */ +#define SOR_SEQ_PU_PC(x) (((x) & 0xf) << 0) +#define SOR_SEQ_PU_PC_ALT(x) (((x) & 0xf) << 4) +#define SOR_SEQ_PD_PC(x) (((x) & 0xf) << 8) +#define SOR_SEQ_PD_PC_ALT(x) (((x) & 0xf) << 12) +#define SOR_SEQ_PC(x) (((x) & 0xf) << 16) +#define SOR_SEQ_STATUS BIT(28) +#define SOR_SEQ_SWITCH BIT(30) + +/* HDMI_NV_PDISP_SOR_SEQ_INST(x) (0x60 + (x)) */ +#define SOR_SEQ_INST_WAIT_TIME(x) (((x) & 0x3ff) << 0) +#define SOR_SEQ_INST_WAIT_UNITS_VSYNC (2 << 12) +#define SOR_SEQ_INST_HALT (1 << 15) +#define SOR_SEQ_INST_PIN_A_LOW (0 << 21) +#define SOR_SEQ_INST_PIN_A_HIGH (1 << 21) +#define SOR_SEQ_INST_PIN_B_LOW (0 << 22) +#define SOR_SEQ_INST_PIN_B_HIGH (1 << 22) +#define SOR_SEQ_INST_DRIVE_PWM_OUT_LO (1 << 23) + +/* HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT 0x7e */ +#define DRIVE_CURRENT_LANE0(x) (((x) & 0x3f) << 0) +#define DRIVE_CURRENT_LANE1(x) (((x) & 0x3f) << 8) +#define DRIVE_CURRENT_LANE2(x) (((x) & 0x3f) << 16) +#define DRIVE_CURRENT_LANE3(x) (((x) & 0x3f) << 24) +#define DRIVE_CURRENT_LANE0_T114(x) (((x) & 0x7f) << 0) +#define DRIVE_CURRENT_LANE1_T114(x) (((x) & 0x7f) << 8) +#define DRIVE_CURRENT_LANE2_T114(x) (((x) & 0x7f) << 16) +#define DRIVE_CURRENT_LANE3_T114(x) (((x) & 0x7f) << 24) + +/* Drive current list */ +enum { + DRIVE_CURRENT_1_500_mA, + DRIVE_CURRENT_1_875_mA, + DRIVE_CURRENT_2_250_mA, + DRIVE_CURRENT_2_625_mA, + DRIVE_CURRENT_3_000_mA, + DRIVE_CURRENT_3_375_mA, + DRIVE_CURRENT_3_750_mA, + DRIVE_CURRENT_4_125_mA, + DRIVE_CURRENT_4_500_mA, + DRIVE_CURRENT_4_875_mA, + DRIVE_CURRENT_5_250_mA, + DRIVE_CURRENT_5_625_mA, + DRIVE_CURRENT_6_000_mA, + DRIVE_CURRENT_6_375_mA, + DRIVE_CURRENT_6_750_mA, + DRIVE_CURRENT_7_125_mA, + DRIVE_CURRENT_7_500_mA, + DRIVE_CURRENT_7_875_mA, + DRIVE_CURRENT_8_250_mA, + DRIVE_CURRENT_8_625_mA, + DRIVE_CURRENT_9_000_mA, + DRIVE_CURRENT_9_375_mA, + DRIVE_CURRENT_9_750_mA, + DRIVE_CURRENT_10_125_mA, + DRIVE_CURRENT_10_500_mA, + DRIVE_CURRENT_10_875_mA, + DRIVE_CURRENT_11_250_mA, + DRIVE_CURRENT_11_625_mA, + DRIVE_CURRENT_12_000_mA, + DRIVE_CURRENT_12_375_mA, + DRIVE_CURRENT_12_750_mA, + DRIVE_CURRENT_13_125_mA, + DRIVE_CURRENT_13_500_mA, + DRIVE_CURRENT_13_875_mA, + DRIVE_CURRENT_14_250_mA, + DRIVE_CURRENT_14_625_mA, + DRIVE_CURRENT_15_000_mA, + DRIVE_CURRENT_15_375_mA, + DRIVE_CURRENT_15_750_mA, + DRIVE_CURRENT_16_125_mA, + DRIVE_CURRENT_16_500_mA, + DRIVE_CURRENT_16_875_mA, + DRIVE_CURRENT_17_250_mA, + DRIVE_CURRENT_17_625_mA, + DRIVE_CURRENT_18_000_mA, + DRIVE_CURRENT_18_375_mA, + DRIVE_CURRENT_18_750_mA, + DRIVE_CURRENT_19_125_mA, + DRIVE_CURRENT_19_500_mA, + DRIVE_CURRENT_19_875_mA, + DRIVE_CURRENT_20_250_mA, + DRIVE_CURRENT_20_625_mA, + DRIVE_CURRENT_21_000_mA, + DRIVE_CURRENT_21_375_mA, + DRIVE_CURRENT_21_750_mA, + DRIVE_CURRENT_22_125_mA, + DRIVE_CURRENT_22_500_mA, + DRIVE_CURRENT_22_875_mA, + DRIVE_CURRENT_23_250_mA, + DRIVE_CURRENT_23_625_mA, + DRIVE_CURRENT_24_000_mA, + DRIVE_CURRENT_24_375_mA, + DRIVE_CURRENT_24_750_mA, +}; + +/* Drive current list for T114 */ +enum { + DRIVE_CURRENT_0_000_mA_T114, + DRIVE_CURRENT_0_400_mA_T114, + DRIVE_CURRENT_0_800_mA_T114, + DRIVE_CURRENT_1_200_mA_T114, + DRIVE_CURRENT_1_600_mA_T114, + DRIVE_CURRENT_2_000_mA_T114, + DRIVE_CURRENT_2_400_mA_T114, + DRIVE_CURRENT_2_800_mA_T114, + DRIVE_CURRENT_3_200_mA_T114, + DRIVE_CURRENT_3_600_mA_T114, + DRIVE_CURRENT_4_000_mA_T114, + DRIVE_CURRENT_4_400_mA_T114, + DRIVE_CURRENT_4_800_mA_T114, + DRIVE_CURRENT_5_200_mA_T114, + DRIVE_CURRENT_5_600_mA_T114, + DRIVE_CURRENT_6_000_mA_T114, + DRIVE_CURRENT_6_400_mA_T114, + DRIVE_CURRENT_6_800_mA_T114, + DRIVE_CURRENT_7_200_mA_T114, + DRIVE_CURRENT_7_600_mA_T114, + DRIVE_CURRENT_8_000_mA_T114, + DRIVE_CURRENT_8_400_mA_T114, + DRIVE_CURRENT_8_800_mA_T114, + DRIVE_CURRENT_9_200_mA_T114, + DRIVE_CURRENT_9_600_mA_T114, + DRIVE_CURRENT_10_000_mA_T114, + DRIVE_CURRENT_10_400_mA_T114, + DRIVE_CURRENT_10_800_mA_T114, + DRIVE_CURRENT_11_200_mA_T114, + DRIVE_CURRENT_11_600_mA_T114, + DRIVE_CURRENT_12_000_mA_T114, + DRIVE_CURRENT_12_400_mA_T114, + DRIVE_CURRENT_12_800_mA_T114, + DRIVE_CURRENT_13_200_mA_T114, + DRIVE_CURRENT_13_600_mA_T114, + DRIVE_CURRENT_14_000_mA_T114, + DRIVE_CURRENT_14_400_mA_T114, + DRIVE_CURRENT_14_800_mA_T114, + DRIVE_CURRENT_15_200_mA_T114, + DRIVE_CURRENT_15_600_mA_T114, + DRIVE_CURRENT_16_000_mA_T114, + DRIVE_CURRENT_16_400_mA_T114, + DRIVE_CURRENT_16_800_mA_T114, + DRIVE_CURRENT_17_200_mA_T114, + DRIVE_CURRENT_17_600_mA_T114, + DRIVE_CURRENT_18_000_mA_T114, + DRIVE_CURRENT_18_400_mA_T114, + DRIVE_CURRENT_18_800_mA_T114, + DRIVE_CURRENT_19_200_mA_T114, + DRIVE_CURRENT_19_600_mA_T114, + DRIVE_CURRENT_20_000_mA_T114, + DRIVE_CURRENT_20_400_mA_T114, + DRIVE_CURRENT_20_800_mA_T114, + DRIVE_CURRENT_21_200_mA_T114, + DRIVE_CURRENT_21_600_mA_T114, + DRIVE_CURRENT_22_000_mA_T114, + DRIVE_CURRENT_22_400_mA_T114, + DRIVE_CURRENT_22_800_mA_T114, + DRIVE_CURRENT_23_200_mA_T114, + DRIVE_CURRENT_23_600_mA_T114, + DRIVE_CURRENT_24_000_mA_T114, + DRIVE_CURRENT_24_400_mA_T114, + DRIVE_CURRENT_24_800_mA_T114, + DRIVE_CURRENT_25_200_mA_T114, + DRIVE_CURRENT_25_400_mA_T114, + DRIVE_CURRENT_25_800_mA_T114, + DRIVE_CURRENT_26_200_mA_T114, + DRIVE_CURRENT_26_600_mA_T114, + DRIVE_CURRENT_27_000_mA_T114, + DRIVE_CURRENT_27_400_mA_T114, + DRIVE_CURRENT_27_800_mA_T114, + DRIVE_CURRENT_28_200_mA_T114, +}; + +/* HDMI_NV_PDISP_AUDIO_FS */ +#define AUDIO_FS_LOW(x) (((x) & 0xfff) << 0) +#define AUDIO_FS_HIGH(x) (((x) & 0xfff) << 16) + +/* HDMI_NV_PDISP_AUDIO_CNTRL0 0x8b */ +#define AUDIO_CNTRL0_ERROR_TOLERANCE(x) (((x) & 0xff) << 0) +#define AUDIO_CNTRL0_SOURCE_SELECT_AUTO (0 << 20) +#define AUDIO_CNTRL0_SOURCE_SELECT_SPDIF (1 << 20) +#define AUDIO_CNTRL0_SOURCE_SELECT_HDAL (2 << 20) +#define AUDIO_CNTRL0_FRAMES_PER_BLOCK(x) (((x) & 0xff) << 24) + +/* HDMI_NV_PDISP_AUDIO_N 0x8c */ +#define AUDIO_N_VALUE(x) (((x) & 0xfffff) << 0) +#define AUDIO_N_RESETF (1 << 20) +#define AUDIO_N_GENERATE_NORMAL (0 << 24) +#define AUDIO_N_GENERATE_ALTERNATE (1 << 24) + +/* HDMI_NV_PDISP_SOR_REFCLK 0x95 */ +#define SOR_REFCLK_DIV_INT(x) (((x) & 0xff) << 8) +#define SOR_REFCLK_DIV_FRAC(x) (((x) & 0x03) << 6) + +/* HDMI_NV_PDISP_INPUT_CONTROL 0x97 */ +#define HDMI_SRC_DISPLAYA (0 << 0) +#define HDMI_SRC_DISPLAYB (1 << 0) +#define ARM_VIDEO_RANGE_FULL (0 << 1) +#define ARM_VIDEO_RANGE_LIMITED (1 << 1) + +/* HDMI_NV_PDISP_PE_CURRENT 0x99 */ +#define PE_CURRENT0(x) (((x) & 0xf) << 0) +#define PE_CURRENT1(x) (((x) & 0xf) << 8) +#define PE_CURRENT2(x) (((x) & 0xf) << 16) +#define PE_CURRENT3(x) (((x) & 0xf) << 24) + +enum { + PE_CURRENT_0_0_mA, + PE_CURRENT_0_5_mA, + PE_CURRENT_1_0_mA, + PE_CURRENT_1_5_mA, + PE_CURRENT_2_0_mA, + PE_CURRENT_2_5_mA, + PE_CURRENT_3_0_mA, + PE_CURRENT_3_5_mA, + PE_CURRENT_4_0_mA, + PE_CURRENT_4_5_mA, + PE_CURRENT_5_0_mA, + PE_CURRENT_5_5_mA, + PE_CURRENT_6_0_mA, + PE_CURRENT_6_5_mA, + PE_CURRENT_7_0_mA, + PE_CURRENT_7_5_mA, +}; + +enum { + PE_CURRENT_0_mA_T114, + PE_CURRENT_1_mA_T114, + PE_CURRENT_2_mA_T114, + PE_CURRENT_3_mA_T114, + PE_CURRENT_4_mA_T114, + PE_CURRENT_5_mA_T114, + PE_CURRENT_6_mA_T114, + PE_CURRENT_7_mA_T114, + PE_CURRENT_8_mA_T114, + PE_CURRENT_9_mA_T114, + PE_CURRENT_10_mA_T114, + PE_CURRENT_11_mA_T114, + PE_CURRENT_12_mA_T114, + PE_CURRENT_13_mA_T114, + PE_CURRENT_14_mA_T114, + PE_CURRENT_15_mA_T114, +}; + +/* HDMI_NV_PDISP_SOR_AUDIO_CNTRL0 0xac */ +#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_AUTO (0 << 20) +#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_SPDIF (1 << 20) +#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_HDAL (2 << 20) +#define SOR_AUDIO_CNTRL0_INJECT_NULLSMPL (1 << 29) + +/* HDMI_NV_PDISP_SOR_AUDIO_SPARE0 0xae */ +#define SOR_AUDIO_SPARE0_HBR_ENABLE BIT(27) + +/* HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0 0xba */ +#define SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID BIT(30) +#define SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK 0xffff + +/* HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE 0xbd */ +#define SOR_AUDIO_HDA_PRESENSE_VALID BIT(1) +#define SOR_AUDIO_HDA_PRESENSE_PRESENT BIT(0) + +/* HDMI_NV_PDISP_INT_STATUS 0xcc */ +#define INT_SCRATCH BIT(3) +#define INT_CP_REQUEST BIT(2) +#define INT_CODEC_SCRATCH1 BIT(1) +#define INT_CODEC_SCRATCH0 BIT(0) + +/* HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT 0xd1 */ +#define PEAK_CURRENT_LANE0(x) (((x) & 0x7f) << 0) +#define PEAK_CURRENT_LANE1(x) (((x) & 0x7f) << 8) +#define PEAK_CURRENT_LANE2(x) (((x) & 0x7f) << 16) +#define PEAK_CURRENT_LANE3(x) (((x) & 0x7f) << 24) + +enum { + PEAK_CURRENT_0_000_mA, + PEAK_CURRENT_0_200_mA, + PEAK_CURRENT_0_400_mA, + PEAK_CURRENT_0_600_mA, + PEAK_CURRENT_0_800_mA, + PEAK_CURRENT_1_000_mA, + PEAK_CURRENT_1_200_mA, + PEAK_CURRENT_1_400_mA, + PEAK_CURRENT_1_600_mA, + PEAK_CURRENT_1_800_mA, + PEAK_CURRENT_2_000_mA, + PEAK_CURRENT_2_200_mA, + PEAK_CURRENT_2_400_mA, + PEAK_CURRENT_2_600_mA, + PEAK_CURRENT_2_800_mA, + PEAK_CURRENT_3_000_mA, + PEAK_CURRENT_3_200_mA, + PEAK_CURRENT_3_400_mA, + PEAK_CURRENT_3_600_mA, + PEAK_CURRENT_3_800_mA, + PEAK_CURRENT_4_000_mA, + PEAK_CURRENT_4_200_mA, + PEAK_CURRENT_4_400_mA, + PEAK_CURRENT_4_600_mA, + PEAK_CURRENT_4_800_mA, + PEAK_CURRENT_5_000_mA, + PEAK_CURRENT_5_200_mA, + PEAK_CURRENT_5_400_mA, + PEAK_CURRENT_5_600_mA, + PEAK_CURRENT_5_800_mA, + PEAK_CURRENT_6_000_mA, + PEAK_CURRENT_6_200_mA, + PEAK_CURRENT_6_400_mA, + PEAK_CURRENT_6_600_mA, + PEAK_CURRENT_6_800_mA, + PEAK_CURRENT_7_000_mA, + PEAK_CURRENT_7_200_mA, + PEAK_CURRENT_7_400_mA, + PEAK_CURRENT_7_600_mA, + PEAK_CURRENT_7_800_mA, + PEAK_CURRENT_8_000_mA, + PEAK_CURRENT_8_200_mA, + PEAK_CURRENT_8_400_mA, + PEAK_CURRENT_8_600_mA, + PEAK_CURRENT_8_800_mA, + PEAK_CURRENT_9_000_mA, + PEAK_CURRENT_9_200_mA, + PEAK_CURRENT_9_400_mA, +}; + +#endif /* _TEGRA_HDMI_H */ diff --git a/drivers/video/tegra/host1x.c b/drivers/video/tegra/host1x.c new file mode 100644 index 00000000000..58ab871a3b4 --- /dev/null +++ b/drivers/video/tegra/host1x.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <clk.h> +#include <log.h> +#include <reset.h> +#include <linux/delay.h> + +#include <asm/arch/clock.h> +#include <asm/arch-tegra/clk_rst.h> + +struct tegra_host1x_info { + u32 clk_parent; + u32 rate; +}; + +static int tegra_host1x_probe(struct udevice *dev) +{ + struct clk *clk; + struct reset_ctl reset_ctl; + const struct tegra_host1x_info *info; + int ret; + + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) { + log_debug("%s: cannot get HOST1X clock: %ld\n", + __func__, PTR_ERR(clk)); + return PTR_ERR(clk); + } + + ret = reset_get_by_name(dev, "host1x", &reset_ctl); + if (ret) { + log_debug("%s: cannot get HOST1X reset: %d\n", + __func__, ret); + return ret; + } + + info = (struct tegra_host1x_info *)dev_get_driver_data(dev); + + reset_assert(&reset_ctl); + clock_start_periph_pll(clk->id, info->clk_parent, info->rate); + + mdelay(2); + reset_deassert(&reset_ctl); + + return 0; +} + +static const struct tegra_host1x_info tegra20_host1x_info = { + .clk_parent = CLOCK_ID_CGENERAL, + .rate = 150000000, /* 150 MHz */ +}; + +static const struct tegra_host1x_info tegra114_host1x_info = { + .clk_parent = CLOCK_ID_PERIPH, + .rate = 136000000, /* 136 MHz */ +}; + +static const struct udevice_id tegra_host1x_ids[] = { + { + .compatible = "nvidia,tegra20-host1x", + .data = (ulong)&tegra20_host1x_info + }, { + .compatible = "nvidia,tegra30-host1x", + .data = (ulong)&tegra20_host1x_info + }, { + .compatible = "nvidia,tegra114-host1x", + .data = (ulong)&tegra114_host1x_info + }, { + .compatible = "nvidia,tegra124-host1x", + .data = (ulong)&tegra114_host1x_info + }, { + /* sentinel */ + } +}; + +U_BOOT_DRIVER(tegra_host1x) = { + .name = "tegra_host1x", + .id = UCLASS_SIMPLE_BUS, + .of_match = tegra_host1x_ids, + .probe = tegra_host1x_probe, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/video/tegra20/mipi-phy.c b/drivers/video/tegra/mipi-phy.c index 576262e405d..576262e405d 100644 --- a/drivers/video/tegra20/mipi-phy.c +++ b/drivers/video/tegra/mipi-phy.c diff --git a/drivers/video/tegra20/mipi-phy.h b/drivers/video/tegra/mipi-phy.h index 41889a75035..41889a75035 100644 --- a/drivers/video/tegra20/mipi-phy.h +++ b/drivers/video/tegra/mipi-phy.h diff --git a/drivers/video/tegra20/tegra-mipi.c b/drivers/video/tegra/mipi.c index 2df3c1a9942..a4f4343d008 100644 --- a/drivers/video/tegra20/tegra-mipi.c +++ b/drivers/video/tegra/mipi.c @@ -10,9 +10,10 @@ #include <linux/delay.h> #include <linux/iopoll.h> +#include <asm/arch/clock.h> #include <asm/io.h> -/* MIPI control registers 0x00 ~ 0x60 */ +/* MIPI control registers 0x00 ~ 0x74 */ struct mipi_ctlr { uint mipi_cal_ctrl; uint mipi_cal_autocal_ctrl; @@ -38,8 +39,17 @@ struct mipi_ctlr { uint mipi_cal_bias_pad_cfg0; uint mipi_cal_bias_pad_cfg1; uint mipi_cal_bias_pad_cfg2; + + uint mipi_cal_dsia_config_2; + uint mipi_cal_dsib_config_2; + uint mipi_cal_cilc_config_2; + uint mipi_cal_cild_config_2; + uint mipi_cal_csie_config_2; }; +#define MIPI_DSIA_PADS 0x60 +#define MIPI_DSIB_PADS 0x180 + #define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26) #define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24) #define MIPI_CAL_CTRL_CLKEN_OVR BIT(4) @@ -64,26 +74,25 @@ struct mipi_ctlr { #define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4) #define MIPI_CAL_BIAS_PAD_PDVREG BIT(1) +#define MIPI_CAL_HSCLKPDOSDSI(x) (((x) & 0x1f) << 8) +#define MIPI_CAL_HSCLKPUOSDSI(x) (((x) & 0x1f) << 0) + struct tegra_mipi_priv { struct mipi_ctlr *mipi; struct clk *mipi_cal; + u32 version; }; -static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf, - int size) +enum { + T114, + T124, +}; + +static void tegra114_mipi_pads_cal(struct tegra_mipi_priv *priv, + int calibration_pads) { - struct tegra_mipi_priv *priv = dev_get_priv(dev); u32 value; - value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(0x2) | - MIPI_CAL_BIAS_PAD_DRV_UP_REF(0x0); - writel(value, &priv->mipi->mipi_cal_bias_pad_cfg1); - - value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2); - value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7); - value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7); - writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2); - value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x4) | MIPI_CAL_TERMOS(0x5); @@ -99,6 +108,95 @@ static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf value = readl(&priv->mipi->mipi_cal_config_dsid); value &= ~(MIPI_CAL_SEL(0x1)); writel(value, &priv->mipi->mipi_cal_config_dsid); +} + +static void tegra124_mipi_pads_cal(struct tegra_mipi_priv *priv, + int calibration_pads) +{ + u32 value; + + /* Calibrate DSI-A */ + if (calibration_pads == MIPI_DSIA_PADS) { + printf("Calibrating DSI-A pads\n"); + + value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) | + MIPI_CAL_TERMOS(0x0); + writel(value, &priv->mipi->mipi_cal_config_dsia); + writel(value, &priv->mipi->mipi_cal_config_dsib); + + value = MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSCLKPDOSDSI(0x1) | + MIPI_CAL_HSCLKPUOSDSI(0x2); + writel(value, &priv->mipi->mipi_cal_dsia_config_2); + writel(value, &priv->mipi->mipi_cal_dsib_config_2); + + /* Deselect PAD C */ + value = readl(&priv->mipi->mipi_cal_cilc_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_cilc_config_2); + + /* Deselect PAD D */ + value = readl(&priv->mipi->mipi_cal_cild_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_cild_config_2); + } + + /* Calibrate DSI-B */ + if (calibration_pads == MIPI_DSIB_PADS) { + printf("Calibrating DSI-B pads\n"); + + value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) | + MIPI_CAL_TERMOS(0x0); + writel(value, &priv->mipi->mipi_cal_config_csic); + writel(value, &priv->mipi->mipi_cal_config_csid); + + value = MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSCLKPDOSDSI(0x1) | + MIPI_CAL_HSCLKPUOSDSI(0x2); + writel(value, &priv->mipi->mipi_cal_cilc_config_2); + writel(value, &priv->mipi->mipi_cal_cild_config_2); + + /* Deselect PAD A */ + value = readl(&priv->mipi->mipi_cal_dsia_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_dsia_config_2); + + /* Deselect PAD B */ + value = readl(&priv->mipi->mipi_cal_dsib_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_dsib_config_2); + } +} + +static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf, + int size) +{ + struct tegra_mipi_priv *priv = dev_get_priv(dev); + u32 value; + + value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(0x2) | + MIPI_CAL_BIAS_PAD_DRV_UP_REF(0x0); + writel(value, &priv->mipi->mipi_cal_bias_pad_cfg1); + + value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2); + value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7); + value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7); + writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2); + + switch (priv->version) { + case T114: + tegra114_mipi_pads_cal(priv, offset); + break; + + case T124: + tegra124_mipi_pads_cal(priv, offset); + break; + + default: + return -EINVAL; + } value = readl(&priv->mipi->mipi_cal_ctrl); value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf); @@ -134,6 +232,11 @@ static int tegra_mipi_enable(struct udevice *dev, bool val) struct tegra_mipi_priv *priv = dev_get_priv(dev); u32 value; + reset_set_enable(priv->mipi_cal->id, 1); + mdelay(100); + reset_set_enable(priv->mipi_cal->id, 0); + mdelay(1); + clk_enable(priv->mipi_cal); value = readl(&priv->mipi->mipi_cal_bias_pad_cfg0); @@ -157,6 +260,8 @@ static int tegra_mipi_probe(struct udevice *dev) { struct tegra_mipi_priv *priv = dev_get_priv(dev); + priv->version = dev_get_driver_data(dev); + priv->mipi = (struct mipi_ctlr *)dev_read_addr_ptr(dev); if (!priv->mipi) { log_debug("%s: no MIPI controller address\n", __func__); @@ -174,7 +279,8 @@ static int tegra_mipi_probe(struct udevice *dev) } static const struct udevice_id tegra_mipi_ids[] = { - { .compatible = "nvidia,tegra114-mipi" }, + { .compatible = "nvidia,tegra114-mipi", .data = T114 }, + { .compatible = "nvidia,tegra124-mipi", .data = T124 }, { } }; diff --git a/drivers/video/tegra124/Makefile b/drivers/video/tegra/tegra124/Makefile index a378382628c..a378382628c 100644 --- a/drivers/video/tegra124/Makefile +++ b/drivers/video/tegra/tegra124/Makefile diff --git a/drivers/video/tegra124/display.c b/drivers/video/tegra/tegra124/display.c index abe31e27d84..abe31e27d84 100644 --- a/drivers/video/tegra124/display.c +++ b/drivers/video/tegra/tegra124/display.c diff --git a/drivers/video/tegra124/displayport.h b/drivers/video/tegra/tegra124/displayport.h index a3044475aeb..a3044475aeb 100644 --- a/drivers/video/tegra124/displayport.h +++ b/drivers/video/tegra/tegra124/displayport.h diff --git a/drivers/video/tegra124/dp.c b/drivers/video/tegra/tegra124/dp.c index b95b14da77d..b95b14da77d 100644 --- a/drivers/video/tegra124/dp.c +++ b/drivers/video/tegra/tegra124/dp.c diff --git a/drivers/video/tegra124/sor.c b/drivers/video/tegra/tegra124/sor.c index 1ce5330c6bc..1ce5330c6bc 100644 --- a/drivers/video/tegra124/sor.c +++ b/drivers/video/tegra/tegra124/sor.c diff --git a/drivers/video/tegra124/sor.h b/drivers/video/tegra/tegra124/sor.h index 2fc9a38267d..2fc9a38267d 100644 --- a/drivers/video/tegra124/sor.h +++ b/drivers/video/tegra/tegra124/sor.h diff --git a/drivers/video/tegra20/Kconfig b/drivers/video/tegra20/Kconfig deleted file mode 100644 index f5c4843e119..00000000000 --- a/drivers/video/tegra20/Kconfig +++ /dev/null @@ -1,24 +0,0 @@ -config VIDEO_TEGRA20 - bool "Enable Display Controller support on Tegra20 and Tegra 30" - depends on OF_CONTROL - help - T20/T30 support video output to an attached LCD panel as well as - other options such as HDMI. Only the LCD is supported in U-Boot. - This option enables this support which can be used on devices which - have an LCD display connected. - -config VIDEO_DSI_TEGRA30 - bool "Enable Tegra 30 DSI support" - depends on PANEL && DM_GPIO - select VIDEO_TEGRA20 - select VIDEO_MIPI_DSI - help - T30 has native support for DSI panels. This option enables support - for such panels which can be used on endeavoru and tf600t. - -config TEGRA_BACKLIGHT_PWM - bool "Enable Tegra DC PWM backlight support" - depends on BACKLIGHT - select VIDEO_TEGRA20 - help - Tegra DC dependent backlight. diff --git a/drivers/video/tegra20/Makefile b/drivers/video/tegra20/Makefile deleted file mode 100644 index a75aea2a875..00000000000 --- a/drivers/video/tegra20/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0+ - -obj-$(CONFIG_VIDEO_TEGRA20) += tegra-dc.o -obj-$(CONFIG_VIDEO_DSI_TEGRA30) += tegra-dsi.o tegra-mipi.o mipi-phy.o -obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += tegra-pwm-backlight.o diff --git a/drivers/video/ti/tilcdc.c b/drivers/video/ti/tilcdc.c index 493e2f18cd2..340c79299ba 100644 --- a/drivers/video/ti/tilcdc.c +++ b/drivers/video/ti/tilcdc.c @@ -234,7 +234,7 @@ static int tilcdc_probe(struct udevice *dev) return -EINVAL; } - err = uclass_get_device_by_name(UCLASS_CLK, "lcd_gclk@534", &clk_dev); + err = uclass_get_device_by_name(UCLASS_CLK, "clock-lcd-gclk@534", &clk_dev); if (err) { dev_err(dev, "failed to get lcd_gclk device\n"); return err; @@ -252,7 +252,7 @@ static int tilcdc_probe(struct udevice *dev) return rate; } - err = uclass_get_device_by_name(UCLASS_CLK, "dpll_disp_m2_ck@4a4", + err = uclass_get_device_by_name(UCLASS_CLK, "clock-dpll-disp-m2@4a4", &clk_dev); if (err) { dev_err(dev, "failed to get dpll_disp_m2 clock device\n"); diff --git a/drivers/video/tidss/Makefile b/drivers/video/tidss/Makefile index f0cbe1d4ed1..3381a5fec57 100644 --- a/drivers/video/tidss/Makefile +++ b/drivers/video/tidss/Makefile @@ -9,4 +9,4 @@ # Author: Tomi Valkeinen <tomi.valkeinen@ti.com> -obj-${CONFIG_$(XPL_)VIDEO_TIDSS} = tidss_drv.o +obj-${CONFIG_$(PHASE_)VIDEO_TIDSS} = tidss_drv.o diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index ff4f2199585..c684c994b61 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -245,6 +245,7 @@ int video_fill(struct udevice *dev, u32 colour) *ppix++ = colour; break; } + fallthrough; case VIDEO_BPP32: if (CONFIG_IS_ENABLED(VIDEO_BPP32)) { u32 *ppix = priv->fb; @@ -254,6 +255,7 @@ int video_fill(struct udevice *dev, u32 colour) *ppix++ = colour; break; } + fallthrough; default: memset(priv->fb, colour, priv->fb_size); break; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b39b2546e5c..1bb67f50352 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -95,6 +95,15 @@ config WDT_APPLE The watchdog will perform a full SoC reset resulting in a reboot of the entire system. +config WDT_ADI + bool "Analog Devices watchdog timer support" + select WDT + select SPL_WDT if SPL + depends on ARCH_SC5XX + help + Enable this to support Watchdog Timer on ADI SC57X, SC58X, SC59X, + and SC59X_64 processors + config WDT_ARMADA_37XX bool "Marvell Armada 37xx watchdog timer support" depends on WDT && ARMADA_3700 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 9b6b1a8e8ad..e6bd4c587af 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -53,3 +53,4 @@ obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o obj-$(CONFIG_WDT_XILINX) += xilinx_wwdt.o +obj-$(CONFIG_WDT_ADI) += adi_wdt.o diff --git a/drivers/watchdog/adi_wdt.c b/drivers/watchdog/adi_wdt.c new file mode 100644 index 00000000000..6f5b3d5d042 --- /dev/null +++ b/drivers/watchdog/adi_wdt.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Converted to driver model by Nathan Barrett-Morrison + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * + * adi_wtd.c - driver for ADI on-chip watchdog + * + */ + +#include <clk.h> +#include <dm.h> +#include <wdt.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/io.h> + +#define WDOG_CTL 0x0 +#define WDOG_CNT 0x4 +#define WDOG_STAT 0x8 + +#define RCU_CTL 0x0 +#define RCU_STAT 0x4 + +#define SEC_GCTL 0x0 +#define SEC_FCTL 0x10 +#define SEC_SCTL0 0x800 + +#define WDEN 0x0010 +#define WDDIS 0x0AD0 + +struct adi_wdt_priv { + void __iomem *rcu_base; + void __iomem *sec_base; + void __iomem *wdt_base; + struct clk clock; +}; + +static int adi_wdt_reset(struct udevice *dev) +{ + struct adi_wdt_priv *priv = dev_get_priv(dev); + + iowrite32(0, priv->wdt_base + WDOG_STAT); + + return 0; +} + +static int adi_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) +{ + struct adi_wdt_priv *priv = dev_get_priv(dev); + + /* Disable SYSCD_RESETb input and clear the RCU0 reset status */ + iowrite32(0xf, priv->rcu_base + RCU_STAT); + iowrite32(0x0, priv->rcu_base + RCU_CTL); + + /* reset the SEC controller */ + iowrite32(0x2, priv->sec_base + SEC_GCTL); + iowrite32(0x2, priv->sec_base + SEC_FCTL); + + udelay(50); + + /* enable SEC fault event */ + iowrite32(0x1, priv->sec_base + SEC_GCTL); + + /* ANOMALY 36100004 Spurious External Fault event occurs when FCTL + * is re-programmed when currently active fault is not cleared + */ + iowrite32(0xc0, priv->sec_base + SEC_FCTL); + iowrite32(0xc1, priv->sec_base + SEC_FCTL); + + /* enable SEC fault source for watchdog0 */ + setbits_32(priv->sec_base + SEC_SCTL0 + (3*8), 0x6); + + /* Enable SYSCD_RESETb input */ + iowrite32(0x100, priv->rcu_base + RCU_CTL); + + /* enable watchdog0 */ + iowrite32(WDDIS, priv->wdt_base + WDOG_CTL); + + iowrite32(timeout_ms / 1000 * + (clk_get_rate(&priv->clock) / (IS_ENABLED(CONFIG_SC58X) ? 2 : 1)), + priv->wdt_base + WDOG_CNT); + + iowrite32(0, priv->wdt_base + WDOG_STAT); + iowrite32(WDEN, priv->wdt_base + WDOG_CTL); + + return 0; +} + +static int adi_wdt_probe(struct udevice *dev) +{ + struct adi_wdt_priv *priv = dev_get_priv(dev); + int ret; + struct resource res; + + ret = dev_read_resource_byname(dev, "rcu", &res); + if (ret) + return ret; + priv->rcu_base = devm_ioremap(dev, res.start, resource_size(&res)); + + ret = dev_read_resource_byname(dev, "sec", &res); + if (ret) + return ret; + priv->sec_base = devm_ioremap(dev, res.start, resource_size(&res)); + + ret = dev_read_resource_byname(dev, "wdt", &res); + if (ret) + return ret; + priv->wdt_base = devm_ioremap(dev, res.start, resource_size(&res)); + + ret = clk_get_by_name(dev, "sclk0", &priv->clock); + if (ret < 0) { + printf("Can't get WDT clk: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct wdt_ops adi_wdt_ops = { + .start = adi_wdt_start, + .reset = adi_wdt_reset, +}; + +static const struct udevice_id adi_wdt_ids[] = { + { .compatible = "adi,wdt" }, + {} +}; + +U_BOOT_DRIVER(adi_wdt) = { + .name = "adi_wdt", + .id = UCLASS_WDT, + .of_match = adi_wdt_ids, + .probe = adi_wdt_probe, + .ops = &adi_wdt_ops, + .priv_auto = sizeof(struct adi_wdt_priv), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/watchdog/da9063-wdt.c b/drivers/watchdog/da9063-wdt.c index b7216b57863..ec9bc033011 100644 --- a/drivers/watchdog/da9063-wdt.c +++ b/drivers/watchdog/da9063-wdt.c @@ -145,5 +145,4 @@ U_BOOT_DRIVER(da9063_wdt) = { .id = UCLASS_WDT, .of_match = da9063_wdt_ids, .ops = &da9063_wdt_ops, - .flags = DM_FLAG_PROBE_AFTER_BIND, }; diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 8eeac935760..467db5fe9bf 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -153,10 +153,21 @@ static const struct sunxi_wdt_reg sun20i_wdt_reg = { .wdt_key_val = 0x16aa0000, }; +static const struct sunxi_wdt_reg sun55i_wdt_reg = { + .wdt_ctrl = 0x0c, + .wdt_cfg = 0x10, + .wdt_mode = 0x14, + .wdt_timeout_shift = 4, + .wdt_reset_mask = 0x03, + .wdt_reset_val = 0x01, + .wdt_key_val = 0x16aa0000, +}; + static const struct udevice_id sunxi_wdt_ids[] = { { .compatible = "allwinner,sun4i-a10-wdt", .data = (ulong)&sun4i_wdt_reg }, { .compatible = "allwinner,sun6i-a31-wdt", .data = (ulong)&sun6i_wdt_reg }, { .compatible = "allwinner,sun20i-d1-wdt", .data = (ulong)&sun20i_wdt_reg }, + { .compatible = "allwinner,sun55i-a523-wdt", .data = (ulong)&sun55i_wdt_reg }, { /* sentinel */ } }; |
