diff options
Diffstat (limited to 'drivers')
226 files changed, 15214 insertions, 2538 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index e6702eced4..96ff4f566a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -14,6 +14,8 @@ source "drivers/block/Kconfig" source "drivers/bootcount/Kconfig" +source "drivers/cache/Kconfig" + source "drivers/clk/Kconfig" source "drivers/cpu/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index a7bba3ed56..6635dabd2c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -34,7 +34,7 @@ obj-$(CONFIG_SPL_CRYPTO_SUPPORT) += crypto/ obj-$(CONFIG_SPL_MPC8XXX_INIT_DDR_SUPPORT) += ddr/fsl/ obj-$(CONFIG_ARMADA_38X) += ddr/marvell/a38x/ obj-$(CONFIG_ARMADA_XP) += ddr/marvell/axp/ -obj-$(CONFIG_ALTERA_SDRAM) += ddr/altera/ +obj-$(CONFIG_$(SPL_)ALTERA_SDRAM) += ddr/altera/ obj-$(CONFIG_ARCH_IMX8M) += ddr/imx/imx8m/ obj-$(CONFIG_SPL_POWER_SUPPORT) += power/ power/pmic/ obj-$(CONFIG_SPL_POWER_SUPPORT) += power/regulator/ @@ -77,6 +77,7 @@ obj-$(CONFIG_BIOSEMU) += bios_emulator/ obj-y += block/ obj-y += board/ obj-$(CONFIG_BOOTCOUNT_LIMIT) += bootcount/ +obj-y += cache/ obj-$(CONFIG_CPU) += cpu/ obj-y += crypto/ obj-$(CONFIG_FASTBOOT) += fastboot/ diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 4e95a68a2d..87636ae30f 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -59,6 +59,16 @@ config DWC_AHCI Enable this driver to support Sata devices through Synopsys DWC AHCI module. +config FSL_AHCI + bool "Enable Freescale AHCI driver support" + select SCSI_AHCI + depends on AHCI + depends on DM_SCSI + help + Enable this driver to support Sata devices found in + some Freescale PowerPC SoCs. + + config DWC_AHSATA bool "Enable DWC AHSATA driver support" select LIBATA diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index a69edb10f7..6e03384f81 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -4,6 +4,7 @@ # Wolfgang Denk, DENX Software Engineering, wd@denx.de. obj-$(CONFIG_DWC_AHCI) += dwc_ahci.o +obj-$(CONFIG_FSL_AHCI) += fsl_ahci.o obj-$(CONFIG_AHCI) += ahci-uclass.o obj-$(CONFIG_AHCI_PCI) += ahci-pci.o obj-$(CONFIG_SCSI_AHCI) += ahci.o diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 5fafb63aeb..e3135bb75f 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -55,17 +55,6 @@ __weak void __iomem *ahci_port_base(void __iomem *base, u32 port) return base + 0x100 + (port * 0x80); } - -static void ahci_setup_port(struct ahci_ioports *port, void __iomem *base, - unsigned int port_idx) -{ - base = ahci_port_base(base, port_idx); - - port->cmd_addr = base; - port->scr_addr = base + PORT_SCR; -} - - #define msleep(a) udelay(a * 1000) static void ahci_dcache_flush_range(unsigned long begin, unsigned long len) @@ -240,7 +229,6 @@ static int ahci_host_init(struct ahci_uc_priv *uc_priv) continue; uc_priv->port[i].port_mmio = ahci_port_base(mmio, i); port_mmio = (u8 *)uc_priv->port[i].port_mmio; - ahci_setup_port(&uc_priv->port[i], mmio, i); /* make sure port is not active */ tmp = readl(port_mmio + PORT_CMD); @@ -571,15 +559,12 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port) return -1; } - mem = malloc(AHCI_PORT_PRIV_DMA_SZ + 2048); + mem = memalign(2048, AHCI_PORT_PRIV_DMA_SZ); if (!mem) { free(pp); printf("%s: No mem for table!\n", __func__); return -ENOMEM; } - - /* Aligned to 2048-bytes */ - mem = memalign(2048, AHCI_PORT_PRIV_DMA_SZ); memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ); /* diff --git a/drivers/ata/fsl_ahci.c b/drivers/ata/fsl_ahci.c new file mode 100644 index 0000000000..d04cff3ee7 --- /dev/null +++ b/drivers/ata/fsl_ahci.c @@ -0,0 +1,1030 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NXP PPC SATA platform driver + * + * (C) Copyright 2019 NXP, Inc. + * + */ +#include <common.h> +#include <asm/fsl_serdes.h> +#include <dm/lists.h> +#include <dm.h> +#include <ahci.h> +#include <scsi.h> +#include <libata.h> +#include <sata.h> +#include <malloc.h> +#include <memalign.h> +#include <fis.h> + +#include "fsl_sata.h" + +struct fsl_ahci_priv { + u32 base; + u32 flag; + u32 number; + fsl_sata_t *fsl_sata; +}; + +static int fsl_ahci_bind(struct udevice *dev) +{ + return device_bind_driver(dev, "fsl_ahci_scsi", "fsl_ahci_scsi", NULL); +} + +static int fsl_ahci_ofdata_to_platdata(struct udevice *dev) +{ + struct fsl_ahci_priv *priv = dev_get_priv(dev); + + priv->number = dev_read_u32_default(dev, "sata-number", -1); + priv->flag = dev_read_u32_default(dev, "sata-fpdma", -1); + + priv->base = dev_read_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + return 0; +} + +static int ata_wait_register(unsigned __iomem *addr, u32 mask, + u32 val, u32 timeout_msec) +{ + int i; + + for (i = 0; ((in_le32(addr) & mask) != val) && i < timeout_msec; i++) + mdelay(1); + + return (i < timeout_msec) ? 0 : -1; +} + +static void fsl_sata_dump_sfis(struct sata_fis_d2h *s) +{ + printf("Status FIS dump:\n\r"); + printf("fis_type: %02x\n\r", s->fis_type); + printf("pm_port_i: %02x\n\r", s->pm_port_i); + printf("status: %02x\n\r", s->status); + printf("error: %02x\n\r", s->error); + printf("lba_low: %02x\n\r", s->lba_low); + printf("lba_mid: %02x\n\r", s->lba_mid); + printf("lba_high: %02x\n\r", s->lba_high); + printf("device: %02x\n\r", s->device); + printf("lba_low_exp: %02x\n\r", s->lba_low_exp); + printf("lba_mid_exp: %02x\n\r", s->lba_mid_exp); + printf("lba_high_exp: %02x\n\r", s->lba_high_exp); + printf("res1: %02x\n\r", s->res1); + printf("sector_count: %02x\n\r", s->sector_count); + printf("sector_count_exp: %02x\n\r", s->sector_count_exp); +} + +static void fsl_sata_dump_regs(fsl_sata_reg_t __iomem *reg) +{ + printf("\n\rSATA: %08x\n\r", (u32)reg); + printf("CQR: %08x\n\r", in_le32(®->cqr)); + printf("CAR: %08x\n\r", in_le32(®->car)); + printf("CCR: %08x\n\r", in_le32(®->ccr)); + printf("CER: %08x\n\r", in_le32(®->cer)); + printf("CQR: %08x\n\r", in_le32(®->cqr)); + printf("DER: %08x\n\r", in_le32(®->der)); + printf("CHBA: %08x\n\r", in_le32(®->chba)); + printf("HStatus: %08x\n\r", in_le32(®->hstatus)); + printf("HControl: %08x\n\r", in_le32(®->hcontrol)); + printf("CQPMP: %08x\n\r", in_le32(®->cqpmp)); + printf("SIG: %08x\n\r", in_le32(®->sig)); + printf("ICC: %08x\n\r", in_le32(®->icc)); + printf("SStatus: %08x\n\r", in_le32(®->sstatus)); + printf("SError: %08x\n\r", in_le32(®->serror)); + printf("SControl: %08x\n\r", in_le32(®->scontrol)); + printf("SNotification: %08x\n\r", in_le32(®->snotification)); + printf("TransCfg: %08x\n\r", in_le32(®->transcfg)); + printf("TransStatus: %08x\n\r", in_le32(®->transstatus)); + printf("LinkCfg: %08x\n\r", in_le32(®->linkcfg)); + printf("LinkCfg1: %08x\n\r", in_le32(®->linkcfg1)); + printf("LinkCfg2: %08x\n\r", in_le32(®->linkcfg2)); + printf("LinkStatus: %08x\n\r", in_le32(®->linkstatus)); + printf("LinkStatus1: %08x\n\r", in_le32(®->linkstatus1)); + printf("PhyCtrlCfg: %08x\n\r", in_le32(®->phyctrlcfg)); + printf("SYSPR: %08x\n\r", in_be32(®->syspr)); +} + +static int init_sata(struct fsl_ahci_priv *priv) +{ + int i; + u32 cda; + u32 val32; + u32 sig; + fsl_sata_t *sata; + u32 length, align; + cmd_hdr_tbl_t *cmd_hdr; + fsl_sata_reg_t __iomem *reg; + + int dev = priv->number; + + if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1)) { + printf("the sata index %d is out of ranges\n\r", dev); + return -EINVAL; + } + +#ifdef CONFIG_MPC85xx + if (dev == 0 && (!is_serdes_configured(SATA1))) { + printf("SATA%d [dev = %d] is not enabled\n", dev + 1, dev); + return -EINVAL; + } + if (dev == 1 && (!is_serdes_configured(SATA2))) { + printf("SATA%d [dev = %d] is not enabled\n", dev + 1, dev); + return -EINVAL; + } +#endif + + /* Allocate SATA device driver struct */ + sata = (fsl_sata_t *)malloc(sizeof(fsl_sata_t)); + if (!sata) { + printf("alloc the sata device struct failed\n\r"); + return -ENOMEM; + } + /* Zero all of the device driver struct */ + memset((void *)sata, 0, sizeof(fsl_sata_t)); + + sata->dma_flag = priv->flag; + snprintf(sata->name, 12, "SATA%d", dev); + + /* Set the controller register base address to device struct */ + reg = (fsl_sata_reg_t *)priv->base; + sata->reg_base = reg; + + /* Allocate the command header table, 4 bytes aligned */ + length = sizeof(struct cmd_hdr_tbl); + align = SATA_HC_CMD_HDR_TBL_ALIGN; + sata->cmd_hdr_tbl_offset = (void *)malloc(length + align); + if (!sata->cmd_hdr_tbl_offset) { + printf("alloc the command header failed\n\r"); + return -ENOMEM; + } + + cmd_hdr = (cmd_hdr_tbl_t *)(((u32)sata->cmd_hdr_tbl_offset + align) + & ~(align - 1)); + sata->cmd_hdr = cmd_hdr; + + /* Zero all of the command header table */ + memset((void *)sata->cmd_hdr_tbl_offset, 0, length + align); + + /* Allocate command descriptor for all command */ + length = sizeof(struct cmd_desc) * SATA_HC_MAX_CMD; + align = SATA_HC_CMD_DESC_ALIGN; + sata->cmd_desc_offset = (void *)malloc(length + align); + if (!sata->cmd_desc_offset) { + printf("alloc the command descriptor failed\n\r"); + return -ENOMEM; + } + sata->cmd_desc = (cmd_desc_t *)(((u32)sata->cmd_desc_offset + align) + & ~(align - 1)); + /* Zero all of command descriptor */ + memset((void *)sata->cmd_desc_offset, 0, length + align); + + /* Link the command descriptor to command header */ + for (i = 0; i < SATA_HC_MAX_CMD; i++) { + cda = ((u32)sata->cmd_desc + SATA_HC_CMD_DESC_SIZE * i) + & ~(CMD_HDR_CDA_ALIGN - 1); + cmd_hdr->cmd_slot[i].cda = cpu_to_le32(cda); + } + + /* To have safe state, force the controller offline */ + val32 = in_le32(®->hcontrol); + val32 &= ~HCONTROL_ONOFF; + val32 |= HCONTROL_FORCE_OFFLINE; + out_le32(®->hcontrol, val32); + + /* Wait the controller offline */ + ata_wait_register(®->hstatus, HSTATUS_ONOFF, 0, 1000); + + /* Set the command header base address to CHBA register to tell DMA */ + out_le32(®->chba, (u32)cmd_hdr & ~0x3); + + /* Snoop for the command header */ + val32 = in_le32(®->hcontrol); + val32 |= HCONTROL_HDR_SNOOP; + out_le32(®->hcontrol, val32); + + /* Disable all of interrupts */ + val32 = in_le32(®->hcontrol); + val32 &= ~HCONTROL_INT_EN_ALL; + out_le32(®->hcontrol, val32); + + /* Clear all of interrupts */ + val32 = in_le32(®->hstatus); + out_le32(®->hstatus, val32); + + /* Set the ICC, no interrupt coalescing */ + out_le32(®->icc, 0x01000000); + + /* No PM attatched, the SATA device direct connect */ + out_le32(®->cqpmp, 0); + + /* Clear SError register */ + val32 = in_le32(®->serror); + out_le32(®->serror, val32); + + /* Clear CER register */ + val32 = in_le32(®->cer); + out_le32(®->cer, val32); + + /* Clear DER register */ + val32 = in_le32(®->der); + out_le32(®->der, val32); + + /* No device detection or initialization action requested */ + out_le32(®->scontrol, 0x00000300); + + /* Configure the transport layer, default value */ + out_le32(®->transcfg, 0x08000016); + + /* Configure the link layer, default value */ + out_le32(®->linkcfg, 0x0000ff34); + + /* Bring the controller online */ + val32 = in_le32(®->hcontrol); + val32 |= HCONTROL_ONOFF; + out_le32(®->hcontrol, val32); + + mdelay(100); + + /* print sata device name */ + printf("%s ", sata->name); + + /* Wait PHY RDY signal changed for 500ms */ + ata_wait_register(®->hstatus, HSTATUS_PHY_RDY, + HSTATUS_PHY_RDY, 500); + + /* Check PHYRDY */ + val32 = in_le32(®->hstatus); + if (val32 & HSTATUS_PHY_RDY) { + sata->link = 1; + } else { + sata->link = 0; + printf("(No RDY)\n\r"); + return -EINVAL; + } + + /* Wait for signature updated, which is 1st D2H */ + ata_wait_register(®->hstatus, HSTATUS_SIGNATURE, + HSTATUS_SIGNATURE, 10000); + + if (val32 & HSTATUS_SIGNATURE) { + sig = in_le32(®->sig); + debug("Signature updated, the sig =%08x\n\r", sig); + sata->ata_device_type = ata_dev_classify(sig); + } + + /* Check the speed */ + val32 = in_le32(®->sstatus); + if ((val32 & SSTATUS_SPD_MASK) == SSTATUS_SPD_GEN1) + printf("(1.5 Gbps)\n\r"); + else if ((val32 & SSTATUS_SPD_MASK) == SSTATUS_SPD_GEN2) + printf("(3 Gbps)\n\r"); + + priv->fsl_sata = sata; + + return 0; +} + +static int fsl_ata_exec_ata_cmd(struct fsl_sata *sata, + struct sata_fis_h2d *cfis, + int is_ncq, int tag, + u8 *buffer, u32 len) +{ + cmd_hdr_entry_t *cmd_hdr; + cmd_desc_t *cmd_desc; + sata_fis_h2d_t *h2d; + prd_entry_t *prde; + u32 ext_c_ddc; + u32 prde_count; + u32 val32; + u32 ttl; + u32 der; + int i; + + fsl_sata_reg_t *reg = sata->reg_base; + + /* Check xfer length */ + if (len > SATA_HC_MAX_XFER_LEN) { + printf("max transfer length is 64MB\n\r"); + return 0; + } + + /* Setup the command descriptor */ + cmd_desc = sata->cmd_desc + tag; + + /* Get the pointer cfis of command descriptor */ + h2d = (sata_fis_h2d_t *)cmd_desc->cfis; + + /* Zero the cfis of command descriptor */ + memset((void *)h2d, 0, SATA_HC_CMD_DESC_CFIS_SIZE); + + /* Copy the cfis from user to command descriptor */ + h2d->fis_type = cfis->fis_type; + h2d->pm_port_c = cfis->pm_port_c; + h2d->command = cfis->command; + + h2d->features = cfis->features; + h2d->features_exp = cfis->features_exp; + + h2d->lba_low = cfis->lba_low; + h2d->lba_mid = cfis->lba_mid; + h2d->lba_high = cfis->lba_high; + h2d->lba_low_exp = cfis->lba_low_exp; + h2d->lba_mid_exp = cfis->lba_mid_exp; + h2d->lba_high_exp = cfis->lba_high_exp; + + if (!is_ncq) { + h2d->sector_count = cfis->sector_count; + h2d->sector_count_exp = cfis->sector_count_exp; + } else { /* NCQ */ + h2d->sector_count = (u8)(tag << 3); + } + + h2d->device = cfis->device; + h2d->control = cfis->control; + + /* Setup the PRD table */ + prde = (prd_entry_t *)cmd_desc->prdt; + memset((void *)prde, 0, sizeof(struct prdt)); + + prde_count = 0; + ttl = len; + for (i = 0; i < SATA_HC_MAX_PRD_DIRECT; i++) { + if (!len) + break; + prde->dba = cpu_to_le32((u32)buffer & ~0x3); + debug("dba = %08x\n\r", (u32)buffer); + + if (len < PRD_ENTRY_MAX_XFER_SZ) { + ext_c_ddc = PRD_ENTRY_DATA_SNOOP | len; + debug("ext_c_ddc1 = %08x, len = %08x\n\r", + ext_c_ddc, len); + prde->ext_c_ddc = cpu_to_le32(ext_c_ddc); + prde_count++; + prde++; + } else { + ext_c_ddc = PRD_ENTRY_DATA_SNOOP; /* 4M bytes */ + debug("ext_c_ddc2 = %08x, len = %08x\n\r", + ext_c_ddc, len); + prde->ext_c_ddc = cpu_to_le32(ext_c_ddc); + buffer += PRD_ENTRY_MAX_XFER_SZ; + len -= PRD_ENTRY_MAX_XFER_SZ; + prde_count++; + prde++; + } + } + + /* Setup the command slot of cmd hdr */ + cmd_hdr = (cmd_hdr_entry_t *)&sata->cmd_hdr->cmd_slot[tag]; + + cmd_hdr->cda = cpu_to_le32((u32)cmd_desc & ~0x3); + + val32 = prde_count << CMD_HDR_PRD_ENTRY_SHIFT; + val32 |= sizeof(sata_fis_h2d_t); + cmd_hdr->prde_fis_len = cpu_to_le32(val32); + + cmd_hdr->ttl = cpu_to_le32(ttl); + + if (!is_ncq) + val32 = CMD_HDR_ATTR_RES | CMD_HDR_ATTR_SNOOP; + else + val32 = CMD_HDR_ATTR_RES | CMD_HDR_ATTR_SNOOP | + CMD_HDR_ATTR_FPDMA; + + tag &= CMD_HDR_ATTR_TAG; + val32 |= tag; + + debug("attribute = %08x\n\r", val32); + cmd_hdr->attribute = cpu_to_le32(val32); + + /* Make sure cmd desc and cmd slot valid before command issue */ + sync(); + + /* PMP*/ + val32 = (u32)(h2d->pm_port_c & 0x0f); + out_le32(®->cqpmp, val32); + + /* Wait no active */ + if (ata_wait_register(®->car, (1 << tag), 0, 10000)) + printf("Wait no active time out\n\r"); + + /* Issue command */ + if (!(in_le32(®->cqr) & (1 << tag))) { + val32 = 1 << tag; + out_le32(®->cqr, val32); + } + + /* Wait command completed for 10s */ + if (ata_wait_register(®->ccr, (1 << tag), (1 << tag), 10000)) { + if (!is_ncq) + printf("Non-NCQ command time out\n\r"); + else + printf("NCQ command time out\n\r"); + } + + val32 = in_le32(®->cer); + + if (val32) { + fsl_sata_dump_sfis((struct sata_fis_d2h *)cmd_desc->sfis); + printf("CE at device\n\r"); + fsl_sata_dump_regs(reg); + der = in_le32(®->der); + out_le32(®->cer, val32); + out_le32(®->der, der); + } + + /* Clear complete flags */ + val32 = in_le32(®->ccr); + out_le32(®->ccr, val32); + + return len; +} + +static int fsl_sata_exec_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis, + enum cmd_type command_type, int tag, u8 *buffer, + u32 len) +{ + int rc; + + if (tag > SATA_HC_MAX_CMD || tag < 0) { + printf("tag is out of range, tag=%d\n\r", tag); + return -1; + } + + switch (command_type) { + case CMD_ATA: + rc = fsl_ata_exec_ata_cmd(sata, cfis, 0, tag, buffer, len); + return rc; + case CMD_NCQ: + rc = fsl_ata_exec_ata_cmd(sata, cfis, 1, tag, buffer, len); + return rc; + case CMD_ATAPI: + case CMD_VENDOR_BIST: + case CMD_BIST: + printf("not support now\n\r"); + return -1; + default: + break; + } + + return -1; +} + +static void fsl_sata_identify(fsl_sata_t *sata, u16 *id) +{ + struct sata_fis_h2d h2d, *cfis = &h2d; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = ATA_CMD_ID_ATA; + + fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, (u8 *)id, ATA_ID_WORDS * 2); + ata_swap_buf_le16(id, ATA_ID_WORDS); +} + +static void fsl_sata_xfer_mode(fsl_sata_t *sata, u16 *id) +{ + sata->pio = id[ATA_ID_PIO_MODES]; + sata->mwdma = id[ATA_ID_MWDMA_MODES]; + sata->udma = id[ATA_ID_UDMA_MODES]; + debug("pio %04x, mwdma %04x, udma %04x\n\r", sata->pio, + sata->mwdma, sata->udma); +} + +static void fsl_sata_init_wcache(fsl_sata_t *sata, u16 *id) +{ + if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id)) + sata->wcache = 1; + if (ata_id_has_flush(id)) + sata->flush = 1; + if (ata_id_has_flush_ext(id)) + sata->flush_ext = 1; +} + +static void fsl_sata_set_features(fsl_sata_t *sata) +{ + struct sata_fis_h2d h2d, *cfis = &h2d; + u8 udma_cap; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = ATA_CMD_SET_FEATURES; + cfis->features = SETFEATURES_XFER; + + /* First check the device capablity */ + udma_cap = (u8)(sata->udma & 0xff); + debug("udma_cap %02x\n\r", udma_cap); + + if (udma_cap == ATA_UDMA6) + cfis->sector_count = XFER_UDMA_6; + if (udma_cap == ATA_UDMA5) + cfis->sector_count = XFER_UDMA_5; + if (udma_cap == ATA_UDMA4) + cfis->sector_count = XFER_UDMA_4; + if (udma_cap == ATA_UDMA3) + cfis->sector_count = XFER_UDMA_3; + + fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0); +} + +static u32 fsl_sata_rw_cmd(fsl_sata_t *sata, u32 start, u32 blkcnt, + u8 *buffer, int is_write) +{ + struct sata_fis_h2d h2d, *cfis = &h2d; + u32 block; + + block = start; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = (is_write) ? ATA_CMD_WRITE : ATA_CMD_READ; + cfis->device = ATA_LBA; + + cfis->device |= (block >> 24) & 0xf; + cfis->lba_high = (block >> 16) & 0xff; + cfis->lba_mid = (block >> 8) & 0xff; + cfis->lba_low = block & 0xff; + cfis->sector_count = (u8)(blkcnt & 0xff); + + fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, buffer, + ATA_SECT_SIZE * blkcnt); + return blkcnt; +} + +static void fsl_sata_flush_cache(fsl_sata_t *sata) +{ + struct sata_fis_h2d h2d, *cfis = &h2d; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = ATA_CMD_FLUSH; + + fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0); +} + +static u32 fsl_sata_rw_cmd_ext(fsl_sata_t *sata, u32 start, + u32 blkcnt, u8 *buffer, int is_write) +{ + struct sata_fis_h2d h2d, *cfis = &h2d; + u64 block; + + block = (u64)start; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + + cfis->command = (is_write) ? ATA_CMD_WRITE_EXT + : ATA_CMD_READ_EXT; + + cfis->lba_high_exp = (block >> 40) & 0xff; + cfis->lba_mid_exp = (block >> 32) & 0xff; + cfis->lba_low_exp = (block >> 24) & 0xff; + cfis->lba_high = (block >> 16) & 0xff; + cfis->lba_mid = (block >> 8) & 0xff; + cfis->lba_low = block & 0xff; + cfis->device = ATA_LBA; + cfis->sector_count_exp = (blkcnt >> 8) & 0xff; + cfis->sector_count = blkcnt & 0xff; + + fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, buffer, + ATA_SECT_SIZE * blkcnt); + return blkcnt; +} + +static u32 fsl_sata_rw_ncq_cmd(fsl_sata_t *sata, u32 start, u32 blkcnt, + u8 *buffer, + int is_write) +{ + struct sata_fis_h2d h2d, *cfis = &h2d; + int ncq_channel; + u64 block; + + if (sata->lba48 != 1) { + printf("execute FPDMA command on non-LBA48 hard disk\n\r"); + return -1; + } + + block = (u64)start; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + + cfis->command = (is_write) ? ATA_CMD_FPDMA_WRITE + : ATA_CMD_FPDMA_READ; + + cfis->lba_high_exp = (block >> 40) & 0xff; + cfis->lba_mid_exp = (block >> 32) & 0xff; + cfis->lba_low_exp = (block >> 24) & 0xff; + cfis->lba_high = (block >> 16) & 0xff; + cfis->lba_mid = (block >> 8) & 0xff; + cfis->lba_low = block & 0xff; + + cfis->device = ATA_LBA; + cfis->features_exp = (blkcnt >> 8) & 0xff; + cfis->features = blkcnt & 0xff; + + if (sata->queue_depth >= SATA_HC_MAX_CMD) + ncq_channel = SATA_HC_MAX_CMD - 1; + else + ncq_channel = sata->queue_depth - 1; + + /* Use the latest queue */ + fsl_sata_exec_cmd(sata, cfis, CMD_NCQ, ncq_channel, buffer, + ATA_SECT_SIZE * blkcnt); + return blkcnt; +} + +static void fsl_sata_flush_cache_ext(fsl_sata_t *sata) +{ + struct sata_fis_h2d h2d, *cfis = &h2d; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = ATA_CMD_FLUSH_EXT; + + fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0); +} + +static u32 ata_low_level_rw_lba48(fsl_sata_t *sata, u32 blknr, lbaint_t blkcnt, + const void *buffer, int is_write) +{ + u32 start, blks; + u8 *addr; + int max_blks; + + start = blknr; + blks = blkcnt; + addr = (u8 *)buffer; + + max_blks = ATA_MAX_SECTORS_LBA48; + do { + if (blks > max_blks) { + if (sata->dma_flag != FLAGS_FPDMA) + fsl_sata_rw_cmd_ext(sata, start, max_blks, + addr, is_write); + else + fsl_sata_rw_ncq_cmd(sata, start, max_blks, + addr, is_write); + start += max_blks; + blks -= max_blks; + addr += ATA_SECT_SIZE * max_blks; + } else { + if (sata->dma_flag != FLAGS_FPDMA) + fsl_sata_rw_cmd_ext(sata, start, blks, + addr, is_write); + else + fsl_sata_rw_ncq_cmd(sata, start, blks, + addr, is_write); + start += blks; + blks = 0; + addr += ATA_SECT_SIZE * blks; + } + } while (blks != 0); + + return blks; +} + +static u32 ata_low_level_rw_lba28(fsl_sata_t *sata, u32 blknr, u32 blkcnt, + const void *buffer, int is_write) +{ + u32 start, blks; + u8 *addr; + int max_blks; + + start = blknr; + blks = blkcnt; + addr = (u8 *)buffer; + + max_blks = ATA_MAX_SECTORS; + do { + if (blks > max_blks) { + fsl_sata_rw_cmd(sata, start, max_blks, addr, is_write); + start += max_blks; + blks -= max_blks; + addr += ATA_SECT_SIZE * max_blks; + } else { + fsl_sata_rw_cmd(sata, start, blks, addr, is_write); + start += blks; + blks = 0; + addr += ATA_SECT_SIZE * blks; + } + } while (blks != 0); + + return blks; +} + +/* + * SATA interface between low level driver and command layer + */ +static int sata_read(fsl_sata_t *sata, ulong blknr, lbaint_t blkcnt, + void *buffer) +{ + u32 rc; + + if (sata->lba48) + rc = ata_low_level_rw_lba48(sata, blknr, blkcnt, buffer, + READ_CMD); + else + rc = ata_low_level_rw_lba28(sata, blknr, blkcnt, buffer, + READ_CMD); + return rc; +} + +static int sata_write(fsl_sata_t *sata, ulong blknr, lbaint_t blkcnt, + const void *buffer) +{ + u32 rc; + + if (sata->lba48) { + rc = ata_low_level_rw_lba48(sata, blknr, blkcnt, buffer, + WRITE_CMD); + if (sata->wcache && sata->flush_ext) + fsl_sata_flush_cache_ext(sata); + } else { + rc = ata_low_level_rw_lba28(sata, blknr, blkcnt, buffer, + WRITE_CMD); + if (sata->wcache && sata->flush) + fsl_sata_flush_cache(sata); + } + + return rc; +} + +int sata_getinfo(fsl_sata_t *sata, u16 *id) +{ + /* if no detected link */ + if (!sata->link) + return -EINVAL; + +#ifdef CONFIG_LBA48 + /* Check if support LBA48 */ + if (ata_id_has_lba48(id)) { + sata->lba48 = 1; + debug("Device support LBA48\n\r"); + } else { + debug("Device supports LBA28\n\r"); + } +#endif + + /* Get the NCQ queue depth from device */ + sata->queue_depth = ata_id_queue_depth(id); + + /* Get the xfer mode from device */ + fsl_sata_xfer_mode(sata, id); + + /* Get the write cache status from device */ + fsl_sata_init_wcache(sata, id); + + /* Set the xfer mode to highest speed */ + fsl_sata_set_features(sata); + + return 0; +} + +static int fsl_scsi_exec(fsl_sata_t *sata, struct scsi_cmd *pccb, + bool is_write) +{ + int ret; + u32 temp; + u16 blocks = 0; + lbaint_t start = 0; + u8 *buffer = pccb->pdata; + + /* Retrieve the base LBA number from the ccb structure. */ + if (pccb->cmd[0] == SCSI_READ16) { + memcpy(&start, pccb->cmd + 2, 8); + start = be64_to_cpu(start); + } else { + memcpy(&temp, pccb->cmd + 2, 4); + start = be32_to_cpu(temp); + } + + if (pccb->cmd[0] == SCSI_READ16) + blocks = (((u16)pccb->cmd[13]) << 8) | ((u16)pccb->cmd[14]); + else + blocks = (((u16)pccb->cmd[7]) << 8) | ((u16)pccb->cmd[8]); + + debug("scsi_ahci: %s %u blocks starting from lba 0x" LBAFU "\n", + is_write ? "write" : "read", blocks, start); + + if (is_write) + ret = sata_write(sata, start, blocks, buffer); + else + ret = sata_read(sata, start, blocks, buffer); + + return ret; +} + +static char *fsl_ata_id_strcpy(u16 *target, u16 *src, int len) +{ + int i; + + for (i = 0; i < len / 2; i++) + target[i] = src[i]; + + return (char *)target; +} + +static int fsl_ata_scsiop_inquiry(struct ahci_uc_priv *uc_priv, + struct scsi_cmd *pccb, + fsl_sata_t *sata) +{ + u8 port; + u16 *idbuf; + + ALLOC_CACHE_ALIGN_BUFFER(u16, tmpid, ATA_ID_WORDS); + + /* Clean ccb data buffer */ + memset(pccb->pdata, 0, pccb->datalen); + + if (pccb->datalen <= 35) + return 0; + + /* Read id from sata */ + port = pccb->target; + + fsl_sata_identify(sata, (u16 *)tmpid); + + if (!uc_priv->ataid[port]) { + uc_priv->ataid[port] = malloc(ATA_ID_WORDS * 2); + if (!uc_priv->ataid[port]) { + printf("%s: No memory for ataid[port]\n", __func__); + return -ENOMEM; + } + } + + idbuf = uc_priv->ataid[port]; + + memcpy(idbuf, tmpid, ATA_ID_WORDS * 2); + + memcpy(&pccb->pdata[8], "ATA ", 8); + fsl_ata_id_strcpy((u16 *)&pccb->pdata[16], &idbuf[ATA_ID_PROD], 16); + fsl_ata_id_strcpy((u16 *)&pccb->pdata[32], &idbuf[ATA_ID_FW_REV], 4); + + sata_getinfo(sata, (u16 *)idbuf); +#ifdef DEBUG + ata_dump_id(idbuf); +#endif + return 0; +} + +/* + * SCSI READ CAPACITY10 command operation. + */ +static int fsl_ata_scsiop_read_capacity10(struct ahci_uc_priv *uc_priv, + struct scsi_cmd *pccb) +{ + u32 cap; + u64 cap64; + u32 block_size; + + if (!uc_priv->ataid[pccb->target]) { + printf("scsi_ahci: SCSI READ CAPACITY10 command failure."); + printf("\tNo ATA info!\n"); + printf("\tPlease run SCSI command INQUIRY first!\n"); + return -EPERM; + } + + cap64 = ata_id_n_sectors(uc_priv->ataid[pccb->target]); + if (cap64 > 0x100000000ULL) + cap64 = 0xffffffff; + + cap = cpu_to_be32(cap64); + memcpy(pccb->pdata, &cap, sizeof(cap)); + + block_size = cpu_to_be32((u32)512); + memcpy(&pccb->pdata[4], &block_size, 4); + + return 0; +} + +/* + * SCSI READ CAPACITY16 command operation. + */ +static int fsl_ata_scsiop_read_capacity16(struct ahci_uc_priv *uc_priv, + struct scsi_cmd *pccb) +{ + u64 cap; + u64 block_size; + + if (!uc_priv->ataid[pccb->target]) { + printf("scsi_ahci: SCSI READ CAPACITY16 command failure."); + printf("\tNo ATA info!\n"); + printf("\tPlease run SCSI command INQUIRY first!\n"); + return -EPERM; + } + + cap = ata_id_n_sectors(uc_priv->ataid[pccb->target]); + cap = cpu_to_be64(cap); + memcpy(pccb->pdata, &cap, sizeof(cap)); + + block_size = cpu_to_be64((u64)512); + memcpy(&pccb->pdata[8], &block_size, 8); + + return 0; +} + +/* + * SCSI TEST UNIT READY command operation. + */ +static int fsl_ata_scsiop_test_unit_ready(struct ahci_uc_priv *uc_priv, + struct scsi_cmd *pccb) +{ + return (uc_priv->ataid[pccb->target]) ? 0 : -EPERM; +} + +static int fsl_ahci_scsi_exec(struct udevice *dev, struct scsi_cmd *pccb) +{ + struct ahci_uc_priv *uc_priv = dev_get_uclass_priv(dev->parent); + struct fsl_ahci_priv *priv = dev_get_priv(dev->parent); + fsl_sata_t *sata = priv->fsl_sata; + int ret; + + switch (pccb->cmd[0]) { + case SCSI_READ16: + case SCSI_READ10: + ret = fsl_scsi_exec(sata, pccb, 0); + break; + case SCSI_WRITE10: + ret = fsl_scsi_exec(sata, pccb, 1); + break; + case SCSI_RD_CAPAC10: + ret = fsl_ata_scsiop_read_capacity10(uc_priv, pccb); + break; + case SCSI_RD_CAPAC16: + ret = fsl_ata_scsiop_read_capacity16(uc_priv, pccb); + break; + case SCSI_TST_U_RDY: + ret = fsl_ata_scsiop_test_unit_ready(uc_priv, pccb); + break; + case SCSI_INQUIRY: + ret = fsl_ata_scsiop_inquiry(uc_priv, pccb, sata); + break; + default: + printf("Unsupport SCSI command 0x%02x\n", pccb->cmd[0]); + return -ENOTSUPP; + } + + if (ret) { + debug("SCSI command 0x%02x ret errno %d\n", pccb->cmd[0], ret); + return ret; + } + + return 0; +} + +static int fsl_ahci_probe(struct udevice *dev) +{ + struct fsl_ahci_priv *priv = dev_get_priv(dev); + struct udevice *child_dev; + struct scsi_platdata *uc_plat; + + device_find_first_child(dev, &child_dev); + if (!child_dev) + return -ENODEV; + uc_plat = dev_get_uclass_platdata(child_dev); + uc_plat->base = priv->base; + uc_plat->max_lun = 1; + uc_plat->max_id = 1; + + return init_sata(priv); +} + +struct scsi_ops fsl_scsi_ops = { + .exec = fsl_ahci_scsi_exec, +}; + +static const struct udevice_id fsl_ahci_ids[] = { + { .compatible = "fsl,pq-sata-v2" }, + { } +}; + +U_BOOT_DRIVER(fsl_ahci_scsi) = { + .name = "fsl_ahci_scsi", + .id = UCLASS_SCSI, + .ops = &fsl_scsi_ops, +}; + +U_BOOT_DRIVER(fsl_ahci) = { + .name = "fsl_ahci", + .id = UCLASS_AHCI, + .of_match = fsl_ahci_ids, + .bind = fsl_ahci_bind, + .ofdata_to_platdata = fsl_ahci_ofdata_to_platdata, + .probe = fsl_ahci_probe, + .priv_auto_alloc_size = sizeof(struct fsl_ahci_priv), +}; diff --git a/drivers/ata/fsl_sata.h b/drivers/ata/fsl_sata.h index 1e2da10b02..a4ee83d187 100644 --- a/drivers/ata/fsl_sata.h +++ b/drivers/ata/fsl_sata.h @@ -312,6 +312,7 @@ typedef struct fsl_sata { int wcache; int flush; int flush_ext; + u32 dma_flag; } fsl_sata_t; #define READ_CMD 0 diff --git a/drivers/ata/sata_ceva.c b/drivers/ata/sata_ceva.c index 8887be901c..2d496305d0 100644 --- a/drivers/ata/sata_ceva.c +++ b/drivers/ata/sata_ceva.c @@ -8,6 +8,7 @@ #include <ahci.h> #include <scsi.h> #include <asm/io.h> +#include <linux/ioport.h> /* Vendor Specific Register Offsets */ #define AHCI_VEND_PCFG 0xA4 @@ -88,20 +89,16 @@ #define LS1021_CEVA_PHY4_CFG 0x064a080b #define LS1021_CEVA_PHY5_CFG 0x2aa86470 -/* for ls1088a */ -#define LS1088_ECC_DIS_ADDR_CH2 0x100520 -#define LS1088_ECC_DIS_VAL_CH2 0x40000000 - -/* ecc addr-val pair */ -#define ECC_DIS_ADDR_CH2 0x20140520 +/* ecc val pair */ +#define ECC_DIS_VAL_CH1 0x00020000 #define ECC_DIS_VAL_CH2 0x80000000 -#define SATA_ECC_REG_ADDR 0x20220520 -#define SATA_ECC_DISABLE 0x00020000 +#define ECC_DIS_VAL_CH3 0x40000000 enum ceva_soc { CEVA_1V84, CEVA_LS1012A, CEVA_LS1021A, + CEVA_LS1028A, CEVA_LS1043A, CEVA_LS1046A, CEVA_LS1088A, @@ -110,12 +107,14 @@ enum ceva_soc { struct ceva_sata_priv { ulong base; + ulong ecc_base; enum ceva_soc soc; ulong flag; }; static int ceva_init_sata(struct ceva_sata_priv *priv) { + ulong ecc_addr = priv->ecc_base; ulong base = priv->base; ulong tmp; @@ -132,38 +131,42 @@ static int ceva_init_sata(struct ceva_sata_priv *priv) break; case CEVA_LS1021A: - writel(SATA_ECC_DISABLE, SATA_ECC_REG_ADDR); + if (!ecc_addr) + return -EINVAL; + writel(ECC_DIS_VAL_CH1, ecc_addr); writel(CEVA_PHY1_CFG, base + AHCI_VEND_PPCFG); writel(LS1021_CEVA_PHY2_CFG, base + AHCI_VEND_PP2C); writel(LS1021_CEVA_PHY3_CFG, base + AHCI_VEND_PP3C); writel(LS1021_CEVA_PHY4_CFG, base + AHCI_VEND_PP4C); writel(LS1021_CEVA_PHY5_CFG, base + AHCI_VEND_PP5C); writel(CEVA_TRANS_CFG, base + AHCI_VEND_PTC); - if (priv->flag & FLAG_COHERENT) - writel(CEVA_AXICC_CFG, base + LS1021_AHCI_VEND_AXICC); break; case CEVA_LS1012A: case CEVA_LS1043A: case CEVA_LS1046A: - writel(ECC_DIS_VAL_CH2, ECC_DIS_ADDR_CH2); + if (!ecc_addr) + return -EINVAL; + writel(ECC_DIS_VAL_CH2, ecc_addr); /* fallthrough */ case CEVA_LS2080A: writel(CEVA_PHY1_CFG, base + AHCI_VEND_PPCFG); writel(CEVA_TRANS_CFG, base + AHCI_VEND_PTC); - if (priv->flag & FLAG_COHERENT) - writel(CEVA_AXICC_CFG, base + AHCI_VEND_AXICC); break; + case CEVA_LS1028A: case CEVA_LS1088A: - writel(LS1088_ECC_DIS_VAL_CH2, LS1088_ECC_DIS_ADDR_CH2); + if (!ecc_addr) + return -EINVAL; + writel(ECC_DIS_VAL_CH3, ecc_addr); writel(CEVA_PHY1_CFG, base + AHCI_VEND_PPCFG); writel(CEVA_TRANS_CFG, base + AHCI_VEND_PTC); - if (priv->flag & FLAG_COHERENT) - writel(CEVA_AXICC_CFG, base + AHCI_VEND_AXICC); break; } + if (priv->flag & FLAG_COHERENT) + writel(CEVA_AXICC_CFG, base + AHCI_VEND_AXICC); + return 0; } @@ -187,6 +190,7 @@ static const struct udevice_id sata_ceva_ids[] = { { .compatible = "ceva,ahci-1v84", .data = CEVA_1V84 }, { .compatible = "fsl,ls1012a-ahci", .data = CEVA_LS1012A }, { .compatible = "fsl,ls1021a-ahci", .data = CEVA_LS1021A }, + { .compatible = "fsl,ls1028a-ahci", .data = CEVA_LS1028A }, { .compatible = "fsl,ls1043a-ahci", .data = CEVA_LS1043A }, { .compatible = "fsl,ls1046a-ahci", .data = CEVA_LS1046A }, { .compatible = "fsl,ls1088a-ahci", .data = CEVA_LS1088A }, @@ -197,6 +201,8 @@ static const struct udevice_id sata_ceva_ids[] = { static int sata_ceva_ofdata_to_platdata(struct udevice *dev) { struct ceva_sata_priv *priv = dev_get_priv(dev); + struct resource res_regs; + int ret; if (dev_read_bool(dev, "dma-coherent")) priv->flag |= FLAG_COHERENT; @@ -205,8 +211,18 @@ static int sata_ceva_ofdata_to_platdata(struct udevice *dev) if (priv->base == FDT_ADDR_T_NONE) return -EINVAL; + ret = dev_read_resource_byname(dev, "ecc-addr", &res_regs); + if (ret) + priv->ecc_base = 0; + else + priv->ecc_base = res_regs.start; + priv->soc = dev_get_driver_data(dev); + debug("ccsr-sata-base %lx\t ecc-base %lx\n", + priv->base, + priv->ecc_base); + return 0; } diff --git a/drivers/board/gazerbeam.c b/drivers/board/gazerbeam.c index 481cce8e80..85de4e440c 100644 --- a/drivers/board/gazerbeam.c +++ b/drivers/board/gazerbeam.c @@ -61,7 +61,7 @@ static int _read_board_variant_data(struct udevice *dev) struct udevice *i2c_bus; struct udevice *dummy; char *listname; - int mc4, mc2, sc, con; + int mc4, mc2, sc, mc2_sc, con; int gpio_num; int res; @@ -78,16 +78,16 @@ static int _read_board_variant_data(struct udevice *dev) return -EIO; } - mc2 = !dm_i2c_probe(i2c_bus, MC2_EXPANDER_ADDR, 0, &dummy); + mc2_sc = !dm_i2c_probe(i2c_bus, MC2_EXPANDER_ADDR, 0, &dummy); mc4 = !dm_i2c_probe(i2c_bus, MC4_EXPANDER_ADDR, 0, &dummy); - if (mc2 && mc4) { + if (mc2_sc && mc4) { debug("%s: Board hardware configuration inconsistent.\n", dev->name); return -EINVAL; } - listname = mc2 ? "var-gpios-mc2" : "var-gpios-mc4"; + listname = mc2_sc ? "var-gpios-mc2" : "var-gpios-mc4"; gpio_num = gpio_request_list_by_name(dev, listname, priv->var_gpios, ARRAY_SIZE(priv->var_gpios), @@ -105,12 +105,7 @@ static int _read_board_variant_data(struct udevice *dev) return sc; } - con = dm_gpio_get_value(&priv->var_gpios[CON_GPIO_NO]); - if (con < 0) { - debug("%s: Error while reading 'con' GPIO (err = %d)", - dev->name, con); - return con; - } + mc2 = mc2_sc ? (sc ? 0 : 1) : 0; if ((sc && mc2) || (sc && mc4) || (!sc && !mc2 && !mc4)) { debug("%s: Board hardware configuration inconsistent.\n", @@ -118,6 +113,13 @@ static int _read_board_variant_data(struct udevice *dev) return -EINVAL; } + con = dm_gpio_get_value(&priv->var_gpios[CON_GPIO_NO]); + if (con < 0) { + debug("%s: Error while reading 'con' GPIO (err = %d)", + dev->name, con); + return con; + } + priv->variant = con ? VAR_CON : VAR_CPU; priv->multichannel = mc4 ? 4 : (mc2 ? 2 : (sc ? 1 : 0)); diff --git a/drivers/cache/Kconfig b/drivers/cache/Kconfig new file mode 100644 index 0000000000..24def7ac0f --- /dev/null +++ b/drivers/cache/Kconfig @@ -0,0 +1,25 @@ +# +# Cache controllers +# + +menu "Cache Controller drivers" + +config CACHE + bool "Enable Driver Model for Cache controllers" + depends on DM + help + Enable driver model for cache controllers that are found on + most CPU's. Cache is memory that the CPU can access directly and + is usually located on the same chip. This uclass can be used for + configuring settings that be found from a device tree file. + +config L2X0_CACHE + tristate "PL310 cache driver" + select CACHE + depends on ARM + help + This driver is for the PL310 cache controller commonly found on + ARMv7(32-bit) devices. The driver configures the cache settings + found in the device tree. + +endmenu diff --git a/drivers/cache/Makefile b/drivers/cache/Makefile new file mode 100644 index 0000000000..9deb961d91 --- /dev/null +++ b/drivers/cache/Makefile @@ -0,0 +1,4 @@ + +obj-$(CONFIG_CACHE) += cache-uclass.o +obj-$(CONFIG_SANDBOX) += sandbox_cache.o +obj-$(CONFIG_L2X0_CACHE) += cache-l2x0.o diff --git a/drivers/cache/cache-l2x0.c b/drivers/cache/cache-l2x0.c new file mode 100644 index 0000000000..67c752d076 --- /dev/null +++ b/drivers/cache/cache-l2x0.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Intel Corporation <www.intel.com> + */ +#include <common.h> +#include <command.h> +#include <dm.h> + +#include <asm/io.h> +#include <asm/pl310.h> + +static void l2c310_of_parse_and_init(struct udevice *dev) +{ + u32 tag[3] = { 0, 0, 0 }; + u32 saved_reg, prefetch; + struct pl310_regs *regs = (struct pl310_regs *)dev_read_addr(dev); + + /* Disable the L2 Cache */ + clrbits_le32(®s->pl310_ctrl, L2X0_CTRL_EN); + + saved_reg = readl(®s->pl310_aux_ctrl); + if (!dev_read_u32(dev, "prefetch-data", &prefetch)) { + if (prefetch) + saved_reg |= L310_AUX_CTRL_DATA_PREFETCH_MASK; + else + saved_reg &= ~L310_AUX_CTRL_DATA_PREFETCH_MASK; + } + + if (!dev_read_u32(dev, "prefetch-instr", &prefetch)) { + if (prefetch) + saved_reg |= L310_AUX_CTRL_INST_PREFETCH_MASK; + else + saved_reg &= ~L310_AUX_CTRL_INST_PREFETCH_MASK; + } + + saved_reg |= dev_read_bool(dev, "arm,shared-override"); + writel(saved_reg, ®s->pl310_aux_ctrl); + + saved_reg = readl(®s->pl310_tag_latency_ctrl); + if (!dev_read_u32_array(dev, "arm,tag-latency", tag, 3)) + saved_reg |= L310_LATENCY_CTRL_RD(tag[0] - 1) | + L310_LATENCY_CTRL_WR(tag[1] - 1) | + L310_LATENCY_CTRL_SETUP(tag[2] - 1); + writel(saved_reg, ®s->pl310_tag_latency_ctrl); + + saved_reg = readl(®s->pl310_data_latency_ctrl); + if (!dev_read_u32_array(dev, "arm,data-latency", tag, 3)) + saved_reg |= L310_LATENCY_CTRL_RD(tag[0] - 1) | + L310_LATENCY_CTRL_WR(tag[1] - 1) | + L310_LATENCY_CTRL_SETUP(tag[2] - 1); + writel(saved_reg, ®s->pl310_data_latency_ctrl); + + /* Enable the L2 cache */ + setbits_le32(®s->pl310_ctrl, L2X0_CTRL_EN); +} + +static int l2x0_probe(struct udevice *dev) +{ + l2c310_of_parse_and_init(dev); + + return 0; +} + + +static const struct udevice_id l2x0_ids[] = { + { .compatible = "arm,pl310-cache" }, + {} +}; + +U_BOOT_DRIVER(pl310_cache) = { + .name = "pl310_cache", + .id = UCLASS_CACHE, + .of_match = l2x0_ids, + .probe = l2x0_probe, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/cache/cache-uclass.c b/drivers/cache/cache-uclass.c new file mode 100644 index 0000000000..97ce0249a4 --- /dev/null +++ b/drivers/cache/cache-uclass.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Intel Corporation <www.intel.com> + */ + +#include <common.h> +#include <cache.h> +#include <dm.h> + +int cache_get_info(struct udevice *dev, struct cache_info *info) +{ + struct cache_ops *ops = cache_get_ops(dev); + + if (!ops->get_info) + return -ENOSYS; + + return ops->get_info(dev, info); +} + +UCLASS_DRIVER(cache) = { + .id = UCLASS_CACHE, + .name = "cache", + .post_bind = dm_scan_fdt_dev, +}; diff --git a/drivers/cache/sandbox_cache.c b/drivers/cache/sandbox_cache.c new file mode 100644 index 0000000000..14cc6b0c0a --- /dev/null +++ b/drivers/cache/sandbox_cache.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Intel Corporation <www.intel.com> + */ + +#include <common.h> +#include <cache.h> +#include <dm.h> +#include <errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int sandbox_get_info(struct udevice *dev, struct cache_info *info) +{ + info->base = 0x11223344; + + return 0; +} + +static const struct cache_ops sandbox_cache_ops = { + .get_info = sandbox_get_info, +}; + +static const struct udevice_id sandbox_cache_ids[] = { + { .compatible = "sandbox,cache" }, + { } +}; + +U_BOOT_DRIVER(cache_sandbox) = { + .name = "cache_sandbox", + .id = UCLASS_CACHE, + .of_match = sandbox_cache_ids, + .ops = &sandbox_cache_ops, +}; diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 844b87cc33..79b3b0494c 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -54,28 +54,20 @@ static int clk_of_xlate_default(struct clk *clk, return 0; } -static int clk_get_by_indexed_prop(struct udevice *dev, const char *prop_name, - int index, struct clk *clk) +static int clk_get_by_index_tail(int ret, ofnode node, + struct ofnode_phandle_args *args, + const char *list_name, int index, + struct clk *clk) { - int ret; - struct ofnode_phandle_args args; struct udevice *dev_clk; const struct clk_ops *ops; - debug("%s(dev=%p, index=%d, clk=%p)\n", __func__, dev, index, clk); - assert(clk); clk->dev = NULL; + if (ret) + goto err; - ret = dev_read_phandle_with_args(dev, prop_name, "#clock-cells", 0, - index, &args); - if (ret) { - debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n", - __func__, ret); - return ret; - } - - ret = uclass_get_device_by_ofnode(UCLASS_CLK, args.node, &dev_clk); + ret = uclass_get_device_by_ofnode(UCLASS_CLK, args->node, &dev_clk); if (ret) { debug("%s: uclass_get_device_by_of_offset failed: err=%d\n", __func__, ret); @@ -87,20 +79,67 @@ static int clk_get_by_indexed_prop(struct udevice *dev, const char *prop_name, ops = clk_dev_ops(dev_clk); if (ops->of_xlate) - ret = ops->of_xlate(clk, &args); + ret = ops->of_xlate(clk, args); else - ret = clk_of_xlate_default(clk, &args); + ret = clk_of_xlate_default(clk, args); if (ret) { debug("of_xlate() failed: %d\n", ret); return ret; } return clk_request(dev_clk, clk); +err: + debug("%s: Node '%s', property '%s', failed to request CLK index %d: %d\n", + __func__, ofnode_get_name(node), list_name, index, ret); + return ret; +} + +static int clk_get_by_indexed_prop(struct udevice *dev, const char *prop_name, + int index, struct clk *clk) +{ + int ret; + struct ofnode_phandle_args args; + + debug("%s(dev=%p, index=%d, clk=%p)\n", __func__, dev, index, clk); + + assert(clk); + clk->dev = NULL; + + ret = dev_read_phandle_with_args(dev, prop_name, "#clock-cells", 0, + index, &args); + if (ret) { + debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n", + __func__, ret); + return ret; + } + + + return clk_get_by_index_tail(ret, dev_ofnode(dev), &args, "clocks", + index > 0, clk); } int clk_get_by_index(struct udevice *dev, int index, struct clk *clk) { - return clk_get_by_indexed_prop(dev, "clocks", index, clk); + struct ofnode_phandle_args args; + int ret; + + ret = dev_read_phandle_with_args(dev, "clocks", "#clock-cells", 0, + index, &args); + + return clk_get_by_index_tail(ret, dev_ofnode(dev), &args, "clocks", + index > 0, clk); +} + +int clk_get_by_index_nodev(ofnode node, int index, struct clk *clk) +{ + struct ofnode_phandle_args args; + int ret; + + ret = ofnode_parse_phandle_with_args(node, "clocks", "#clock-cells", 0, + index > 0, &args); + + return clk_get_by_index_tail(ret, node, &args, "clocks", + index > 0, clk); } int clk_get_bulk(struct udevice *dev, struct clk_bulk *bulk) diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c index 24859fd054..6272b00b9e 100644 --- a/drivers/clk/clk_stm32mp1.c +++ b/drivers/clk/clk_stm32mp1.c @@ -1448,6 +1448,71 @@ static void pll_csg(struct stm32mp1_clk_priv *priv, int pll_id, u32 *csg) setbits_le32(priv->base + pll[pll_id].pllxcr, RCC_PLLNCR_SSCG_CTRL); } +static __maybe_unused int pll_set_rate(struct udevice *dev, + int pll_id, + int div_id, + unsigned long clk_rate) +{ + struct stm32mp1_clk_priv *priv = dev_get_priv(dev); + unsigned int pllcfg[PLLCFG_NB]; + ofnode plloff; + char name[12]; + const struct stm32mp1_clk_pll *pll = priv->data->pll; + enum stm32mp1_plltype type = pll[pll_id].plltype; + int divm, divn, divy; + int ret; + ulong fck_ref; + u32 fracv; + u64 value; + + if (div_id > _DIV_NB) + return -EINVAL; + + sprintf(name, "st,pll@%d", pll_id); + plloff = dev_read_subnode(dev, name); + if (!ofnode_valid(plloff)) + return -FDT_ERR_NOTFOUND; + + ret = ofnode_read_u32_array(plloff, "cfg", + pllcfg, PLLCFG_NB); + if (ret < 0) + return -FDT_ERR_NOTFOUND; + + fck_ref = pll_get_fref_ck(priv, pll_id); + + divm = pllcfg[PLLCFG_M]; + /* select output divider = 0: for _DIV_P, 1:_DIV_Q 2:_DIV_R */ + divy = pllcfg[PLLCFG_P + div_id]; + + /* For: PLL1 & PLL2 => VCO is * 2 but ck_pll_y is also / 2 + * So same final result than PLL2 et 4 + * with FRACV + * Fck_pll_y = Fck_ref * ((DIVN + 1) + FRACV / 2^13) + * / (DIVy + 1) * (DIVM + 1) + * value = (DIVN + 1) * 2^13 + FRACV / 2^13 + * = Fck_pll_y (DIVy + 1) * (DIVM + 1) * 2^13 / Fck_ref + */ + value = ((u64)clk_rate * (divy + 1) * (divm + 1)) << 13; + value = lldiv(value, fck_ref); + + divn = (value >> 13) - 1; + if (divn < DIVN_MIN || + divn > stm32mp1_pll[type].divn_max) { + pr_err("divn invalid = %d", divn); + return -EINVAL; + } + fracv = value - ((divn + 1) << 13); + pllcfg[PLLCFG_N] = divn; + + /* reconfigure PLL */ + pll_stop(priv, pll_id); + pll_config(priv, pll_id, pllcfg, fracv); + pll_start(priv, pll_id); + pll_output(priv, pll_id, pllcfg[PLLCFG_O]); + + return 0; +} + static int set_clksrc(struct stm32mp1_clk_priv *priv, unsigned int clksrc) { u32 address = priv->base + (clksrc >> 4); @@ -1820,6 +1885,11 @@ static ulong stm32mp1_clk_set_rate(struct clk *clk, unsigned long clk_rate) int p; switch (clk->id) { +#if defined(STM32MP1_CLOCK_TREE_INIT) && \ + defined(CONFIG_STM32MP1_DDR_INTERACTIVE) + case DDRPHYC: + break; +#endif case LTDC_PX: case DSI_PX: break; @@ -1833,6 +1903,19 @@ static ulong stm32mp1_clk_set_rate(struct clk *clk, unsigned long clk_rate) return -EINVAL; switch (p) { +#if defined(STM32MP1_CLOCK_TREE_INIT) && \ + defined(CONFIG_STM32MP1_DDR_INTERACTIVE) + case _PLL2_R: /* DDRPHYC */ + { + /* only for change DDR clock in interactive mode */ + ulong result; + + set_clksrc(priv, CLK_AXI_HSI); + result = pll_set_rate(clk->dev, _PLL2, _DIV_R, clk_rate); + set_clksrc(priv, CLK_AXI_PLL2P); + return result; + } +#endif case _PLL4_Q: /* for LTDC_PX and DSI_PX case */ return pll_set_output_rate(clk->dev, _PLL4, _DIV_Q, clk_rate); diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index 5505ae52e2..eb379c188a 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -3,3 +3,8 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_CLK_IMX8) += clk-imx8.o + +ifdef CONFIG_CLK_IMX8 +obj-$(CONFIG_IMX8QXP) += clk-imx8qxp.o +obj-$(CONFIG_IMX8QM) += clk-imx8qm.o +endif diff --git a/drivers/clk/imx/clk-imx8.c b/drivers/clk/imx/clk-imx8.c index d03fcc2fdd..a755e26501 100644 --- a/drivers/clk/imx/clk-imx8.c +++ b/drivers/clk/imx/clk-imx8.c @@ -13,302 +13,21 @@ #include <dt-bindings/soc/imx_rsrc.h> #include <misc.h> -struct imx8_clks { - ulong id; - const char *name; -}; - -#if CONFIG_IS_ENABLED(CMD_CLK) -static struct imx8_clks imx8_clk_names[] = { - { IMX8QXP_A35_DIV, "A35_DIV" }, - { IMX8QXP_I2C0_CLK, "I2C0" }, - { IMX8QXP_I2C1_CLK, "I2C1" }, - { IMX8QXP_I2C2_CLK, "I2C2" }, - { IMX8QXP_I2C3_CLK, "I2C3" }, - { IMX8QXP_UART0_CLK, "UART0" }, - { IMX8QXP_UART1_CLK, "UART1" }, - { IMX8QXP_UART2_CLK, "UART2" }, - { IMX8QXP_UART3_CLK, "UART3" }, - { IMX8QXP_SDHC0_CLK, "SDHC0" }, - { IMX8QXP_SDHC1_CLK, "SDHC1" }, - { IMX8QXP_ENET0_AHB_CLK, "ENET0_AHB" }, - { IMX8QXP_ENET0_IPG_CLK, "ENET0_IPG" }, - { IMX8QXP_ENET0_REF_DIV, "ENET0_REF" }, - { IMX8QXP_ENET0_PTP_CLK, "ENET0_PTP" }, - { IMX8QXP_ENET1_AHB_CLK, "ENET1_AHB" }, - { IMX8QXP_ENET1_IPG_CLK, "ENET1_IPG" }, - { IMX8QXP_ENET1_REF_DIV, "ENET1_REF" }, - { IMX8QXP_ENET1_PTP_CLK, "ENET1_PTP" }, -}; -#endif +#include "clk-imx8.h" -static ulong imx8_clk_get_rate(struct clk *clk) +__weak ulong imx8_clk_get_rate(struct clk *clk) { - sc_pm_clk_t pm_clk; - ulong rate; - u16 resource; - int ret; - - debug("%s(#%lu)\n", __func__, clk->id); - - switch (clk->id) { - case IMX8QXP_A35_DIV: - resource = SC_R_A35; - pm_clk = SC_PM_CLK_CPU; - break; - case IMX8QXP_I2C0_CLK: - resource = SC_R_I2C_0; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_I2C1_CLK: - resource = SC_R_I2C_1; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_I2C2_CLK: - resource = SC_R_I2C_2; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_I2C3_CLK: - resource = SC_R_I2C_3; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_SDHC0_IPG_CLK: - case IMX8QXP_SDHC0_CLK: - case IMX8QXP_SDHC0_DIV: - resource = SC_R_SDHC_0; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_SDHC1_IPG_CLK: - case IMX8QXP_SDHC1_CLK: - case IMX8QXP_SDHC1_DIV: - resource = SC_R_SDHC_1; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_UART0_IPG_CLK: - case IMX8QXP_UART0_CLK: - resource = SC_R_UART_0; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_UART1_CLK: - resource = SC_R_UART_1; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_UART2_CLK: - resource = SC_R_UART_2; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_UART3_CLK: - resource = SC_R_UART_3; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_ENET0_IPG_CLK: - case IMX8QXP_ENET0_AHB_CLK: - case IMX8QXP_ENET0_REF_DIV: - case IMX8QXP_ENET0_PTP_CLK: - resource = SC_R_ENET_0; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_ENET1_IPG_CLK: - case IMX8QXP_ENET1_AHB_CLK: - case IMX8QXP_ENET1_REF_DIV: - case IMX8QXP_ENET1_PTP_CLK: - resource = SC_R_ENET_1; - pm_clk = SC_PM_CLK_PER; - break; - default: - if (clk->id < IMX8QXP_UART0_IPG_CLK || - clk->id >= IMX8QXP_CLK_END) { - printf("%s(Invalid clk ID #%lu)\n", - __func__, clk->id); - return -EINVAL; - } - return -ENOTSUPP; - }; - - ret = sc_pm_get_clock_rate(-1, resource, pm_clk, - (sc_pm_clock_rate_t *)&rate); - if (ret) { - printf("%s err %d\n", __func__, ret); - return ret; - } - - return rate; + return 0; } -static ulong imx8_clk_set_rate(struct clk *clk, unsigned long rate) +__weak ulong imx8_clk_set_rate(struct clk *clk, unsigned long rate) { - sc_pm_clk_t pm_clk; - u32 new_rate = rate; - u16 resource; - int ret; - - debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate); - - switch (clk->id) { - case IMX8QXP_I2C0_CLK: - resource = SC_R_I2C_0; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_I2C1_CLK: - resource = SC_R_I2C_1; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_I2C2_CLK: - resource = SC_R_I2C_2; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_I2C3_CLK: - resource = SC_R_I2C_3; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_UART0_CLK: - resource = SC_R_UART_0; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_UART1_CLK: - resource = SC_R_UART_1; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_UART2_CLK: - resource = SC_R_UART_2; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_UART3_CLK: - resource = SC_R_UART_3; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_SDHC0_IPG_CLK: - case IMX8QXP_SDHC0_CLK: - case IMX8QXP_SDHC0_DIV: - resource = SC_R_SDHC_0; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_SDHC1_SEL: - case IMX8QXP_SDHC0_SEL: - return 0; - case IMX8QXP_SDHC1_IPG_CLK: - case IMX8QXP_SDHC1_CLK: - case IMX8QXP_SDHC1_DIV: - resource = SC_R_SDHC_1; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_ENET0_IPG_CLK: - case IMX8QXP_ENET0_AHB_CLK: - case IMX8QXP_ENET0_REF_DIV: - case IMX8QXP_ENET0_PTP_CLK: - resource = SC_R_ENET_0; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_ENET1_IPG_CLK: - case IMX8QXP_ENET1_AHB_CLK: - case IMX8QXP_ENET1_REF_DIV: - case IMX8QXP_ENET1_PTP_CLK: - resource = SC_R_ENET_1; - pm_clk = SC_PM_CLK_PER; - break; - default: - if (clk->id < IMX8QXP_UART0_IPG_CLK || - clk->id >= IMX8QXP_CLK_END) { - printf("%s(Invalid clk ID #%lu)\n", - __func__, clk->id); - return -EINVAL; - } - return -ENOTSUPP; - }; - - ret = sc_pm_set_clock_rate(-1, resource, pm_clk, &new_rate); - if (ret) { - printf("%s err %d\n", __func__, ret); - return ret; - } - - return new_rate; + return 0; } -static int __imx8_clk_enable(struct clk *clk, bool enable) +__weak int __imx8_clk_enable(struct clk *clk, bool enable) { - sc_pm_clk_t pm_clk; - u16 resource; - int ret; - - debug("%s(#%lu)\n", __func__, clk->id); - - switch (clk->id) { - case IMX8QXP_I2C0_CLK: - resource = SC_R_I2C_0; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_I2C1_CLK: - resource = SC_R_I2C_1; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_I2C2_CLK: - resource = SC_R_I2C_2; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_I2C3_CLK: - resource = SC_R_I2C_3; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_UART0_CLK: - resource = SC_R_UART_0; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_UART1_CLK: - resource = SC_R_UART_1; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_UART2_CLK: - resource = SC_R_UART_2; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_UART3_CLK: - resource = SC_R_UART_3; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_SDHC0_IPG_CLK: - case IMX8QXP_SDHC0_CLK: - case IMX8QXP_SDHC0_DIV: - resource = SC_R_SDHC_0; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_SDHC1_IPG_CLK: - case IMX8QXP_SDHC1_CLK: - case IMX8QXP_SDHC1_DIV: - resource = SC_R_SDHC_1; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_ENET0_IPG_CLK: - case IMX8QXP_ENET0_AHB_CLK: - case IMX8QXP_ENET0_REF_DIV: - case IMX8QXP_ENET0_PTP_CLK: - resource = SC_R_ENET_0; - pm_clk = SC_PM_CLK_PER; - break; - case IMX8QXP_ENET1_IPG_CLK: - case IMX8QXP_ENET1_AHB_CLK: - case IMX8QXP_ENET1_REF_DIV: - case IMX8QXP_ENET1_PTP_CLK: - resource = SC_R_ENET_1; - pm_clk = SC_PM_CLK_PER; - break; - default: - if (clk->id < IMX8QXP_UART0_IPG_CLK || - clk->id >= IMX8QXP_CLK_END) { - printf("%s(Invalid clk ID #%lu)\n", - __func__, clk->id); - return -EINVAL; - } - return -ENOTSUPP; - } - - ret = sc_pm_clock_enable(-1, resource, pm_clk, enable, 0); - if (ret) { - printf("%s err %d\n", __func__, ret); - return ret; - } - - return 0; + return -ENOTSUPP; } static int imx8_clk_disable(struct clk *clk) @@ -336,7 +55,7 @@ int soc_clk_dump(void) printf("Clk\t\tHz\n"); - for (i = 0; i < ARRAY_SIZE(imx8_clk_names); i++) { + for (i = 0; i < num_clks; i++) { clk.id = imx8_clk_names[i].id; ret = clk_request(dev, &clk); if (ret < 0) { @@ -382,6 +101,7 @@ static int imx8_clk_probe(struct udevice *dev) static const struct udevice_id imx8_clk_ids[] = { { .compatible = "fsl,imx8qxp-clk" }, + { .compatible = "fsl,imx8qm-clk" }, { }, }; diff --git a/drivers/clk/imx/clk-imx8.h b/drivers/clk/imx/clk-imx8.h new file mode 100644 index 0000000000..68ad6755e8 --- /dev/null +++ b/drivers/clk/imx/clk-imx8.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2018 NXP + * Peng Fan <peng.fan@nxp.com> + */ + +struct imx8_clks { + ulong id; + const char *name; +}; + +#if CONFIG_IS_ENABLED(CMD_CLK) +extern struct imx8_clks imx8_clk_names[]; +extern int num_clks; +#endif + +ulong imx8_clk_get_rate(struct clk *clk); +ulong imx8_clk_set_rate(struct clk *clk, unsigned long rate); +int __imx8_clk_enable(struct clk *clk, bool enable); diff --git a/drivers/clk/imx/clk-imx8qm.c b/drivers/clk/imx/clk-imx8qm.c new file mode 100644 index 0000000000..6b5561e178 --- /dev/null +++ b/drivers/clk/imx/clk-imx8qm.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 NXP + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <asm/arch/sci/sci.h> +#include <asm/arch/clock.h> +#include <dt-bindings/clock/imx8qm-clock.h> +#include <dt-bindings/soc/imx_rsrc.h> +#include <misc.h> + +#include "clk-imx8.h" + +#if CONFIG_IS_ENABLED(CMD_CLK) +struct imx8_clks imx8_clk_names[] = { + { IMX8QM_A53_DIV, "A53_DIV" }, + { IMX8QM_UART0_CLK, "UART0" }, + { IMX8QM_UART1_CLK, "UART1" }, + { IMX8QM_UART2_CLK, "UART2" }, + { IMX8QM_UART3_CLK, "UART3" }, + { IMX8QM_SDHC0_CLK, "SDHC0" }, + { IMX8QM_SDHC1_CLK, "SDHC1" }, + { IMX8QM_SDHC2_CLK, "SDHC2" }, + { IMX8QM_ENET0_AHB_CLK, "ENET0_AHB" }, + { IMX8QM_ENET0_IPG_CLK, "ENET0_IPG" }, + { IMX8QM_ENET0_REF_DIV, "ENET0_REF" }, + { IMX8QM_ENET0_PTP_CLK, "ENET0_PTP" }, + { IMX8QM_ENET1_AHB_CLK, "ENET1_AHB" }, + { IMX8QM_ENET1_IPG_CLK, "ENET1_IPG" }, + { IMX8QM_ENET1_REF_DIV, "ENET1_REF" }, + { IMX8QM_ENET1_PTP_CLK, "ENET1_PTP" }, +}; + +int num_clks = ARRAY_SIZE(imx8_clk_names); +#endif + +ulong imx8_clk_get_rate(struct clk *clk) +{ + sc_pm_clk_t pm_clk; + ulong rate; + u16 resource; + int ret; + + debug("%s(#%lu)\n", __func__, clk->id); + + switch (clk->id) { + case IMX8QM_A53_DIV: + resource = SC_R_A53; + pm_clk = SC_PM_CLK_CPU; + break; + case IMX8QM_I2C0_CLK: + resource = SC_R_I2C_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_I2C1_CLK: + resource = SC_R_I2C_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_I2C2_CLK: + resource = SC_R_I2C_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_I2C3_CLK: + resource = SC_R_I2C_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_SDHC0_IPG_CLK: + case IMX8QM_SDHC0_CLK: + case IMX8QM_SDHC0_DIV: + resource = SC_R_SDHC_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_SDHC1_IPG_CLK: + case IMX8QM_SDHC1_CLK: + case IMX8QM_SDHC1_DIV: + resource = SC_R_SDHC_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_UART0_IPG_CLK: + case IMX8QM_UART0_CLK: + resource = SC_R_UART_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_UART1_CLK: + resource = SC_R_UART_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_UART2_CLK: + resource = SC_R_UART_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_UART3_CLK: + resource = SC_R_UART_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_ENET0_IPG_CLK: + case IMX8QM_ENET0_AHB_CLK: + case IMX8QM_ENET0_REF_DIV: + case IMX8QM_ENET0_PTP_CLK: + resource = SC_R_ENET_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_ENET1_IPG_CLK: + case IMX8QM_ENET1_AHB_CLK: + case IMX8QM_ENET1_REF_DIV: + case IMX8QM_ENET1_PTP_CLK: + resource = SC_R_ENET_1; + pm_clk = SC_PM_CLK_PER; + break; + default: + if (clk->id < IMX8QM_UART0_IPG_CLK || + clk->id >= IMX8QM_CLK_END) { + printf("%s(Invalid clk ID #%lu)\n", + __func__, clk->id); + return -EINVAL; + } + return -ENOTSUPP; + }; + + ret = sc_pm_get_clock_rate(-1, resource, pm_clk, + (sc_pm_clock_rate_t *)&rate); + if (ret) { + printf("%s err %d\n", __func__, ret); + return ret; + } + + return rate; +} + +ulong imx8_clk_set_rate(struct clk *clk, unsigned long rate) +{ + sc_pm_clk_t pm_clk; + u32 new_rate = rate; + u16 resource; + int ret; + + debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate); + + switch (clk->id) { + case IMX8QM_I2C0_CLK: + resource = SC_R_I2C_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_I2C1_CLK: + resource = SC_R_I2C_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_I2C2_CLK: + resource = SC_R_I2C_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_I2C3_CLK: + resource = SC_R_I2C_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_UART0_CLK: + resource = SC_R_UART_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_UART1_CLK: + resource = SC_R_UART_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_UART2_CLK: + resource = SC_R_UART_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_UART3_CLK: + resource = SC_R_UART_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_SDHC0_IPG_CLK: + case IMX8QM_SDHC0_CLK: + case IMX8QM_SDHC0_DIV: + resource = SC_R_SDHC_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_SDHC1_IPG_CLK: + case IMX8QM_SDHC1_CLK: + case IMX8QM_SDHC1_DIV: + resource = SC_R_SDHC_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_ENET0_IPG_CLK: + case IMX8QM_ENET0_AHB_CLK: + case IMX8QM_ENET0_REF_DIV: + case IMX8QM_ENET0_PTP_CLK: + case IMX8QM_ENET0_ROOT_DIV: + resource = SC_R_ENET_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_ENET1_IPG_CLK: + case IMX8QM_ENET1_AHB_CLK: + case IMX8QM_ENET1_REF_DIV: + case IMX8QM_ENET1_PTP_CLK: + case IMX8QM_ENET1_ROOT_DIV: + resource = SC_R_ENET_1; + pm_clk = SC_PM_CLK_PER; + break; + default: + if (clk->id < IMX8QM_UART0_IPG_CLK || + clk->id >= IMX8QM_CLK_END) { + printf("%s(Invalid clk ID #%lu)\n", + __func__, clk->id); + return -EINVAL; + } + return -ENOTSUPP; + }; + + ret = sc_pm_set_clock_rate(-1, resource, pm_clk, &new_rate); + if (ret) { + printf("%s err %d\n", __func__, ret); + return ret; + } + + return new_rate; +} + +int __imx8_clk_enable(struct clk *clk, bool enable) +{ + sc_pm_clk_t pm_clk; + u16 resource; + int ret; + + debug("%s(#%lu)\n", __func__, clk->id); + + switch (clk->id) { + case IMX8QM_I2C0_CLK: + resource = SC_R_I2C_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_I2C1_CLK: + resource = SC_R_I2C_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_I2C2_CLK: + resource = SC_R_I2C_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_I2C3_CLK: + resource = SC_R_I2C_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_UART0_CLK: + resource = SC_R_UART_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_UART1_CLK: + resource = SC_R_UART_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_UART2_CLK: + resource = SC_R_UART_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_UART3_CLK: + resource = SC_R_UART_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_SDHC0_IPG_CLK: + case IMX8QM_SDHC0_CLK: + case IMX8QM_SDHC0_DIV: + resource = SC_R_SDHC_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_SDHC1_IPG_CLK: + case IMX8QM_SDHC1_CLK: + case IMX8QM_SDHC1_DIV: + resource = SC_R_SDHC_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_ENET0_IPG_CLK: + case IMX8QM_ENET0_AHB_CLK: + case IMX8QM_ENET0_REF_DIV: + case IMX8QM_ENET0_PTP_CLK: + resource = SC_R_ENET_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QM_ENET1_IPG_CLK: + case IMX8QM_ENET1_AHB_CLK: + case IMX8QM_ENET1_REF_DIV: + case IMX8QM_ENET1_PTP_CLK: + resource = SC_R_ENET_1; + pm_clk = SC_PM_CLK_PER; + break; + default: + if (clk->id < IMX8QM_UART0_IPG_CLK || + clk->id >= IMX8QM_CLK_END) { + printf("%s(Invalid clk ID #%lu)\n", + __func__, clk->id); + return -EINVAL; + } + return -ENOTSUPP; + } + + ret = sc_pm_clock_enable(-1, resource, pm_clk, enable, 0); + if (ret) { + printf("%s err %d\n", __func__, ret); + return ret; + } + + return 0; +} diff --git a/drivers/clk/imx/clk-imx8qxp.c b/drivers/clk/imx/clk-imx8qxp.c new file mode 100644 index 0000000000..1fca36ac91 --- /dev/null +++ b/drivers/clk/imx/clk-imx8qxp.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 NXP + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <asm/arch/sci/sci.h> +#include <asm/arch/clock.h> +#include <dt-bindings/clock/imx8qxp-clock.h> +#include <dt-bindings/soc/imx_rsrc.h> +#include <misc.h> + +#include "clk-imx8.h" + +#if CONFIG_IS_ENABLED(CMD_CLK) +struct imx8_clks imx8_clk_names[] = { + { IMX8QXP_A35_DIV, "A35_DIV" }, + { IMX8QXP_I2C0_CLK, "I2C0" }, + { IMX8QXP_I2C1_CLK, "I2C1" }, + { IMX8QXP_I2C2_CLK, "I2C2" }, + { IMX8QXP_I2C3_CLK, "I2C3" }, + { IMX8QXP_UART0_CLK, "UART0" }, + { IMX8QXP_UART1_CLK, "UART1" }, + { IMX8QXP_UART2_CLK, "UART2" }, + { IMX8QXP_UART3_CLK, "UART3" }, + { IMX8QXP_SDHC0_CLK, "SDHC0" }, + { IMX8QXP_SDHC1_CLK, "SDHC1" }, + { IMX8QXP_ENET0_AHB_CLK, "ENET0_AHB" }, + { IMX8QXP_ENET0_IPG_CLK, "ENET0_IPG" }, + { IMX8QXP_ENET0_REF_DIV, "ENET0_REF" }, + { IMX8QXP_ENET0_PTP_CLK, "ENET0_PTP" }, + { IMX8QXP_ENET1_AHB_CLK, "ENET1_AHB" }, + { IMX8QXP_ENET1_IPG_CLK, "ENET1_IPG" }, + { IMX8QXP_ENET1_REF_DIV, "ENET1_REF" }, + { IMX8QXP_ENET1_PTP_CLK, "ENET1_PTP" }, +}; + +int num_clks = ARRAY_SIZE(imx8_clk_names); +#endif + +ulong imx8_clk_get_rate(struct clk *clk) +{ + sc_pm_clk_t pm_clk; + ulong rate; + u16 resource; + int ret; + + debug("%s(#%lu)\n", __func__, clk->id); + + switch (clk->id) { + case IMX8QXP_A35_DIV: + resource = SC_R_A35; + pm_clk = SC_PM_CLK_CPU; + break; + case IMX8QXP_I2C0_CLK: + resource = SC_R_I2C_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C1_CLK: + resource = SC_R_I2C_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C2_CLK: + resource = SC_R_I2C_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C3_CLK: + resource = SC_R_I2C_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_SDHC0_IPG_CLK: + case IMX8QXP_SDHC0_CLK: + case IMX8QXP_SDHC0_DIV: + resource = SC_R_SDHC_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_SDHC1_IPG_CLK: + case IMX8QXP_SDHC1_CLK: + case IMX8QXP_SDHC1_DIV: + resource = SC_R_SDHC_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART0_IPG_CLK: + case IMX8QXP_UART0_CLK: + resource = SC_R_UART_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART1_CLK: + resource = SC_R_UART_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART2_CLK: + resource = SC_R_UART_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART3_CLK: + resource = SC_R_UART_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_ENET0_IPG_CLK: + case IMX8QXP_ENET0_AHB_CLK: + case IMX8QXP_ENET0_REF_DIV: + case IMX8QXP_ENET0_PTP_CLK: + resource = SC_R_ENET_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_ENET1_IPG_CLK: + case IMX8QXP_ENET1_AHB_CLK: + case IMX8QXP_ENET1_REF_DIV: + case IMX8QXP_ENET1_PTP_CLK: + resource = SC_R_ENET_1; + pm_clk = SC_PM_CLK_PER; + break; + default: + if (clk->id < IMX8QXP_UART0_IPG_CLK || + clk->id >= IMX8QXP_CLK_END) { + printf("%s(Invalid clk ID #%lu)\n", + __func__, clk->id); + return -EINVAL; + } + return -ENOTSUPP; + }; + + ret = sc_pm_get_clock_rate(-1, resource, pm_clk, + (sc_pm_clock_rate_t *)&rate); + if (ret) { + printf("%s err %d\n", __func__, ret); + return ret; + } + + return rate; +} + +ulong imx8_clk_set_rate(struct clk *clk, unsigned long rate) +{ + sc_pm_clk_t pm_clk; + u32 new_rate = rate; + u16 resource; + int ret; + + debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate); + + switch (clk->id) { + case IMX8QXP_I2C0_CLK: + resource = SC_R_I2C_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C1_CLK: + resource = SC_R_I2C_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C2_CLK: + resource = SC_R_I2C_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C3_CLK: + resource = SC_R_I2C_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART0_CLK: + resource = SC_R_UART_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART1_CLK: + resource = SC_R_UART_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART2_CLK: + resource = SC_R_UART_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART3_CLK: + resource = SC_R_UART_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_SDHC0_IPG_CLK: + case IMX8QXP_SDHC0_CLK: + case IMX8QXP_SDHC0_DIV: + resource = SC_R_SDHC_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_SDHC1_SEL: + case IMX8QXP_SDHC0_SEL: + return 0; + case IMX8QXP_SDHC1_IPG_CLK: + case IMX8QXP_SDHC1_CLK: + case IMX8QXP_SDHC1_DIV: + resource = SC_R_SDHC_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_ENET0_IPG_CLK: + case IMX8QXP_ENET0_AHB_CLK: + case IMX8QXP_ENET0_REF_DIV: + case IMX8QXP_ENET0_PTP_CLK: + resource = SC_R_ENET_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_ENET1_IPG_CLK: + case IMX8QXP_ENET1_AHB_CLK: + case IMX8QXP_ENET1_REF_DIV: + case IMX8QXP_ENET1_PTP_CLK: + resource = SC_R_ENET_1; + pm_clk = SC_PM_CLK_PER; + break; + default: + if (clk->id < IMX8QXP_UART0_IPG_CLK || + clk->id >= IMX8QXP_CLK_END) { + printf("%s(Invalid clk ID #%lu)\n", + __func__, clk->id); + return -EINVAL; + } + return -ENOTSUPP; + }; + + ret = sc_pm_set_clock_rate(-1, resource, pm_clk, &new_rate); + if (ret) { + printf("%s err %d\n", __func__, ret); + return ret; + } + + return new_rate; +} + +int __imx8_clk_enable(struct clk *clk, bool enable) +{ + sc_pm_clk_t pm_clk; + u16 resource; + int ret; + + debug("%s(#%lu)\n", __func__, clk->id); + + switch (clk->id) { + case IMX8QXP_I2C0_CLK: + resource = SC_R_I2C_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C1_CLK: + resource = SC_R_I2C_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C2_CLK: + resource = SC_R_I2C_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C3_CLK: + resource = SC_R_I2C_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART0_CLK: + resource = SC_R_UART_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART1_CLK: + resource = SC_R_UART_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART2_CLK: + resource = SC_R_UART_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART3_CLK: + resource = SC_R_UART_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_SDHC0_IPG_CLK: + case IMX8QXP_SDHC0_CLK: + case IMX8QXP_SDHC0_DIV: + resource = SC_R_SDHC_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_SDHC1_IPG_CLK: + case IMX8QXP_SDHC1_CLK: + case IMX8QXP_SDHC1_DIV: + resource = SC_R_SDHC_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_ENET0_IPG_CLK: + case IMX8QXP_ENET0_AHB_CLK: + case IMX8QXP_ENET0_REF_DIV: + case IMX8QXP_ENET0_PTP_CLK: + resource = SC_R_ENET_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_ENET1_IPG_CLK: + case IMX8QXP_ENET1_AHB_CLK: + case IMX8QXP_ENET1_REF_DIV: + case IMX8QXP_ENET1_PTP_CLK: + resource = SC_R_ENET_1; + pm_clk = SC_PM_CLK_PER; + break; + default: + if (clk->id < IMX8QXP_UART0_IPG_CLK || + clk->id >= IMX8QXP_CLK_END) { + printf("%s(Invalid clk ID #%lu)\n", + __func__, clk->id); + return -EINVAL; + } + return -ENOTSUPP; + } + + ret = sc_pm_clock_enable(-1, resource, pm_clk, enable, 0); + if (ret) { + printf("%s err %d\n", __func__, ret); + return ret; + } + + return 0; +} diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c index fedc9eb7dd..112326e553 100644 --- a/drivers/clk/meson/g12a.c +++ b/drivers/clk/meson/g12a.c @@ -22,6 +22,8 @@ struct meson_clk { struct regmap *map; }; +static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned long id, + ulong rate, ulong current_rate); static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id); #define NUM_CLKS 178 @@ -36,6 +38,8 @@ static struct meson_gate gates[NUM_CLKS] = { MESON_GATE(CLKID_SD_EMMC_C, HHI_GCLK_MPEG0, 26), MESON_GATE(CLKID_ETH, HHI_GCLK_MPEG1, 3), MESON_GATE(CLKID_UART1, HHI_GCLK_MPEG1, 16), + MESON_GATE(CLKID_USB, HHI_GCLK_MPEG1, 25), + MESON_GATE(CLKID_USB1_DDR_BRIDGE, HHI_GCLK_MPEG2, 8), /* Peripheral Gates */ MESON_GATE(CLKID_SD_EMMC_B_CLK0, HHI_SD_EMMC_CLK_CNTL, 23), @@ -231,6 +235,36 @@ static ulong meson_pll_get_rate(struct clk *clk, unsigned long id) return ((parent_rate_mhz * m / n) >> od) * 1000000; } +static struct parm meson_pcie_pll_parm[3] = { + {HHI_PCIE_PLL_CNTL0, 0, 8}, /* pm */ + {HHI_PCIE_PLL_CNTL0, 10, 5}, /* pn */ + {HHI_PCIE_PLL_CNTL0, 16, 5}, /* pod */ +}; + +static ulong meson_pcie_pll_get_rate(struct clk *clk) +{ + struct meson_clk *priv = dev_get_priv(clk->dev); + struct parm *pm, *pn, *pod; + unsigned long parent_rate_mhz = XTAL_RATE / 1000000; + u16 n, m, od; + uint reg; + + pm = &meson_pcie_pll_parm[0]; + pn = &meson_pcie_pll_parm[1]; + pod = &meson_pcie_pll_parm[2]; + + regmap_read(priv->map, pn->reg_off, ®); + n = PARM_GET(pn->width, pn->shift, reg); + + regmap_read(priv->map, pm->reg_off, ®); + m = PARM_GET(pm->width, pm->shift, reg); + + regmap_read(priv->map, pod->reg_off, ®); + od = PARM_GET(pod->width, pod->shift, reg); + + return ((parent_rate_mhz * m / n) / 2 / od / 2) * 1000000; +} + static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id) { ulong rate; @@ -263,6 +297,9 @@ static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id) case CLKID_CLK81: rate = meson_clk81_get_rate(clk); break; + case CLKID_PCIE_PLL: + rate = meson_pcie_pll_get_rate(clk); + break; default: if (gates[id].reg != 0) { /* a clock gate */ @@ -281,6 +318,71 @@ static ulong meson_clk_get_rate(struct clk *clk) return meson_clk_get_rate_by_id(clk, clk->id); } +static ulong meson_pcie_pll_set_rate(struct clk *clk, ulong rate) +{ + struct meson_clk *priv = dev_get_priv(clk->dev); + + regmap_write(priv->map, HHI_PCIE_PLL_CNTL0, 0x20090496); + regmap_write(priv->map, HHI_PCIE_PLL_CNTL0, 0x30090496); + regmap_write(priv->map, HHI_PCIE_PLL_CNTL1, 0x00000000); + regmap_write(priv->map, HHI_PCIE_PLL_CNTL2, 0x00001100); + regmap_write(priv->map, HHI_PCIE_PLL_CNTL3, 0x10058e00); + regmap_write(priv->map, HHI_PCIE_PLL_CNTL4, 0x000100c0); + regmap_write(priv->map, HHI_PCIE_PLL_CNTL5, 0x68000048); + regmap_write(priv->map, HHI_PCIE_PLL_CNTL5, 0x68000068); + udelay(20); + regmap_write(priv->map, HHI_PCIE_PLL_CNTL4, 0x008100c0); + udelay(10); + regmap_write(priv->map, HHI_PCIE_PLL_CNTL0, 0x34090496); + regmap_write(priv->map, HHI_PCIE_PLL_CNTL0, 0x14090496); + udelay(10); + regmap_write(priv->map, HHI_PCIE_PLL_CNTL2, 0x00001000); + regmap_update_bits(priv->map, HHI_PCIE_PLL_CNTL0, + 0x1f << 16, 9 << 16); + + return 100000000; +} + +static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned long id, + ulong rate, ulong current_rate) +{ + if (current_rate == rate) + return 0; + + switch (id) { + /* Fixed clocks */ + case CLKID_PCIE_PLL: + return meson_pcie_pll_set_rate(clk, rate); + + default: + return -ENOENT; + } + + return -EINVAL; +} + + +static ulong meson_clk_set_rate(struct clk *clk, ulong rate) +{ + ulong current_rate = meson_clk_get_rate_by_id(clk, clk->id); + int ret; + + if (IS_ERR_VALUE(current_rate)) + return current_rate; + + debug("%s: setting rate of %ld from %ld to %ld\n", + __func__, clk->id, current_rate, rate); + + ret = meson_clk_set_rate_by_id(clk, clk->id, rate, current_rate); + if (IS_ERR_VALUE(ret)) + return ret; + + debug("clock %lu has new rate %lu\n", clk->id, + meson_clk_get_rate_by_id(clk, clk->id)); + + return 0; +} + static int meson_clk_probe(struct udevice *dev) { struct meson_clk *priv = dev_get_priv(dev); @@ -298,6 +400,7 @@ static struct clk_ops meson_clk_ops = { .disable = meson_clk_disable, .enable = meson_clk_enable, .get_rate = meson_clk_get_rate, + .set_rate = meson_clk_set_rate, }; static const struct udevice_id meson_clk_ids[] = { diff --git a/drivers/clk/mpc83xx_clk.c b/drivers/clk/mpc83xx_clk.c index 489004190e..32d2db9eda 100644 --- a/drivers/clk/mpc83xx_clk.c +++ b/drivers/clk/mpc83xx_clk.c @@ -275,6 +275,12 @@ static ulong mpc83xx_clk_get_rate(struct clk *clk) return priv->speed[clk->id]; } +static int mpc83xx_clk_enable(struct clk *clk) +{ + /* MPC83xx clocks are always enabled */ + return 0; +} + int get_clocks(void) { /* Empty implementation to keep the prototype in common.h happy */ @@ -301,6 +307,7 @@ int get_serial_clock(void) const struct clk_ops mpc83xx_clk_ops = { .request = mpc83xx_clk_request, .get_rate = mpc83xx_clk_get_rate, + .enable = mpc83xx_clk_enable, }; static const struct udevice_id mpc83xx_clk_match[] = { diff --git a/drivers/clk/rockchip/clk_rk3036.c b/drivers/clk/rockchip/clk_rk3036.c index 9c4e8901e8..9bf9cedaf8 100644 --- a/drivers/clk/rockchip/clk_rk3036.c +++ b/drivers/clk/rockchip/clk_rk3036.c @@ -9,9 +9,9 @@ #include <errno.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3036.h> -#include <asm/arch/hardware.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3036.h> +#include <asm/arch-rockchip/hardware.h> #include <dm/lists.h> #include <dt-bindings/clock/rk3036-cru.h> #include <linux/log2.h> diff --git a/drivers/clk/rockchip/clk_rk3128.c b/drivers/clk/rockchip/clk_rk3128.c index 7da785abc6..efda8c830b 100644 --- a/drivers/clk/rockchip/clk_rk3128.c +++ b/drivers/clk/rockchip/clk_rk3128.c @@ -9,9 +9,9 @@ #include <errno.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3128.h> -#include <asm/arch/hardware.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3128.h> +#include <asm/arch-rockchip/hardware.h> #include <bitfield.h> #include <dm/lists.h> #include <dt-bindings/clock/rk3128-cru.h> diff --git a/drivers/clk/rockchip/clk_rk3188.c b/drivers/clk/rockchip/clk_rk3188.c index db7479a237..9bb9959c9d 100644 --- a/drivers/clk/rockchip/clk_rk3188.c +++ b/drivers/clk/rockchip/clk_rk3188.c @@ -12,10 +12,10 @@ #include <mapmem.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3188.h> -#include <asm/arch/grf_rk3188.h> -#include <asm/arch/hardware.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3188.h> +#include <asm/arch-rockchip/grf_rk3188.h> +#include <asm/arch-rockchip/hardware.h> #include <dt-bindings/clock/rk3188-cru.h> #include <dm/device-internal.h> #include <dm/lists.h> diff --git a/drivers/clk/rockchip/clk_rk322x.c b/drivers/clk/rockchip/clk_rk322x.c index 46a569c9ec..f09730c91b 100644 --- a/drivers/clk/rockchip/clk_rk322x.c +++ b/drivers/clk/rockchip/clk_rk322x.c @@ -9,9 +9,9 @@ #include <errno.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk322x.h> -#include <asm/arch/hardware.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk322x.h> +#include <asm/arch-rockchip/hardware.h> #include <dm/lists.h> #include <dt-bindings/clock/rk3228-cru.h> #include <linux/log2.h> @@ -121,10 +121,10 @@ static void rkclk_init(struct rk322x_cru *cru) assert((aclk_div + 1) * BUS_ACLK_HZ == GPLL_HZ && aclk_div <= 0x1f); pclk_div = BUS_ACLK_HZ / BUS_PCLK_HZ - 1; - assert((pclk_div + 1) * BUS_PCLK_HZ == GPLL_HZ && pclk_div <= 0x7); + assert((pclk_div + 1) * BUS_PCLK_HZ == BUS_ACLK_HZ && pclk_div <= 0x7); hclk_div = BUS_ACLK_HZ / BUS_HCLK_HZ - 1; - assert((hclk_div + 1) * BUS_HCLK_HZ == GPLL_HZ && hclk_div <= 0x3); + assert((hclk_div + 1) * BUS_HCLK_HZ == BUS_ACLK_HZ && hclk_div <= 0x3); rk_clrsetreg(&cru->cru_clksel_con[0], BUS_ACLK_PLL_SEL_MASK | BUS_ACLK_DIV_MASK, @@ -217,6 +217,7 @@ static ulong rockchip_mmc_get_clk(struct rk322x_cru *cru, uint clk_general_rate, switch (periph) { case HCLK_EMMC: case SCLK_EMMC: + case SCLK_EMMC_SAMPLE: con = readl(&cru->cru_clksel_con[11]); mux = (con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT; con = readl(&cru->cru_clksel_con[12]); @@ -293,6 +294,7 @@ static ulong rockchip_mmc_set_clk(struct rk322x_cru *cru, uint clk_general_rate, switch (periph) { case HCLK_EMMC: case SCLK_EMMC: + case SCLK_EMMC_SAMPLE: rk_clrsetreg(&cru->cru_clksel_con[11], EMMC_PLL_MASK, mux << EMMC_PLL_SHIFT); diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c index 930c99f4d9..375d7f8acb 100644 --- a/drivers/clk/rockchip/clk_rk3288.c +++ b/drivers/clk/rockchip/clk_rk3288.c @@ -13,10 +13,10 @@ #include <mapmem.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3288.h> -#include <asm/arch/grf_rk3288.h> -#include <asm/arch/hardware.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3288.h> +#include <asm/arch-rockchip/grf_rk3288.h> +#include <asm/arch-rockchip/hardware.h> #include <dt-bindings/clock/rk3288-cru.h> #include <dm/device-internal.h> #include <dm/lists.h> diff --git a/drivers/clk/rockchip/clk_rk3328.c b/drivers/clk/rockchip/clk_rk3328.c index 106621fe7c..a89e2ecc4a 100644 --- a/drivers/clk/rockchip/clk_rk3328.c +++ b/drivers/clk/rockchip/clk_rk3328.c @@ -9,10 +9,10 @@ #include <dm.h> #include <errno.h> #include <syscon.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3328.h> -#include <asm/arch/hardware.h> -#include <asm/arch/grf_rk3328.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3328.h> +#include <asm/arch-rockchip/hardware.h> +#include <asm/arch-rockchip/grf_rk3328.h> #include <asm/io.h> #include <dm/lists.h> #include <dt-bindings/clock/rk3328-cru.h> diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index 9492cc2a36..89cbae59c5 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -13,9 +13,9 @@ #include <mapmem.h> #include <syscon.h> #include <bitfield.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3368.h> -#include <asm/arch/hardware.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3368.h> +#include <asm/arch-rockchip/hardware.h> #include <asm/io.h> #include <dm/lists.h> #include <dt-bindings/clock/rk3368-cru.h> diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c index cab2bd9943..aa6a8ad1c9 100644 --- a/drivers/clk/rockchip/clk_rk3399.c +++ b/drivers/clk/rockchip/clk_rk3399.c @@ -13,9 +13,9 @@ #include <syscon.h> #include <bitfield.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3399.h> -#include <asm/arch/hardware.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3399.h> +#include <asm/arch-rockchip/hardware.h> #include <dm/lists.h> #include <dt-bindings/clock/rk3399-cru.h> @@ -912,7 +912,9 @@ static ulong rk3399_clk_get_rate(struct clk *clk) rate = rk3399_spi_get_clk(priv->cru, clk->id); break; case SCLK_UART0: + case SCLK_UART1: case SCLK_UART2: + case SCLK_UART3: return 24000000; break; case PCLK_HDMI_CTRL: diff --git a/drivers/clk/rockchip/clk_rv1108.c b/drivers/clk/rockchip/clk_rv1108.c index 914e2f4b21..3ebb007fab 100644 --- a/drivers/clk/rockchip/clk_rv1108.c +++ b/drivers/clk/rockchip/clk_rv1108.c @@ -11,9 +11,9 @@ #include <errno.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rv1108.h> -#include <asm/arch/hardware.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rv1108.h> +#include <asm/arch-rockchip/hardware.h> #include <dm/lists.h> #include <dt-bindings/clock/rv1108-cru.h> diff --git a/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig index 81fc9f8fda..644881b948 100644 --- a/drivers/clk/sifive/Kconfig +++ b/drivers/clk/sifive/Kconfig @@ -17,3 +17,10 @@ config CLK_SIFIVE_FU540_PRCI Supports the Power Reset Clock interface (PRCI) IP block found in FU540 SoCs. If this kernel is meant to run on a SiFive FU540 SoC, enable this driver. + +config CLK_SIFIVE_GEMGXL_MGMT + bool "GEMGXL management for SiFive FU540 SoCs" + depends on CLK_SIFIVE + help + Supports the GEMGXL management IP block found in FU540 SoCs to + control GEM TX clock operation mode for 10/100/1000 Mbps. diff --git a/drivers/clk/sifive/Makefile b/drivers/clk/sifive/Makefile index 1155e07e37..f8263e79b7 100644 --- a/drivers/clk/sifive/Makefile +++ b/drivers/clk/sifive/Makefile @@ -3,3 +3,5 @@ obj-$(CONFIG_CLK_ANALOGBITS_WRPLL_CLN28HPC) += wrpll-cln28hpc.o obj-$(CONFIG_CLK_SIFIVE_FU540_PRCI) += fu540-prci.o + +obj-$(CONFIG_CLK_SIFIVE_GEMGXL_MGMT) += gemgxl-mgmt.o diff --git a/drivers/clk/sifive/fu540-prci.c b/drivers/clk/sifive/fu540-prci.c index e1b5f8e6a9..2d47ebc6b1 100644 --- a/drivers/clk/sifive/fu540-prci.c +++ b/drivers/clk/sifive/fu540-prci.c @@ -28,10 +28,10 @@ * - SiFive FU540-C000 manual v1p0, Chapter 7 "Clocking and Reset" */ +#include <common.h> #include <asm/io.h> #include <clk-uclass.h> #include <clk.h> -#include <common.h> #include <div64.h> #include <dm.h> #include <errno.h> diff --git a/drivers/clk/sifive/gemgxl-mgmt.c b/drivers/clk/sifive/gemgxl-mgmt.c new file mode 100644 index 0000000000..eb37416b5e --- /dev/null +++ b/drivers/clk/sifive/gemgxl-mgmt.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <asm/io.h> + +struct gemgxl_mgmt_regs { + __u32 tx_clk_sel; +}; + +struct gemgxl_mgmt_platdata { + struct gemgxl_mgmt_regs *regs; +}; + +static int gemgxl_mgmt_ofdata_to_platdata(struct udevice *dev) +{ + struct gemgxl_mgmt_platdata *plat = dev_get_platdata(dev); + + plat->regs = (struct gemgxl_mgmt_regs *)dev_read_addr(dev); + + return 0; +} + +static ulong gemgxl_mgmt_set_rate(struct clk *clk, ulong rate) +{ + struct gemgxl_mgmt_platdata *plat = dev_get_platdata(clk->dev); + + /* + * GEMGXL TX clock operation mode: + * + * 0 = GMII mode. Use 125 MHz gemgxlclk from PRCI in TX logic + * and output clock on GMII output signal GTX_CLK + * 1 = MII mode. Use MII input signal TX_CLK in TX logic + */ + writel(rate != 125000000, &plat->regs->tx_clk_sel); + + return 0; +} + +const struct clk_ops gemgxl_mgmt_ops = { + .set_rate = gemgxl_mgmt_set_rate, +}; + +static const struct udevice_id gemgxl_mgmt_match[] = { + { .compatible = "sifive,cadencegemgxlmgmt0", }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(sifive_gemgxl_mgmt) = { + .name = "sifive-gemgxl-mgmt", + .id = UCLASS_CLK, + .of_match = gemgxl_mgmt_match, + .ofdata_to_platdata = gemgxl_mgmt_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct gemgxl_mgmt_platdata), + .ops = &gemgxl_mgmt_ops, +}; diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 785f5c3acf..c72c6e2673 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -39,7 +39,7 @@ int ofnode_read_u32(ofnode node, const char *propname, u32 *outp) return 0; } -int ofnode_read_u32_default(ofnode node, const char *propname, u32 def) +u32 ofnode_read_u32_default(ofnode node, const char *propname, u32 def) { assert(ofnode_valid(node)); ofnode_read_u32(node, propname, &def); @@ -251,7 +251,7 @@ int ofnode_read_size(ofnode node, const char *propname) return -EINVAL; } -fdt_addr_t ofnode_get_addr_index(ofnode node, int index) +fdt_addr_t ofnode_get_addr_size_index(ofnode node, int index, fdt_size_t *size) { int na, ns; @@ -260,7 +260,7 @@ fdt_addr_t ofnode_get_addr_index(ofnode node, int index) uint flags; prop_val = of_get_address(ofnode_to_np(node), index, - NULL, &flags); + (u64 *)size, &flags); if (!prop_val) return FDT_ADDR_T_NONE; @@ -277,12 +277,19 @@ fdt_addr_t ofnode_get_addr_index(ofnode node, int index) ns = ofnode_read_simple_size_cells(ofnode_get_parent(node)); return fdtdec_get_addr_size_fixed(gd->fdt_blob, ofnode_to_offset(node), "reg", - index, na, ns, NULL, true); + index, na, ns, size, true); } return FDT_ADDR_T_NONE; } +fdt_addr_t ofnode_get_addr_index(ofnode node, int index) +{ + fdt_size_t size; + + return ofnode_get_addr_size_index(node, index, &size); +} + fdt_addr_t ofnode_get_addr(ofnode node) { return ofnode_get_addr_index(node, 0); @@ -546,7 +553,7 @@ fdt_addr_t ofnode_get_addr_size(ofnode node, const char *property, ns = of_n_size_cells(np); *sizep = of_read_number(prop + na, ns); - if (IS_ENABLED(CONFIG_OF_TRANSLATE) && ns > 0) + if (CONFIG_IS_ENABLED(OF_TRANSLATE) && ns > 0) return of_translate_address(np, prop); else return of_read_number(prop, na); diff --git a/drivers/core/root.c b/drivers/core/root.c index 8fa096648e..aa5ca4087a 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -342,7 +342,7 @@ int dm_extended_scan_fdt(const void *blob, bool pre_reloc_only) { int ret; - ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only); + ret = dm_scan_fdt(blob, pre_reloc_only); if (ret) { debug("dm_scan_fdt() failed: %d\n", ret); return ret; diff --git a/drivers/ddr/altera/Kconfig b/drivers/ddr/altera/Kconfig index 8f60b56eb8..2b1c1be3b5 100644 --- a/drivers/ddr/altera/Kconfig +++ b/drivers/ddr/altera/Kconfig @@ -1,7 +1,8 @@ -config ALTERA_SDRAM - bool "SoCFPGA DDR SDRAM driver" - depends on TARGET_SOCFPGA_GEN5 || TARGET_SOCFPGA_ARRIA10 - select RAM if TARGET_SOCFPGA_GEN5 - select SPL_RAM if TARGET_SOCFPGA_GEN5 +config SPL_ALTERA_SDRAM + bool "SoCFPGA DDR SDRAM driver in SPL" + depends on SPL + depends on TARGET_SOCFPGA_GEN5 || TARGET_SOCFPGA_ARRIA10 || TARGET_SOCFPGA_STRATIX10 + select RAM if TARGET_SOCFPGA_GEN5 || TARGET_SOCFPGA_STRATIX10 + select SPL_RAM if TARGET_SOCFPGA_GEN5 || TARGET_SOCFPGA_STRATIX10 help Enable DDR SDRAM controller for the SoCFPGA devices. diff --git a/drivers/ddr/altera/Makefile b/drivers/ddr/altera/Makefile index 3615b617ec..341ac0d73b 100644 --- a/drivers/ddr/altera/Makefile +++ b/drivers/ddr/altera/Makefile @@ -6,7 +6,7 @@ # (C) Copyright 2010, Thomas Chou <thomas@wytron.com.tw> # Copyright (C) 2014 Altera Corporation <www.altera.com> -ifdef CONFIG_ALTERA_SDRAM +ifdef CONFIG_$(SPL_)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_s10.o diff --git a/drivers/ddr/altera/sdram_s10.c b/drivers/ddr/altera/sdram_s10.c index e4d4a02ca2..56cbbac9fe 100644 --- a/drivers/ddr/altera/sdram_s10.c +++ b/drivers/ddr/altera/sdram_s10.c @@ -5,17 +5,31 @@ */ #include <common.h> +#include <dm.h> #include <errno.h> #include <div64.h> #include <fdtdec.h> -#include <asm/io.h> +#include <ram.h> +#include <reset.h> +#include "sdram_s10.h" #include <wait_bit.h> #include <asm/arch/firewall_s10.h> -#include <asm/arch/sdram_s10.h> #include <asm/arch/system_manager.h> #include <asm/arch/reset_manager.h> +#include <asm/io.h> #include <linux/sizes.h> +struct altera_sdram_priv { + struct ram_info info; + struct reset_ctl_bulk resets; +}; + +struct altera_sdram_platdata { + void __iomem *hmc; + void __iomem *ddr_sch; + void __iomem *iomhc; +}; + DECLARE_GLOBAL_DATA_PTR; static const struct socfpga_system_manager *sysmgr_regs = @@ -51,25 +65,26 @@ u32 ddr_config[] = { DDR_CONFIG(1, 4, 10, 17), }; -static u32 hmc_readl(u32 reg) +static u32 hmc_readl(struct altera_sdram_platdata *plat, u32 reg) { - return readl(((void __iomem *)SOCFPGA_HMC_MMR_IO48_ADDRESS + (reg))); + return readl(plat->iomhc + reg); } -static u32 hmc_ecc_readl(u32 reg) +static u32 hmc_ecc_readl(struct altera_sdram_platdata *plat, u32 reg) { - return readl((void __iomem *)SOCFPGA_SDR_ADDRESS + (reg)); + return readl(plat->hmc + reg); } -static u32 hmc_ecc_writel(u32 data, u32 reg) +static u32 hmc_ecc_writel(struct altera_sdram_platdata *plat, + u32 data, u32 reg) { - return writel(data, (void __iomem *)SOCFPGA_SDR_ADDRESS + (reg)); + return writel(data, plat->hmc + reg); } -static u32 ddr_sch_writel(u32 data, u32 reg) +static u32 ddr_sch_writel(struct altera_sdram_platdata *plat, u32 data, + u32 reg) { - return writel(data, - (void __iomem *)SOCFPGA_SDR_SCHEDULER_ADDRESS + (reg)); + return writel(data, plat->ddr_sch + reg); } int match_ddr_conf(u32 ddr_conf) @@ -83,37 +98,38 @@ int match_ddr_conf(u32 ddr_conf) return 0; } -static int emif_clear(void) +static int emif_clear(struct altera_sdram_platdata *plat) { - hmc_ecc_writel(0, RSTHANDSHAKECTRL); + hmc_ecc_writel(plat, 0, RSTHANDSHAKECTRL); - return wait_for_bit_le32((const void *)(SOCFPGA_SDR_ADDRESS + + return wait_for_bit_le32((const void *)(plat->hmc + RSTHANDSHAKESTAT), DDR_HMC_RSTHANDSHAKE_MASK, false, 1000, false); } -static int emif_reset(void) +static int emif_reset(struct altera_sdram_platdata *plat) { u32 c2s, s2c, ret; - c2s = hmc_ecc_readl(RSTHANDSHAKECTRL) & DDR_HMC_RSTHANDSHAKE_MASK; - s2c = hmc_ecc_readl(RSTHANDSHAKESTAT) & DDR_HMC_RSTHANDSHAKE_MASK; + c2s = hmc_ecc_readl(plat, RSTHANDSHAKECTRL) & DDR_HMC_RSTHANDSHAKE_MASK; + s2c = hmc_ecc_readl(plat, RSTHANDSHAKESTAT) & DDR_HMC_RSTHANDSHAKE_MASK; debug("DDR: c2s=%08x s2c=%08x nr0=%08x nr1=%08x nr2=%08x dst=%08x\n", - c2s, s2c, hmc_readl(NIOSRESERVED0), hmc_readl(NIOSRESERVED1), - hmc_readl(NIOSRESERVED2), hmc_readl(DRAMSTS)); + c2s, s2c, hmc_readl(plat, NIOSRESERVED0), + hmc_readl(plat, NIOSRESERVED1), hmc_readl(plat, NIOSRESERVED2), + hmc_readl(plat, DRAMSTS)); - if (s2c && emif_clear()) { + if (s2c && emif_clear(plat)) { printf("DDR: emif_clear() failed\n"); return -1; } debug("DDR: Triggerring emif reset\n"); - hmc_ecc_writel(DDR_HMC_CORE2SEQ_INT_REQ, RSTHANDSHAKECTRL); + hmc_ecc_writel(plat, DDR_HMC_CORE2SEQ_INT_REQ, RSTHANDSHAKECTRL); /* if seq2core[3] = 0, we are good */ - ret = wait_for_bit_le32((const void *)(SOCFPGA_SDR_ADDRESS + + ret = wait_for_bit_le32((const void *)(plat->hmc + RSTHANDSHAKESTAT), DDR_HMC_SEQ2CORE_INT_RESP_MASK, false, 1000, false); @@ -122,7 +138,7 @@ static int emif_reset(void) return ret; } - ret = emif_clear(); + ret = emif_clear(plat); if (ret) { printf("DDR: emif_clear() failed\n"); return ret; @@ -241,12 +257,36 @@ static void sdram_size_check(bd_t *bd) } /** + * sdram_calculate_size() - Calculate SDRAM size + * + * Calculate SDRAM device size based on SDRAM controller parameters. + * Size is specified in bytes. + */ +static phys_size_t sdram_calculate_size(struct altera_sdram_platdata *plat) +{ + u32 dramaddrw = hmc_readl(plat, DRAMADDRW); + + phys_size_t size = 1 << (DRAMADDRW_CFG_CS_ADDR_WIDTH(dramaddrw) + + DRAMADDRW_CFG_BANK_GRP_ADDR_WIDTH(dramaddrw) + + DRAMADDRW_CFG_BANK_ADDR_WIDTH(dramaddrw) + + DRAMADDRW_CFG_ROW_ADDR_WIDTH(dramaddrw) + + DRAMADDRW_CFG_COL_ADDR_WIDTH(dramaddrw)); + + size *= (2 << (hmc_ecc_readl(plat, DDRIOCTRL) & + DDR_HMC_DDRIOCTRL_IOSIZE_MSK)); + + return size; +} + +/** * sdram_mmr_init_full() - Function to initialize SDRAM MMR * * Initialize the SDRAM MMR. */ -int sdram_mmr_init_full(unsigned int unused) +static int sdram_mmr_init_full(struct udevice *dev) { + struct altera_sdram_platdata *plat = dev->platdata; + struct altera_sdram_priv *priv = dev_get_priv(dev); u32 update_value, io48_value, ddrioctl; u32 i; int ret; @@ -303,19 +343,16 @@ int sdram_mmr_init_full(unsigned int unused) return -1; } - /* release DDR scheduler from reset */ - socfpga_per_reset(SOCFPGA_RESET(SDR), 0); - /* Try 3 times to do a calibration */ for (i = 0; i < 3; i++) { - ret = wait_for_bit_le32((const void *)(SOCFPGA_SDR_ADDRESS + + ret = wait_for_bit_le32((const void *)(plat->hmc + DDRCALSTAT), DDR_HMC_DDRCALSTAT_CAL_MSK, true, 1000, false); if (!ret) break; - emif_reset(); + emif_reset(plat); } if (ret) { @@ -324,16 +361,16 @@ int sdram_mmr_init_full(unsigned int unused) } debug("DDR: Calibration success\n"); - u32 ctrlcfg0 = hmc_readl(CTRLCFG0); - u32 ctrlcfg1 = hmc_readl(CTRLCFG1); - u32 dramaddrw = hmc_readl(DRAMADDRW); - u32 dramtim0 = hmc_readl(DRAMTIMING0); - u32 caltim0 = hmc_readl(CALTIMING0); - u32 caltim1 = hmc_readl(CALTIMING1); - u32 caltim2 = hmc_readl(CALTIMING2); - u32 caltim3 = hmc_readl(CALTIMING3); - u32 caltim4 = hmc_readl(CALTIMING4); - u32 caltim9 = hmc_readl(CALTIMING9); + u32 ctrlcfg0 = hmc_readl(plat, CTRLCFG0); + u32 ctrlcfg1 = hmc_readl(plat, CTRLCFG1); + u32 dramaddrw = hmc_readl(plat, DRAMADDRW); + u32 dramtim0 = hmc_readl(plat, DRAMTIMING0); + u32 caltim0 = hmc_readl(plat, CALTIMING0); + u32 caltim1 = hmc_readl(plat, CALTIMING1); + u32 caltim2 = hmc_readl(plat, CALTIMING2); + u32 caltim3 = hmc_readl(plat, CALTIMING3); + u32 caltim4 = hmc_readl(plat, CALTIMING4); + u32 caltim9 = hmc_readl(plat, CALTIMING9); /* * Configure the DDR IO size [0xFFCFB008] @@ -349,12 +386,12 @@ int sdram_mmr_init_full(unsigned int unused) * bit[9:6] = Minor Release # * bit[14:10] = Major Release # */ - update_value = hmc_readl(NIOSRESERVED0); - hmc_ecc_writel(((update_value & 0xFF) >> 5), DDRIOCTRL); - ddrioctl = hmc_ecc_readl(DDRIOCTRL); + update_value = hmc_readl(plat, NIOSRESERVED0); + hmc_ecc_writel(plat, ((update_value & 0xFF) >> 5), DDRIOCTRL); + ddrioctl = hmc_ecc_readl(plat, DDRIOCTRL); /* enable HPS interface to HMC */ - hmc_ecc_writel(DDR_HMC_HPSINTFCSEL_ENABLE_MASK, HPSINTFCSEL); + hmc_ecc_writel(plat, DDR_HMC_HPSINTFCSEL_ENABLE_MASK, HPSINTFCSEL); /* Set the DDR Configuration */ io48_value = DDR_CONFIG(CTRLCFG1_CFG_ADDR_ORDER(ctrlcfg1), @@ -365,10 +402,10 @@ int sdram_mmr_init_full(unsigned int unused) update_value = match_ddr_conf(io48_value); if (update_value) - ddr_sch_writel(update_value, DDR_SCH_DDRCONF); + ddr_sch_writel(plat, update_value, DDR_SCH_DDRCONF); /* Configure HMC dramaddrw */ - hmc_ecc_writel(hmc_readl(DRAMADDRW), DRAMADDRWIDTH); + hmc_ecc_writel(plat, hmc_readl(plat, DRAMADDRW), DRAMADDRWIDTH); /* * Configure DDR timing @@ -392,7 +429,7 @@ int sdram_mmr_init_full(unsigned int unused) CALTIMING0_CFG_ACT_TO_RDWR(caltim0) + CALTIMING4_CFG_PCH_TO_VALID(caltim4)); - ddr_sch_writel(((CALTIMING0_CFG_ACT_TO_ACT(caltim0) << + ddr_sch_writel(plat, ((CALTIMING0_CFG_ACT_TO_ACT(caltim0) << DDR_SCH_DDRTIMING_ACTTOACT_OFF) | (update_value << DDR_SCH_DDRTIMING_RDTOMISS_OFF) | (io48_value << DDR_SCH_DDRTIMING_WRTOMISS_OFF) | @@ -406,12 +443,12 @@ int sdram_mmr_init_full(unsigned int unused) DDR_SCH_DDRTIMING); /* Configure DDR mode [precharge = 0] */ - ddr_sch_writel(((ddrioctl ? 0 : 1) << + ddr_sch_writel(plat, ((ddrioctl ? 0 : 1) << DDR_SCH_DDRMOD_BWRATIOEXTENDED_OFF), DDR_SCH_DDRMODE); /* Configure the read latency */ - ddr_sch_writel((DRAMTIMING0_CFG_TCL(dramtim0) >> 1) + + ddr_sch_writel(plat, (DRAMTIMING0_CFG_TCL(dramtim0) >> 1) + DDR_READ_LATENCY_DELAY, DDR_SCH_READ_LATENCY); @@ -419,7 +456,7 @@ int sdram_mmr_init_full(unsigned int unused) * Configuring timing values concerning activate commands * [FAWBANK alway 1 because always 4 bank DDR] */ - ddr_sch_writel(((CALTIMING0_CFG_ACT_TO_ACT_DB(caltim0) << + ddr_sch_writel(plat, ((CALTIMING0_CFG_ACT_TO_ACT_DB(caltim0) << DDR_SCH_ACTIVATE_RRD_OFF) | (CALTIMING9_CFG_4_ACT_TO_ACT(caltim9) << DDR_SCH_ACTIVATE_FAW_OFF) | @@ -431,7 +468,7 @@ int sdram_mmr_init_full(unsigned int unused) * Configuring timing values concerning device to device data bus * ownership change */ - ddr_sch_writel(((CALTIMING1_CFG_RD_TO_RD_DC(caltim1) << + ddr_sch_writel(plat, ((CALTIMING1_CFG_RD_TO_RD_DC(caltim1) << DDR_SCH_DEVTODEV_BUSRDTORD_OFF) | (CALTIMING1_CFG_RD_TO_WR_DC(caltim1) << DDR_SCH_DEVTODEV_BUSRDTOWR_OFF) | @@ -440,7 +477,7 @@ int sdram_mmr_init_full(unsigned int unused) DDR_SCH_DEVTODEV); /* assigning the SDRAM size */ - unsigned long long size = sdram_calculate_size(); + unsigned long long size = sdram_calculate_size(plat); /* If the size is invalid, use default Config size */ if (size <= 0) hw_size = PHYS_SDRAM_1_SIZE; @@ -462,18 +499,17 @@ int sdram_mmr_init_full(unsigned int unused) /* Enable or disable the SDRAM ECC */ if (CTRLCFG1_CFG_CTRL_EN_ECC(ctrlcfg1)) { - setbits_le32(SOCFPGA_SDR_ADDRESS + ECCCTRL1, + setbits_le32(plat->hmc + ECCCTRL1, (DDR_HMC_ECCCTL_AWB_CNT_RST_SET_MSK | DDR_HMC_ECCCTL_CNT_RST_SET_MSK | DDR_HMC_ECCCTL_ECC_EN_SET_MSK)); - clrbits_le32(SOCFPGA_SDR_ADDRESS + ECCCTRL1, + clrbits_le32(plat->hmc + ECCCTRL1, (DDR_HMC_ECCCTL_AWB_CNT_RST_SET_MSK | DDR_HMC_ECCCTL_CNT_RST_SET_MSK)); - setbits_le32(SOCFPGA_SDR_ADDRESS + ECCCTRL2, + setbits_le32(plat->hmc + ECCCTRL2, (DDR_HMC_ECCCTL2_RMW_EN_SET_MSK | DDR_HMC_ECCCTL2_AWB_EN_SET_MSK)); - writel(DDR_HMC_ERRINTEN_INTMASK, - SOCFPGA_SDR_ADDRESS + ERRINTENS); + hmc_ecc_writel(plat, DDR_HMC_ERRINTEN_INTMASK, ERRINTENS); /* Enable non-secure writes to HMC Adapter for SDRAM ECC */ writel(FW_HMC_ADAPTOR_MPU_MASK, FW_HMC_ADAPTOR_REG_ADDR); @@ -482,39 +518,98 @@ int sdram_mmr_init_full(unsigned int unused) if (!cpu_has_been_warmreset()) sdram_init_ecc_bits(&bd); } else { - clrbits_le32(SOCFPGA_SDR_ADDRESS + ECCCTRL1, + clrbits_le32(plat->hmc + ECCCTRL1, (DDR_HMC_ECCCTL_AWB_CNT_RST_SET_MSK | DDR_HMC_ECCCTL_CNT_RST_SET_MSK | DDR_HMC_ECCCTL_ECC_EN_SET_MSK)); - clrbits_le32(SOCFPGA_SDR_ADDRESS + ECCCTRL2, + clrbits_le32(plat->hmc + ECCCTRL2, (DDR_HMC_ECCCTL2_RMW_EN_SET_MSK | DDR_HMC_ECCCTL2_AWB_EN_SET_MSK)); } sdram_size_check(&bd); + priv->info.base = bd.bi_dram[0].start; + priv->info.size = gd->ram_size; + debug("DDR: HMC init success\n"); return 0; } -/** - * sdram_calculate_size() - Calculate SDRAM size - * - * Calculate SDRAM device size based on SDRAM controller parameters. - * Size is specified in bytes. - */ -phys_size_t sdram_calculate_size(void) +static int altera_sdram_ofdata_to_platdata(struct udevice *dev) { - u32 dramaddrw = hmc_readl(DRAMADDRW); + struct altera_sdram_platdata *plat = dev->platdata; + fdt_addr_t addr; - phys_size_t size = 1 << (DRAMADDRW_CFG_CS_ADDR_WIDTH(dramaddrw) + - DRAMADDRW_CFG_BANK_GRP_ADDR_WIDTH(dramaddrw) + - DRAMADDRW_CFG_BANK_ADDR_WIDTH(dramaddrw) + - DRAMADDRW_CFG_ROW_ADDR_WIDTH(dramaddrw) + - DRAMADDRW_CFG_COL_ADDR_WIDTH(dramaddrw)); + addr = dev_read_addr_index(dev, 0); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + plat->ddr_sch = (void __iomem *)addr; - size *= (2 << (hmc_ecc_readl(DDRIOCTRL) & - DDR_HMC_DDRIOCTRL_IOSIZE_MSK)); + addr = dev_read_addr_index(dev, 1); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + plat->iomhc = (void __iomem *)addr; - return size; + addr = dev_read_addr_index(dev, 2); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + plat->hmc = (void __iomem *)addr; + + return 0; } + +static int altera_sdram_probe(struct udevice *dev) +{ + int ret; + struct altera_sdram_priv *priv = dev_get_priv(dev); + + ret = reset_get_bulk(dev, &priv->resets); + if (ret) { + dev_err(dev, "Can't get reset: %d\n", ret); + return -ENODEV; + } + reset_deassert_bulk(&priv->resets); + + if (sdram_mmr_init_full(dev) != 0) { + puts("SDRAM init failed.\n"); + goto failed; + } + + return 0; + +failed: + reset_release_bulk(&priv->resets); + return -ENODEV; +} + +static int altera_sdram_get_info(struct udevice *dev, + struct ram_info *info) +{ + struct altera_sdram_priv *priv = dev_get_priv(dev); + + info->base = priv->info.base; + info->size = priv->info.size; + + return 0; +} + +static struct ram_ops altera_sdram_ops = { + .get_info = altera_sdram_get_info, +}; + +static const struct udevice_id altera_sdram_ids[] = { + { .compatible = "altr,sdr-ctl-s10" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(altera_sdram) = { + .name = "altr_sdr_ctl", + .id = UCLASS_RAM, + .of_match = altera_sdram_ids, + .ops = &altera_sdram_ops, + .ofdata_to_platdata = altera_sdram_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct altera_sdram_platdata), + .probe = altera_sdram_probe, + .priv_auto_alloc_size = sizeof(struct altera_sdram_priv), +}; diff --git a/drivers/ddr/altera/sdram_s10.h b/drivers/ddr/altera/sdram_s10.h new file mode 100644 index 0000000000..096c06cba2 --- /dev/null +++ b/drivers/ddr/altera/sdram_s10.h @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2017-2018 Intel Corporation <www.intel.com> + * + */ + +#ifndef _SDRAM_S10_H_ +#define _SDRAM_S10_H_ + +#define DDR_TWR 15 +#define DDR_READ_LATENCY_DELAY 40 +#define DDR_ACTIVATE_FAWBANK 0x1 + +/* ECC HMC registers */ +#define DDRIOCTRL 0x8 +#define DDRCALSTAT 0xc +#define DRAMADDRWIDTH 0xe0 +#define ECCCTRL1 0x100 +#define ECCCTRL2 0x104 +#define ERRINTEN 0x110 +#define ERRINTENS 0x114 +#define INTMODE 0x11c +#define INTSTAT 0x120 +#define AUTOWB_CORRADDR 0x138 +#define ECC_REG2WRECCDATABUS 0x144 +#define ECC_DIAGON 0x150 +#define ECC_DECSTAT 0x154 +#define HPSINTFCSEL 0x210 +#define RSTHANDSHAKECTRL 0x214 +#define RSTHANDSHAKESTAT 0x218 + +#define DDR_HMC_DDRIOCTRL_IOSIZE_MSK 0x00000003 +#define DDR_HMC_DDRCALSTAT_CAL_MSK BIT(0) +#define DDR_HMC_ECCCTL_AWB_CNT_RST_SET_MSK BIT(16) +#define DDR_HMC_ECCCTL_CNT_RST_SET_MSK BIT(8) +#define DDR_HMC_ECCCTL_ECC_EN_SET_MSK BIT(0) +#define DDR_HMC_ECCCTL2_RMW_EN_SET_MSK BIT(8) +#define DDR_HMC_ECCCTL2_AWB_EN_SET_MSK BIT(0) +#define DDR_HMC_ECC_DIAGON_ECCDIAGON_EN_SET_MSK BIT(16) +#define DDR_HMC_ECC_DIAGON_WRDIAGON_EN_SET_MSK BIT(0) +#define DDR_HMC_ERRINTEN_SERRINTEN_EN_SET_MSK BIT(0) +#define DDR_HMC_ERRINTEN_DERRINTEN_EN_SET_MSK BIT(1) +#define DDR_HMC_INTSTAT_SERRPENA_SET_MSK BIT(0) +#define DDR_HMC_INTSTAT_DERRPENA_SET_MSK BIT(1) +#define DDR_HMC_INTSTAT_ADDRMTCFLG_SET_MSK BIT(16) +#define DDR_HMC_INTMODE_INTMODE_SET_MSK BIT(0) +#define DDR_HMC_RSTHANDSHAKE_MASK 0x000000ff +#define DDR_HMC_CORE2SEQ_INT_REQ 0xF +#define DDR_HMC_SEQ2CORE_INT_RESP_MASK BIT(3) +#define DDR_HMC_HPSINTFCSEL_ENABLE_MASK 0x001f1f1f + +#define DDR_HMC_ERRINTEN_INTMASK \ + (DDR_HMC_ERRINTEN_SERRINTEN_EN_SET_MSK | \ + DDR_HMC_ERRINTEN_DERRINTEN_EN_SET_MSK) + +/* NOC DDR scheduler */ +#define DDR_SCH_ID_COREID 0 +#define DDR_SCH_ID_REVID 0x4 +#define DDR_SCH_DDRCONF 0x8 +#define DDR_SCH_DDRTIMING 0xc +#define DDR_SCH_DDRMODE 0x10 +#define DDR_SCH_READ_LATENCY 0x14 +#define DDR_SCH_ACTIVATE 0x38 +#define DDR_SCH_DEVTODEV 0x3c +#define DDR_SCH_DDR4TIMING 0x40 + +#define DDR_SCH_DDRTIMING_ACTTOACT_OFF 0 +#define DDR_SCH_DDRTIMING_RDTOMISS_OFF 6 +#define DDR_SCH_DDRTIMING_WRTOMISS_OFF 12 +#define DDR_SCH_DDRTIMING_BURSTLEN_OFF 18 +#define DDR_SCH_DDRTIMING_RDTOWR_OFF 21 +#define DDR_SCH_DDRTIMING_WRTORD_OFF 26 +#define DDR_SCH_DDRTIMING_BWRATIO_OFF 31 +#define DDR_SCH_DDRMOD_BWRATIOEXTENDED_OFF 1 +#define DDR_SCH_ACTIVATE_RRD_OFF 0 +#define DDR_SCH_ACTIVATE_FAW_OFF 4 +#define DDR_SCH_ACTIVATE_FAWBANK_OFF 10 +#define DDR_SCH_DEVTODEV_BUSRDTORD_OFF 0 +#define DDR_SCH_DEVTODEV_BUSRDTOWR_OFF 2 +#define DDR_SCH_DEVTODEV_BUSWRTORD_OFF 4 + +/* HMC MMR IO48 registers */ +#define CTRLCFG0 0x28 +#define CTRLCFG1 0x2c +#define DRAMTIMING0 0x50 +#define CALTIMING0 0x7c +#define CALTIMING1 0x80 +#define CALTIMING2 0x84 +#define CALTIMING3 0x88 +#define CALTIMING4 0x8c +#define CALTIMING9 0xa0 +#define DRAMADDRW 0xa8 +#define DRAMSTS 0xec +#define NIOSRESERVED0 0x110 +#define NIOSRESERVED1 0x114 +#define NIOSRESERVED2 0x118 + +#define DRAMADDRW_CFG_COL_ADDR_WIDTH(x) \ + (((x) >> 0) & 0x1F) +#define DRAMADDRW_CFG_ROW_ADDR_WIDTH(x) \ + (((x) >> 5) & 0x1F) +#define DRAMADDRW_CFG_BANK_ADDR_WIDTH(x) \ + (((x) >> 10) & 0xF) +#define DRAMADDRW_CFG_BANK_GRP_ADDR_WIDTH(x) \ + (((x) >> 14) & 0x3) +#define DRAMADDRW_CFG_CS_ADDR_WIDTH(x) \ + (((x) >> 16) & 0x7) + +#define CTRLCFG0_CFG_MEMTYPE(x) \ + (((x) >> 0) & 0xF) +#define CTRLCFG0_CFG_DIMM_TYPE(x) \ + (((x) >> 4) & 0x7) +#define CTRLCFG0_CFG_AC_POS(x) \ + (((x) >> 7) & 0x3) +#define CTRLCFG0_CFG_CTRL_BURST_LEN(x) \ + (((x) >> 9) & 0x1F) + +#define CTRLCFG1_CFG_DBC3_BURST_LEN(x) \ + (((x) >> 0) & 0x1F) +#define CTRLCFG1_CFG_ADDR_ORDER(x) \ + (((x) >> 5) & 0x3) +#define CTRLCFG1_CFG_CTRL_EN_ECC(x) \ + (((x) >> 7) & 0x1) + +#define DRAMTIMING0_CFG_TCL(x) \ + (((x) >> 0) & 0x7F) + +#define CALTIMING0_CFG_ACT_TO_RDWR(x) \ + (((x) >> 0) & 0x3F) +#define CALTIMING0_CFG_ACT_TO_PCH(x) \ + (((x) >> 6) & 0x3F) +#define CALTIMING0_CFG_ACT_TO_ACT(x) \ + (((x) >> 12) & 0x3F) +#define CALTIMING0_CFG_ACT_TO_ACT_DB(x) \ + (((x) >> 18) & 0x3F) + +#define CALTIMING1_CFG_RD_TO_RD(x) \ + (((x) >> 0) & 0x3F) +#define CALTIMING1_CFG_RD_TO_RD_DC(x) \ + (((x) >> 6) & 0x3F) +#define CALTIMING1_CFG_RD_TO_RD_DB(x) \ + (((x) >> 12) & 0x3F) +#define CALTIMING1_CFG_RD_TO_WR(x) \ + (((x) >> 18) & 0x3F) +#define CALTIMING1_CFG_RD_TO_WR_DC(x) \ + (((x) >> 24) & 0x3F) + +#define CALTIMING2_CFG_RD_TO_WR_DB(x) \ + (((x) >> 0) & 0x3F) +#define CALTIMING2_CFG_RD_TO_WR_PCH(x) \ + (((x) >> 6) & 0x3F) +#define CALTIMING2_CFG_RD_AP_TO_VALID(x) \ + (((x) >> 12) & 0x3F) +#define CALTIMING2_CFG_WR_TO_WR(x) \ + (((x) >> 18) & 0x3F) +#define CALTIMING2_CFG_WR_TO_WR_DC(x) \ + (((x) >> 24) & 0x3F) + +#define CALTIMING3_CFG_WR_TO_WR_DB(x) \ + (((x) >> 0) & 0x3F) +#define CALTIMING3_CFG_WR_TO_RD(x) \ + (((x) >> 6) & 0x3F) +#define CALTIMING3_CFG_WR_TO_RD_DC(x) \ + (((x) >> 12) & 0x3F) +#define CALTIMING3_CFG_WR_TO_RD_DB(x) \ + (((x) >> 18) & 0x3F) +#define CALTIMING3_CFG_WR_TO_PCH(x) \ + (((x) >> 24) & 0x3F) + +#define CALTIMING4_CFG_WR_AP_TO_VALID(x) \ + (((x) >> 0) & 0x3F) +#define CALTIMING4_CFG_PCH_TO_VALID(x) \ + (((x) >> 6) & 0x3F) +#define CALTIMING4_CFG_PCH_ALL_TO_VALID(x) \ + (((x) >> 12) & 0x3F) +#define CALTIMING4_CFG_ARF_TO_VALID(x) \ + (((x) >> 18) & 0xFF) +#define CALTIMING4_CFG_PDN_TO_VALID(x) \ + (((x) >> 26) & 0x3F) + +#define CALTIMING9_CFG_4_ACT_TO_ACT(x) \ + (((x) >> 0) & 0xFF) + +/* Firewall DDR scheduler MPFE */ +#define FW_HMC_ADAPTOR_REG_ADDR 0xf8020004 +#define FW_HMC_ADAPTOR_MPU_MASK BIT(0) + +#endif /* _SDRAM_S10_H_ */ diff --git a/drivers/ddr/fsl/main.c b/drivers/ddr/fsl/main.c index 6d018fde2b..e1f69a1d25 100644 --- a/drivers/ddr/fsl/main.c +++ b/drivers/ddr/fsl/main.c @@ -23,8 +23,12 @@ * 0x80_8000_0000 ~ 0xff_ffff_ffff */ #ifndef CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY +#ifdef CONFIG_MPC83xx +#define CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY CONFIG_SYS_SDRAM_BASE +#else #define CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY CONFIG_SYS_DDR_SDRAM_BASE #endif +#endif #ifdef CONFIG_PPC #include <asm/fsl_law.h> diff --git a/drivers/ddr/imx/imx8m/Kconfig b/drivers/ddr/imx/imx8m/Kconfig index 71f466f5ec..a83b0f43d7 100644 --- a/drivers/ddr/imx/imx8m/Kconfig +++ b/drivers/ddr/imx/imx8m/Kconfig @@ -1,3 +1,6 @@ +menu "i.MX8M DDR controllers" + depends on ARCH_IMX8M + config IMX8M_DRAM bool "imx8m dram" @@ -20,3 +23,4 @@ config SAVED_DRAM_TIMING_BASE info into memory for low power use. OCRAM_S is used for this purpose on i.MX8MM. default 0x180000 +endmenu diff --git a/drivers/dma/apbh_dma.c b/drivers/dma/apbh_dma.c index 017cc89a89..ac589feeb7 100644 --- a/drivers/dma/apbh_dma.c +++ b/drivers/dma/apbh_dma.c @@ -81,7 +81,7 @@ static int mxs_dma_read_semaphore(int channel) return tmp; } -#ifndef CONFIG_SYS_DCACHE_OFF +#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) void mxs_dma_flush_desc(struct mxs_dma_desc *desc) { uint32_t addr; diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index f78a01aa8f..a5fc7809bc 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -575,14 +575,6 @@ static int udma_get_tchan(struct udma_chan *uc) pr_debug("chan%d: got tchan%d\n", uc->id, uc->tchan->id); - if (udma_is_chan_running(uc)) { - dev_warn(ud->dev, "chan%d: tchan%d is running!\n", uc->id, - uc->tchan->id); - udma_stop(uc); - if (udma_is_chan_running(uc)) - dev_err(ud->dev, "chan%d: won't stop!\n", uc->id); - } - return 0; } @@ -602,14 +594,6 @@ static int udma_get_rchan(struct udma_chan *uc) pr_debug("chan%d: got rchan%d\n", uc->id, uc->rchan->id); - if (udma_is_chan_running(uc)) { - dev_warn(ud->dev, "chan%d: rchan%d is running!\n", uc->id, - uc->rchan->id); - udma_stop(uc); - if (udma_is_chan_running(uc)) - dev_err(ud->dev, "chan%d: won't stop!\n", uc->id); - } - return 0; } @@ -652,14 +636,6 @@ static int udma_get_chan_pair(struct udma_chan *uc) pr_debug("chan%d: got t/rchan%d pair\n", uc->id, chan_id); - if (udma_is_chan_running(uc)) { - dev_warn(ud->dev, "chan%d: t/rchan%d pair is running!\n", - uc->id, chan_id); - udma_stop(uc); - if (udma_is_chan_running(uc)) - dev_err(ud->dev, "chan%d: won't stop!\n", uc->id); - } - return 0; } @@ -1071,6 +1047,15 @@ static int udma_alloc_chan_resources(struct udma_chan *uc) } } + if (udma_is_chan_running(uc)) { + dev_warn(ud->dev, "chan%d: is running!\n", uc->id); + udma_stop(uc); + if (udma_is_chan_running(uc)) { + dev_err(ud->dev, "chan%d: won't stop!\n", uc->id); + goto err_free_res; + } + } + /* PSI-L pairing */ ret = udma_navss_psil_pair(ud, uc->src_thread, uc->dst_thread); if (ret) { @@ -1492,7 +1477,7 @@ static int udma_send(struct dma *dma, void *src, size_t len, void *metadata) u32 tc_ring_id; int ret; - if (!metadata) + if (metadata) packet_data = *((struct ti_udma_drv_packet_data *)metadata); if (dma->id >= (ud->rchan_cnt + ud->tchan_cnt)) { diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 1196ce0712..303aa6a631 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -158,7 +158,7 @@ static inline int ti_sci_get_response(struct ti_sci_info *info, int ret; /* Receive the response */ - ret = mbox_recv(chan, msg, info->desc->max_rx_timeout_ms); + ret = mbox_recv(chan, msg, info->desc->max_rx_timeout_ms * 1000); if (ret) { dev_err(info->dev, "%s: Message receive failed. ret = %d\n", __func__, ret); @@ -257,7 +257,8 @@ static int ti_sci_cmd_get_revision(struct ti_sci_handle *handle) info = handle_to_ti_sci_info(handle); - xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_VERSION, 0x0, + xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_VERSION, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, (u32 *)&hdr, sizeof(struct ti_sci_msg_hdr), sizeof(*rev_info)); if (IS_ERR(xfer)) { @@ -499,8 +500,8 @@ static int ti_sci_get_device_state(const struct ti_sci_handle *handle, info = handle_to_ti_sci_info(handle); - /* Response is expected, so need of any flags */ - xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_GET_DEVICE_STATE, 0, + xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_GET_DEVICE_STATE, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, (u32 *)&req, sizeof(req), sizeof(*resp)); if (IS_ERR(xfer)) { ret = PTR_ERR(xfer); @@ -2574,8 +2575,8 @@ static int ti_sci_cmd_change_fwl_owner(const struct ti_sci_handle *handle, info = handle_to_ti_sci_info(handle); - xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_FWL_GET, - TISCI_MSG_FWL_CHANGE_OWNER, + xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_FWL_CHANGE_OWNER, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, (u32 *)&req, sizeof(req), sizeof(*resp)); if (IS_ERR(xfer)) { ret = PTR_ERR(xfer); diff --git a/drivers/fpga/socfpga_arria10.c b/drivers/fpga/socfpga_arria10.c index 114dd910ab..285280e507 100644 --- a/drivers/fpga/socfpga_arria10.c +++ b/drivers/fpga/socfpga_arria10.c @@ -1,8 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2017 Intel Corporation <www.intel.com> + * Copyright (C) 2017-2019 Intel Corporation <www.intel.com> */ - #include <asm/io.h> #include <asm/arch/fpga_manager.h> #include <asm/arch/reset_manager.h> @@ -10,8 +9,11 @@ #include <asm/arch/sdram.h> #include <asm/arch/misc.h> #include <altera.h> +#include <asm/arch/pinmux.h> #include <common.h> +#include <dm/ofnode.h> #include <errno.h> +#include <fs_loader.h> #include <wait_bit.h> #include <watchdog.h> @@ -21,6 +23,9 @@ #define COMPRESSION_OFFSET 229 #define FPGA_TIMEOUT_MSEC 1000 /* timeout in ms */ #define FPGA_TIMEOUT_CNT 0x1000000 +#define DEFAULT_DDR_LOAD_ADDRESS 0x400 + +DECLARE_GLOBAL_DATA_PTR; static const struct socfpga_fpga_manager *fpga_manager_base = (void *)SOCFPGA_FPGAMGRREGS_ADDRESS; @@ -64,7 +69,7 @@ static int wait_for_user_mode(void) 1, FPGA_TIMEOUT_MSEC, false); } -static int is_fpgamgr_early_user_mode(void) +int is_fpgamgr_early_user_mode(void) { return (readl(&fpga_manager_base->imgcfg_stat) & ALT_FPGAMGR_IMGCFG_STAT_F2S_EARLY_USERMODE_SET_MSK) != 0; @@ -94,7 +99,7 @@ int fpgamgr_wait_early_user_mode(void) i++; } - debug("Additional %i sync word needed\n", i); + debug("FPGA: Additional %i sync word needed\n", i); /* restoring original CDRATIO */ fpgamgr_set_cd_ratio(cd_ratio); @@ -172,9 +177,10 @@ static int fpgamgr_set_cdratio_cdwidth(unsigned int cfg_width, u32 *rbf_data, compress = (rbf_data[COMPRESSION_OFFSET] >> 1) & 1; compress = !compress; - debug("header word %d = %08x\n", 69, rbf_data[69]); - debug("header word %d = %08x\n", 229, rbf_data[229]); - debug("read from rbf header: encrypt=%d compress=%d\n", encrypt, compress); + debug("FPGA: Header word %d = %08x.\n", 69, rbf_data[69]); + debug("FPGA: Header word %d = %08x.\n", 229, rbf_data[229]); + debug("FPGA: Read from rbf header: encrypt=%d compress=%d.\n", encrypt, + compress); /* * from the register map description of cdratio in imgcfg_ctrl_02: @@ -359,6 +365,7 @@ static int fpgamgr_program_poll_cd(void) printf("nstatus == 0 while waiting for condone\n"); return -EPERM; } + WATCHDOG_RESET(); } if (i == FPGA_TIMEOUT_CNT) @@ -432,7 +439,6 @@ int fpgamgr_program_finish(void) printf("FPGA: Poll CD failed with error code %d\n", status); return -EPERM; } - WATCHDOG_RESET(); /* Ensure the FPGA entering user mode */ status = fpgamgr_program_poll_usermode(); @@ -447,27 +453,493 @@ int fpgamgr_program_finish(void) return 0; } -/* - * FPGA Manager to program the FPGA. This is the interface used by FPGA driver. - * Return 0 for sucess, non-zero for error. - */ +ofnode get_fpga_mgr_ofnode(ofnode from) +{ + return ofnode_by_compatible(from, "altr,socfpga-a10-fpga-mgr"); +} + +const char *get_fpga_filename(void) +{ + const char *fpga_filename = NULL; + + ofnode fpgamgr_node = get_fpga_mgr_ofnode(ofnode_null()); + + if (ofnode_valid(fpgamgr_node)) + fpga_filename = ofnode_read_string(fpgamgr_node, + "altr,bitstream"); + + return fpga_filename; +} + +static void get_rbf_image_info(struct rbf_info *rbf, u16 *buffer) +{ + /* + * Magic ID starting at: + * -> 1st dword[15:0] in periph.rbf + * -> 2nd dword[15:0] in core.rbf + * Note: dword == 32 bits + */ + u32 word_reading_max = 2; + u32 i; + + for (i = 0; i < word_reading_max; i++) { + if (*(buffer + i) == FPGA_SOCFPGA_A10_RBF_UNENCRYPTED) { + rbf->security = unencrypted; + } else if (*(buffer + i) == FPGA_SOCFPGA_A10_RBF_ENCRYPTED) { + rbf->security = encrypted; + } else if (*(buffer + i + 1) == + FPGA_SOCFPGA_A10_RBF_UNENCRYPTED) { + rbf->security = unencrypted; + } else if (*(buffer + i + 1) == + FPGA_SOCFPGA_A10_RBF_ENCRYPTED) { + rbf->security = encrypted; + } else { + rbf->security = invalid; + continue; + } + + /* PERIPH RBF(buffer + i + 1), CORE RBF(buffer + i + 2) */ + if (*(buffer + i + 1) == FPGA_SOCFPGA_A10_RBF_PERIPH) { + rbf->section = periph_section; + break; + } else if (*(buffer + i + 1) == FPGA_SOCFPGA_A10_RBF_CORE) { + rbf->section = core_section; + break; + } else if (*(buffer + i + 2) == FPGA_SOCFPGA_A10_RBF_PERIPH) { + rbf->section = periph_section; + break; + } else if (*(buffer + i + 2) == FPGA_SOCFPGA_A10_RBF_CORE) { + rbf->section = core_section; + break; + } + + rbf->section = unknown; + break; + + WATCHDOG_RESET(); + } +} + +#ifdef CONFIG_FS_LOADER +static int first_loading_rbf_to_buffer(struct udevice *dev, + struct fpga_loadfs_info *fpga_loadfs, + u32 *buffer, size_t *buffer_bsize) +{ + u32 *buffer_p = (u32 *)*buffer; + u32 *loadable = buffer_p; + size_t buffer_size = *buffer_bsize; + size_t fit_size; + int ret, i, count, confs_noffset, images_noffset, rbf_offset, rbf_size; + const char *fpga_node_name = NULL; + const char *uname = NULL; + + /* Load image header into buffer */ + ret = request_firmware_into_buf(dev, + fpga_loadfs->fpga_fsinfo->filename, + buffer_p, sizeof(struct image_header), + 0); + if (ret < 0) { + debug("FPGA: Failed to read image header from flash.\n"); + return -ENOENT; + } + + if (image_get_magic((struct image_header *)buffer_p) != FDT_MAGIC) { + debug("FPGA: No FDT magic was found.\n"); + return -EBADF; + } + + fit_size = fdt_totalsize(buffer_p); + + if (fit_size > buffer_size) { + debug("FPGA: FIT image is larger than available buffer.\n"); + debug("Please use FIT external data or increasing buffer.\n"); + return -ENOMEM; + } + + /* Load entire FIT into buffer */ + ret = request_firmware_into_buf(dev, + fpga_loadfs->fpga_fsinfo->filename, + buffer_p, fit_size, 0); + if (ret < 0) + return ret; + + ret = fit_check_format(buffer_p); + if (!ret) { + debug("FPGA: No valid FIT image was found.\n"); + return -EBADF; + } + + confs_noffset = fdt_path_offset(buffer_p, FIT_CONFS_PATH); + images_noffset = fdt_path_offset(buffer_p, FIT_IMAGES_PATH); + if (confs_noffset < 0 || images_noffset < 0) { + debug("FPGA: No Configurations or images nodes were found.\n"); + return -ENOENT; + } + + /* Get default configuration unit name from default property */ + confs_noffset = fit_conf_get_node(buffer_p, NULL); + if (confs_noffset < 0) { + debug("FPGA: No default configuration was found in config.\n"); + return -ENOENT; + } + + count = fit_conf_get_prop_node_count(buffer_p, confs_noffset, + FIT_FPGA_PROP); + if (count < 0) { + debug("FPGA: Invalid configuration format for FPGA node.\n"); + return count; + } + debug("FPGA: FPGA node count: %d\n", count); + + for (i = 0; i < count; i++) { + images_noffset = fit_conf_get_prop_node_index(buffer_p, + confs_noffset, + FIT_FPGA_PROP, i); + uname = fit_get_name(buffer_p, images_noffset, NULL); + if (uname) { + debug("FPGA: %s\n", uname); + + if (strstr(uname, "fpga-periph") && + (!is_fpgamgr_early_user_mode() || + is_fpgamgr_user_mode())) { + fpga_node_name = uname; + printf("FPGA: Start to program "); + printf("peripheral/full bitstream ...\n"); + break; + } else if (strstr(uname, "fpga-core") && + (is_fpgamgr_early_user_mode() && + !is_fpgamgr_user_mode())) { + fpga_node_name = uname; + printf("FPGA: Start to program core "); + printf("bitstream ...\n"); + break; + } + } + WATCHDOG_RESET(); + } + + if (!fpga_node_name) { + debug("FPGA: No suitable bitstream was found, count: %d.\n", i); + return 1; + } + + images_noffset = fit_image_get_node(buffer_p, fpga_node_name); + if (images_noffset < 0) { + debug("FPGA: No node '%s' was found in FIT.\n", + fpga_node_name); + return -ENOENT; + } + + if (!fit_image_get_data_position(buffer_p, images_noffset, + &rbf_offset)) { + debug("FPGA: Data position was found.\n"); + } else if (!fit_image_get_data_offset(buffer_p, images_noffset, + &rbf_offset)) { + /* + * For FIT with external data, figure out where + * the external images start. This is the base + * for the data-offset properties in each image. + */ + rbf_offset += ((fdt_totalsize(buffer_p) + 3) & ~3); + debug("FPGA: Data offset was found.\n"); + } else { + debug("FPGA: No data position/offset was found.\n"); + return -ENOENT; + } + + ret = fit_image_get_data_size(buffer_p, images_noffset, &rbf_size); + if (ret < 0) { + debug("FPGA: No data size was found (err=%d).\n", ret); + return -ENOENT; + } + + if (gd->ram_size < rbf_size) { + debug("FPGA: Using default OCRAM buffer and size.\n"); + } else { + ret = fit_image_get_load(buffer_p, images_noffset, + (ulong *)loadable); + if (ret < 0) { + buffer_p = (u32 *)DEFAULT_DDR_LOAD_ADDRESS; + debug("FPGA: No loadable was found.\n"); + debug("FPGA: Using default DDR load address: 0x%x .\n", + DEFAULT_DDR_LOAD_ADDRESS); + } else { + buffer_p = (u32 *)*loadable; + debug("FPGA: Found loadable address = 0x%x.\n", + *loadable); + } + + buffer_size = rbf_size; + } + + debug("FPGA: External data: offset = 0x%x, size = 0x%x.\n", + rbf_offset, rbf_size); + + fpga_loadfs->remaining = rbf_size; + + /* + * Determine buffer size vs bitstream size, and calculating number of + * chunk by chunk transfer is required due to smaller buffer size + * compare to bitstream + */ + if (rbf_size <= buffer_size) { + /* Loading whole bitstream into buffer */ + buffer_size = rbf_size; + fpga_loadfs->remaining = 0; + } else { + fpga_loadfs->remaining -= buffer_size; + } + + fpga_loadfs->offset = rbf_offset; + /* Loading bitstream into buffer */ + ret = request_firmware_into_buf(dev, + fpga_loadfs->fpga_fsinfo->filename, + buffer_p, buffer_size, + fpga_loadfs->offset); + if (ret < 0) { + debug("FPGA: Failed to read bitstream from flash.\n"); + return -ENOENT; + } + + /* Getting info about bitstream types */ + get_rbf_image_info(&fpga_loadfs->rbfinfo, (u16 *)buffer_p); + + /* Update next reading bitstream offset */ + fpga_loadfs->offset += buffer_size; + + /* Update the final addr for bitstream */ + *buffer = (u32)buffer_p; + + /* Update the size of bitstream to be programmed into FPGA */ + *buffer_bsize = buffer_size; + + return 0; +} + +static int subsequent_loading_rbf_to_buffer(struct udevice *dev, + struct fpga_loadfs_info *fpga_loadfs, + u32 *buffer, size_t *buffer_bsize) +{ + int ret = 0; + u32 *buffer_p = (u32 *)*buffer; + + /* Read the bitstream chunk by chunk. */ + if (fpga_loadfs->remaining > *buffer_bsize) { + fpga_loadfs->remaining -= *buffer_bsize; + } else { + *buffer_bsize = fpga_loadfs->remaining; + fpga_loadfs->remaining = 0; + } + + ret = request_firmware_into_buf(dev, + fpga_loadfs->fpga_fsinfo->filename, + buffer_p, *buffer_bsize, + fpga_loadfs->offset); + if (ret < 0) { + debug("FPGA: Failed to read bitstream from flash.\n"); + return -ENOENT; + } + + /* Update next reading bitstream offset */ + fpga_loadfs->offset += *buffer_bsize; + + return 0; +} + +int socfpga_loadfs(fpga_fs_info *fpga_fsinfo, const void *buf, size_t bsize, + u32 offset) +{ + struct fpga_loadfs_info fpga_loadfs; + struct udevice *dev; + int status, ret, size; + u32 buffer = (uintptr_t)buf; + size_t buffer_sizebytes = bsize; + size_t buffer_sizebytes_ori = bsize; + size_t total_sizeof_image = 0; + ofnode node; + const fdt32_t *phandle_p; + u32 phandle; + + node = get_fpga_mgr_ofnode(ofnode_null()); + + if (ofnode_valid(node)) { + phandle_p = ofnode_get_property(node, "firmware-loader", &size); + if (!phandle_p) { + node = ofnode_path("/chosen"); + if (!ofnode_valid(node)) { + debug("FPGA: /chosen node was not found.\n"); + return -ENOENT; + } + + phandle_p = ofnode_get_property(node, "firmware-loader", + &size); + if (!phandle_p) { + debug("FPGA: firmware-loader property was not"); + debug(" found.\n"); + return -ENOENT; + } + } + } else { + debug("FPGA: FPGA manager node was not found.\n"); + return -ENOENT; + } + + phandle = fdt32_to_cpu(*phandle_p); + ret = uclass_get_device_by_phandle_id(UCLASS_FS_FIRMWARE_LOADER, + phandle, &dev); + if (ret) + return ret; + + memset(&fpga_loadfs, 0, sizeof(fpga_loadfs)); + + fpga_loadfs.fpga_fsinfo = fpga_fsinfo; + fpga_loadfs.offset = offset; + + printf("FPGA: Checking FPGA configuration setting ...\n"); + + /* + * Note: Both buffer and buffer_sizebytes values can be altered by + * function below. + */ + ret = first_loading_rbf_to_buffer(dev, &fpga_loadfs, &buffer, + &buffer_sizebytes); + if (ret == 1) { + printf("FPGA: Skipping configuration ...\n"); + return 0; + } else if (ret) { + return ret; + } + + if (fpga_loadfs.rbfinfo.section == core_section && + !(is_fpgamgr_early_user_mode() && !is_fpgamgr_user_mode())) { + debug("FPGA : Must be in Early Release mode to program "); + debug("core bitstream.\n"); + return -EPERM; + } + + /* Disable all signals from HPS peripheral controller to FPGA */ + writel(0, &system_manager_base->fpgaintf_en_global); + + /* Disable all axi bridges (hps2fpga, lwhps2fpga & fpga2hps) */ + socfpga_bridges_reset(); + + if (fpga_loadfs.rbfinfo.section == periph_section) { + /* Initialize the FPGA Manager */ + status = fpgamgr_program_init((u32 *)buffer, buffer_sizebytes); + if (status) { + debug("FPGA: Init with peripheral bitstream failed.\n"); + return -EPERM; + } + } + + /* Transfer bitstream to FPGA Manager */ + fpgamgr_program_write((void *)buffer, buffer_sizebytes); + + total_sizeof_image += buffer_sizebytes; + + while (fpga_loadfs.remaining) { + ret = subsequent_loading_rbf_to_buffer(dev, + &fpga_loadfs, + &buffer, + &buffer_sizebytes_ori); + + if (ret) + return ret; + + /* Transfer data to FPGA Manager */ + fpgamgr_program_write((void *)buffer, + buffer_sizebytes_ori); + + total_sizeof_image += buffer_sizebytes_ori; + + WATCHDOG_RESET(); + } + + if (fpga_loadfs.rbfinfo.section == periph_section) { + if (fpgamgr_wait_early_user_mode() != -ETIMEDOUT) { + config_pins(gd->fdt_blob, "shared"); + puts("FPGA: Early Release Succeeded.\n"); + } else { + debug("FPGA: Failed to see Early Release.\n"); + return -EIO; + } + + /* For monolithic bitstream */ + if (is_fpgamgr_user_mode()) { + /* Ensure the FPGA entering config done */ + status = fpgamgr_program_finish(); + if (status) + return status; + + config_pins(gd->fdt_blob, "fpga"); + puts("FPGA: Enter user mode.\n"); + } + } else if (fpga_loadfs.rbfinfo.section == core_section) { + /* Ensure the FPGA entering config done */ + status = fpgamgr_program_finish(); + if (status) + return status; + + config_pins(gd->fdt_blob, "fpga"); + puts("FPGA: Enter user mode.\n"); + } else { + debug("FPGA: Config Error: Unsupported bitstream type.\n"); + return -ENOEXEC; + } + + return (int)total_sizeof_image; +} + +void fpgamgr_program(const void *buf, size_t bsize, u32 offset) +{ + fpga_fs_info fpga_fsinfo; + + fpga_fsinfo.filename = get_fpga_filename(); + + if (fpga_fsinfo.filename) + socfpga_loadfs(&fpga_fsinfo, buf, bsize, offset); +} +#endif + +/* This function is used to load the core bitstream from the OCRAM. */ int socfpga_load(Altera_desc *desc, const void *rbf_data, size_t rbf_size) { - int status; + unsigned long status; + struct rbf_info rbfinfo; - /* disable all signals from hps peripheral controller to fpga */ + memset(&rbfinfo, 0, sizeof(rbfinfo)); + + /* Disable all signals from hps peripheral controller to fpga */ writel(0, &system_manager_base->fpgaintf_en_global); - /* disable all axi bridge (hps2fpga, lwhps2fpga & fpga2hps) */ + /* Disable all axi bridge (hps2fpga, lwhps2fpga & fpga2hps) */ socfpga_bridges_reset(); - /* Initialize the FPGA Manager */ - status = fpgamgr_program_init((u32 *)rbf_data, rbf_size); - if (status) - return status; + /* Getting info about bitstream types */ + get_rbf_image_info(&rbfinfo, (u16 *)rbf_data); + + if (rbfinfo.section == periph_section) { + /* Initialize the FPGA Manager */ + status = fpgamgr_program_init((u32 *)rbf_data, rbf_size); + if (status) + return status; + } - /* Write the RBF data to FPGA Manager */ + if (rbfinfo.section == core_section && + !(is_fpgamgr_early_user_mode() && !is_fpgamgr_user_mode())) { + debug("FPGA : Must be in early release mode to program "); + debug("core bitstream.\n"); + return -EPERM; + } + + /* Write the bitstream to FPGA Manager */ fpgamgr_program_write(rbf_data, rbf_size); - return fpgamgr_program_finish(); + status = fpgamgr_program_finish(); + if (status) { + config_pins(gd->fdt_blob, "fpga"); + puts("FPGA: Enter user mode.\n"); + } + + return status; } diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b3e4ecc50e..e36a8abc42 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -169,6 +169,12 @@ config RCAR_GPIO help This driver supports the GPIO banks on Renesas RCar SoCs. +config RZA1_GPIO + bool "Renesas RZ/A1 GPIO driver" + depends on DM_GPIO && RZA1 + help + This driver supports the GPIO banks on Renesas RZ/A1 R7S72100 SoCs. + config ROCKCHIP_GPIO bool "Rockchip GPIO driver" depends on DM_GPIO @@ -351,7 +357,7 @@ config MPC8XXX_GPIO config MT7621_GPIO bool "MediaTek MT7621 GPIO driver" - depends on DM_GPIO && ARCH_MT7620 + depends on DM_GPIO && SOC_MT7628 default y help Say yes here to support MediaTek MT7621 compatible GPIOs. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 3be325044f..7337153e0e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_PCA953X) += pca953x.o obj-$(CONFIG_PCA9698) += pca9698.o obj-$(CONFIG_ROCKCHIP_GPIO) += rk_gpio.o obj-$(CONFIG_RCAR_GPIO) += gpio-rcar.o +obj-$(CONFIG_RZA1_GPIO) += gpio-rza1.o obj-$(CONFIG_S5P) += s5p_gpio.o obj-$(CONFIG_SANDBOX_GPIO) += sandbox.o obj-$(CONFIG_SPEAR_GPIO) += spear_gpio.o diff --git a/drivers/gpio/dwapb_gpio.c b/drivers/gpio/dwapb_gpio.c index e55fb4ac73..2eb1547b4f 100644 --- a/drivers/gpio/dwapb_gpio.c +++ b/drivers/gpio/dwapb_gpio.c @@ -17,8 +17,6 @@ #include <errno.h> #include <reset.h> -DECLARE_GLOBAL_DATA_PTR; - #define GPIO_SWPORT_DR(p) (0x00 + (p) * 0xc) #define GPIO_SWPORT_DDR(p) (0x04 + (p) * 0xc) #define GPIO_INTEN 0x30 @@ -150,10 +148,10 @@ static int gpio_dwapb_probe(struct udevice *dev) static int gpio_dwapb_bind(struct udevice *dev) { struct gpio_dwapb_platdata *plat = dev_get_platdata(dev); - const void *blob = gd->fdt_blob; struct udevice *subdev; fdt_addr_t base; - int ret, node, bank = 0; + int ret, bank = 0; + ofnode node; /* If this is a child device, there is nothing to do here */ if (plat) @@ -165,10 +163,9 @@ static int gpio_dwapb_bind(struct udevice *dev) return -ENXIO; } - for (node = fdt_first_subnode(blob, dev_of_offset(dev)); - node > 0; - node = fdt_next_subnode(blob, node)) { - if (!fdtdec_get_bool(blob, node, "gpio-controller")) + for (node = dev_read_first_subnode(dev); ofnode_valid(node); + node = dev_read_next_subnode(node)) { + if (!ofnode_read_bool(node, "gpio-controller")) continue; plat = devm_kcalloc(dev, 1, sizeof(*plat), GFP_KERNEL); @@ -177,23 +174,22 @@ static int gpio_dwapb_bind(struct udevice *dev) plat->base = base; plat->bank = bank; - plat->pins = fdtdec_get_int(blob, node, "snps,nr-gpios", 0); - plat->name = fdt_stringlist_get(blob, node, "bank-name", 0, - NULL); - if (!plat->name) { + plat->pins = ofnode_read_u32_default(node, "snps,nr-gpios", 0); + + if (ofnode_read_string_index(node, "bank-name", 0, + &plat->name)) { /* * Fall back to node name. This means accessing pins * via bank name won't work. */ - plat->name = fdt_get_name(blob, node, NULL); + plat->name = ofnode_get_name(node); } - ret = device_bind(dev, dev->driver, plat->name, - plat, -1, &subdev); + ret = device_bind_ofnode(dev, dev->driver, plat->name, + plat, node, &subdev); if (ret) return ret; - dev_set_of_offset(subdev, node); bank++; } diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 6fd1270640..594e0a470a 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -6,6 +6,7 @@ #include <common.h> #include <clk.h> #include <dm.h> +#include <dm/pinctrl.h> #include <errno.h> #include <asm/gpio.h> #include <asm/io.h> @@ -117,19 +118,17 @@ static int rcar_gpio_get_function(struct udevice *dev, unsigned offset) static int rcar_gpio_request(struct udevice *dev, unsigned offset, const char *label) { - struct rcar_gpio_priv *priv = dev_get_priv(dev); - struct udevice *pctldev; - int ret; - - ret = uclass_get_device(UCLASS_PINCTRL, 0, &pctldev); - if (ret) - return ret; + return pinctrl_gpio_request(dev, offset); +} - return sh_pfc_config_mux_for_gpio(pctldev, priv->pfc_offset + offset); +static int rcar_gpio_free(struct udevice *dev, unsigned offset) +{ + return pinctrl_gpio_free(dev, offset); } static const struct dm_gpio_ops rcar_gpio_ops = { .request = rcar_gpio_request, + .free = rcar_gpio_free, .direction_input = rcar_gpio_direction_input, .direction_output = rcar_gpio_direction_output, .get_value = rcar_gpio_get_value, diff --git a/drivers/gpio/gpio-rza1.c b/drivers/gpio/gpio-rza1.c new file mode 100644 index 0000000000..ce2453e2ba --- /dev/null +++ b/drivers/gpio/gpio-rza1.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Marek Vasut <marek.vasut@gmail.com> + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <errno.h> +#include <asm/gpio.h> +#include <asm/io.h> + +#define P(bank) (0x0000 + (bank) * 4) +#define PSR(bank) (0x0100 + (bank) * 4) +#define PPR(bank) (0x0200 + (bank) * 4) +#define PM(bank) (0x0300 + (bank) * 4) +#define PMC(bank) (0x0400 + (bank) * 4) +#define PFC(bank) (0x0500 + (bank) * 4) +#define PFCE(bank) (0x0600 + (bank) * 4) +#define PNOT(bank) (0x0700 + (bank) * 4) +#define PMSR(bank) (0x0800 + (bank) * 4) +#define PMCSR(bank) (0x0900 + (bank) * 4) +#define PFCAE(bank) (0x0A00 + (bank) * 4) +#define PIBC(bank) (0x4000 + (bank) * 4) +#define PBDC(bank) (0x4100 + (bank) * 4) +#define PIPC(bank) (0x4200 + (bank) * 4) + +#define RZA1_MAX_GPIO_PER_BANK 16 + +DECLARE_GLOBAL_DATA_PTR; + +struct r7s72100_gpio_priv { + void __iomem *regs; + int bank; +}; + +static int r7s72100_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct r7s72100_gpio_priv *priv = dev_get_priv(dev); + + return !!(readw(priv->regs + PPR(priv->bank)) & BIT(offset)); +} + +static int r7s72100_gpio_set_value(struct udevice *dev, unsigned line, + int value) +{ + struct r7s72100_gpio_priv *priv = dev_get_priv(dev); + + writel(BIT(line + 16) | (value ? BIT(line) : 0), + priv->regs + PSR(priv->bank)); + + return 0; +} + +static void r7s72100_gpio_set_direction(struct udevice *dev, unsigned line, + bool output) +{ + struct r7s72100_gpio_priv *priv = dev_get_priv(dev); + + writel(BIT(line + 16), priv->regs + PMCSR(priv->bank)); + writel(BIT(line + 16) | (output ? 0 : BIT(line)), + priv->regs + PMSR(priv->bank)); + + clrsetbits_le16(priv->regs + PIBC(priv->bank), BIT(line), + output ? 0 : BIT(line)); +} + +static int r7s72100_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + r7s72100_gpio_set_direction(dev, offset, false); + return 0; +} + +static int r7s72100_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + /* write GPIO value to output before selecting output mode of pin */ + r7s72100_gpio_set_value(dev, offset, value); + r7s72100_gpio_set_direction(dev, offset, true); + + return 0; +} + +static int r7s72100_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct r7s72100_gpio_priv *priv = dev_get_priv(dev); + + if (readw(priv->regs + PM(priv->bank)) & BIT(offset)) + return GPIOF_INPUT; + else + return GPIOF_OUTPUT; +} + +static const struct dm_gpio_ops r7s72100_gpio_ops = { + .direction_input = r7s72100_gpio_direction_input, + .direction_output = r7s72100_gpio_direction_output, + .get_value = r7s72100_gpio_get_value, + .set_value = r7s72100_gpio_set_value, + .get_function = r7s72100_gpio_get_function, +}; + +static int r7s72100_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct r7s72100_gpio_priv *priv = dev_get_priv(dev); + struct fdtdec_phandle_args args; + int node = dev_of_offset(dev); + int ret; + + fdt_addr_t addr_base; + + uc_priv->bank_name = dev->name; + dev = dev_get_parent(dev); + addr_base = devfdt_get_addr(dev); + if (addr_base == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = (void __iomem *)addr_base; + + ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, node, "gpio-ranges", + NULL, 3, 0, &args); + priv->bank = ret == 0 ? (args.args[1] / RZA1_MAX_GPIO_PER_BANK) : -1; + uc_priv->gpio_count = ret == 0 ? args.args[2] : RZA1_MAX_GPIO_PER_BANK; + + return 0; +} + +U_BOOT_DRIVER(r7s72100_gpio) = { + .name = "r7s72100-gpio", + .id = UCLASS_GPIO, + .ops = &r7s72100_gpio_ops, + .priv_auto_alloc_size = sizeof(struct r7s72100_gpio_priv), + .probe = r7s72100_gpio_probe, +}; diff --git a/drivers/gpio/rk_gpio.c b/drivers/gpio/rk_gpio.c index 21df227717..3d96678a45 100644 --- a/drivers/gpio/rk_gpio.c +++ b/drivers/gpio/rk_gpio.c @@ -12,7 +12,8 @@ #include <linux/errno.h> #include <asm/gpio.h> #include <asm/io.h> -#include <asm/arch/clock.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/gpio.h> #include <dm/pinctrl.h> #include <dt-bindings/clock/rk3288-cru.h> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 215624020f..095a9bc6a4 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -161,7 +161,10 @@ config SYS_I2C_MXC channels and operating on standard mode up to 100 kbits/s and fast mode up to 400 kbits/s. -if SYS_I2C_MXC +# These settings are not used with DM_I2C, however SPL doesn't use +# DM_I2C even if DM_I2C is enabled, and so might use these settings even +# when main u-boot does not! +if SYS_I2C_MXC && (!DM_I2C || SPL) config SYS_I2C_MXC_I2C1 bool "NXP MXC I2C1" help diff --git a/drivers/i2c/ihs_i2c.c b/drivers/i2c/ihs_i2c.c index 0922fe9bb1..f7b59d36f9 100644 --- a/drivers/i2c/ihs_i2c.c +++ b/drivers/i2c/ihs_i2c.c @@ -8,8 +8,7 @@ #include <i2c.h> #ifdef CONFIG_DM_I2C #include <dm.h> -#include <fpgamap.h> -#include "../misc/gdsys_soc.h" +#include <regmap.h> #else #include <gdsys_fpga.h> #endif @@ -18,18 +17,24 @@ #ifdef CONFIG_DM_I2C struct ihs_i2c_priv { uint speed; - phys_addr_t addr; + struct regmap *map; }; -enum { - REG_INTERRUPT_STATUS = 0x00, - REG_INTERRUPT_ENABLE_CONTROL = 0x02, - REG_WRITE_MAILBOX_EXT = 0x04, - REG_WRITE_MAILBOX = 0x06, - REG_READ_MAILBOX_EXT = 0x08, - REG_READ_MAILBOX = 0x0A, +struct ihs_i2c_regs { + u16 interrupt_status; + u16 interrupt_enable_control; + u16 write_mailbox_ext; + u16 write_mailbox; + u16 read_mailbox_ext; + u16 read_mailbox; }; +#define ihs_i2c_set(map, member, val) \ + regmap_set(map, struct ihs_i2c_regs, member, val) + +#define ihs_i2c_get(map, member, valp) \ + regmap_get(map, struct ihs_i2c_regs, member, valp) + #else /* !CONFIG_DM_I2C */ DECLARE_GLOBAL_DATA_PTR; @@ -92,14 +97,10 @@ static int wait_for_int(bool read) uint ctr = 0; #ifdef CONFIG_DM_I2C struct ihs_i2c_priv *priv = dev_get_priv(dev); - struct udevice *fpga; - - gdsys_soc_get_fpga(dev, &fpga); #endif #ifdef CONFIG_DM_I2C - fpgamap_read(fpga, priv->addr + REG_INTERRUPT_STATUS, &val, - FPGAMAP_SIZE_16); + ihs_i2c_get(priv->map, interrupt_status, &val); #else I2C_GET_REG(interrupt_status, &val); #endif @@ -107,17 +108,18 @@ static int wait_for_int(bool read) while (!(val & (I2CINT_ERROR_EV | (read ? I2CINT_RECEIVE_EV : I2CINT_TRANSMIT_EV)))) { udelay(10); - if (ctr++ > 5000) - return 1; + if (ctr++ > 5000) { + debug("%s: timed out\n", __func__); + return -ETIMEDOUT; + } #ifdef CONFIG_DM_I2C - fpgamap_read(fpga, priv->addr + REG_INTERRUPT_STATUS, &val, - FPGAMAP_SIZE_16); + ihs_i2c_get(priv->map, interrupt_status, &val); #else I2C_GET_REG(interrupt_status, &val); #endif } - return (val & I2CINT_ERROR_EV) ? 1 : 0; + return (val & I2CINT_ERROR_EV) ? -EIO : 0; } #ifdef CONFIG_DM_I2C @@ -130,20 +132,16 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, { u16 val; u16 data; + int res; #ifdef CONFIG_DM_I2C struct ihs_i2c_priv *priv = dev_get_priv(dev); - struct udevice *fpga; - - gdsys_soc_get_fpga(dev, &fpga); #endif /* Clear interrupt status */ data = I2CINT_ERROR_EV | I2CINT_RECEIVE_EV | I2CINT_TRANSMIT_EV; #ifdef CONFIG_DM_I2C - fpgamap_write(fpga, priv->addr + REG_INTERRUPT_STATUS, &data, - FPGAMAP_SIZE_16); - fpgamap_read(fpga, priv->addr + REG_INTERRUPT_STATUS, &val, - FPGAMAP_SIZE_16); + ihs_i2c_set(priv->map, interrupt_status, data); + ihs_i2c_get(priv->map, interrupt_status, &val); #else I2C_SET_REG(interrupt_status, data); I2C_GET_REG(interrupt_status, &val); @@ -156,8 +154,7 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, if (len > 1) val |= buffer[1] << 8; #ifdef CONFIG_DM_I2C - fpgamap_write(fpga, priv->addr + REG_WRITE_MAILBOX_EXT, &val, - FPGAMAP_SIZE_16); + ihs_i2c_set(priv->map, write_mailbox_ext, val); #else I2C_SET_REG(write_mailbox_ext, val); #endif @@ -170,24 +167,27 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, | (is_last ? 0 : I2CMB_HOLD_BUS); #ifdef CONFIG_DM_I2C - fpgamap_write(fpga, priv->addr + REG_WRITE_MAILBOX, &data, - FPGAMAP_SIZE_16); + ihs_i2c_set(priv->map, write_mailbox, data); #else I2C_SET_REG(write_mailbox, data); #endif #ifdef CONFIG_DM_I2C - if (wait_for_int(dev, read)) + res = wait_for_int(dev, read); #else - if (wait_for_int(read)) + res = wait_for_int(read); #endif - return 1; + if (res) { + if (res == -ETIMEDOUT) + debug("%s: time out while waiting for event\n", __func__); + + return res; + } /* If we want to read, get the bytes from the mailbox */ if (read) { #ifdef CONFIG_DM_I2C - fpgamap_read(fpga, priv->addr + REG_READ_MAILBOX_EXT, &val, - FPGAMAP_SIZE_16); + ihs_i2c_get(priv->map, read_mailbox_ext, &val); #else I2C_GET_REG(read_mailbox_ext, &val); #endif @@ -206,19 +206,21 @@ static int ihs_i2c_send_buffer(uchar chip, u8 *data, int len, bool hold_bus, int read) #endif { + int res; + while (len) { int transfer = min(len, 2); bool is_last = len <= transfer; #ifdef CONFIG_DM_I2C - if (ihs_i2c_transfer(dev, chip, data, transfer, read, - hold_bus ? false : is_last)) - return 1; + res = ihs_i2c_transfer(dev, chip, data, transfer, read, + hold_bus ? false : is_last); #else - if (ihs_i2c_transfer(chip, data, transfer, read, - hold_bus ? false : is_last)) - return 1; + res = ihs_i2c_transfer(chip, data, transfer, read, + hold_bus ? false : is_last); #endif + if (res) + return res; data += transfer; len -= transfer; @@ -249,14 +251,19 @@ static int ihs_i2c_access(struct i2c_adapter *adap, uchar chip, u8 *addr, int alen, uchar *buffer, int len, int read) #endif { + int res; + /* Don't hold the bus if length of data to send/receive is zero */ + if (len <= 0) + return -EINVAL; + #ifdef CONFIG_DM_I2C - if (len <= 0 || ihs_i2c_address(dev, chip, addr, alen, len)) - return 1; + res = ihs_i2c_address(dev, chip, addr, alen, len); #else - if (len <= 0 || ihs_i2c_address(chip, addr, alen, len)) - return 1; + res = ihs_i2c_address(chip, addr, alen, len); #endif + if (res) + return res; #ifdef CONFIG_DM_I2C return ihs_i2c_send_buffer(dev, chip, buffer, len, false, read); @@ -270,11 +277,8 @@ static int ihs_i2c_access(struct i2c_adapter *adap, uchar chip, u8 *addr, int ihs_i2c_probe(struct udevice *bus) { struct ihs_i2c_priv *priv = dev_get_priv(bus); - int addr; - - addr = dev_read_u32_default(bus, "reg", -1); - priv->addr = addr; + regmap_init_mem(dev_ofnode(bus), &priv->map); return 0; } @@ -284,7 +288,7 @@ static int ihs_i2c_set_bus_speed(struct udevice *bus, uint speed) struct ihs_i2c_priv *priv = dev_get_priv(bus); if (speed != priv->speed && priv->speed != 0) - return 1; + return -EINVAL; priv->speed = speed; @@ -301,8 +305,8 @@ static int ihs_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) * actucal data) or one message (just data) */ if (nmsgs > 2 || nmsgs == 0) { - debug("%s: Only one or two messages are supported.", __func__); - return -1; + debug("%s: Only one or two messages are supported\n", __func__); + return -ENOTSUPP; } omsg = nmsgs == 1 ? &dummy : msg; @@ -322,9 +326,11 @@ static int ihs_i2c_probe_chip(struct udevice *bus, u32 chip_addr, u32 chip_flags) { uchar buffer[2]; + int res; - if (ihs_i2c_transfer(bus, chip_addr, buffer, 0, I2COP_READ, true)) - return 1; + res = ihs_i2c_transfer(bus, chip_addr, buffer, 0, I2COP_READ, true); + if (res) + return res; return 0; } @@ -366,9 +372,11 @@ static void ihs_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) static int ihs_i2c_probe(struct i2c_adapter *adap, uchar chip) { uchar buffer[2]; + int res; - if (ihs_i2c_transfer(chip, buffer, 0, I2COP_READ, true)) - return 1; + res = ihs_i2c_transfer(chip, buffer, 0, I2COP_READ, true); + if (res) + return res; return 0; } @@ -399,7 +407,7 @@ static unsigned int ihs_i2c_set_bus_speed(struct i2c_adapter *adap, unsigned int speed) { if (speed != adap->speed) - return 1; + return -EINVAL; return speed; } diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c index 74ac0a4aa7..0a2dafcec6 100644 --- a/drivers/i2c/mvtwsi.c +++ b/drivers/i2c/mvtwsi.c @@ -271,6 +271,17 @@ static int twsi_wait(struct mvtwsi_registers *twsi, int expected_status, do { control = readl(&twsi->control); if (control & MVTWSI_CONTROL_IFLG) { + /* + * On Armada 38x it seems that the controller works as + * if it first set the MVTWSI_CONTROL_IFLAG in the + * control register and only after that it changed the + * status register. + * This sometimes caused weird bugs which only appeared + * on selected I2C speeds and even then only sometimes. + * We therefore add here a simple ndealy(100), which + * seems to fix this weird bug. + */ + ndelay(100); status = readl(&twsi->status); if (status == expected_status) return 0; diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 5420afbc8e..23119cce65 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -482,8 +482,13 @@ static int i2c_write_data(struct mxc_i2c_bus *i2c_bus, u8 chip, const u8 *buf, return ret; } +/* Will generate a STOP after the last byte if "last" is true, i.e. this is the + * final message of a transaction. If not, it switches the bus back to TX mode + * and does not send a STOP, leaving the bus in a state where a repeated start + * and address can be sent for another message. + */ static int i2c_read_data(struct mxc_i2c_bus *i2c_bus, uchar chip, uchar *buf, - int len) + int len, bool last) { int ret; unsigned int temp; @@ -513,17 +518,31 @@ static int i2c_read_data(struct mxc_i2c_bus *i2c_bus, uchar chip, uchar *buf, return ret; } - /* - * It must generate STOP before read I2DR to prevent - * controller from generating another clock cycle - */ if (i == (len - 1)) { - i2c_imx_stop(i2c_bus); + /* Final byte has already been received by master! When + * we read it from I2DR, the master will start another + * cycle. We must program it first to send a STOP or + * switch to TX to avoid this. + */ + if (last) { + i2c_imx_stop(i2c_bus); + } else { + /* Final read, no stop, switch back to tx */ + temp = readb(base + (I2CR << reg_shift)); + temp |= I2CR_MTX | I2CR_TX_NO_AK; + writeb(temp, base + (I2CR << reg_shift)); + } } else if (i == (len - 2)) { + /* Master has already recevied penultimate byte. When + * we read it from I2DR, master will start RX of final + * byte. We must set TX_NO_AK now so it does not ACK + * that final byte. + */ temp = readb(base + (I2CR << reg_shift)); temp |= I2CR_TX_NO_AK; writeb(temp, base + (I2CR << reg_shift)); } + writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift)); buf[i] = readb(base + (I2DR << reg_shift)); } @@ -533,13 +552,34 @@ static int i2c_read_data(struct mxc_i2c_bus *i2c_bus, uchar chip, uchar *buf, debug(" 0x%02x", buf[ret]); debug("\n"); - i2c_imx_stop(i2c_bus); + /* It is not clear to me that this is necessary */ + if (last) + i2c_imx_stop(i2c_bus); return 0; } #ifndef CONFIG_DM_I2C /* * Read data from I2C device + * + * The transactions use the syntax defined in the Linux kernel I2C docs. + * + * If alen is > 0, then this function will send a transaction of the form: + * S Chip Wr [A] Addr [A] S Chip Rd [A] [data] A ... NA P + * This is a normal I2C register read: writing the register address, then doing + * a repeated start and reading the data. + * + * If alen == 0, then we get this transaction: + * S Chip Wr [A] S Chip Rd [A] [data] A ... NA P + * This is somewhat unusual, though valid, transaction. It addresses the chip + * in write mode, but doesn't actually write any register address or data, then + * does a repeated start and reads data. + * + * If alen < 0, then we get this transaction: + * S Chip Rd [A] [data] A ... NA P + * The chip is addressed in read mode and then data is read. No register + * address is written first. This is perfectly valid on most devices and + * required on some (usually those that don't act like an array of registers). */ static int bus_i2c_read(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr, int alen, u8 *buf, int len) @@ -566,7 +606,7 @@ static int bus_i2c_read(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr, return ret; } - ret = i2c_read_data(i2c_bus, chip, buf, len); + ret = i2c_read_data(i2c_bus, chip, buf, len, true); i2c_imx_stop(i2c_bus); return ret; @@ -574,6 +614,20 @@ static int bus_i2c_read(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr, /* * Write data to I2C device + * + * If alen > 0, we get this transaction: + * S Chip Wr [A] addr [A] data [A] ... [A] P + * An ordinary write register command. + * + * If alen == 0, then we get this: + * S Chip Wr [A] data [A] ... [A] P + * This is a simple I2C write. + * + * If alen < 0, then we get this: + * S data [A] ... [A] P + * This is most likely NOT something that should be used. It doesn't send the + * chip address first, so in effect, the first byte of data will be used as the + * address. */ static int bus_i2c_write(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr, int alen, const u8 *buf, int len) @@ -881,6 +935,7 @@ static int mxc_i2c_probe(struct udevice *bus) return 0; } +/* Sends: S Addr Wr [A|NA] P */ static int mxc_i2c_probe_chip(struct udevice *bus, u32 chip_addr, u32 chip_flags) { @@ -905,42 +960,54 @@ static int mxc_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) ulong base = i2c_bus->base; int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT; + int read_mode; - /* - * Here the 3rd parameter addr and the 4th one alen are set to 0, - * because here we only want to send out chip address. The register - * address is wrapped in msg. + /* Here address len is set to -1 to not send any address at first. + * Otherwise i2c_init_transfer will send the chip address with write + * mode set. This is wrong if the 1st message is read. */ - ret = i2c_init_transfer(i2c_bus, msg->addr, 0, 0); + ret = i2c_init_transfer(i2c_bus, msg->addr, 0, -1); if (ret < 0) { debug("i2c_init_transfer error: %d\n", ret); return ret; } + read_mode = -1; /* So it's always different on the first message */ for (; nmsgs > 0; nmsgs--, msg++) { - bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); - debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); - if (msg->flags & I2C_M_RD) - ret = i2c_read_data(i2c_bus, msg->addr, msg->buf, - msg->len); - else { - ret = i2c_write_data(i2c_bus, msg->addr, msg->buf, - msg->len); - if (ret) - break; - if (next_is_read) { - /* Reuse ret */ + const int msg_is_read = !!(msg->flags & I2C_M_RD); + + debug("i2c_xfer: chip=0x%x, len=0x%x, dir=%c\n", msg->addr, + msg->len, msg_is_read ? 'R' : 'W'); + + if (msg_is_read != read_mode) { + /* Send repeated start if not 1st message */ + if (read_mode != -1) { + debug("i2c_xfer: [RSTART]\n"); ret = readb(base + (I2CR << reg_shift)); ret |= I2CR_RSTA; writeb(ret, base + (I2CR << reg_shift)); - - ret = tx_byte(i2c_bus, (msg->addr << 1) | 1); - if (ret < 0) { - i2c_imx_stop(i2c_bus); - break; - } } + debug("i2c_xfer: [ADDR %02x | %c]\n", msg->addr, + msg_is_read ? 'R' : 'W'); + ret = tx_byte(i2c_bus, (msg->addr << 1) | msg_is_read); + if (ret < 0) { + debug("i2c_xfer: [STOP]\n"); + i2c_imx_stop(i2c_bus); + break; + } + read_mode = msg_is_read; } + + if (msg->flags & I2C_M_RD) + ret = i2c_read_data(i2c_bus, msg->addr, msg->buf, + msg->len, nmsgs == 1 || + (msg->flags & I2C_M_STOP)); + else + ret = i2c_write_data(i2c_bus, msg->addr, msg->buf, + msg->len); + + if (ret < 0) + break; } if (ret) diff --git a/drivers/i2c/rk_i2c.c b/drivers/i2c/rk_i2c.c index f9a5796b96..cdd94bb05a 100644 --- a/drivers/i2c/rk_i2c.c +++ b/drivers/i2c/rk_i2c.c @@ -12,9 +12,9 @@ #include <errno.h> #include <i2c.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/i2c.h> -#include <asm/arch/periph.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/i2c.h> +#include <asm/arch-rockchip/periph.h> #include <dm/pinctrl.h> #include <linux/sizes.h> diff --git a/drivers/i2c/stm32f7_i2c.c b/drivers/i2c/stm32f7_i2c.c index 3872364d6b..50c4fd0de2 100644 --- a/drivers/i2c/stm32f7_i2c.c +++ b/drivers/i2c/stm32f7_i2c.c @@ -500,7 +500,7 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup, af_delay_max = setup->analog_filter ? STM32_I2C_ANALOG_FILTER_DELAY_MAX : 0; - sdadel_min = setup->fall_time - i2c_specs[setup->speed].hddat_min - + sdadel_min = i2c_specs[setup->speed].hddat_min + setup->fall_time - af_delay_min - (setup->dnf + 3) * i2cclk; sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time - @@ -540,8 +540,12 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup, p_prev = p; list_add_tail(&v->node, solutions); + break; } } + + if (p_prev == p) + break; } } diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0e645f58be..cb8b5c04db 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -13,6 +13,24 @@ config MISC set of generic read, write and ioctl methods may be used to access the device. +config SPL_MISC + bool "Enable Driver Model for Misc drivers in SPL" + depends on SPL_DM + help + Enable driver model for miscellaneous devices. This class is + used only for those do not fit other more general classes. A + set of generic read, write and ioctl methods may be used to + access the device. + +config TPL_MISC + bool "Enable Driver Model for Misc drivers in TPL" + depends on TPL_DM + help + Enable driver model for miscellaneous devices. This class is + used only for those do not fit other more general classes. A + set of generic read, write and ioctl methods may be used to + access the device. + config ALTERA_SYSID bool "Altera Sysid support" depends on MISC @@ -68,6 +86,24 @@ config CROS_EC control access to the battery and main PMIC depending on the device. You can use the 'crosec' command to access it. +config SPL_CROS_EC + bool "Enable Chrome OS EC in SPL" + help + Enable access to the Chrome OS EC in SPL. This is a separate + microcontroller typically available on a SPI bus on Chromebooks. It + provides access to the keyboard, some internal storage and may + control access to the battery and main PMIC depending on the + device. You can use the 'crosec' command to access it. + +config TPL_CROS_EC + bool "Enable Chrome OS EC in TPL" + help + Enable access to the Chrome OS EC in TPL. This is a separate + microcontroller typically available on a SPI bus on Chromebooks. It + provides access to the keyboard, some internal storage and may + control access to the battery and main PMIC depending on the + device. You can use the 'crosec' command to access it. + config CROS_EC_I2C bool "Enable Chrome OS EC I2C driver" depends on CROS_EC @@ -86,6 +122,24 @@ config CROS_EC_LPC through a legacy port interface, so on x86 machines the main function of the EC is power and thermal management. +config SPL_CROS_EC_LPC + bool "Enable Chrome OS EC LPC driver in SPL" + depends on CROS_EC + help + Enable I2C access to the Chrome OS EC. This is used on x86 + Chromebooks such as link and falco. The keyboard is provided + through a legacy port interface, so on x86 machines the main + function of the EC is power and thermal management. + +config TPL_CROS_EC_LPC + bool "Enable Chrome OS EC LPC driver in TPL" + depends on CROS_EC + help + Enable I2C access to the Chrome OS EC. This is used on x86 + Chromebooks such as link and falco. The keyboard is provided + through a legacy port interface, so on x86 machines the main + function of the EC is power and thermal management. + config CROS_EC_SANDBOX bool "Enable Chrome OS EC sandbox driver" depends on CROS_EC && SANDBOX @@ -95,6 +149,24 @@ config CROS_EC_SANDBOX EC flash read/write/erase support and a few other things. It is enough to perform a Chrome OS verified boot on sandbox. +config SPL_CROS_EC_SANDBOX + bool "Enable Chrome OS EC sandbox driver in SPL" + depends on SPL_CROS_EC && SANDBOX + help + Enable a sandbox emulation of the Chrome OS EC in SPL. This supports + keyboard (use the -l flag to enable the LCD), verified boot context, + EC flash read/write/erase support and a few other things. It is + enough to perform a Chrome OS verified boot on sandbox. + +config TPL_CROS_EC_SANDBOX + bool "Enable Chrome OS EC sandbox driver in TPL" + depends on TPL_CROS_EC && SANDBOX + help + Enable a sandbox emulation of the Chrome OS EC in TPL. This supports + keyboard (use the -l flag to enable the LCD), verified boot context, + EC flash read/write/erase support and a few other things. It is + enough to perform a Chrome OS verified boot on sandbox. + config CROS_EC_SPI bool "Enable Chrome OS EC SPI driver" depends on CROS_EC diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 6bdf5054f4..509c588582 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -4,11 +4,13 @@ # Wolfgang Denk, DENX Software Engineering, wd@denx.de. obj-$(CONFIG_MISC) += misc-uclass.o + +obj-$(CONFIG_$(SPL_TPL_)CROS_EC) += cros_ec.o +obj-$(CONFIG_$(SPL_TPL_)CROS_EC_SANDBOX) += cros_ec_sandbox.o +obj-$(CONFIG_$(SPL_TPL_)CROS_EC_LPC) += cros_ec_lpc.o + ifndef CONFIG_SPL_BUILD -obj-$(CONFIG_CROS_EC) += cros_ec.o -obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o -obj-$(CONFIG_CROS_EC_SANDBOX) += cros_ec_sandbox.o obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o endif diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c index 565de040fe..382f826286 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -1482,7 +1482,7 @@ int cros_ec_set_lid_shutdown_mask(struct udevice *dev, int enable) UCLASS_DRIVER(cros_ec) = { .id = UCLASS_CROS_EC, - .name = "cros_ec", + .name = "cros-ec", .per_device_auto_alloc_size = sizeof(struct cros_ec_dev), .post_bind = dm_scan_fdt_dev, .flags = DM_UC_FLAG_ALLOC_PRIV_DMA, diff --git a/drivers/misc/gdsys_rxaui_ctrl.c b/drivers/misc/gdsys_rxaui_ctrl.c index 9a63c329bc..c56abce4d4 100644 --- a/drivers/misc/gdsys_rxaui_ctrl.c +++ b/drivers/misc/gdsys_rxaui_ctrl.c @@ -29,6 +29,7 @@ struct gdsys_rxaui_ctrl_regs { struct gdsys_rxaui_ctrl_priv { struct regmap *map; + bool state; }; int gdsys_rxaui_set_polarity_inversion(struct udevice *dev, bool val) @@ -36,6 +37,8 @@ int gdsys_rxaui_set_polarity_inversion(struct udevice *dev, bool val) struct gdsys_rxaui_ctrl_priv *priv = dev_get_priv(dev); u16 state; + priv->state = !priv->state; + rxaui_ctrl_get(priv->map, ctrl_1, &state); if (val) @@ -45,7 +48,7 @@ int gdsys_rxaui_set_polarity_inversion(struct udevice *dev, bool val) rxaui_ctrl_set(priv->map, ctrl_1, state); - return 0; + return !priv->state; } static const struct misc_ops gdsys_rxaui_ctrl_ops = { @@ -56,7 +59,9 @@ int gdsys_rxaui_ctrl_probe(struct udevice *dev) { struct gdsys_rxaui_ctrl_priv *priv = dev_get_priv(dev); - regmap_init_mem(dev, &priv->map); + regmap_init_mem(dev_ofnode(dev), &priv->map); + + priv->state = false; return 0; } diff --git a/drivers/misc/imx8/Makefile b/drivers/misc/imx8/Makefile index ee05893cbb..48fdb5b61c 100644 --- a/drivers/misc/imx8/Makefile +++ b/drivers/misc/imx8/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ obj-y += scu_api.o scu.o +obj-$(CONFIG_CMD_FUSE) += fuse.o diff --git a/drivers/misc/imx8/fuse.c b/drivers/misc/imx8/fuse.c new file mode 100644 index 0000000000..29d2256a22 --- /dev/null +++ b/drivers/misc/imx8/fuse.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP + */ + +#include <common.h> +#include <console.h> +#include <errno.h> +#include <fuse.h> +#include <asm/arch/sci/sci.h> +#include <asm/arch/sys_proto.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define FSL_ECC_WORD_START_1 0x10 +#define FSL_ECC_WORD_END_1 0x10F + +#ifdef CONFIG_IMX8QXP +#define FSL_ECC_WORD_START_2 0x220 +#define FSL_ECC_WORD_END_2 0x31F + +#define FSL_QXP_FUSE_GAP_START 0x110 +#define FSL_QXP_FUSE_GAP_END 0x21F +#endif + +#define FSL_SIP_OTP_READ 0xc200000A +#define FSL_SIP_OTP_WRITE 0xc200000B + +int fuse_read(u32 bank, u32 word, u32 *val) +{ + return fuse_sense(bank, word, val); +} + +int fuse_sense(u32 bank, u32 word, u32 *val) +{ + unsigned long ret = 0, value = 0; + + if (bank != 0) { + printf("Invalid bank argument, ONLY bank 0 is supported\n"); + return -EINVAL; + } + + ret = call_imx_sip_ret2(FSL_SIP_OTP_READ, (unsigned long)word, &value, + 0, 0); + *val = (u32)value; + + return ret; +} + +int fuse_prog(u32 bank, u32 word, u32 val) +{ + if (bank != 0) { + printf("Invalid bank argument, ONLY bank 0 is supported\n"); + return -EINVAL; + } + + if (IS_ENABLED(CONFIG_IMX8QXP)) { + if (word >= FSL_QXP_FUSE_GAP_START && + word <= FSL_QXP_FUSE_GAP_END) { + printf("Invalid word argument for this SoC\n"); + return -EINVAL; + } + } + + if ((word >= FSL_ECC_WORD_START_1 && word <= FSL_ECC_WORD_END_1) || + (word >= FSL_ECC_WORD_START_2 && word <= FSL_ECC_WORD_END_2)) { + puts("Warning: Words in this index range have ECC protection\n" + "and can only be programmed once per word. Individual bit\n" + "operations will be rejected after the first one.\n" + "\n\n Really program this word? <y/N>\n"); + + if (!confirm_yesno()) { + puts("Word programming aborted\n"); + return -EPERM; + } + } + + return call_imx_sip(FSL_SIP_OTP_WRITE, (unsigned long)word, + (unsigned long)val, 0); +} + +int fuse_override(u32 bank, u32 word, u32 val) +{ + printf("Override fuse to i.MX8 in u-boot is forbidden\n"); + return -EPERM; +} diff --git a/drivers/misc/imx8/scu.c b/drivers/misc/imx8/scu.c index 1b9c49c99c..9ec00457b8 100644 --- a/drivers/misc/imx8/scu.c +++ b/drivers/misc/imx8/scu.c @@ -219,11 +219,21 @@ static int imx8_scu_bind(struct udevice *dev) int ret; struct udevice *child; int node; + char *clk_compatible, *iomuxc_compatible; + + if (IS_ENABLED(CONFIG_IMX8QXP)) { + clk_compatible = "fsl,imx8qxp-clk"; + iomuxc_compatible = "fsl,imx8qxp-iomuxc"; + } else if (IS_ENABLED(CONFIG_IMX8QM)) { + clk_compatible = "fsl,imx8qm-clk"; + iomuxc_compatible = "fsl,imx8qm-iomuxc"; + } else { + return -EINVAL; + } debug("%s(dev=%p)\n", __func__, dev); - node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, - "fsl,imx8qxp-clk"); + node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, clk_compatible); if (node < 0) panic("No clk node found\n"); @@ -234,7 +244,7 @@ static int imx8_scu_bind(struct udevice *dev) plat->clk = child; node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, - "fsl,imx8qxp-iomuxc"); + iomuxc_compatible); if (node < 0) panic("No iomuxc node found\n"); diff --git a/drivers/misc/mxc_ocotp.c b/drivers/misc/mxc_ocotp.c index f84fe88db1..1b945e9727 100644 --- a/drivers/misc/mxc_ocotp.c +++ b/drivers/misc/mxc_ocotp.c @@ -321,6 +321,11 @@ int fuse_sense(u32 bank, u32 word, u32 *val) struct ocotp_regs *regs; int ret; + if (is_imx8mq() && is_soc_rev(CHIP_REV_2_1)) { + printf("mxc_ocotp %s(): fuse sense is disabled\n", __func__); + return -EPERM; + } + ret = prepare_read(®s, bank, word, val, __func__); if (ret) return ret; @@ -354,13 +359,17 @@ static int prepare_write(struct ocotp_regs **regs, u32 bank, u32 word, /* Only bank 0 and 1 are redundancy mode, others are ECC mode */ if (bank != 0 && bank != 1) { - ret = fuse_sense(bank, word, &val); - if (ret) - return ret; - - if (val != 0) { - printf("mxc_ocotp: The word has been programmed, no more write\n"); - return -EPERM; + if ((soc_rev() < CHIP_REV_2_0) || + ((soc_rev() >= CHIP_REV_2_0) && + bank != 9 && bank != 10 && bank != 28)) { + ret = fuse_sense(bank, word, &val); + if (ret) + return ret; + + if (val != 0) { + printf("mxc_ocotp: The word has been programmed, no more write\n"); + return -EPERM; + } } } #endif diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index c34dd5d187..c23299ea96 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -78,6 +78,12 @@ config SUPPORT_EMMC_RPMB Enable support for reading, writing and programming the key for the Replay Protection Memory Block partition in eMMC. +config SUPPORT_EMMC_BOOT + bool "Support some additional features of the eMMC boot partitions" + help + Enable support for eMMC boot partitions. This also enables + extensions within the mmc command. + config MMC_IO_VOLTAGE bool "Support IO voltage configuration" help @@ -385,6 +391,20 @@ config MMC_SDHCI_SDMA This enables support for the SDMA (Single Operation DMA) defined in the SD Host Controller Standard Specification Version 1.00 . +config MMC_SDHCI_ADMA + bool "Support SDHCI ADMA2" + depends on MMC_SDHCI + help + This enables support for the ADMA (Advanced DMA) defined + in the SD Host Controller Standard Specification Version 3.00 + +config SPL_MMC_SDHCI_ADMA + bool "Support SDHCI ADMA2 in SPL" + depends on MMC_SDHCI + help + This enables support for the ADMA (Advanced DMA) defined + in the SD Host Controller Standard Specification Version 3.00 in SPL. + config MMC_SDHCI_ATMEL bool "Atmel SDHCI controller support" depends on ARCH_AT91 diff --git a/drivers/mmc/bcmstb_sdhci.c b/drivers/mmc/bcmstb_sdhci.c index 443ae8d481..eef46f3af1 100644 --- a/drivers/mmc/bcmstb_sdhci.c +++ b/drivers/mmc/bcmstb_sdhci.c @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2018 Cisco Systems, Inc. + * (C) Copyright 2019 Synamedia * * Author: Thomas Fitzsimmons <fitzsim@fitzsim.org> */ #include <common.h> +#include <dm.h> #include <mach/sdhci.h> #include <malloc.h> #include <sdhci.h> @@ -36,32 +38,67 @@ */ #define BCMSTB_SDHCI_MINIMUM_CLOCK_FREQUENCY 400000 -static char *BCMSTB_SDHCI_NAME = "bcmstb-sdhci"; - /* * This driver has only been tested with eMMC devices; SD devices may * not work. */ -int bcmstb_sdhci_init(phys_addr_t regbase) +struct sdhci_bcmstb_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +static int sdhci_bcmstb_bind(struct udevice *dev) { - struct sdhci_host *host = NULL; + struct sdhci_bcmstb_plat *plat = dev_get_platdata(dev); - host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); - if (!host) { - printf("%s: Failed to allocate memory\n", __func__); - return 1; - } - memset(host, 0, sizeof(*host)); + return sdhci_bind(dev, &plat->mmc, &plat->cfg); +} + +static int sdhci_bcmstb_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct sdhci_bcmstb_plat *plat = dev_get_platdata(dev); + struct sdhci_host *host = dev_get_priv(dev); + fdt_addr_t base; + int ret; - host->name = BCMSTB_SDHCI_NAME; - host->ioaddr = (void *)regbase; - host->quirks = 0; + base = devfdt_get_addr(dev); + if (base == FDT_ADDR_T_NONE) + return -EINVAL; - host->cfg.part_type = PART_TYPE_DOS; + host->name = dev->name; + host->ioaddr = (void *)base; - host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + ret = mmc_of_parse(dev, &plat->cfg); + if (ret) + return ret; - return add_sdhci(host, - BCMSTB_SDHCI_MAXIMUM_CLOCK_FREQUENCY, - BCMSTB_SDHCI_MINIMUM_CLOCK_FREQUENCY); + ret = sdhci_setup_cfg(&plat->cfg, host, + BCMSTB_SDHCI_MAXIMUM_CLOCK_FREQUENCY, + BCMSTB_SDHCI_MINIMUM_CLOCK_FREQUENCY); + if (ret) + return ret; + + upriv->mmc = &plat->mmc; + host->mmc = &plat->mmc; + host->mmc->priv = host; + + return sdhci_probe(dev); } + +static const struct udevice_id sdhci_bcmstb_match[] = { + { .compatible = "brcm,bcm7425-sdhci" }, + { .compatible = "brcm,sdhci-brcmstb" }, + { } +}; + +U_BOOT_DRIVER(sdhci_bcmstb) = { + .name = "sdhci-bcmstb", + .id = UCLASS_MMC, + .of_match = sdhci_bcmstb_match, + .ops = &sdhci_ops, + .bind = sdhci_bcmstb_bind, + .probe = sdhci_bcmstb_probe, + .priv_auto_alloc_size = sizeof(struct sdhci_host), + .platdata_auto_alloc_size = sizeof(struct sdhci_bcmstb_plat), +}; diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 93a836eac3..1992d61182 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -74,15 +74,15 @@ static void dwmci_prepare_data(struct dwmci_host *host, dwmci_set_idma_desc(cur_idmac, flags, cnt, (ulong)bounce_buffer + (i * PAGE_SIZE)); + cur_idmac++; if (blk_cnt <= 8) break; blk_cnt -= 8; - cur_idmac++; i++; } while(1); data_end = (ulong)cur_idmac; - flush_dcache_range(data_start, data_end + ARCH_DMA_MINALIGN); + flush_dcache_range(data_start, roundup(data_end, ARCH_DMA_MINALIGN)); ctrl = dwmci_readl(host, DWMCI_CTRL); ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN; @@ -114,22 +114,40 @@ static int dwmci_fifo_ready(struct dwmci_host *host, u32 bit, u32 *len) return 0; } +static unsigned int dwmci_get_timeout(struct mmc *mmc, const unsigned int size) +{ + unsigned int timeout; + + timeout = size * 8 * 1000; /* counting in bits and msec */ + timeout *= 2; /* wait twice as long */ + timeout /= mmc->clock; + timeout /= mmc->bus_width; + timeout /= mmc->ddr_mode ? 2 : 1; + timeout = (timeout < 1000) ? 1000 : timeout; + + return timeout; +} + static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data) { + struct mmc *mmc = host->mmc; int ret = 0; - u32 timeout = 240000; - u32 mask, size, i, len = 0; + u32 timeout, mask, size, i, len = 0; u32 *buf = NULL; ulong start = get_timer(0); u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1) * 2; - size = data->blocksize * data->blocks / 4; + size = data->blocksize * data->blocks; if (data->flags == MMC_DATA_READ) buf = (unsigned int *)data->dest; else buf = (unsigned int *)data->src; + timeout = dwmci_get_timeout(mmc, size); + + size /= 4; + for (;;) { mask = dwmci_readl(host, DWMCI_RINTSTS); /* Error during data transfer. */ @@ -252,14 +270,20 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); } else { if (data->flags == MMC_DATA_READ) { - bounce_buffer_start(&bbstate, (void*)data->dest, + ret = bounce_buffer_start(&bbstate, + (void*)data->dest, data->blocksize * data->blocks, GEN_BB_WRITE); } else { - bounce_buffer_start(&bbstate, (void*)data->src, + ret = bounce_buffer_start(&bbstate, + (void*)data->src, data->blocksize * data->blocks, GEN_BB_READ); } + + if (ret) + return ret; + dwmci_prepare_data(host, data, cur_idmac, bbstate.bounce_buffer); } diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 9e34557d16..672691fa6a 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -297,6 +297,13 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); return -ETIMEDOUT; } + } else { +#ifdef CONFIG_DM_GPIO + if (dm_gpio_is_valid(&priv->wp_gpio) && dm_gpio_get_value(&priv->wp_gpio)) { + printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); + return -ETIMEDOUT; + } +#endif } esdhc_clrsetbits32(®s->wml, WML_WR_WML_MASK, @@ -1428,7 +1435,9 @@ void fdt_fixup_esdhc(void *blob, bd_t *bd) #endif #if CONFIG_IS_ENABLED(DM_MMC) +#ifndef CONFIG_PPC #include <asm/arch/clock.h> +#endif __weak void init_clk_usdhc(u32 index) { } @@ -1453,8 +1462,11 @@ static int fsl_esdhc_probe(struct udevice *dev) addr = dev_read_addr(dev); if (addr == FDT_ADDR_T_NONE) return -EINVAL; - +#ifdef CONFIG_PPC + priv->esdhc_regs = (struct fsl_esdhc *)lower_32_bits(addr); +#else priv->esdhc_regs = (struct fsl_esdhc *)addr; +#endif priv->dev = dev; priv->mode = -1; if (data) { @@ -1489,14 +1501,15 @@ static int fsl_esdhc_probe(struct udevice *dev) #endif } - priv->wp_enable = 1; - + if (dev_read_prop(dev, "fsl,wp-controller", NULL)) { + priv->wp_enable = 1; + } else { + priv->wp_enable = 0; #ifdef CONFIG_DM_GPIO - ret = gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, + gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, GPIOD_IS_IN); - if (ret) - priv->wp_enable = 0; #endif + } priv->vs18_enable = 0; @@ -1560,7 +1573,11 @@ static int fsl_esdhc_probe(struct udevice *dev) priv->sdhc_clk = clk_get_rate(&priv->per_clk); } else { +#ifndef CONFIG_PPC priv->sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK + dev->seq); +#else + priv->sdhc_clk = gd->arch.sdhc_clk; +#endif if (priv->sdhc_clk <= 0) { dev_err(dev, "Unable to get clk for %s\n", dev->name); return -EINVAL; diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index 6c51ccc294..7c53aa221e 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -23,24 +23,126 @@ /* SCC registers */ #define RENESAS_SDHI_SCC_DTCNTL 0x800 -#define RENESAS_SDHI_SCC_DTCNTL_TAPEN BIT(0) -#define RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16 -#define RENESAS_SDHI_SCC_DTCNTL_TAPNUM_MASK 0xff +#define RENESAS_SDHI_SCC_DTCNTL_TAPEN BIT(0) +#define RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16 +#define RENESAS_SDHI_SCC_DTCNTL_TAPNUM_MASK 0xff #define RENESAS_SDHI_SCC_TAPSET 0x804 #define RENESAS_SDHI_SCC_DT2FF 0x808 #define RENESAS_SDHI_SCC_CKSEL 0x80c -#define RENESAS_SDHI_SCC_CKSEL_DTSEL BIT(0) -#define RENESAS_SDHI_SCC_RVSCNTL 0x810 -#define RENESAS_SDHI_SCC_RVSCNTL_RVSEN BIT(0) +#define RENESAS_SDHI_SCC_CKSEL_DTSEL BIT(0) +#define RENESAS_SDHI_SCC_RVSCNTL 0x810 +#define RENESAS_SDHI_SCC_RVSCNTL_RVSEN BIT(0) #define RENESAS_SDHI_SCC_RVSREQ 0x814 -#define RENESAS_SDHI_SCC_RVSREQ_RVSERR BIT(2) +#define RENESAS_SDHI_SCC_RVSREQ_RVSERR BIT(2) #define RENESAS_SDHI_SCC_SMPCMP 0x818 -#define RENESAS_SDHI_SCC_TMPPORT2 0x81c -#define RENESAS_SDHI_SCC_TMPPORT2_HS400EN BIT(31) -#define RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4) +#define RENESAS_SDHI_SCC_TMPPORT2 0x81c +#define RENESAS_SDHI_SCC_TMPPORT2_HS400EN BIT(31) +#define RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4) +#define RENESAS_SDHI_SCC_TMPPORT3 0x828 +#define RENESAS_SDHI_SCC_TMPPORT3_OFFSET_0 3 +#define RENESAS_SDHI_SCC_TMPPORT3_OFFSET_1 2 +#define RENESAS_SDHI_SCC_TMPPORT3_OFFSET_2 1 +#define RENESAS_SDHI_SCC_TMPPORT3_OFFSET_3 0 +#define RENESAS_SDHI_SCC_TMPPORT3_OFFSET_MASK 0x3 +#define RENESAS_SDHI_SCC_TMPPORT4 0x82c +#define RENESAS_SDHI_SCC_TMPPORT4_DLL_ACC_START BIT(0) +#define RENESAS_SDHI_SCC_TMPPORT5 0x830 +#define RENESAS_SDHI_SCC_TMPPORT5_DLL_RW_SEL_R BIT(8) +#define RENESAS_SDHI_SCC_TMPPORT5_DLL_RW_SEL_W (0 << 8) +#define RENESAS_SDHI_SCC_TMPPORT5_DLL_ADR_MASK 0x3F +#define RENESAS_SDHI_SCC_TMPPORT6 0x834 +#define RENESAS_SDHI_SCC_TMPPORT7 0x838 +#define RENESAS_SDHI_SCC_TMPPORT_DISABLE_WP_CODE 0xa5000000 +#define RENESAS_SDHI_SCC_TMPPORT_CALIB_CODE_MASK 0x1f +#define RENESAS_SDHI_SCC_TMPPORT_MANUAL_MODE BIT(7) #define RENESAS_SDHI_MAX_TAP 3 +static u32 sd_scc_tmpport_read32(struct tmio_sd_priv *priv, u32 addr) +{ + /* read mode */ + tmio_sd_writel(priv, RENESAS_SDHI_SCC_TMPPORT5_DLL_RW_SEL_R | + (RENESAS_SDHI_SCC_TMPPORT5_DLL_ADR_MASK & addr), + RENESAS_SDHI_SCC_TMPPORT5); + + /* access start and stop */ + tmio_sd_writel(priv, RENESAS_SDHI_SCC_TMPPORT4_DLL_ACC_START, + RENESAS_SDHI_SCC_TMPPORT4); + tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_TMPPORT4); + + return tmio_sd_readl(priv, RENESAS_SDHI_SCC_TMPPORT7); +} + +static void sd_scc_tmpport_write32(struct tmio_sd_priv *priv, u32 addr, u32 val) +{ + /* write mode */ + tmio_sd_writel(priv, RENESAS_SDHI_SCC_TMPPORT5_DLL_RW_SEL_W | + (RENESAS_SDHI_SCC_TMPPORT5_DLL_ADR_MASK & addr), + RENESAS_SDHI_SCC_TMPPORT5); + tmio_sd_writel(priv, val, RENESAS_SDHI_SCC_TMPPORT6); + + /* access start and stop */ + tmio_sd_writel(priv, RENESAS_SDHI_SCC_TMPPORT4_DLL_ACC_START, + RENESAS_SDHI_SCC_TMPPORT4); + tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_TMPPORT4); +} + +static void renesas_sdhi_adjust_hs400_mode_enable(struct tmio_sd_priv *priv) +{ + u32 calib_code; + + if (!priv->adjust_hs400_enable) + return; + + if (!priv->needs_adjust_hs400) + return; + + /* + * Enabled Manual adjust HS400 mode + * + * 1) Disabled Write Protect + * W(addr=0x00, WP_DISABLE_CODE) + * 2) Read Calibration code and adjust + * R(addr=0x26) - adjust value + * 3) Enabled Manual Calibration + * W(addr=0x22, manual mode | Calibration code) + * 4) Set Offset value to TMPPORT3 Reg + */ + sd_scc_tmpport_write32(priv, 0x00, + RENESAS_SDHI_SCC_TMPPORT_DISABLE_WP_CODE); + calib_code = sd_scc_tmpport_read32(priv, 0x26); + calib_code &= RENESAS_SDHI_SCC_TMPPORT_CALIB_CODE_MASK; + if (calib_code > priv->adjust_hs400_calibrate) + calib_code -= priv->adjust_hs400_calibrate; + else + calib_code = 0; + sd_scc_tmpport_write32(priv, 0x22, + RENESAS_SDHI_SCC_TMPPORT_MANUAL_MODE | + calib_code); + tmio_sd_writel(priv, priv->adjust_hs400_offset, + RENESAS_SDHI_SCC_TMPPORT3); + + /* Clear flag */ + priv->needs_adjust_hs400 = false; +} + +static void renesas_sdhi_adjust_hs400_mode_disable(struct tmio_sd_priv *priv) +{ + + /* Disabled Manual adjust HS400 mode + * + * 1) Disabled Write Protect + * W(addr=0x00, WP_DISABLE_CODE) + * 2) Disabled Manual Calibration + * W(addr=0x22, 0) + * 3) Clear offset value to TMPPORT3 Reg + */ + sd_scc_tmpport_write32(priv, 0x00, + RENESAS_SDHI_SCC_TMPPORT_DISABLE_WP_CODE); + sd_scc_tmpport_write32(priv, 0x22, 0); + tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_TMPPORT3); +} + static unsigned int renesas_sdhi_init_tuning(struct tmio_sd_priv *priv) { u32 reg; @@ -96,6 +198,9 @@ static void renesas_sdhi_reset_tuning(struct tmio_sd_priv *priv) RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL); tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_TMPPORT2); + /* Disable HS400 mode adjustment */ + renesas_sdhi_adjust_hs400_mode_disable(priv); + reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); reg |= TMIO_SD_CLKCTL_SCLKEN; tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); @@ -137,6 +242,10 @@ static int renesas_sdhi_hs400(struct udevice *dev) tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_TMPPORT2); + /* Disable HS400 mode adjustment */ + if (!hs400) + renesas_sdhi_adjust_hs400_mode_disable(priv); + tmio_sd_writel(priv, (0x8 << RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) | RENESAS_SDHI_SCC_DTCNTL_TAPEN, RENESAS_SDHI_SCC_DTCNTL); @@ -159,6 +268,10 @@ static int renesas_sdhi_hs400(struct udevice *dev) reg |= RENESAS_SDHI_SCC_RVSCNTL_RVSEN; tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); + /* Execute adjust hs400 offset after setting to HS400 mode */ + if (hs400) + priv->needs_adjust_hs400 = true; + return 0; } @@ -188,6 +301,8 @@ static int renesas_sdhi_select_tuning(struct tmio_sd_priv *priv, bool select = false; u32 reg; + priv->needs_adjust_hs400 = false; + /* Clear SCC_RVSREQ */ tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_RVSREQ); @@ -405,8 +520,29 @@ static int renesas_sdhi_wait_dat0(struct udevice *dev, int state, int timeout) } #endif +static int renesas_sdhi_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int ret; + + ret = tmio_sd_send_cmd(dev, cmd, data); + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ + CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ + CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) + struct tmio_sd_priv *priv = dev_get_priv(dev); + + if (cmd->cmdidx == MMC_CMD_SEND_STATUS) + renesas_sdhi_adjust_hs400_mode_enable(priv); +#endif + + return 0; +} + static const struct dm_mmc_ops renesas_sdhi_ops = { - .send_cmd = tmio_sd_send_cmd, + .send_cmd = renesas_sdhi_send_cmd, .set_ios = renesas_sdhi_set_ios, .get_cd = tmio_sd_get_cd, #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ @@ -451,14 +587,37 @@ static void renesas_sdhi_filter_caps(struct udevice *dev) if (!(priv->caps & TMIO_SD_CAP_RCAR_GEN3)) return; - /* HS400 is not supported on H3 ES1.x and M3W ES1.0,ES1.1 */ + /* HS400 is not supported on H3 ES1.x and M3W ES1.0,ES1.1,ES1.2 */ if (((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7795) && (rmobile_get_cpu_rev_integer() <= 1)) || ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7796) && (rmobile_get_cpu_rev_integer() == 1) && - (rmobile_get_cpu_rev_fraction() <= 1))) + (rmobile_get_cpu_rev_fraction() <= 2))) plat->cfg.host_caps &= ~MMC_MODE_HS400; + /* M3W ES1.x for x>2 can use HS400 with manual adjustment */ + if ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7796) && + (rmobile_get_cpu_rev_integer() == 1) && + (rmobile_get_cpu_rev_fraction() > 2)) { + priv->adjust_hs400_enable = true; + priv->adjust_hs400_offset = 0; + priv->adjust_hs400_calibrate = 0x9; + } + + /* M3N can use HS400 with manual adjustment */ + if (rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A77965) { + priv->adjust_hs400_enable = true; + priv->adjust_hs400_offset = 0; + priv->adjust_hs400_calibrate = 0x0; + } + + /* E3 can use HS400 with manual adjustment */ + if (rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A77990) { + priv->adjust_hs400_enable = true; + priv->adjust_hs400_offset = 0; + priv->adjust_hs400_calibrate = 0x2; + } + /* H3 ES2.0 uses 4 tuning taps */ if ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7795) && (rmobile_get_cpu_rev_integer() == 2)) diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c index bf2d83a52c..b2a1201631 100644 --- a/drivers/mmc/rockchip_dw_mmc.c +++ b/drivers/mmc/rockchip_dw_mmc.c @@ -13,8 +13,8 @@ #include <pwrseq.h> #include <syscon.h> #include <asm/gpio.h> -#include <asm/arch/clock.h> -#include <asm/arch/periph.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/periph.h> #include <linux/err.h> struct rockchip_mmc_plat { diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index cdeba914f9..e2bb90abbd 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -67,17 +67,123 @@ static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data) } } -static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, - unsigned int start_addr) +#if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA) +static void sdhci_adma_desc(struct sdhci_host *host, char *buf, u16 len, + bool end) +{ + struct sdhci_adma_desc *desc; + u8 attr; + + desc = &host->adma_desc_table[host->desc_slot]; + + attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA; + if (!end) + host->desc_slot++; + else + attr |= ADMA_DESC_ATTR_END; + + desc->attr = attr; + desc->len = len; + desc->reserved = 0; + desc->addr_lo = (dma_addr_t)buf; +#ifdef CONFIG_DMA_ADDR_T_64BIT + desc->addr_hi = (u64)buf >> 32; +#endif +} + +static void sdhci_prepare_adma_table(struct sdhci_host *host, + struct mmc_data *data) +{ + uint trans_bytes = data->blocksize * data->blocks; + uint desc_count = DIV_ROUND_UP(trans_bytes, ADMA_MAX_LEN); + int i = desc_count; + char *buf; + + host->desc_slot = 0; + + if (data->flags & MMC_DATA_READ) + buf = data->dest; + else + buf = (char *)data->src; + + while (--i) { + sdhci_adma_desc(host, buf, ADMA_MAX_LEN, false); + buf += ADMA_MAX_LEN; + trans_bytes -= ADMA_MAX_LEN; + } + + sdhci_adma_desc(host, buf, trans_bytes, true); + + flush_cache((dma_addr_t)host->adma_desc_table, + ROUND(desc_count * sizeof(struct sdhci_adma_desc), + ARCH_DMA_MINALIGN)); +} +#elif defined(CONFIG_MMC_SDHCI_SDMA) +static void sdhci_prepare_adma_table(struct sdhci_host *host, + struct mmc_data *data) +{} +#endif +#if (defined(CONFIG_MMC_SDHCI_SDMA) || CONFIG_IS_ENABLED(MMC_SDHCI_ADMA)) +static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data, + int *is_aligned, int trans_bytes) { - unsigned int stat, rdy, mask, timeout, block = 0; - bool transfer_done = false; -#ifdef CONFIG_MMC_SDHCI_SDMA unsigned char ctrl; + + if (data->flags == MMC_DATA_READ) + host->start_addr = (dma_addr_t)data->dest; + else + host->start_addr = (dma_addr_t)data->src; + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl &= ~SDHCI_CTRL_DMA_MASK; + if (host->flags & USE_ADMA64) + ctrl |= SDHCI_CTRL_ADMA64; + else if (host->flags & USE_ADMA) + ctrl |= SDHCI_CTRL_ADMA32; sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + + if (host->flags & USE_SDMA) { + if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && + (host->start_addr & 0x7) != 0x0) { + *is_aligned = 0; + host->start_addr = (unsigned long)aligned_buffer; + if (data->flags != MMC_DATA_READ) + memcpy(aligned_buffer, data->src, trans_bytes); + } + +#if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER) + /* + * Always use this bounce-buffer when + * CONFIG_FIXED_SDHCI_ALIGNED_BUFFER is defined + */ + *is_aligned = 0; + host->start_addr = (unsigned long)aligned_buffer; + if (data->flags != MMC_DATA_READ) + memcpy(aligned_buffer, data->src, trans_bytes); +#endif + sdhci_writel(host, host->start_addr, SDHCI_DMA_ADDRESS); + + } else if (host->flags & (USE_ADMA | USE_ADMA64)) { + sdhci_prepare_adma_table(host, data); + + sdhci_writel(host, (u32)host->adma_addr, SDHCI_ADMA_ADDRESS); + if (host->flags & USE_ADMA64) + sdhci_writel(host, (u64)host->adma_addr >> 32, + SDHCI_ADMA_ADDRESS_HI); + } + + flush_cache(host->start_addr, ROUND(trans_bytes, ARCH_DMA_MINALIGN)); +} +#else +static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data, + int *is_aligned, int trans_bytes) +{} #endif +static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data) +{ + dma_addr_t start_addr = host->start_addr; + unsigned int stat, rdy, mask, timeout, block = 0; + bool transfer_done = false; timeout = 1000000; rdy = SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL; @@ -104,14 +210,17 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, continue; } } -#ifdef CONFIG_MMC_SDHCI_SDMA - if (!transfer_done && (stat & SDHCI_INT_DMA_END)) { + if ((host->flags & USE_DMA) && !transfer_done && + (stat & SDHCI_INT_DMA_END)) { sdhci_writel(host, SDHCI_INT_DMA_END, SDHCI_INT_STATUS); - start_addr &= ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1); - start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE; - sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); + if (host->flags & USE_SDMA) { + start_addr &= + ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1); + start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE; + sdhci_writel(host, start_addr, + SDHCI_DMA_ADDRESS); + } } -#endif if (timeout-- > 0) udelay(10); else { @@ -149,10 +258,11 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, int ret = 0; int trans_bytes = 0, is_aligned = 1; u32 mask, flags, mode; - unsigned int time = 0, start_addr = 0; + unsigned int time = 0; int mmc_dev = mmc_get_blk_desc(mmc)->devnum; ulong start = get_timer(0); + host->start_addr = 0; /* Timeout unit - ms */ static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT; @@ -218,33 +328,11 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, if (data->flags == MMC_DATA_READ) mode |= SDHCI_TRNS_READ; -#ifdef CONFIG_MMC_SDHCI_SDMA - if (data->flags == MMC_DATA_READ) - start_addr = (unsigned long)data->dest; - else - start_addr = (unsigned long)data->src; - if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && - (start_addr & 0x7) != 0x0) { - is_aligned = 0; - start_addr = (unsigned long)aligned_buffer; - if (data->flags != MMC_DATA_READ) - memcpy(aligned_buffer, data->src, trans_bytes); + if (host->flags & USE_DMA) { + mode |= SDHCI_TRNS_DMA; + sdhci_prepare_dma(host, data, &is_aligned, trans_bytes); } -#if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER) - /* - * Always use this bounce-buffer when - * CONFIG_FIXED_SDHCI_ALIGNED_BUFFER is defined - */ - is_aligned = 0; - start_addr = (unsigned long)aligned_buffer; - if (data->flags != MMC_DATA_READ) - memcpy(aligned_buffer, data->src, trans_bytes); -#endif - - sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); - mode |= SDHCI_TRNS_DMA; -#endif sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, data->blocksize), SDHCI_BLOCK_SIZE); @@ -255,12 +343,6 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, } sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); -#ifdef CONFIG_MMC_SDHCI_SDMA - if (data) { - trans_bytes = ALIGN(trans_bytes, CONFIG_SYS_CACHELINE_SIZE); - flush_cache(start_addr, trans_bytes); - } -#endif sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); start = get_timer(0); do { @@ -286,7 +368,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, ret = -1; if (!ret && data) - ret = sdhci_transfer_data(host, data, start_addr); + ret = sdhci_transfer_data(host, data); if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD) udelay(1000); @@ -570,6 +652,24 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host, __func__); return -EINVAL; } + + host->flags |= USE_SDMA; +#endif +#if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA) + if (!(caps & SDHCI_CAN_DO_ADMA2)) { + printf("%s: Your controller doesn't support SDMA!!\n", + __func__); + return -EINVAL; + } + host->adma_desc_table = (struct sdhci_adma_desc *) + memalign(ARCH_DMA_MINALIGN, ADMA_TABLE_SZ); + + host->adma_addr = (dma_addr_t)host->adma_desc_table; +#ifdef CONFIG_DMA_ADDR_T_64BIT + host->flags |= USE_ADMA64; +#else + host->flags |= USE_ADMA; +#endif #endif if (host->quirks & SDHCI_QUIRK_REG32_RW) host->version = diff --git a/drivers/mmc/tmio-common.h b/drivers/mmc/tmio-common.h index 58ce3d65b0..51607de142 100644 --- a/drivers/mmc/tmio-common.h +++ b/drivers/mmc/tmio-common.h @@ -139,6 +139,10 @@ struct tmio_sd_priv { #if CONFIG_IS_ENABLED(RENESAS_SDHI) u8 tap_set; u8 nrtaps; + bool needs_adjust_hs400; + bool adjust_hs400_enable; + u8 adjust_hs400_offset; + u8 adjust_hs400_calibrate; #endif ulong (*clk_get_rate)(struct tmio_sd_priv *); }; diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index e6a84a52b4..cfa9b535c8 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -730,43 +730,6 @@ static int nand_davinci_dev_ready(struct mtd_info *mtd) return __raw_readl(&davinci_emif_regs->nandfsr) & 0x1; } -static void nand_flash_init(void) -{ - /* This is for DM6446 EVM and *very* similar. DO NOT GROW THIS! - * Instead, have your board_init() set EMIF timings, based on its - * knowledge of the clocks and what devices are hooked up ... and - * don't even do that unless no UBL handled it. - */ -#ifdef CONFIG_SOC_DM644X - u_int32_t acfg1 = 0x3ffffffc; - - /*------------------------------------------------------------------* - * NAND FLASH CHIP TIMEOUT @ 459 MHz * - * * - * AEMIF.CLK freq = PLL1/6 = 459/6 = 76.5 MHz * - * AEMIF.CLK period = 1/76.5 MHz = 13.1 ns * - * * - *------------------------------------------------------------------*/ - acfg1 = 0 - | (0 << 31) /* selectStrobe */ - | (0 << 30) /* extWait */ - | (1 << 26) /* writeSetup 10 ns */ - | (3 << 20) /* writeStrobe 40 ns */ - | (1 << 17) /* writeHold 10 ns */ - | (1 << 13) /* readSetup 10 ns */ - | (5 << 7) /* readStrobe 60 ns */ - | (1 << 4) /* readHold 10 ns */ - | (3 << 2) /* turnAround ?? ns */ - | (0 << 0) /* asyncSize 8-bit bus */ - ; - - __raw_writel(acfg1, &davinci_emif_regs->ab1cr); /* CS2 */ - - /* NAND flash on CS2 */ - __raw_writel(0x00000101, &davinci_emif_regs->nandfcr); -#endif -} - void davinci_nand_init(struct nand_chip *nand) { #if defined CONFIG_KEYSTONE_RBL_NAND @@ -820,8 +783,6 @@ void davinci_nand_init(struct nand_chip *nand) nand->write_buf = nand_davinci_write_buf; nand->dev_ready = nand_davinci_dev_ready; - - nand_flash_init(); } int board_nand_init(struct nand_chip *chip) __attribute__((weak)); diff --git a/drivers/mtd/nand/raw/fsl_elbc_spl.c b/drivers/mtd/nand/raw/fsl_elbc_spl.c index 30c3308940..099d86427c 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_spl.c +++ b/drivers/mtd/nand/raw/fsl_elbc_spl.c @@ -14,6 +14,10 @@ #include <asm/fsl_lbc.h> #include <nand.h> +#ifdef CONFIG_MPC83xx +#include "../../../arch/powerpc/cpu/mpc83xx/elbc/elbc.h" +#endif + #define WINDOW_SIZE 8192 static void nand_wait(void) diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c index be4ee2c7f8..b93d77a395 100644 --- a/drivers/mtd/nand/raw/mxs_nand.c +++ b/drivers/mtd/nand/raw/mxs_nand.c @@ -50,7 +50,7 @@ struct nand_ecclayout fake_ecc_layout; /* * Cache management functions */ -#ifndef CONFIG_SYS_DCACHE_OFF +#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) static void mxs_nand_flush_data_buf(struct mxs_nand_info *info) { uint32_t addr = (uint32_t)info->data_buf; diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index c4e2f6a08f..1acff745d1 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -116,7 +116,6 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, SPI_MEM_OP_ADDR(nor->addr_width, to, 1), SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_OUT(len, buf, 1)); - size_t remaining = len; int ret; /* get transfer protocols. */ @@ -127,22 +126,16 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) op.addr.nbytes = 0; - while (remaining) { - op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX; - ret = spi_mem_adjust_op_size(nor->spi, &op); - if (ret) - return ret; - - ret = spi_mem_exec_op(nor->spi, &op); - if (ret) - return ret; + ret = spi_mem_adjust_op_size(nor->spi, &op); + if (ret) + return ret; + op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes; - op.addr.val += op.data.nbytes; - remaining -= op.data.nbytes; - op.data.buf.out += op.data.nbytes; - } + ret = spi_mem_exec_op(nor->spi, &op); + if (ret) + return ret; - return len; + return op.data.nbytes; } /* @@ -1101,10 +1094,6 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, goto write_err; *retlen += written; i += written; - if (written != page_remain) { - ret = -EIO; - goto write_err; - } } write_err: diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index cf84783356..2b17eae947 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -9,7 +9,6 @@ config CONFIG_UBI_SILENCE_MSG config MTD_UBI bool "Enable UBI - Unsorted block images" - select CRC32 select RBTREE select MTD_PARTITIONS help diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 6e436b56ab..e6a4fdf30e 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -195,6 +195,12 @@ config FEC_MXC This driver supports the 10/100 Fast Ethernet controller for NXP i.MX processors. +config FMAN_ENET + bool "Freescale FMan ethernet support" + depends on ARM || PPC + help + This driver support the Freescale FMan Ethernet controller + config FTMAC100 bool "Ftmac100 Ethernet Support" help @@ -269,7 +275,7 @@ config MACB_ZYNQ config MT7628_ETH bool "MediaTek MT7628 Ethernet Interface" - depends on ARCH_MT7620 + depends on SOC_MT7628 help The MediaTek MT7628 ethernet interface is used on MT7628 and MT7688 based boards. diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 9f1c5af46e..590e756f5c 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -241,7 +241,7 @@ struct eqos_tegra186_regs { */ #if EQOS_DESCRIPTOR_SIZE < ARCH_DMA_MINALIGN #if !defined(CONFIG_SYS_NONCACHED_MEMORY) && \ - !defined(CONFIG_SYS_DCACHE_OFF) && !defined(CONFIG_X86) + !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) && !defined(CONFIG_X86) #warning Cache line size is larger than descriptor size #endif #endif diff --git a/drivers/net/fm/fm.c b/drivers/net/fm/fm.c index e19d7777dc..0a43dfe74e 100644 --- a/drivers/net/fm/fm.c +++ b/drivers/net/fm/fm.c @@ -459,7 +459,7 @@ int fm_init_common(int index, struct ccsr_fman *reg) printf("NAND read of FMAN firmware at offset 0x%x failed %d\n", CONFIG_SYS_FMAN_FW_ADDR, rc); } -#elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH) +#elif defined(CONFIG_SYS_QE_FMAN_FW_IN_SPIFLASH) struct spi_flash *ucode_flash; void *addr = malloc(CONFIG_SYS_QE_FMAN_FW_LENGTH); int ret = 0; diff --git a/drivers/net/gmac_rockchip.c b/drivers/net/gmac_rockchip.c index c01ae758c7..26a6121175 100644 --- a/drivers/net/gmac_rockchip.c +++ b/drivers/net/gmac_rockchip.c @@ -11,15 +11,15 @@ #include <phy.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/periph.h> -#include <asm/arch/clock.h> -#include <asm/arch/hardware.h> -#include <asm/arch/grf_rk322x.h> -#include <asm/arch/grf_rk3288.h> -#include <asm/arch/grf_rk3328.h> -#include <asm/arch/grf_rk3368.h> -#include <asm/arch/grf_rk3399.h> -#include <asm/arch/grf_rv1108.h> +#include <asm/arch-rockchip/periph.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/hardware.h> +#include <asm/arch-rockchip/grf_rk322x.h> +#include <asm/arch-rockchip/grf_rk3288.h> +#include <asm/arch-rockchip/grf_rk3328.h> +#include <asm/arch-rockchip/grf_rk3368.h> +#include <asm/arch-rockchip/grf_rk3399.h> +#include <asm/arch-rockchip/grf_rv1108.h> #include <dm/pinctrl.h> #include <dt-bindings/clock/rk3288-cru.h> #include "designware.h" diff --git a/drivers/net/ldpaa_eth/ldpaa_eth.c b/drivers/net/ldpaa_eth/ldpaa_eth.c index 73b7ba29df..34253e3924 100644 --- a/drivers/net/ldpaa_eth/ldpaa_eth.c +++ b/drivers/net/ldpaa_eth/ldpaa_eth.c @@ -1074,6 +1074,7 @@ int ldpaa_eth_init(int dpmac_id, phy_interface_t enet_if) priv = (struct ldpaa_eth_priv *)malloc(sizeof(struct ldpaa_eth_priv)); if (!priv) { printf("ldpaa_eth_priv malloc() failed\n"); + free(net_dev); return -ENOMEM; } memset(priv, 0, sizeof(struct ldpaa_eth_priv)); diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 72614164e9..c5560a7111 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -488,15 +488,58 @@ static int macb_phy_find(struct macb_device *macb, const char *name) /** * macb_linkspd_cb - Linkspeed change callback function - * @regs: Base Register of MACB devices + * @dev/@regs: MACB udevice (DM version) or + * Base Register of MACB devices (non-DM version) * @speed: Linkspeed * Returns 0 when operation success and negative errno number * when operation failed. */ +#ifdef CONFIG_DM_ETH +int __weak macb_linkspd_cb(struct udevice *dev, unsigned int speed) +{ +#ifdef CONFIG_CLK + struct clk tx_clk; + ulong rate; + int ret; + + /* + * "tx_clk" is an optional clock source for MACB. + * Ignore if it does not exist in DT. + */ + ret = clk_get_by_name(dev, "tx_clk", &tx_clk); + if (ret) + return 0; + + switch (speed) { + case _10BASET: + rate = 2500000; /* 2.5 MHz */ + break; + case _100BASET: + rate = 25000000; /* 25 MHz */ + break; + case _1000BASET: + rate = 125000000; /* 125 MHz */ + break; + default: + /* does not change anything */ + return 0; + } + + if (tx_clk.dev) { + ret = clk_set_rate(&tx_clk, rate); + if (ret) + return ret; + } +#endif + + return 0; +} +#else int __weak macb_linkspd_cb(void *regs, unsigned int speed) { return 0; } +#endif #ifdef CONFIG_DM_ETH static int macb_phy_init(struct udevice *dev, const char *name) @@ -589,7 +632,11 @@ static int macb_phy_init(struct macb_device *macb, const char *name) macb_writel(macb, NCFGR, ncfgr); +#ifdef CONFIG_DM_ETH + ret = macb_linkspd_cb(dev, _1000BASET); +#else ret = macb_linkspd_cb(macb->regs, _1000BASET); +#endif if (ret) return ret; @@ -614,9 +661,17 @@ static int macb_phy_init(struct macb_device *macb, const char *name) ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD) | GEM_BIT(GBE)); if (speed) { ncfgr |= MACB_BIT(SPD); +#ifdef CONFIG_DM_ETH + ret = macb_linkspd_cb(dev, _100BASET); +#else ret = macb_linkspd_cb(macb->regs, _100BASET); +#endif } else { +#ifdef CONFIG_DM_ETH + ret = macb_linkspd_cb(dev, _10BASET); +#else ret = macb_linkspd_cb(macb->regs, _10BASET); +#endif } if (ret) diff --git a/drivers/net/mscc_eswitch/Kconfig b/drivers/net/mscc_eswitch/Kconfig index 6359d0b610..80dd22f98b 100644 --- a/drivers/net/mscc_eswitch/Kconfig +++ b/drivers/net/mscc_eswitch/Kconfig @@ -29,3 +29,10 @@ config MSCC_SERVALT_SWITCH select PHYLIB help This driver supports the Servalt network switch device. + +config MSCC_SERVAL_SWITCH + bool "Serval switch driver" + depends on DM_ETH && ARCH_MSCC + select PHYLIB + help + This driver supports the Serval network switch device. diff --git a/drivers/net/mscc_eswitch/Makefile b/drivers/net/mscc_eswitch/Makefile index bffd8ec77b..02f39a76bb 100644 --- a/drivers/net/mscc_eswitch/Makefile +++ b/drivers/net/mscc_eswitch/Makefile @@ -1,5 +1,6 @@ -obj-$(CONFIG_MSCC_OCELOT_SWITCH) += ocelot_switch.o mscc_miim.o mscc_xfer.o mscc_mac_table.o -obj-$(CONFIG_MSCC_LUTON_SWITCH) += luton_switch.o mscc_miim.o mscc_xfer.o mscc_mac_table.o +obj-$(CONFIG_MSCC_OCELOT_SWITCH) += ocelot_switch.o mscc_xfer.o mscc_mac_table.o +obj-$(CONFIG_MSCC_LUTON_SWITCH) += luton_switch.o mscc_xfer.o mscc_mac_table.o obj-$(CONFIG_MSCC_JR2_SWITCH) += jr2_switch.o mscc_xfer.o obj-$(CONFIG_MSCC_SERVALT_SWITCH) += servalt_switch.o mscc_xfer.o +obj-$(CONFIG_MSCC_SERVAL_SWITCH) += serval_switch.o mscc_xfer.o mscc_mac_table.o diff --git a/drivers/net/mscc_eswitch/luton_switch.c b/drivers/net/mscc_eswitch/luton_switch.c index 6667614966..94852b06e7 100644 --- a/drivers/net/mscc_eswitch/luton_switch.c +++ b/drivers/net/mscc_eswitch/luton_switch.c @@ -15,10 +15,21 @@ #include <net.h> #include <wait_bit.h> -#include "mscc_miim.h" #include "mscc_xfer.h" #include "mscc_mac_table.h" +#define GCB_MIIM_MII_STATUS 0x0 +#define GCB_MIIM_STAT_BUSY BIT(3) +#define GCB_MIIM_MII_CMD 0x8 +#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) +#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) +#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) +#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) +#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) +#define GCB_MIIM_MII_CMD_VLD BIT(31) +#define GCB_MIIM_DATA 0xC +#define GCB_MIIM_DATA_ERROR (0x2 << 16) + #define ANA_PORT_VLAN_CFG(x) (0x00 + 0x80 * (x)) #define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20) #define ANA_PORT_VLAN_CFG_POP_CNT(x) ((x) << 18) @@ -136,61 +147,53 @@ #define PGID_UNICAST 29 #define PGID_SRC 80 -enum luton_target { - PORT0, - PORT1, - PORT2, - PORT3, - PORT4, - PORT5, - PORT6, - PORT7, - PORT8, - PORT9, - PORT10, - PORT11, - PORT12, - PORT13, - PORT14, - PORT15, - PORT16, - PORT17, - PORT18, - PORT19, - PORT20, - PORT21, - PORT22, - PORT23, - SYS, +static const char * const regs_names[] = { + "port0", "port1", "port2", "port3", "port4", "port5", "port6", "port7", + "port8", "port9", "port10", "port11", "port12", "port13", "port14", + "port15", "port16", "port17", "port18", "port19", "port20", "port21", + "port22", "port23", + "sys", "ana", "rew", "gcb", "qs", "hsio", +}; + +#define REGS_NAMES_COUNT ARRAY_SIZE(regs_names) + 1 +#define MAX_PORT 24 + +enum luton_ctrl_regs { + SYS = MAX_PORT, ANA, REW, GCB, QS, - HSIO, - TARGET_MAX, + HSIO }; -#define MAX_PORT (PORT23 - PORT0 + 1) +#define MIN_INT_PORT 0 +#define PORT10 10 +#define PORT11 11 +#define MAX_INT_PORT 12 +#define MIN_EXT_PORT MAX_INT_PORT +#define MAX_EXT_PORT MAX_PORT -#define MIN_INT_PORT PORT0 -#define MAX_INT_PORT (PORT11 - PORT0 + 1) -#define MIN_EXT_PORT PORT12 -#define MAX_EXT_PORT MAX_PORT +#define LUTON_MIIM_BUS_COUNT 2 -enum luton_mdio_target { - MIIM, - TARGET_MDIO_MAX, +struct luton_phy_port_t { + size_t phy_addr; + struct mii_dev *bus; + u8 serdes_index; + u8 phy_mode; }; -enum luton_phy_id { - INTERNAL, - EXTERNAL, - NUM_PHY, +struct luton_private { + void __iomem *regs[REGS_NAMES_COUNT]; + struct mii_dev *bus[LUTON_MIIM_BUS_COUNT]; + struct luton_phy_port_t ports[MAX_PORT]; }; -struct luton_private { - void __iomem *regs[TARGET_MAX]; - struct mii_dev *bus[NUM_PHY]; +struct mscc_miim_dev { + void __iomem *regs; + phys_addr_t miim_base; + unsigned long miim_size; + struct mii_dev *bus; }; static const unsigned long luton_regs_qs[] = { @@ -207,53 +210,85 @@ static const unsigned long luton_regs_ana_table[] = { [MSCC_ANA_TABLES_MACACCESS] = 0x11b8, }; -static struct mscc_miim_dev miim[NUM_PHY]; +static struct mscc_miim_dev miim[LUTON_MIIM_BUS_COUNT]; +static int miim_count = -1; -static struct mii_dev *luton_mdiobus_init(struct udevice *dev, - int mdiobus_id) +static int mscc_miim_wait_ready(struct mscc_miim_dev *miim) +{ + return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS, + GCB_MIIM_STAT_BUSY, false, 250, false); +} + +static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; + u32 val; + int ret; + + ret = mscc_miim_wait_ready(miim); + if (ret) + goto out; + + writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | + GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, + miim->regs + GCB_MIIM_MII_CMD); + + ret = mscc_miim_wait_ready(miim); + if (ret) + goto out; + + val = readl(miim->regs + GCB_MIIM_DATA); + if (val & GCB_MIIM_DATA_ERROR) { + ret = -EIO; + goto out; + } + + ret = val & 0xFFFF; + out: + return ret; +} + +static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; + int ret; + + ret = mscc_miim_wait_ready(miim); + if (ret < 0) + goto out; + + writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | + GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | + GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); + out: + return ret; +} + +static struct mii_dev *serval_mdiobus_init(phys_addr_t miim_base, + unsigned long miim_size) { - unsigned long phy_size[NUM_PHY]; - phys_addr_t phy_base[NUM_PHY]; - struct ofnode_phandle_args phandle; - ofnode eth_node, node, mdio_node; - struct resource res; struct mii_dev *bus; - fdt32_t faddr; - int i; bus = mdio_alloc(); if (!bus) return NULL; - /* gather only the first mdio bus */ - eth_node = dev_read_first_subnode(dev); - node = ofnode_first_subnode(eth_node); - ofnode_parse_phandle_with_args(node, "phy-handle", NULL, 0, 0, - &phandle); - mdio_node = ofnode_get_parent(phandle.node); - - for (i = 0; i < TARGET_MDIO_MAX; i++) { - if (ofnode_read_resource(mdio_node, i, &res)) { - pr_err("%s: get OF resource failed\n", __func__); - return NULL; - } - - faddr = cpu_to_fdt32(res.start); - phy_base[i] = ofnode_translate_address(mdio_node, &faddr); - phy_size[i] = res.end - res.start; - } + ++miim_count; + sprintf(bus->name, "miim-bus%d", miim_count); - strcpy(bus->name, "miim-internal"); - miim[mdiobus_id].regs = ioremap(phy_base[mdiobus_id], - phy_size[mdiobus_id]); - bus->priv = &miim[mdiobus_id]; + miim[miim_count].regs = ioremap(miim_base, miim_size); + miim[miim_count].miim_base = miim_base; + miim[miim_count].miim_size = miim_size; + bus->priv = &miim[miim_count]; bus->read = mscc_miim_read; bus->write = mscc_miim_write; if (mdio_register(bus)) return NULL; - else - return bus; + + miim[miim_count].bus = bus; + return bus; } static void luton_stop(struct udevice *dev) @@ -324,10 +359,10 @@ static void luton_gmii_port_init(struct luton_private *priv, int port) writel(ANA_PORT_VLAN_CFG_AWARE_ENA | ANA_PORT_VLAN_CFG_POP_CNT(1) | MAC_VID, - priv->regs[ANA] + ANA_PORT_VLAN_CFG(port - PORT0)); + priv->regs[ANA] + ANA_PORT_VLAN_CFG(port)); /* Enable switching to/from port */ - setbits_le32(priv->regs[SYS] + SYS_SWITCH_PORT_MODE(port - PORT0), + setbits_le32(priv->regs[SYS] + SYS_SWITCH_PORT_MODE(port), SYS_SWITCH_PORT_MODE_PORT_ENA); } @@ -346,10 +381,10 @@ static void luton_port_init(struct luton_private *priv, int port) writel(ANA_PORT_VLAN_CFG_AWARE_ENA | ANA_PORT_VLAN_CFG_POP_CNT(1) | MAC_VID, - priv->regs[ANA] + ANA_PORT_VLAN_CFG(port - PORT0)); + priv->regs[ANA] + ANA_PORT_VLAN_CFG(port)); /* Enable switching to/from port */ - setbits_le32(priv->regs[SYS] + SYS_SWITCH_PORT_MODE(port - PORT0), + setbits_le32(priv->regs[SYS] + SYS_SWITCH_PORT_MODE(port), SYS_SWITCH_PORT_MODE_PORT_ENA); } @@ -393,35 +428,34 @@ static void luton_ext_port_init(struct luton_private *priv, int port) writel(ANA_PORT_VLAN_CFG_AWARE_ENA | ANA_PORT_VLAN_CFG_POP_CNT(1) | MAC_VID, - priv->regs[ANA] + ANA_PORT_VLAN_CFG(port - PORT0)); + priv->regs[ANA] + ANA_PORT_VLAN_CFG(port)); /* Enable switching to/from port */ - setbits_le32(priv->regs[SYS] + SYS_SWITCH_PORT_MODE(port - PORT0), + setbits_le32(priv->regs[SYS] + SYS_SWITCH_PORT_MODE(port), SYS_SWITCH_PORT_MODE_PORT_ENA); } -static void serdes6g_write(struct luton_private *priv, u32 addr) +static void serdes6g_write(void __iomem *base, u32 addr) { u32 data; writel(HSIO_MCB_SERDES6G_CFG_WR_ONE_SHOT | HSIO_MCB_SERDES6G_CFG_ADDR(addr), - priv->regs[HSIO] + HSIO_MCB_SERDES6G_CFG); + base + HSIO_MCB_SERDES6G_CFG); do { - data = readl(priv->regs[HSIO] + HSIO_MCB_SERDES6G_CFG); + data = readl(base + HSIO_MCB_SERDES6G_CFG); } while (data & HSIO_MCB_SERDES6G_CFG_WR_ONE_SHOT); - - mdelay(100); } -static void serdes6g_cfg(struct luton_private *priv) +static void serdes6g_setup(void __iomem *base, uint32_t addr, + phy_interface_t interface) { writel(HSIO_RCOMP_CFG_CFG0_MODE_SEL(0x3) | HSIO_RCOMP_CFG_CFG0_RUN_CAL, - priv->regs[HSIO] + HSIO_RCOMP_CFG_CFG0); + base + HSIO_RCOMP_CFG_CFG0); - while (readl(priv->regs[HSIO] + HSIO_RCOMP_STATUS) & + while (readl(base + HSIO_RCOMP_STATUS) & HSIO_RCOMP_STATUS_BUSY) ; @@ -430,50 +464,64 @@ static void serdes6g_cfg(struct luton_private *priv) HSIO_SERDES6G_ANA_CFG_OB_CFG_POST0(0x10) | HSIO_SERDES6G_ANA_CFG_OB_CFG_POL | HSIO_SERDES6G_ANA_CFG_OB_CFG_ENA1V_MODE, - priv->regs[HSIO] + HSIO_SERDES6G_ANA_CFG_OB_CFG); + base + HSIO_SERDES6G_ANA_CFG_OB_CFG); writel(HSIO_SERDES6G_ANA_CFG_OB_CFG1_LEV(0x18) | HSIO_SERDES6G_ANA_CFG_OB_CFG1_ENA_CAS(0x1), - priv->regs[HSIO] + HSIO_SERDES6G_ANA_CFG_OB_CFG1); + base + HSIO_SERDES6G_ANA_CFG_OB_CFG1); writel(HSIO_SERDES6G_ANA_CFG_IB_CFG_RESISTOR_CTRL(0xc) | HSIO_SERDES6G_ANA_CFG_IB_CFG_VBCOM(0x4) | HSIO_SERDES6G_ANA_CFG_IB_CFG_VBAC(0x5) | HSIO_SERDES6G_ANA_CFG_IB_CFG_RT(0xf) | HSIO_SERDES6G_ANA_CFG_IB_CFG_RF(0x4), - priv->regs[HSIO] + HSIO_SERDES6G_ANA_CFG_IB_CFG); + base + HSIO_SERDES6G_ANA_CFG_IB_CFG); writel(HSIO_SERDES6G_ANA_CFG_IB_CFG1_RST | HSIO_SERDES6G_ANA_CFG_IB_CFG1_ENA_OFFSDC | HSIO_SERDES6G_ANA_CFG_IB_CFG1_ENA_OFFSAC | HSIO_SERDES6G_ANA_CFG_IB_CFG1_ANEG_MODE | HSIO_SERDES6G_ANA_CFG_IB_CFG1_CHF | HSIO_SERDES6G_ANA_CFG_IB_CFG1_C(0x4), - priv->regs[HSIO] + HSIO_SERDES6G_ANA_CFG_IB_CFG1); + base + HSIO_SERDES6G_ANA_CFG_IB_CFG1); writel(HSIO_SERDES6G_ANA_CFG_DES_CFG_BW_ANA(0x5) | HSIO_SERDES6G_ANA_CFG_DES_CFG_BW_HYST(0x5) | HSIO_SERDES6G_ANA_CFG_DES_CFG_MBTR_CTRL(0x2) | HSIO_SERDES6G_ANA_CFG_DES_CFG_PHS_CTRL(0x6), - priv->regs[HSIO] + HSIO_SERDES6G_ANA_CFG_DES_CFG); + base + HSIO_SERDES6G_ANA_CFG_DES_CFG); writel(HSIO_SERDES6G_ANA_CFG_PLL_CFG_FSM_ENA | HSIO_SERDES6G_ANA_CFG_PLL_CFG_FSM_CTRL_DATA(0x78), - priv->regs[HSIO] + HSIO_SERDES6G_ANA_CFG_PLL_CFG); + base + HSIO_SERDES6G_ANA_CFG_PLL_CFG); writel(HSIO_SERDES6G_ANA_CFG_COMMON_CFG_IF_MODE(0x30) | HSIO_SERDES6G_ANA_CFG_COMMON_CFG_ENA_LANE, - priv->regs[HSIO] + HSIO_SERDES6G_ANA_CFG_COMMON_CFG); + base + HSIO_SERDES6G_ANA_CFG_COMMON_CFG); /* * There are 4 serdes6g, configure all except serdes6g0, therefore * the address is b1110 */ - serdes6g_write(priv, 0xe); + serdes6g_write(base, addr); - writel(readl(priv->regs[HSIO] + HSIO_SERDES6G_ANA_CFG_COMMON_CFG) | + writel(readl(base + HSIO_SERDES6G_ANA_CFG_COMMON_CFG) | HSIO_SERDES6G_ANA_CFG_COMMON_CFG_SYS_RST, - priv->regs[HSIO] + HSIO_SERDES6G_ANA_CFG_COMMON_CFG); - serdes6g_write(priv, 0xe); + base + HSIO_SERDES6G_ANA_CFG_COMMON_CFG); + serdes6g_write(base, addr); - clrbits_le32(priv->regs[HSIO] + HSIO_SERDES6G_ANA_CFG_IB_CFG1, + clrbits_le32(base + HSIO_SERDES6G_ANA_CFG_IB_CFG1, HSIO_SERDES6G_ANA_CFG_IB_CFG1_RST); writel(HSIO_SERDES6G_DIG_CFG_MISC_CFG_LANE_RST, - priv->regs[HSIO] + HSIO_SERDES6G_DIG_CFG_MISC_CFG); - serdes6g_write(priv, 0xe); + base + HSIO_SERDES6G_DIG_CFG_MISC_CFG); + serdes6g_write(base, addr); +} + +static void serdes_setup(struct luton_private *priv) +{ + size_t mask; + int i = 0; + + for (i = 0; i < MAX_PORT; ++i) { + if (!priv->ports[i].bus || priv->ports[i].serdes_index == 0xff) + continue; + + mask = BIT(priv->ports[i].serdes_index); + serdes6g_setup(priv->regs[HSIO], mask, priv->ports[i].phy_mode); + } } static int luton_switch_init(struct luton_private *priv) @@ -495,8 +543,8 @@ static int luton_switch_init(struct luton_private *priv) setbits_le32(priv->regs[SYS] + SYS_SYSTEM_RST_CFG, SYS_SYSTEM_RST_CORE_ENA); - /* Setup the Serdes6g macros */ - serdes6g_cfg(priv); + /* Setup the Serdes macros */ + serdes_setup(priv); return 0; } @@ -525,7 +573,7 @@ static int luton_initialize(struct luton_private *priv) writel(2000000000 / 4, priv->regs[SYS] + SYS_FRM_AGING); - for (i = PORT0; i < MAX_PORT; i++) { + for (i = 0; i < MAX_PORT; i++) { if (i < PORT10) luton_gmii_port_init(priv, i); else @@ -608,56 +656,51 @@ static int luton_recv(struct udevice *dev, int flags, uchar **packetp) return byte_cnt; } +static struct mii_dev *get_mdiobus(phys_addr_t base, unsigned long size) +{ + int i = 0; + + for (i = 0; i < LUTON_MIIM_BUS_COUNT; ++i) + if (miim[i].miim_base == base && miim[i].miim_size == size) + return miim[i].bus; + + return NULL; +} + +static void add_port_entry(struct luton_private *priv, size_t index, + size_t phy_addr, struct mii_dev *bus, + u8 serdes_index, u8 phy_mode) +{ + priv->ports[index].phy_addr = phy_addr; + priv->ports[index].bus = bus; + priv->ports[index].serdes_index = serdes_index; + priv->ports[index].phy_mode = phy_mode; +} + static int luton_probe(struct udevice *dev) { struct luton_private *priv = dev_get_priv(dev); - int i; - - struct { - enum luton_target id; - char *name; - } reg[] = { - { PORT0, "port0" }, - { PORT1, "port1" }, - { PORT2, "port2" }, - { PORT3, "port3" }, - { PORT4, "port4" }, - { PORT5, "port5" }, - { PORT6, "port6" }, - { PORT7, "port7" }, - { PORT8, "port8" }, - { PORT9, "port9" }, - { PORT10, "port10" }, - { PORT11, "port11" }, - { PORT12, "port12" }, - { PORT13, "port13" }, - { PORT14, "port14" }, - { PORT15, "port15" }, - { PORT16, "port16" }, - { PORT17, "port17" }, - { PORT18, "port18" }, - { PORT19, "port19" }, - { PORT20, "port20" }, - { PORT21, "port21" }, - { PORT22, "port22" }, - { PORT23, "port23" }, - { SYS, "sys" }, - { ANA, "ana" }, - { REW, "rew" }, - { GCB, "gcb" }, - { QS, "qs" }, - { HSIO, "hsio" }, - }; + int i, ret; + struct resource res; + fdt32_t faddr; + phys_addr_t addr_base; + unsigned long addr_size; + ofnode eth_node, node, mdio_node; + size_t phy_addr; + struct mii_dev *bus; + struct ofnode_phandle_args phandle; + struct phy_device *phy; if (!priv) return -EINVAL; - for (i = 0; i < ARRAY_SIZE(reg); i++) { - priv->regs[reg[i].id] = dev_remap_addr_name(dev, reg[i].name); - if (!priv->regs[reg[i].id]) { + /* Get registers and map them to the private structure */ + for (i = 0; i < ARRAY_SIZE(regs_names); i++) { + priv->regs[i] = dev_remap_addr_name(dev, regs_names[i]); + if (!priv->regs[i]) { debug ("Error can't get regs base addresses for %s\n", - reg[i].name); + regs_names[i]); return -ENOMEM; } } @@ -666,7 +709,7 @@ static int luton_probe(struct udevice *dev) writel(0, priv->regs[GCB] + GCB_DEVCPU_RST_SOFT_CHIP_RST); /* Ports with ext phy don't need to reset clk */ - for (i = PORT0; i < MAX_INT_PORT; i++) { + for (i = 0; i < MAX_INT_PORT; i++) { if (i < PORT10) clrbits_le32(priv->regs[i] + DEV_GMII_PORT_MODE_CLK, DEV_GMII_PORT_MODE_CLK_PHY_RST); @@ -680,20 +723,76 @@ static int luton_probe(struct udevice *dev) GCB_MISC_STAT_PHY_READY, true, 500, false)) return -EACCES; - priv->bus[INTERNAL] = luton_mdiobus_init(dev, INTERNAL); - for (i = 0; i < MAX_INT_PORT; i++) { - phy_connect(priv->bus[INTERNAL], i, dev, - PHY_INTERFACE_MODE_NONE); + /* Initialize miim buses */ + memset(&miim, 0x0, sizeof(miim) * LUTON_MIIM_BUS_COUNT); + + /* iterate all the ports and find out on which bus they are */ + i = 0; + eth_node = dev_read_first_subnode(dev); + for (node = ofnode_first_subnode(eth_node); + ofnode_valid(node); + node = ofnode_next_subnode(node)) { + if (ofnode_read_resource(node, 0, &res)) + return -ENOMEM; + i = res.start; + + ret = ofnode_parse_phandle_with_args(node, "phy-handle", NULL, + 0, 0, &phandle); + if (ret) + continue; + + /* Get phy address on mdio bus */ + if (ofnode_read_resource(phandle.node, 0, &res)) + return -ENOMEM; + phy_addr = res.start; + + /* Get mdio node */ + mdio_node = ofnode_get_parent(phandle.node); + + if (ofnode_read_resource(mdio_node, 0, &res)) + return -ENOMEM; + faddr = cpu_to_fdt32(res.start); + + addr_base = ofnode_translate_address(mdio_node, &faddr); + addr_size = res.end - res.start; + + /* If the bus is new then create a new bus */ + if (!get_mdiobus(addr_base, addr_size)) + priv->bus[miim_count] = + serval_mdiobus_init(addr_base, addr_size); + + /* Connect mdio bus with the port */ + bus = get_mdiobus(addr_base, addr_size); + + /* Get serdes info */ + ret = ofnode_parse_phandle_with_args(node, "phys", NULL, + 3, 0, &phandle); + if (ret) + add_port_entry(priv, i, phy_addr, bus, 0xff, 0xff); + else + add_port_entry(priv, i, phy_addr, bus, phandle.args[1], + phandle.args[2]); + } + + for (i = 0; i < MAX_PORT; i++) { + if (!priv->ports[i].bus) + continue; + + phy = phy_connect(priv->ports[i].bus, + priv->ports[i].phy_addr, dev, + PHY_INTERFACE_MODE_NONE); + if (phy && i >= MAX_INT_PORT) + board_phy_config(phy); } /* * coma_mode is need on only one phy, because all the other phys * will be affected. */ - mscc_miim_write(priv->bus[INTERNAL], 0, 0, 31, 0x10); - mscc_miim_write(priv->bus[INTERNAL], 0, 0, 14, 0x800); - mscc_miim_write(priv->bus[INTERNAL], 0, 0, 31, 0); + mscc_miim_write(priv->ports[0].bus, 0, 0, 31, 0x10); + mscc_miim_write(priv->ports[0].bus, 0, 0, 14, 0x800); + mscc_miim_write(priv->ports[0].bus, 0, 0, 31, 0); return 0; } @@ -703,7 +802,7 @@ static int luton_remove(struct udevice *dev) struct luton_private *priv = dev_get_priv(dev); int i; - for (i = 0; i < NUM_PHY; i++) { + for (i = 0; i < LUTON_MIIM_BUS_COUNT; i++) { mdio_unregister(priv->bus[i]); mdio_free(priv->bus[i]); } diff --git a/drivers/net/mscc_eswitch/ocelot_switch.c b/drivers/net/mscc_eswitch/ocelot_switch.c index 815c2da264..5c7e6961be 100644 --- a/drivers/net/mscc_eswitch/ocelot_switch.c +++ b/drivers/net/mscc_eswitch/ocelot_switch.c @@ -15,7 +15,6 @@ #include <net.h> #include <wait_bit.h> -#include "mscc_miim.h" #include "mscc_xfer.h" #include "mscc_mac_table.h" @@ -26,6 +25,20 @@ #define PHY_STAT 0x4 #define PHY_STAT_SUPERVISOR_COMPLETE BIT(0) +#define GCB_MIIM_MII_STATUS 0x0 +#define GCB_MIIM_STAT_BUSY BIT(3) +#define GCB_MIIM_MII_CMD 0x8 +#define GCB_MIIM_MII_CMD_SCAN BIT(0) +#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) +#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) +#define GCB_MIIM_MII_CMD_SINGLE_SCAN BIT(3) +#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) +#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) +#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) +#define GCB_MIIM_MII_CMD_VLD BIT(31) +#define GCB_MIIM_DATA 0xC +#define GCB_MIIM_DATA_ERROR (0x3 << 16) + #define ANA_PORT_VLAN_CFG(x) (0x7000 + 0x100 * (x)) #define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20) #define ANA_PORT_VLAN_CFG_POP_CNT(x) ((x) << 18) @@ -33,6 +46,41 @@ #define ANA_PORT_PORT_CFG_RECV_ENA BIT(6) #define ANA_PGID(x) (0x8c00 + 4 * (x)) +#define HSIO_ANA_SERDES1G_DES_CFG 0x4c +#define HSIO_ANA_SERDES1G_DES_CFG_BW_HYST(x) ((x) << 1) +#define HSIO_ANA_SERDES1G_DES_CFG_BW_ANA(x) ((x) << 5) +#define HSIO_ANA_SERDES1G_DES_CFG_MBTR_CTRL(x) ((x) << 8) +#define HSIO_ANA_SERDES1G_DES_CFG_PHS_CTRL(x) ((x) << 13) +#define HSIO_ANA_SERDES1G_IB_CFG 0x50 +#define HSIO_ANA_SERDES1G_IB_CFG_RESISTOR_CTRL(x) (x) +#define HSIO_ANA_SERDES1G_IB_CFG_EQ_GAIN(x) ((x) << 6) +#define HSIO_ANA_SERDES1G_IB_CFG_ENA_OFFSET_COMP BIT(9) +#define HSIO_ANA_SERDES1G_IB_CFG_ENA_DETLEV BIT(11) +#define HSIO_ANA_SERDES1G_IB_CFG_ENA_CMV_TERM BIT(13) +#define HSIO_ANA_SERDES1G_IB_CFG_ACJTAG_HYST(x) ((x) << 24) +#define HSIO_ANA_SERDES1G_OB_CFG 0x54 +#define HSIO_ANA_SERDES1G_OB_CFG_RESISTOR_CTRL(x) (x) +#define HSIO_ANA_SERDES1G_OB_CFG_VCM_CTRL(x) ((x) << 4) +#define HSIO_ANA_SERDES1G_OB_CFG_CMM_BIAS_CTRL(x) ((x) << 10) +#define HSIO_ANA_SERDES1G_OB_CFG_AMP_CTRL(x) ((x) << 13) +#define HSIO_ANA_SERDES1G_OB_CFG_SLP(x) ((x) << 17) +#define HSIO_ANA_SERDES1G_SER_CFG 0x58 +#define HSIO_ANA_SERDES1G_COMMON_CFG 0x5c +#define HSIO_ANA_SERDES1G_COMMON_CFG_IF_MODE BIT(0) +#define HSIO_ANA_SERDES1G_COMMON_CFG_ENA_LANE BIT(18) +#define HSIO_ANA_SERDES1G_COMMON_CFG_SYS_RST BIT(31) +#define HSIO_ANA_SERDES1G_PLL_CFG 0x60 +#define HSIO_ANA_SERDES1G_PLL_CFG_FSM_ENA BIT(7) +#define HSIO_ANA_SERDES1G_PLL_CFG_FSM_CTRL_DATA(x) ((x) << 8) +#define HSIO_ANA_SERDES1G_PLL_CFG_ENA_RC_DIV2 BIT(21) +#define HSIO_DIG_SERDES1G_DFT_CFG0 0x68 +#define HSIO_DIG_SERDES1G_MISC_CFG 0x7c +#define HSIO_DIG_SERDES1G_MISC_CFG_LANE_RST BIT(0) +#define HSIO_MCB_SERDES1G_CFG 0x88 +#define HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT BIT(31) +#define HSIO_MCB_SERDES1G_CFG_ADDR(x) (x) +#define HSIO_HW_CFGSTAT_HW_CFG 0x10c + #define SYS_FRM_AGING 0x574 #define SYS_FRM_AGING_ENA BIT(20) @@ -83,49 +131,58 @@ #define QS_INJ_GRP_CFG_BYTE_SWAP BIT(0) #define IFH_INJ_BYPASS BIT(31) -#define IFH_TAG_TYPE_C 0 -#define MAC_VID 1 +#define IFH_TAG_TYPE_C 0 +#define MAC_VID 1 #define CPU_PORT 11 -#define INTERNAL_PORT_MSK 0xF +#define INTERNAL_PORT_MSK 0x2FF #define IFH_LEN 4 #define ETH_ALEN 6 -#define PGID_BROADCAST 13 -#define PGID_UNICAST 14 -#define PGID_SRC 80 +#define PGID_BROADCAST 13 +#define PGID_UNICAST 14 +#define PGID_SRC 80 -enum ocelot_target { - ANA, - QS, - QSYS, +static const char * const regs_names[] = { + "port0", "port1", "port2", "port3", "port4", "port5", "port6", "port7", + "port8", "port9", "port10", "sys", "rew", "qs", "hsio", "qsys", "ana", +}; + +#define REGS_NAMES_COUNT ARRAY_SIZE(regs_names) + 1 +#define MAX_PORT 11 + +enum ocelot_ctrl_regs { + SYS = MAX_PORT, REW, - SYS, + QS, HSIO, - PORT0, - PORT1, - PORT2, - PORT3, - TARGET_MAX, + QSYS, + ANA, }; -#define MAX_PORT (PORT3 - PORT0) +#define OCELOT_MIIM_BUS_COUNT 2 -enum ocelot_mdio_target { - MIIM, - PHY, - TARGET_MDIO_MAX, +struct ocelot_phy_port_t { + size_t phy_addr; + struct mii_dev *bus; + u8 serdes_index; + u8 phy_mode; }; -enum ocelot_phy_id { - INTERNAL, - EXTERNAL, - NUM_PHY, +struct ocelot_private { + void __iomem *regs[REGS_NAMES_COUNT]; + struct mii_dev *bus[OCELOT_MIIM_BUS_COUNT]; + struct ocelot_phy_port_t ports[MAX_PORT]; }; -struct ocelot_private { - void __iomem *regs[TARGET_MAX]; - struct mii_dev *bus[NUM_PHY]; +struct mscc_miim_dev { + void __iomem *regs; + phys_addr_t miim_base; + unsigned long miim_size; + struct mii_dev *bus; }; +static struct mscc_miim_dev miim[OCELOT_MIIM_BUS_COUNT]; +static int miim_count = -1; + static const unsigned long ocelot_regs_qs[] = { [MSCC_QS_XTR_RD] = 0x8, [MSCC_QS_XTR_FLUSH] = 0x18, @@ -140,65 +197,95 @@ static const unsigned long ocelot_regs_ana_table[] = { [MSCC_ANA_TABLES_MACACCESS] = 0x8b3c, }; -static struct mscc_miim_dev miim[NUM_PHY]; - static void mscc_phy_reset(void) { - writel(0, miim[INTERNAL].phy_regs + PHY_CFG); + writel(0, BASE_DEVCPU_GCB + PERF_PHY_CFG + PHY_CFG); writel(PHY_CFG_RST | PHY_CFG_COMMON_RST - | PHY_CFG_ENA, miim[INTERNAL].phy_regs + PHY_CFG); - if (wait_for_bit_le32(miim[INTERNAL].phy_regs + PHY_STAT, - PHY_STAT_SUPERVISOR_COMPLETE, + | PHY_CFG_ENA, BASE_DEVCPU_GCB + PERF_PHY_CFG + PHY_CFG); + if (wait_for_bit_le32((const void *)(BASE_DEVCPU_GCB + PERF_PHY_CFG) + + PHY_STAT, PHY_STAT_SUPERVISOR_COMPLETE, true, 2000, false)) { pr_err("Timeout in phy reset\n"); } } -/* For now only setup the internal mdio bus */ -static struct mii_dev *ocelot_mdiobus_init(struct udevice *dev) +static int mscc_miim_wait_ready(struct mscc_miim_dev *miim) +{ + return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS, + GCB_MIIM_STAT_BUSY, false, 250, false); +} + +static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; + u32 val; + int ret; + + ret = mscc_miim_wait_ready(miim); + if (ret) + goto out; + + writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | + GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, + miim->regs + GCB_MIIM_MII_CMD); + + ret = mscc_miim_wait_ready(miim); + if (ret) + goto out; + + val = readl(miim->regs + GCB_MIIM_DATA); + if (val & GCB_MIIM_DATA_ERROR) { + ret = -EIO; + goto out; + } + + ret = val & 0xFFFF; + out: + return ret; +} + +static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; + int ret; + + ret = mscc_miim_wait_ready(miim); + if (ret < 0) + goto out; + + writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | + GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | + GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); + out: + return ret; +} + +static struct mii_dev *ocelot_mdiobus_init(phys_addr_t miim_base, + unsigned long miim_size) { - unsigned long phy_size[TARGET_MAX]; - phys_addr_t phy_base[TARGET_MAX]; - struct ofnode_phandle_args phandle; - ofnode eth_node, node, mdio_node; - struct resource res; struct mii_dev *bus; - fdt32_t faddr; - int i; bus = mdio_alloc(); if (!bus) return NULL; - /* gathered only the first mdio bus */ - eth_node = dev_read_first_subnode(dev); - node = ofnode_first_subnode(eth_node); - ofnode_parse_phandle_with_args(node, "phy-handle", NULL, 0, 0, - &phandle); - mdio_node = ofnode_get_parent(phandle.node); - - for (i = 0; i < TARGET_MDIO_MAX; i++) { - if (ofnode_read_resource(mdio_node, i, &res)) { - pr_err("%s: get OF resource failed\n", __func__); - return NULL; - } - faddr = cpu_to_fdt32(res.start); - phy_base[i] = ofnode_translate_address(mdio_node, &faddr); - phy_size[i] = res.end - res.start; - } + ++miim_count; + sprintf(bus->name, "miim-bus%d", miim_count); - strcpy(bus->name, "miim-internal"); - miim[INTERNAL].phy_regs = ioremap(phy_base[PHY], phy_size[PHY]); - miim[INTERNAL].regs = ioremap(phy_base[MIIM], phy_size[MIIM]); - bus->priv = &miim[INTERNAL]; + miim[miim_count].regs = ioremap(miim_base, miim_size); + miim[miim_count].miim_base = miim_base; + miim[miim_count].miim_size = miim_size; + bus->priv = &miim[miim_count]; bus->read = mscc_miim_read; bus->write = mscc_miim_write; if (mdio_register(bus)) return NULL; - else - return bus; + + miim[miim_count].bus = bus; + return bus; } __weak void mscc_switch_reset(void) @@ -291,13 +378,87 @@ static void ocelot_port_init(struct ocelot_private *priv, int port) /* Make VLAN aware for CPU traffic */ writel(ANA_PORT_VLAN_CFG_AWARE_ENA | ANA_PORT_VLAN_CFG_POP_CNT(1) | - MAC_VID, priv->regs[ANA] + ANA_PORT_VLAN_CFG(port - PORT0)); + MAC_VID, priv->regs[ANA] + ANA_PORT_VLAN_CFG(port)); /* Enable the port in the core */ - setbits_le32(priv->regs[QSYS] + QSYS_SWITCH_PORT_MODE(port - PORT0), + setbits_le32(priv->regs[QSYS] + QSYS_SWITCH_PORT_MODE(port), QSYS_SWITCH_PORT_MODE_PORT_ENA); } +static void serdes1g_write(void __iomem *base, u32 addr) +{ + u32 data; + + writel(HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT | + HSIO_MCB_SERDES1G_CFG_ADDR(addr), + base + HSIO_MCB_SERDES1G_CFG); + + do { + data = readl(base + HSIO_MCB_SERDES1G_CFG); + } while (data & HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT); +} + +static void serdes1g_setup(void __iomem *base, uint32_t addr, + phy_interface_t interface) +{ + writel(0x34, base + HSIO_HW_CFGSTAT_HW_CFG); + + writel(0x0, base + HSIO_ANA_SERDES1G_SER_CFG); + writel(0x0, base + HSIO_DIG_SERDES1G_DFT_CFG0); + writel(HSIO_ANA_SERDES1G_IB_CFG_RESISTOR_CTRL(11) | + HSIO_ANA_SERDES1G_IB_CFG_EQ_GAIN(0) | + HSIO_ANA_SERDES1G_IB_CFG_ENA_OFFSET_COMP | + HSIO_ANA_SERDES1G_IB_CFG_ENA_CMV_TERM | + HSIO_ANA_SERDES1G_IB_CFG_ACJTAG_HYST(1), + base + HSIO_ANA_SERDES1G_IB_CFG); + writel(HSIO_ANA_SERDES1G_DES_CFG_BW_HYST(7) | + HSIO_ANA_SERDES1G_DES_CFG_BW_ANA(6) | + HSIO_ANA_SERDES1G_DES_CFG_MBTR_CTRL(2) | + HSIO_ANA_SERDES1G_DES_CFG_PHS_CTRL(6), + base + HSIO_ANA_SERDES1G_DES_CFG); + writel(HSIO_ANA_SERDES1G_OB_CFG_RESISTOR_CTRL(1) | + HSIO_ANA_SERDES1G_OB_CFG_VCM_CTRL(4) | + HSIO_ANA_SERDES1G_OB_CFG_CMM_BIAS_CTRL(2) | + HSIO_ANA_SERDES1G_OB_CFG_AMP_CTRL(12) | + HSIO_ANA_SERDES1G_OB_CFG_SLP(3), + base + HSIO_ANA_SERDES1G_OB_CFG); + writel(HSIO_ANA_SERDES1G_COMMON_CFG_IF_MODE | + HSIO_ANA_SERDES1G_COMMON_CFG_ENA_LANE, + base + HSIO_ANA_SERDES1G_COMMON_CFG); + writel(HSIO_ANA_SERDES1G_PLL_CFG_FSM_ENA | + HSIO_ANA_SERDES1G_PLL_CFG_FSM_CTRL_DATA(200) | + HSIO_ANA_SERDES1G_PLL_CFG_ENA_RC_DIV2, + base + HSIO_ANA_SERDES1G_PLL_CFG); + writel(HSIO_DIG_SERDES1G_MISC_CFG_LANE_RST, + base + HSIO_DIG_SERDES1G_MISC_CFG); + + serdes1g_write(base, addr); + + writel(HSIO_ANA_SERDES1G_COMMON_CFG_IF_MODE | + HSIO_ANA_SERDES1G_COMMON_CFG_ENA_LANE | + HSIO_ANA_SERDES1G_COMMON_CFG_SYS_RST, + base + HSIO_ANA_SERDES1G_COMMON_CFG); + serdes1g_write(base, addr); + + writel(0x0, base + HSIO_DIG_SERDES1G_MISC_CFG); + serdes1g_write(base, addr); +} + +static void serdes_setup(struct ocelot_private *priv) +{ + size_t mask; + int i = 0; + + for (i = 0; i < MAX_PORT; ++i) { + if (!priv->ports[i].bus || priv->ports[i].serdes_index == 0xff) + continue; + + mask = BIT(priv->ports[i].serdes_index); + serdes1g_setup(priv->regs[HSIO], mask, + priv->ports[i].phy_mode); + } +} + static int ocelot_switch_init(struct ocelot_private *priv) { /* Reset switch & memories */ @@ -315,6 +476,7 @@ static int ocelot_switch_init(struct ocelot_private *priv) setbits_le32(priv->regs[SYS] + SYS_SYSTEM_RST_CFG, SYS_SYSTEM_RST_CORE_ENA); + serdes_setup(priv); return 0; } @@ -331,7 +493,7 @@ static int ocelot_initialize(struct ocelot_private *priv) * Put fron ports in "port isolation modes" - i.e. they cant send * to other ports - via the PGID sorce masks. */ - for (i = 0; i <= MAX_PORT; i++) + for (i = 0; i < MAX_PORT; i++) writel(0, priv->regs[ANA] + ANA_PGID(PGID_SRC + i)); /* Flush queues */ @@ -341,7 +503,7 @@ static int ocelot_initialize(struct ocelot_private *priv) writel(SYS_FRM_AGING_ENA | (20000000 / 65), priv->regs[SYS] + SYS_FRM_AGING); - for (i = PORT0; i <= PORT3; i++) + for (i = 0; i < MAX_PORT; i++) ocelot_port_init(priv, i); ocelot_cpu_capture_setup(priv); @@ -433,43 +595,119 @@ static int ocelot_recv(struct udevice *dev, int flags, uchar **packetp) return byte_cnt; } +static struct mii_dev *get_mdiobus(phys_addr_t base, unsigned long size) +{ + int i = 0; + + for (i = 0; i < OCELOT_MIIM_BUS_COUNT; ++i) + if (miim[i].miim_base == base && miim[i].miim_size == size) + return miim[i].bus; + + return NULL; +} + +static void add_port_entry(struct ocelot_private *priv, size_t index, + size_t phy_addr, struct mii_dev *bus, + u8 serdes_index, u8 phy_mode) +{ + priv->ports[index].phy_addr = phy_addr; + priv->ports[index].bus = bus; + priv->ports[index].serdes_index = serdes_index; + priv->ports[index].phy_mode = phy_mode; +} + +static int external_bus(struct ocelot_private *priv, size_t port_index) +{ + return priv->ports[port_index].serdes_index != 0xff; +} + static int ocelot_probe(struct udevice *dev) { struct ocelot_private *priv = dev_get_priv(dev); - int ret, i; + int i, ret; + struct resource res; + fdt32_t faddr; + phys_addr_t addr_base; + unsigned long addr_size; + ofnode eth_node, node, mdio_node; + size_t phy_addr; + struct mii_dev *bus; + struct ofnode_phandle_args phandle; + struct phy_device *phy; + + if (!priv) + return -EINVAL; - struct { - enum ocelot_target id; - char *name; - } reg[] = { - { SYS, "sys" }, - { REW, "rew" }, - { QSYS, "qsys" }, - { ANA, "ana" }, - { QS, "qs" }, - { HSIO, "hsio" }, - { PORT0, "port0" }, - { PORT1, "port1" }, - { PORT2, "port2" }, - { PORT3, "port3" }, - }; - - for (i = 0; i < ARRAY_SIZE(reg); i++) { - priv->regs[reg[i].id] = dev_remap_addr_name(dev, reg[i].name); - if (!priv->regs[reg[i].id]) { - pr_err - ("Error %d: can't get regs base addresses for %s\n", - ret, reg[i].name); + for (i = 0; i < ARRAY_SIZE(regs_names); i++) { + priv->regs[i] = dev_remap_addr_name(dev, regs_names[i]); + if (!priv->regs[i]) { + debug + ("Error can't get regs base addresses for %s\n", + regs_names[i]); return -ENOMEM; } } - priv->bus[INTERNAL] = ocelot_mdiobus_init(dev); + /* Initialize miim buses */ + memset(&miim, 0x0, sizeof(struct mscc_miim_dev) * + OCELOT_MIIM_BUS_COUNT); + + /* iterate all the ports and find out on which bus they are */ + i = 0; + eth_node = dev_read_first_subnode(dev); + for (node = ofnode_first_subnode(eth_node); ofnode_valid(node); + node = ofnode_next_subnode(node)) { + if (ofnode_read_resource(node, 0, &res)) + return -ENOMEM; + i = res.start; + + ofnode_parse_phandle_with_args(node, "phy-handle", NULL, 0, 0, + &phandle); + + /* Get phy address on mdio bus */ + if (ofnode_read_resource(phandle.node, 0, &res)) + return -ENOMEM; + phy_addr = res.start; + + /* Get mdio node */ + mdio_node = ofnode_get_parent(phandle.node); + + if (ofnode_read_resource(mdio_node, 0, &res)) + return -ENOMEM; + faddr = cpu_to_fdt32(res.start); + + addr_base = ofnode_translate_address(mdio_node, &faddr); + addr_size = res.end - res.start; + + /* If the bus is new then create a new bus */ + if (!get_mdiobus(addr_base, addr_size)) + priv->bus[miim_count] = + ocelot_mdiobus_init(addr_base, addr_size); + + /* Connect mdio bus with the port */ + bus = get_mdiobus(addr_base, addr_size); + + /* Get serdes info */ + ret = ofnode_parse_phandle_with_args(node, "phys", NULL, + 3, 0, &phandle); + if (ret) + add_port_entry(priv, i, phy_addr, bus, 0xff, 0xff); + else + add_port_entry(priv, i, phy_addr, bus, phandle.args[1], + phandle.args[2]); + } + mscc_phy_reset(); - for (i = 0; i < 4; i++) { - phy_connect(priv->bus[INTERNAL], i, dev, - PHY_INTERFACE_MODE_NONE); + for (i = 0; i < MAX_PORT; i++) { + if (!priv->ports[i].bus) + continue; + + phy = phy_connect(priv->ports[i].bus, + priv->ports[i].phy_addr, dev, + PHY_INTERFACE_MODE_NONE); + if (phy && external_bus(priv, i)) + board_phy_config(phy); } return 0; @@ -480,7 +718,7 @@ static int ocelot_remove(struct udevice *dev) struct ocelot_private *priv = dev_get_priv(dev); int i; - for (i = 0; i < NUM_PHY; i++) { + for (i = 0; i < OCELOT_MIIM_BUS_COUNT; i++) { mdio_unregister(priv->bus[i]); mdio_free(priv->bus[i]); } diff --git a/drivers/net/mscc_eswitch/serval_switch.c b/drivers/net/mscc_eswitch/serval_switch.c new file mode 100644 index 0000000000..2559f5d0cd --- /dev/null +++ b/drivers/net/mscc_eswitch/serval_switch.c @@ -0,0 +1,703 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2019 Microsemi Corporation + */ + +#include <common.h> +#include <config.h> +#include <dm.h> +#include <dm/of_access.h> +#include <dm/of_addr.h> +#include <fdt_support.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <miiphy.h> +#include <net.h> +#include <wait_bit.h> + +#include "mscc_xfer.h" +#include "mscc_mac_table.h" + +#define GCB_MIIM_MII_STATUS 0x0 +#define GCB_MIIM_STAT_BUSY BIT(3) +#define GCB_MIIM_MII_CMD 0x8 +#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) +#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) +#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) +#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) +#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) +#define GCB_MIIM_MII_CMD_VLD BIT(31) +#define GCB_MIIM_DATA 0xC +#define GCB_MIIM_DATA_ERROR (0x2 << 16) + +#define ANA_PORT_VLAN_CFG(x) (0xc000 + 0x100 * (x)) +#define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20) +#define ANA_PORT_VLAN_CFG_POP_CNT(x) ((x) << 18) +#define ANA_PORT_PORT_CFG(x) (0xc070 + 0x100 * (x)) +#define ANA_PORT_PORT_CFG_RECV_ENA BIT(6) +#define ANA_PGID(x) (0x9c00 + 4 * (x)) + +#define HSIO_ANA_SERDES1G_DES_CFG 0x3c +#define HSIO_ANA_SERDES1G_DES_CFG_BW_HYST(x) ((x) << 1) +#define HSIO_ANA_SERDES1G_DES_CFG_BW_ANA(x) ((x) << 5) +#define HSIO_ANA_SERDES1G_DES_CFG_MBTR_CTRL(x) ((x) << 8) +#define HSIO_ANA_SERDES1G_DES_CFG_PHS_CTRL(x) ((x) << 13) +#define HSIO_ANA_SERDES1G_IB_CFG 0x40 +#define HSIO_ANA_SERDES1G_IB_CFG_RESISTOR_CTRL(x) (x) +#define HSIO_ANA_SERDES1G_IB_CFG_EQ_GAIN(x) ((x) << 6) +#define HSIO_ANA_SERDES1G_IB_CFG_ENA_OFFSET_COMP BIT(9) +#define HSIO_ANA_SERDES1G_IB_CFG_ENA_DETLEV BIT(11) +#define HSIO_ANA_SERDES1G_IB_CFG_ENA_CMV_TERM BIT(13) +#define HSIO_ANA_SERDES1G_IB_CFG_DET_LEV(x) ((x) << 19) +#define HSIO_ANA_SERDES1G_IB_CFG_ACJTAG_HYST(x) ((x) << 24) +#define HSIO_ANA_SERDES1G_OB_CFG 0x44 +#define HSIO_ANA_SERDES1G_OB_CFG_RESISTOR_CTRL(x) (x) +#define HSIO_ANA_SERDES1G_OB_CFG_VCM_CTRL(x) ((x) << 4) +#define HSIO_ANA_SERDES1G_OB_CFG_CMM_BIAS_CTRL(x) ((x) << 10) +#define HSIO_ANA_SERDES1G_OB_CFG_AMP_CTRL(x) ((x) << 13) +#define HSIO_ANA_SERDES1G_OB_CFG_SLP(x) ((x) << 17) +#define HSIO_ANA_SERDES1G_SER_CFG 0x48 +#define HSIO_ANA_SERDES1G_COMMON_CFG 0x4c +#define HSIO_ANA_SERDES1G_COMMON_CFG_IF_MODE BIT(0) +#define HSIO_ANA_SERDES1G_COMMON_CFG_ENA_LANE BIT(18) +#define HSIO_ANA_SERDES1G_COMMON_CFG_SYS_RST BIT(31) +#define HSIO_ANA_SERDES1G_PLL_CFG 0x50 +#define HSIO_ANA_SERDES1G_PLL_CFG_FSM_ENA BIT(7) +#define HSIO_ANA_SERDES1G_PLL_CFG_FSM_CTRL_DATA(x) ((x) << 8) +#define HSIO_ANA_SERDES1G_PLL_CFG_ENA_RC_DIV2 BIT(21) +#define HSIO_DIG_SERDES1G_DFT_CFG0 0x58 +#define HSIO_DIG_SERDES1G_MISC_CFG 0x6c +#define HSIO_DIG_SERDES1G_MISC_CFG_LANE_RST BIT(0) +#define HSIO_MCB_SERDES1G_CFG 0x74 +#define HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT BIT(31) +#define HSIO_MCB_SERDES1G_CFG_ADDR(x) (x) + +#define SYS_FRM_AGING 0x584 +#define SYS_FRM_AGING_ENA BIT(20) +#define SYS_SYSTEM_RST_CFG 0x518 +#define SYS_SYSTEM_RST_MEM_INIT BIT(5) +#define SYS_SYSTEM_RST_MEM_ENA BIT(6) +#define SYS_SYSTEM_RST_CORE_ENA BIT(7) +#define SYS_PORT_MODE(x) (0x524 + 0x4 * (x)) +#define SYS_PORT_MODE_INCL_INJ_HDR(x) ((x) << 4) +#define SYS_PORT_MODE_INCL_XTR_HDR(x) ((x) << 2) +#define SYS_PAUSE_CFG(x) (0x65c + 0x4 * (x)) +#define SYS_PAUSE_CFG_PAUSE_ENA BIT(0) + +#define QSYS_SWITCH_PORT_MODE(x) (0x15a34 + 0x4 * (x)) +#define QSYS_SWITCH_PORT_MODE_PORT_ENA BIT(13) +#define QSYS_EGR_NO_SHARING 0x15a9c +#define QSYS_QMAP 0x15adc + +/* Port registers */ +#define DEV_CLOCK_CFG 0x0 +#define DEV_CLOCK_CFG_LINK_SPEED_1000 1 +#define DEV_MAC_ENA_CFG 0x10 +#define DEV_MAC_ENA_CFG_RX_ENA BIT(4) +#define DEV_MAC_ENA_CFG_TX_ENA BIT(0) +#define DEV_MAC_IFG_CFG 0x24 +#define DEV_MAC_IFG_CFG_TX_IFG(x) ((x) << 8) +#define DEV_MAC_IFG_CFG_RX_IFG2(x) ((x) << 4) +#define DEV_MAC_IFG_CFG_RX_IFG1(x) (x) +#define PCS1G_CFG 0x3c +#define PCS1G_MODE_CFG_SGMII_MODE_ENA BIT(0) +#define PCS1G_MODE_CFG 0x40 +#define PCS1G_SD_CFG 0x44 +#define PCS1G_ANEG_CFG 0x48 +#define PCS1G_ANEG_CFG_ADV_ABILITY(x) ((x) << 16) + +#define QS_XTR_GRP_CFG(x) (4 * (x)) +#define QS_XTR_GRP_CFG_MODE(x) ((x) << 2) +#define QS_XTR_GRP_CFG_BYTE_SWAP BIT(0) +#define QS_INJ_GRP_CFG(x) (0x24 + (x) * 4) +#define QS_INJ_GRP_CFG_MODE(x) ((x) << 2) +#define QS_INJ_GRP_CFG_BYTE_SWAP BIT(0) + +#define IFH_INJ_BYPASS BIT(31) +#define IFH_TAG_TYPE_C 0 +#define MAC_VID 1 +#define CPU_PORT 11 +#define INTERNAL_PORT_MSK 0xFF +#define IFH_LEN 4 +#define ETH_ALEN 6 +#define PGID_BROADCAST 13 +#define PGID_UNICAST 14 + +static const char *const regs_names[] = { + "port0", "port1", "port2", "port3", "port4", "port5", "port6", + "port7", "port8", "port9", "port10", + "ana", "qs", "qsys", "rew", "sys", "hsio", +}; + +#define REGS_NAMES_COUNT ARRAY_SIZE(regs_names) + 1 +#define MAX_PORT 11 + +enum serval_ctrl_regs { + ANA = MAX_PORT, + QS, + QSYS, + REW, + SYS, + HSIO, +}; + +#define SERVAL_MIIM_BUS_COUNT 2 + +struct serval_phy_port_t { + size_t phy_addr; + struct mii_dev *bus; + u8 serdes_index; + u8 phy_mode; +}; + +struct serval_private { + void __iomem *regs[REGS_NAMES_COUNT]; + struct mii_dev *bus[SERVAL_MIIM_BUS_COUNT]; + struct serval_phy_port_t ports[MAX_PORT]; +}; + +struct mscc_miim_dev { + void __iomem *regs; + phys_addr_t miim_base; + unsigned long miim_size; + struct mii_dev *bus; +}; + +static const unsigned long serval_regs_qs[] = { + [MSCC_QS_XTR_RD] = 0x8, + [MSCC_QS_XTR_FLUSH] = 0x18, + [MSCC_QS_XTR_DATA_PRESENT] = 0x1c, + [MSCC_QS_INJ_WR] = 0x2c, + [MSCC_QS_INJ_CTRL] = 0x34, +}; + +static const unsigned long serval_regs_ana_table[] = { + [MSCC_ANA_TABLES_MACHDATA] = 0x9b34, + [MSCC_ANA_TABLES_MACLDATA] = 0x9b38, + [MSCC_ANA_TABLES_MACACCESS] = 0x9b3c, +}; + +static struct mscc_miim_dev miim[SERVAL_MIIM_BUS_COUNT]; +static int miim_count = -1; + +static int mscc_miim_wait_ready(struct mscc_miim_dev *miim) +{ + return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS, + GCB_MIIM_STAT_BUSY, false, 250, false); +} + +static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; + u32 val; + int ret; + + ret = mscc_miim_wait_ready(miim); + if (ret) + goto out; + + writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | + GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, + miim->regs + GCB_MIIM_MII_CMD); + + ret = mscc_miim_wait_ready(miim); + if (ret) + goto out; + + val = readl(miim->regs + GCB_MIIM_DATA); + if (val & GCB_MIIM_DATA_ERROR) { + ret = -EIO; + goto out; + } + + ret = val & 0xFFFF; + out: + return ret; +} + +static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; + int ret; + + ret = mscc_miim_wait_ready(miim); + if (ret < 0) + goto out; + + writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | + GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | + GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); + out: + return ret; +} + +static struct mii_dev *serval_mdiobus_init(phys_addr_t miim_base, + unsigned long miim_size) +{ + struct mii_dev *bus; + + bus = mdio_alloc(); + if (!bus) + return NULL; + + ++miim_count; + sprintf(bus->name, "miim-bus%d", miim_count); + + miim[miim_count].regs = ioremap(miim_base, miim_size); + miim[miim_count].miim_base = miim_base; + miim[miim_count].miim_size = miim_size; + bus->priv = &miim[miim_count]; + bus->read = mscc_miim_read; + bus->write = mscc_miim_write; + + if (mdio_register(bus)) + return NULL; + + miim[miim_count].bus = bus; + return bus; +} + +static void serval_cpu_capture_setup(struct serval_private *priv) +{ + int i; + + /* map the 8 CPU extraction queues to CPU port 11 */ + writel(0, priv->regs[QSYS] + QSYS_QMAP); + + for (i = 0; i <= 1; i++) { + /* + * Do byte-swap and expect status after last data word + * Extraction: Mode: manual extraction) | Byte_swap + */ + writel(QS_XTR_GRP_CFG_MODE(1) | QS_XTR_GRP_CFG_BYTE_SWAP, + priv->regs[QS] + QS_XTR_GRP_CFG(i)); + /* + * Injection: Mode: manual extraction | Byte_swap + */ + writel(QS_INJ_GRP_CFG_MODE(1) | QS_INJ_GRP_CFG_BYTE_SWAP, + priv->regs[QS] + QS_INJ_GRP_CFG(i)); + } + + for (i = 0; i <= 1; i++) + /* Enable IFH insertion/parsing on CPU ports */ + writel(SYS_PORT_MODE_INCL_INJ_HDR(1) | + SYS_PORT_MODE_INCL_XTR_HDR(1), + priv->regs[SYS] + SYS_PORT_MODE(CPU_PORT + i)); + /* + * Setup the CPU port as VLAN aware to support switching frames + * based on tags + */ + writel(ANA_PORT_VLAN_CFG_AWARE_ENA | ANA_PORT_VLAN_CFG_POP_CNT(1) | + MAC_VID, priv->regs[ANA] + ANA_PORT_VLAN_CFG(CPU_PORT)); + + /* Disable learning (only RECV_ENA must be set) */ + writel(ANA_PORT_PORT_CFG_RECV_ENA, + priv->regs[ANA] + ANA_PORT_PORT_CFG(CPU_PORT)); + + /* Enable switching to/from cpu port */ + setbits_le32(priv->regs[QSYS] + QSYS_SWITCH_PORT_MODE(CPU_PORT), + QSYS_SWITCH_PORT_MODE_PORT_ENA); + + /* No pause on CPU port - not needed (off by default) */ + clrbits_le32(priv->regs[SYS] + SYS_PAUSE_CFG(CPU_PORT), + SYS_PAUSE_CFG_PAUSE_ENA); + + setbits_le32(priv->regs[QSYS] + QSYS_EGR_NO_SHARING, BIT(CPU_PORT)); +} + +static void serval_port_init(struct serval_private *priv, int port) +{ + void __iomem *regs = priv->regs[port]; + + /* Enable PCS */ + writel(PCS1G_MODE_CFG_SGMII_MODE_ENA, regs + PCS1G_CFG); + + /* Disable Signal Detect */ + writel(0, regs + PCS1G_SD_CFG); + + /* Enable MAC RX and TX */ + writel(DEV_MAC_ENA_CFG_RX_ENA | DEV_MAC_ENA_CFG_TX_ENA, + regs + DEV_MAC_ENA_CFG); + + /* Clear sgmii_mode_ena */ + writel(0, regs + PCS1G_MODE_CFG); + + /* + * Clear sw_resolve_ena(bit 0) and set adv_ability to + * something meaningful just in case + */ + writel(PCS1G_ANEG_CFG_ADV_ABILITY(0x20), regs + PCS1G_ANEG_CFG); + + /* Set MAC IFG Gaps */ + writel(DEV_MAC_IFG_CFG_TX_IFG(5) | DEV_MAC_IFG_CFG_RX_IFG1(5) | + DEV_MAC_IFG_CFG_RX_IFG2(1), regs + DEV_MAC_IFG_CFG); + + /* Set link speed and release all resets */ + writel(DEV_CLOCK_CFG_LINK_SPEED_1000, regs + DEV_CLOCK_CFG); + + /* Make VLAN aware for CPU traffic */ + writel(ANA_PORT_VLAN_CFG_AWARE_ENA | ANA_PORT_VLAN_CFG_POP_CNT(1) | + MAC_VID, priv->regs[ANA] + ANA_PORT_VLAN_CFG(port)); + + /* Enable the port in the core */ + setbits_le32(priv->regs[QSYS] + QSYS_SWITCH_PORT_MODE(port), + QSYS_SWITCH_PORT_MODE_PORT_ENA); +} + +static void serdes_write(void __iomem *base, u32 addr) +{ + u32 data; + + writel(HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT | + HSIO_MCB_SERDES1G_CFG_ADDR(addr), + base + HSIO_MCB_SERDES1G_CFG); + + do { + data = readl(base + HSIO_MCB_SERDES1G_CFG); + } while (data & HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT); + + mdelay(100); +} + +static void serdes1g_setup(void __iomem *base, uint32_t addr, + phy_interface_t interface) +{ + writel(0x0, base + HSIO_ANA_SERDES1G_SER_CFG); + writel(0x0, base + HSIO_DIG_SERDES1G_DFT_CFG0); + writel(HSIO_ANA_SERDES1G_IB_CFG_RESISTOR_CTRL(11) | + HSIO_ANA_SERDES1G_IB_CFG_EQ_GAIN(0) | + HSIO_ANA_SERDES1G_IB_CFG_ENA_OFFSET_COMP | + HSIO_ANA_SERDES1G_IB_CFG_ENA_CMV_TERM | + HSIO_ANA_SERDES1G_IB_CFG_ACJTAG_HYST(1), + base + HSIO_ANA_SERDES1G_IB_CFG); + writel(HSIO_ANA_SERDES1G_DES_CFG_BW_HYST(7) | + HSIO_ANA_SERDES1G_DES_CFG_BW_ANA(6) | + HSIO_ANA_SERDES1G_DES_CFG_MBTR_CTRL(2) | + HSIO_ANA_SERDES1G_DES_CFG_PHS_CTRL(6), + base + HSIO_ANA_SERDES1G_DES_CFG); + writel(HSIO_ANA_SERDES1G_OB_CFG_RESISTOR_CTRL(1) | + HSIO_ANA_SERDES1G_OB_CFG_VCM_CTRL(4) | + HSIO_ANA_SERDES1G_OB_CFG_CMM_BIAS_CTRL(2) | + HSIO_ANA_SERDES1G_OB_CFG_AMP_CTRL(12) | + HSIO_ANA_SERDES1G_OB_CFG_SLP(3), + base + HSIO_ANA_SERDES1G_OB_CFG); + writel(HSIO_ANA_SERDES1G_COMMON_CFG_IF_MODE | + HSIO_ANA_SERDES1G_COMMON_CFG_ENA_LANE, + base + HSIO_ANA_SERDES1G_COMMON_CFG); + writel(HSIO_ANA_SERDES1G_PLL_CFG_FSM_ENA | + HSIO_ANA_SERDES1G_PLL_CFG_FSM_CTRL_DATA(200) | + HSIO_ANA_SERDES1G_PLL_CFG_ENA_RC_DIV2, + base + HSIO_ANA_SERDES1G_PLL_CFG); + writel(HSIO_DIG_SERDES1G_MISC_CFG_LANE_RST, + base + HSIO_DIG_SERDES1G_MISC_CFG); + serdes_write(base, addr); + + writel(HSIO_ANA_SERDES1G_COMMON_CFG_IF_MODE | + HSIO_ANA_SERDES1G_COMMON_CFG_ENA_LANE | + HSIO_ANA_SERDES1G_COMMON_CFG_SYS_RST, + base + HSIO_ANA_SERDES1G_COMMON_CFG); + serdes_write(base, addr); + + writel(0x0, base + HSIO_DIG_SERDES1G_MISC_CFG); + serdes_write(base, addr); +} + +static void serdes_setup(struct serval_private *priv) +{ + size_t mask; + int i = 0; + + for (i = 0; i < MAX_PORT; ++i) { + if (!priv->ports[i].bus) + continue; + + mask = BIT(priv->ports[i].serdes_index); + serdes1g_setup(priv->regs[HSIO], mask, + priv->ports[i].phy_mode); + } +} + +static int serval_switch_init(struct serval_private *priv) +{ + /* Reset switch & memories */ + writel(SYS_SYSTEM_RST_MEM_ENA | SYS_SYSTEM_RST_MEM_INIT, + priv->regs[SYS] + SYS_SYSTEM_RST_CFG); + + if (wait_for_bit_le32(priv->regs[SYS] + SYS_SYSTEM_RST_CFG, + SYS_SYSTEM_RST_MEM_INIT, false, 2000, false)) { + pr_err("Timeout in memory reset\n"); + return -EIO; + } + + /* Enable switch core */ + setbits_le32(priv->regs[SYS] + SYS_SYSTEM_RST_CFG, + SYS_SYSTEM_RST_CORE_ENA); + + serdes_setup(priv); + + return 0; +} + +static int serval_initialize(struct serval_private *priv) +{ + int ret, i; + + /* Initialize switch memories, enable core */ + ret = serval_switch_init(priv); + if (ret) + return ret; + + /* Flush queues */ + mscc_flush(priv->regs[QS], serval_regs_qs); + + /* Setup frame ageing - "2 sec" - The unit is 6.5us on serval */ + writel(SYS_FRM_AGING_ENA | (20000000 / 65), + priv->regs[SYS] + SYS_FRM_AGING); + + for (i = 0; i < MAX_PORT; i++) + serval_port_init(priv, i); + + serval_cpu_capture_setup(priv); + + debug("Ports enabled\n"); + + return 0; +} + +static int serval_write_hwaddr(struct udevice *dev) +{ + struct serval_private *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + + mscc_mac_table_add(priv->regs[ANA], serval_regs_ana_table, + pdata->enetaddr, PGID_UNICAST); + + writel(BIT(CPU_PORT), priv->regs[ANA] + ANA_PGID(PGID_UNICAST)); + + return 0; +} + +static int serval_start(struct udevice *dev) +{ + struct serval_private *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + const unsigned char mac[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff }; + int ret; + + ret = serval_initialize(priv); + if (ret) + return ret; + + /* Set MAC address tables entries for CPU redirection */ + mscc_mac_table_add(priv->regs[ANA], serval_regs_ana_table, mac, + PGID_BROADCAST); + + writel(BIT(CPU_PORT) | INTERNAL_PORT_MSK, + priv->regs[ANA] + ANA_PGID(PGID_BROADCAST)); + + /* It should be setup latter in serval_write_hwaddr */ + mscc_mac_table_add(priv->regs[ANA], serval_regs_ana_table, + pdata->enetaddr, PGID_UNICAST); + + writel(BIT(CPU_PORT), priv->regs[ANA] + ANA_PGID(PGID_UNICAST)); + return 0; +} + +static void serval_stop(struct udevice *dev) +{ + writel(ICPU_RESET_CORE_RST_PROTECT, BASE_CFG + ICPU_RESET); + writel(PERF_SOFT_RST_SOFT_CHIP_RST, BASE_DEVCPU_GCB + PERF_SOFT_RST); +} + +static int serval_send(struct udevice *dev, void *packet, int length) +{ + struct serval_private *priv = dev_get_priv(dev); + u32 ifh[IFH_LEN]; + u32 *buf = packet; + + /* + * Generate the IFH for frame injection + * + * The IFH is a 128bit-value + * bit 127: bypass the analyzer processing + * bit 57-67: destination mask + * bit 28-29: pop_cnt: 3 disables all rewriting of the frame + * bit 20-27: cpu extraction queue mask + * bit 16: tag type 0: C-tag, 1: S-tag + * bit 0-11: VID + */ + ifh[0] = IFH_INJ_BYPASS; + ifh[1] = (0x07); + ifh[2] = (0x7f) << 25; + ifh[3] = (IFH_TAG_TYPE_C << 16); + + return mscc_send(priv->regs[QS], serval_regs_qs, + ifh, IFH_LEN, buf, length); +} + +static int serval_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct serval_private *priv = dev_get_priv(dev); + u32 *rxbuf = (u32 *)net_rx_packets[0]; + int byte_cnt = 0; + + byte_cnt = mscc_recv(priv->regs[QS], serval_regs_qs, rxbuf, IFH_LEN, + false); + + *packetp = net_rx_packets[0]; + + return byte_cnt; +} + +static struct mii_dev *get_mdiobus(phys_addr_t base, unsigned long size) +{ + int i = 0; + + for (i = 0; i < SERVAL_MIIM_BUS_COUNT; ++i) + if (miim[i].miim_base == base && miim[i].miim_size == size) + return miim[i].bus; + + return NULL; +} + +static void add_port_entry(struct serval_private *priv, size_t index, + size_t phy_addr, struct mii_dev *bus, + u8 serdes_index, u8 phy_mode) +{ + priv->ports[index].phy_addr = phy_addr; + priv->ports[index].bus = bus; + priv->ports[index].serdes_index = serdes_index; + priv->ports[index].phy_mode = phy_mode; +} + +static int serval_probe(struct udevice *dev) +{ + struct serval_private *priv = dev_get_priv(dev); + int i, ret; + struct resource res; + fdt32_t faddr; + phys_addr_t addr_base; + unsigned long addr_size; + ofnode eth_node, node, mdio_node; + size_t phy_addr; + struct mii_dev *bus; + struct ofnode_phandle_args phandle; + struct phy_device *phy; + + if (!priv) + return -EINVAL; + + /* Get registers and map them to the private structure */ + for (i = 0; i < ARRAY_SIZE(regs_names); i++) { + priv->regs[i] = dev_remap_addr_name(dev, regs_names[i]); + if (!priv->regs[i]) { + debug + ("Error can't get regs base addresses for %s\n", + regs_names[i]); + return -ENOMEM; + } + } + + /* Initialize miim buses */ + memset(&miim, 0x0, sizeof(miim) * SERVAL_MIIM_BUS_COUNT); + + /* iterate all the ports and find out on which bus they are */ + i = 0; + eth_node = dev_read_first_subnode(dev); + for (node = ofnode_first_subnode(eth_node); + ofnode_valid(node); + node = ofnode_next_subnode(node)) { + if (ofnode_read_resource(node, 0, &res)) + return -ENOMEM; + i = res.start; + + ret = ofnode_parse_phandle_with_args(node, "phy-handle", NULL, + 0, 0, &phandle); + if (ret) + continue; + + /* Get phy address on mdio bus */ + if (ofnode_read_resource(phandle.node, 0, &res)) + return -ENOMEM; + phy_addr = res.start; + + /* Get mdio node */ + mdio_node = ofnode_get_parent(phandle.node); + + if (ofnode_read_resource(mdio_node, 0, &res)) + return -ENOMEM; + faddr = cpu_to_fdt32(res.start); + + addr_base = ofnode_translate_address(mdio_node, &faddr); + addr_size = res.end - res.start; + + /* If the bus is new then create a new bus */ + if (!get_mdiobus(addr_base, addr_size)) + priv->bus[miim_count] = + serval_mdiobus_init(addr_base, addr_size); + + /* Connect mdio bus with the port */ + bus = get_mdiobus(addr_base, addr_size); + + /* Get serdes info */ + ret = ofnode_parse_phandle_with_args(node, "phys", NULL, + 3, 0, &phandle); + if (ret) + return -ENOMEM; + + add_port_entry(priv, i, phy_addr, bus, phandle.args[1], + phandle.args[2]); + } + + for (i = 0; i < MAX_PORT; i++) { + if (!priv->ports[i].bus) + continue; + + phy = phy_connect(priv->ports[i].bus, + priv->ports[i].phy_addr, dev, + PHY_INTERFACE_MODE_NONE); + if (phy) + board_phy_config(phy); + } + + return 0; +} + +static int serval_remove(struct udevice *dev) +{ + struct serval_private *priv = dev_get_priv(dev); + int i; + + for (i = 0; i < SERVAL_MIIM_BUS_COUNT; i++) { + mdio_unregister(priv->bus[i]); + mdio_free(priv->bus[i]); + } + + return 0; +} + +static const struct eth_ops serval_ops = { + .start = serval_start, + .stop = serval_stop, + .send = serval_send, + .recv = serval_recv, + .write_hwaddr = serval_write_hwaddr, +}; + +static const struct udevice_id mscc_serval_ids[] = { + {.compatible = "mscc,vsc7418-switch"}, + { /* Sentinel */ } +}; + +U_BOOT_DRIVER(serval) = { + .name = "serval-switch", + .id = UCLASS_ETH, + .of_match = mscc_serval_ids, + .probe = serval_probe, + .remove = serval_remove, + .ops = &serval_ops, + .priv_auto_alloc_size = sizeof(struct serval_private), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; diff --git a/drivers/net/mtk_eth.c b/drivers/net/mtk_eth.c index cc09404830..0ef814c78b 100644 --- a/drivers/net/mtk_eth.c +++ b/drivers/net/mtk_eth.c @@ -1130,13 +1130,14 @@ static int mtk_eth_ofdata_to_platdata(struct udevice *dev) &priv->rst_gpio, GPIOD_IS_OUT); } } else { - subnode = ofnode_find_subnode(dev_ofnode(dev), "phy-handle"); - if (!ofnode_valid(subnode)) { + ret = dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, + 0, &args); + if (ret) { printf("error: phy-handle is not specified\n"); return ret; } - priv->phy_addr = ofnode_read_s32_default(subnode, "reg", -1); + priv->phy_addr = ofnode_read_s32_default(args.node, "reg", -1); if (priv->phy_addr < 0) { printf("error: phy address is not specified\n"); return ret; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 3dc0822d9c..2a3da068c9 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -119,21 +119,19 @@ config PHY_MICREL bool "Micrel Ethernet PHYs support" help Enable support for the GbE PHYs manufactured by Micrel (now - a part of Microchip). This includes drivers for the KSZ804, - KSZ8031, KSZ8051, KSZ8081, KSZ8895, KSZ886x, KSZ8721 - either/or KSZ9021 (see the "Micrel KSZ9021 family support" - config option for details), and KSZ9031 (if configured). + a part of Microchip). This includes drivers for the KSZ804, KSZ8031, + KSZ8051, KSZ8081, KSZ8895, KSZ886x and KSZ8721 (if "Micrel KSZ8xxx + family support" is selected) and the KSZ9021 and KSZ9031 (if "Micrel + KSZ90x1 family support" is selected). if PHY_MICREL config PHY_MICREL_KSZ9021 bool - select PHY_GIGE select PHY_MICREL_KSZ90X1 config PHY_MICREL_KSZ9031 bool - select PHY_GIGE select PHY_MICREL_KSZ90X1 config PHY_MICREL_KSZ90X1 @@ -146,20 +144,13 @@ config PHY_MICREL_KSZ90X1 delays configured in the device tree will be applied to the PHY during initialization. - This should not be enabled at the same time with PHY_MICREL_KSZ8XXX - as the KSZ9021 and KS8721 share the same ID. - config PHY_MICREL_KSZ8XXX bool "Micrel KSZ8xxx family support" - default y if !PHY_MICREL_KSZ90X1 help - Enable support for the 8000 series GbE PHYs manufactured by Micrel + Enable support for the 8000 series 10/100 PHYs manufactured by Micrel (now a part of Microchip). This includes drivers for the KSZ804, KSZ8031, KSZ8051, KSZ8081, KSZ8895, KSZ886x, and KSZ8721. - This should not be enabled at the same time with PHY_MICREL_KSZ90X1 - as the KSZ9021 and KS8721 share the same ID. - endif # PHY_MICREL config PHY_MSCC @@ -202,6 +193,26 @@ config RTL8211X_PHY_FORCE_MASTER If unsure, say N. +config RTL8211F_PHY_FORCE_EEE_RXC_ON + bool "Ethernet PHY RTL8211F: do not stop receiving the xMII clock during LPI" + depends on PHY_REALTEK + default n + help + The IEEE 802.3az-2010 (EEE) standard provides a protocol to coordinate + transitions to/from a lower power consumption level (Low Power Idle + mode) based on link utilization. When no packets are being + transmitted, the system goes to Low Power Idle mode to save power. + + Under particular circumstances this setting can cause issues where + the PHY is unable to transmit or receive any packet when in LPI mode. + The problem is caused when the PHY is configured to stop receiving + the xMII clock while it is signaling LPI. For some PHYs the bit + configuring this behavior is set by the Linux kernel, causing the + issue in U-Boot on reboot if the PHY retains the register value. + + Default n, which means that the PHY state is not changed. To work + around the issues, change this setting to y. + config PHY_SMSC bool "Microchip(SMSC) Ethernet PHYs support" diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c index 12df09877d..5c3298d612 100644 --- a/drivers/net/phy/aquantia.c +++ b/drivers/net/phy/aquantia.c @@ -303,9 +303,14 @@ int aquantia_config(struct phy_device *phydev) AQUANTIA_SYSTEM_INTERFACE_SR); /* If SI is USXGMII then start USXGMII autoneg */ if ((val & AQUANTIA_SI_IN_USE_MASK) == AQUANTIA_SI_USXGMII) { + reg_val1 = phy_read(phydev, MDIO_MMD_PHYXS, + AQUANTIA_VENDOR_PROVISIONING_REG); + + reg_val1 |= AQUANTIA_USX_AUTONEG_CONTROL_ENA; + phy_write(phydev, MDIO_MMD_PHYXS, AQUANTIA_VENDOR_PROVISIONING_REG, - AQUANTIA_USX_AUTONEG_CONTROL_ENA); + reg_val1); printf("%s: system interface USXGMII\n", phydev->dev->name); } else { diff --git a/drivers/net/phy/micrel_ksz8xxx.c b/drivers/net/phy/micrel_ksz8xxx.c index 3411150ab9..daa57ce33c 100644 --- a/drivers/net/phy/micrel_ksz8xxx.c +++ b/drivers/net/phy/micrel_ksz8xxx.c @@ -147,11 +147,13 @@ static struct phy_driver ksz8895_driver = { .shutdown = &genphy_shutdown, }; -/* Micrel used the exact same part number for the KSZ9021. */ +/* Micrel used the exact same model number for the KSZ9021, + * so the revision number is used to distinguish them. + */ static struct phy_driver KS8721_driver = { .name = "Micrel KS8721BL", - .uid = 0x221610, - .mask = 0xfffff0, + .uid = 0x221618, + .mask = 0xfffffc, .features = PHY_BASIC_FEATURES, .config = &genphy_config, .startup = &genphy_startup, diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c index 63e7b0242b..f18e40a2fe 100644 --- a/drivers/net/phy/micrel_ksz90x1.c +++ b/drivers/net/phy/micrel_ksz90x1.c @@ -33,10 +33,14 @@ #define CTRL1000_CONFIG_MASTER (1 << 11) #define CTRL1000_MANUAL_CONFIG (1 << 12) +#define KSZ9021_PS_TO_REG 120 + /* KSZ9031 PHY Registers */ #define MII_KSZ9031_MMD_ACCES_CTRL 0x0d #define MII_KSZ9031_MMD_REG_DATA 0x0e +#define KSZ9031_PS_TO_REG 60 + static int ksz90xx_startup(struct phy_device *phydev) { unsigned phy_ctl; @@ -102,20 +106,28 @@ static const struct ksz90x1_reg_field ksz9031_clk_grp[] = { }; static int ksz90x1_of_config_group(struct phy_device *phydev, - struct ksz90x1_ofcfg *ofcfg) + struct ksz90x1_ofcfg *ofcfg, + int ps_to_regval) { struct udevice *dev = phydev->dev; struct phy_driver *drv = phydev->drv; - const int ps_to_regval = 60; int val[4]; int i, changed = 0, offset, max; u16 regval = 0; + ofnode node; if (!drv || !drv->writeext) return -EOPNOTSUPP; + /* Look for a PHY node under the Ethernet node */ + node = dev_read_subnode(dev, "ethernet-phy"); + if (!ofnode_valid(node)) { + /* No node found, look in the Ethernet node */ + node = dev_ofnode(dev); + } + for (i = 0; i < ofcfg->grpsz; i++) { - val[i] = dev_read_u32_default(dev, ofcfg->grp[i].name, ~0); + val[i] = ofnode_read_u32_default(node, ofcfg->grp[i].name, ~0); offset = ofcfg->grp[i].off; if (val[i] == -1) { /* Default register value for KSZ9021 */ @@ -148,7 +160,8 @@ static int ksz9021_of_config(struct phy_device *phydev) int i, ret = 0; for (i = 0; i < ARRAY_SIZE(ofcfg); i++) { - ret = ksz90x1_of_config_group(phydev, &(ofcfg[i])); + ret = ksz90x1_of_config_group(phydev, &ofcfg[i], + KSZ9021_PS_TO_REG); if (ret) return ret; } @@ -167,7 +180,8 @@ static int ksz9031_of_config(struct phy_device *phydev) int i, ret = 0; for (i = 0; i < ARRAY_SIZE(ofcfg); i++) { - ret = ksz90x1_of_config_group(phydev, &(ofcfg[i])); + ret = ksz90x1_of_config_group(phydev, &ofcfg[i], + KSZ9031_PS_TO_REG); if (ret) return ret; } @@ -271,7 +285,7 @@ static int ksz9021_config(struct phy_device *phydev) static struct phy_driver ksz9021_driver = { .name = "Micrel ksz9021", .uid = 0x221610, - .mask = 0xfffff0, + .mask = 0xfffffe, .features = PHY_GBIT_FEATURES, .config = &ksz9021_config, .startup = &ksz90xx_startup, diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 4e8d2943ee..c1c1af9abd 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -462,6 +462,18 @@ static LIST_HEAD(phy_drivers); int phy_init(void) { +#ifdef CONFIG_NEEDS_MANUAL_RELOC + /* + * The pointers inside phy_drivers also needs to be updated incase of + * manual reloc, without which these points to some invalid + * pre reloc address and leads to invalid accesses, hangs. + */ + struct list_head *head = &phy_drivers; + + head->next = (void *)head->next + gd->reloc_off; + head->prev = (void *)head->prev + gd->reloc_off; +#endif + #ifdef CONFIG_B53_SWITCH phy_b53_init(); #endif @@ -549,6 +561,10 @@ int phy_register(struct phy_driver *drv) drv->readext += gd->reloc_off; if (drv->writeext) drv->writeext += gd->reloc_off; + if (drv->read_mmd) + drv->read_mmd += gd->reloc_off; + if (drv->write_mmd) + drv->write_mmd += gd->reloc_off; #endif return 0; } @@ -655,7 +671,10 @@ static struct phy_device *phy_device_create(struct mii_dev *bus, int addr, dev->drv = get_phy_driver(dev, interface); - phy_probe(dev); + if (phy_probe(dev)) { + printf("%s, PHY probe failed\n", __func__); + return NULL; + } if (addr >= 0 && addr < PHY_MAX_ADDR) bus->phymap[addr] = dev; diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index dd45e11b3a..8f1d759632 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -12,6 +12,7 @@ #define PHY_RTL8211x_FORCE_MASTER BIT(1) #define PHY_RTL8211E_PINE64_GIGABIT_FIX BIT(2) +#define PHY_RTL8211F_FORCE_EEE_RXC_ON BIT(3) #define PHY_AUTONEGOTIATE_TIMEOUT 5000 @@ -102,6 +103,15 @@ static int rtl8211e_probe(struct phy_device *phydev) return 0; } +static int rtl8211f_probe(struct phy_device *phydev) +{ +#ifdef CONFIG_RTL8211F_PHY_FORCE_EEE_RXC_ON + phydev->flags |= PHY_RTL8211F_FORCE_EEE_RXC_ON; +#endif + + return 0; +} + /* RealTek RTL8211x */ static int rtl8211x_config(struct phy_device *phydev) { @@ -151,6 +161,14 @@ static int rtl8211f_config(struct phy_device *phydev) { u16 reg; + if (phydev->flags & PHY_RTL8211F_FORCE_EEE_RXC_ON) { + unsigned int reg; + + reg = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); + reg &= ~MDIO_PCS_CTRL1_CLKSTOP_EN; + phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, reg); + } + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); phy_write(phydev, MDIO_DEVAD_NONE, @@ -360,6 +378,7 @@ static struct phy_driver RTL8211F_driver = { .uid = 0x1cc916, .mask = 0xffffff, .features = PHY_GBIT_FEATURES, + .probe = &rtl8211f_probe, .config = &rtl8211f_config, .startup = &rtl8211f_startup, .shutdown = &genphy_shutdown, diff --git a/drivers/net/phy/ti.c b/drivers/net/phy/ti.c index 6db6edd0d0..25f1332ca9 100644 --- a/drivers/net/phy/ti.c +++ b/drivers/net/phy/ti.c @@ -73,16 +73,6 @@ #define MII_DP83867_CFG2_SPEEDOPT_INTLOW 0x2000 #define MII_DP83867_CFG2_MASK 0x003F -#define MII_MMD_CTRL 0x0d /* MMD Access Control Register */ -#define MII_MMD_DATA 0x0e /* MMD Access Data Register */ - -/* MMD Access Control register fields */ -#define MII_MMD_CTRL_DEVAD_MASK 0x1f /* Mask MMD DEVAD*/ -#define MII_MMD_CTRL_ADDR 0x0000 /* Address */ -#define MII_MMD_CTRL_NOINCR 0x4000 /* no post increment */ -#define MII_MMD_CTRL_INCR_RDWT 0x8000 /* post increment on reads & writes */ -#define MII_MMD_CTRL_INCR_ON_WT 0xC000 /* post increment on writes only */ - /* User setting - can be taken from DTS */ #define DEFAULT_RX_ID_DELAY DP83867_RGMIIDCTL_2_25_NS #define DEFAULT_TX_ID_DELAY DP83867_RGMIIDCTL_2_75_NS @@ -116,88 +106,20 @@ struct dp83867_private { int clk_output_sel; }; -/** - * phy_read_mmd_indirect - reads data from the MMD registers - * @phydev: The PHY device bus - * @prtad: MMD Address - * @devad: MMD DEVAD - * @addr: PHY address on the MII bus - * - * Description: it reads data from the MMD registers (clause 22 to access to - * clause 45) of the specified phy address. - * To read these registers we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Read reg 14 // Read MMD data - */ -int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, int addr) -{ - int value = -1; - - /* Write the desired MMD Devad */ - phy_write(phydev, addr, MII_MMD_CTRL, devad); - - /* Write the desired MMD register address */ - phy_write(phydev, addr, MII_MMD_DATA, prtad); - - /* Select the Function : DATA with no post increment */ - phy_write(phydev, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); - - /* Read the content of the MMD's selected register */ - value = phy_read(phydev, addr, MII_MMD_DATA); - return value; -} - -/** - * phy_write_mmd_indirect - writes data to the MMD registers - * @phydev: The PHY device - * @prtad: MMD Address - * @devad: MMD DEVAD - * @addr: PHY address on the MII bus - * @data: data to write in the MMD register - * - * Description: Write data from the MMD registers of the specified - * phy address. - * To write these registers we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Write reg 14 // Write MMD data - */ -void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, int addr, u32 data) -{ - /* Write the desired MMD Devad */ - phy_write(phydev, addr, MII_MMD_CTRL, devad); - - /* Write the desired MMD register address */ - phy_write(phydev, addr, MII_MMD_DATA, prtad); - - /* Select the Function : DATA with no post increment */ - phy_write(phydev, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); - - /* Write the data into MMD's selected register */ - phy_write(phydev, addr, MII_MMD_DATA, data); -} - static int dp83867_config_port_mirroring(struct phy_device *phydev) { struct dp83867_private *dp83867 = (struct dp83867_private *)phydev->priv; u16 val; - val = phy_read_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR, - phydev->addr); + val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4); if (dp83867->port_mirroring == DP83867_PORT_MIRRORING_EN) val |= DP83867_CFG4_PORT_MIRROR_EN; else val &= ~DP83867_CFG4_PORT_MIRROR_EN; - phy_write_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR, - phydev->addr, val); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val); return 0; } @@ -216,6 +138,10 @@ static int dp83867_of_init(struct phy_device *phydev) /* Optional configuration */ + node = phy_get_ofnode(phydev); + if (!ofnode_valid(node)) + return -EINVAL; + /* * Keep the default value if ti,clk-output-sel is not set * or to high @@ -225,10 +151,6 @@ static int dp83867_of_init(struct phy_device *phydev) ofnode_read_u32_default(node, "ti,clk-output-sel", DP83867_CLK_O_SEL_REF_CLK); - node = phy_get_ofnode(phydev); - if (!ofnode_valid(node)) - return -EINVAL; - if (ofnode_read_bool(node, "ti,max-output-impedance")) dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX; else if (ofnode_read_bool(node, "ti,min-output-impedance")) @@ -257,13 +179,13 @@ static int dp83867_of_init(struct phy_device *phydev) /* Clock output selection if muxing property is set */ if (dp83867->clk_output_sel != DP83867_CLK_O_SEL_REF_CLK) { - val = phy_read_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR, phydev->addr); + val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_IO_MUX_CFG); val &= ~DP83867_IO_MUX_CFG_CLK_O_SEL_MASK; val |= (dp83867->clk_output_sel << DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT); - phy_write_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR, phydev->addr, val); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_IO_MUX_CFG, val); } return 0; @@ -308,11 +230,11 @@ static int dp83867_config(struct phy_device *phydev) /* Mode 1 or 2 workaround */ if (dp83867->rxctrl_strap_quirk) { - val = phy_read_mmd_indirect(phydev, DP83867_CFG4, - DP83867_DEVADDR, phydev->addr); + val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_CFG4); val &= ~BIT(7); - phy_write_mmd_indirect(phydev, DP83867_CFG4, - DP83867_DEVADDR, phydev->addr, val); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_CFG4, val); } if (phy_interface_is_rgmii(phydev)) { @@ -332,8 +254,8 @@ static int dp83867_config(struct phy_device *phydev) * register's bit 11 (marked as RESERVED). */ - bs = phy_read_mmd_indirect(phydev, DP83867_STRAP_STS1, - DP83867_DEVADDR, phydev->addr); + bs = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_STRAP_STS1); val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL); if (bs & DP83867_STRAP_STS1_RESERVED) { val &= ~DP83867_PHYCR_RESERVED_MASK; @@ -354,8 +276,8 @@ static int dp83867_config(struct phy_device *phydev) MII_DP83867_CFG2_SPEEDOPT_INTLOW); phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_CFG2, cfg2); - phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, phydev->addr, 0x0); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_RGMIICTL, 0x0); phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL, DP83867_PHYCTRL_SGMIIEN | @@ -367,8 +289,8 @@ static int dp83867_config(struct phy_device *phydev) } if (phy_interface_is_rgmii(phydev)) { - val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, phydev->addr); + val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_RGMIICTL); if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) val |= (DP83867_RGMII_TX_CLK_DELAY_EN | @@ -380,26 +302,24 @@ static int dp83867_config(struct phy_device *phydev) if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) val |= DP83867_RGMII_RX_CLK_DELAY_EN; - phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, phydev->addr, val); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_RGMIICTL, val); delay = (dp83867->rx_id_delay | (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT)); - phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL, - DP83867_DEVADDR, phydev->addr, delay); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_RGMIIDCTL, delay); if (dp83867->io_impedance >= 0) { - val = phy_read_mmd_indirect(phydev, - DP83867_IO_MUX_CFG, - DP83867_DEVADDR, - phydev->addr); + val = phy_read_mmd(phydev, + DP83867_DEVADDR, + DP83867_IO_MUX_CFG); val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; val |= dp83867->io_impedance & DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; - phy_write_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR, phydev->addr, - val); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_IO_MUX_CFG, val); } } diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c index 749562db96..11abe5e0c9 100644 --- a/drivers/net/ravb.c +++ b/drivers/net/ravb.c @@ -46,6 +46,8 @@ #define CSR_OPS 0x0000000F #define CSR_OPS_CONFIG BIT(1) +#define APSR_TDM BIT(14) + #define TCCR_TSRQ0 BIT(0) #define RFLR_RFL_MIN 0x05EE @@ -389,9 +391,14 @@ static int ravb_dmac_init(struct udevice *dev) /* FIFO size set */ writel(0x00222210, eth->iobase + RAVB_REG_TGC); - /* Delay CLK: 2ns */ - if (pdata->max_speed == 1000) - writel(BIT(14), eth->iobase + RAVB_REG_APSR); + /* Delay CLK: 2ns (not applicable on R-Car E3/D3) */ + if ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A77990) || + (rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A77995)) + return 0; + + if ((pdata->phy_interface == PHY_INTERFACE_MODE_RGMII_ID) || + (pdata->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID)) + writel(APSR_TDM, eth->iobase + RAVB_REG_APSR); return 0; } diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index a78f3d233f..521e5909a2 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -257,6 +257,7 @@ static struct { {"RTL-8168/8111g", 0x4c, 0xff7e1880,}, {"RTL-8101e", 0x34, 0xff7e1880,}, {"RTL-8100e", 0x32, 0xff7e1880,}, + {"RTL-8168h/8111h", 0x54, 0xff7e1880,}, }; enum _DescStatusBit { @@ -301,7 +302,7 @@ static unsigned char rxdata[RX_BUF_LEN]; */ #if RTL8169_DESC_SIZE < ARCH_DMA_MINALIGN #if !defined(CONFIG_SYS_NONCACHED_MEMORY) && \ - !defined(CONFIG_SYS_DCACHE_OFF) && !defined(CONFIG_X86) + !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) && !defined(CONFIG_X86) #warning cache-line size is larger than descriptor size #endif #endif @@ -941,6 +942,23 @@ static void rtl_halt(struct eth_device *dev) } #endif +#ifdef CONFIG_DM_ETH +static int rtl8169_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *plat = dev_get_platdata(dev); + unsigned int i; + + RTL_W8(Cfg9346, Cfg9346_Unlock); + + for (i = 0; i < MAC_ADDR_LEN; i++) + RTL_W8(MAC0 + i, plat->enetaddr[i]); + + RTL_W8(Cfg9346, Cfg9346_Lock); + + return 0; +} +#endif + /************************************************************************** INIT - Look for an adapter, this routine's visible to the outside ***************************************************************************/ @@ -1195,6 +1213,7 @@ static const struct eth_ops rtl8169_eth_ops = { .send = rtl8169_eth_send, .recv = rtl8169_eth_recv, .stop = rtl8169_eth_stop, + .write_hwaddr = rtl8169_write_hwaddr, }; static const struct udevice_id rtl8169_eth_ids[] = { diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index 4646f2ba4e..da79b766a6 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -34,7 +34,8 @@ # error "Please define CONFIG_SH_ETHER_PHY_ADDR" #endif -#if defined(CONFIG_SH_ETHER_CACHE_WRITEBACK) && !defined(CONFIG_SYS_DCACHE_OFF) +#if defined(CONFIG_SH_ETHER_CACHE_WRITEBACK) && \ + !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) #define flush_cache_wback(addr, len) \ flush_dcache_range((u32)addr, \ (u32)(addr + ALIGN(len, CONFIG_SH_ETHER_ALIGNE_SIZE))) @@ -425,7 +426,7 @@ static int sh_eth_phy_regs_config(struct sh_eth_dev *eth) sh_eth_write(port_info, GECMR_100B, GECMR); #elif defined(CONFIG_CPU_SH7757) || defined(CONFIG_CPU_SH7752) sh_eth_write(port_info, 1, RTRATE); -#elif defined(CONFIG_CPU_SH7724) || defined(CONFIG_RCAR_GEN2) +#elif defined(CONFIG_RCAR_GEN2) val = ECMR_RTM; #endif } else if (phy->speed == 10) { @@ -806,9 +807,11 @@ static int sh_ether_probe(struct udevice *udev) priv->iobase = pdata->iobase; +#if CONFIG_IS_ENABLED(CLK) ret = clk_get_by_index(udev, 0, &priv->clk); if (ret < 0) return ret; +#endif ret = dev_read_phandle_with_args(udev, "phy-handle", NULL, 0, 0, &phandle_args); if (!ret) { @@ -843,9 +846,11 @@ static int sh_ether_probe(struct udevice *udev) eth->port_info[eth->port].iobase = (void __iomem *)(BASE_IO_ADDR + 0x800 * eth->port); +#if CONFIG_IS_ENABLED(CLK) ret = clk_enable(&priv->clk); if (ret) goto err_mdio_register; +#endif ret = sh_eth_phy_config(udev); if (ret) { @@ -856,7 +861,9 @@ static int sh_ether_probe(struct udevice *udev) return 0; err_phy_config: +#if CONFIG_IS_ENABLED(CLK) clk_disable(&priv->clk); +#endif err_mdio_register: mdio_free(mdiodev); return ret; @@ -868,7 +875,9 @@ static int sh_ether_remove(struct udevice *udev) struct sh_eth_dev *eth = &priv->shdev; struct sh_eth_info *port_info = ð->port_info[eth->port]; +#if CONFIG_IS_ENABLED(CLK) clk_disable(&priv->clk); +#endif free(port_info->phydev); mdio_unregister(priv->bus); mdio_free(priv->bus); @@ -917,6 +926,7 @@ int sh_ether_ofdata_to_platdata(struct udevice *dev) } static const struct udevice_id sh_ether_ids[] = { + { .compatible = "renesas,ether-r7s72100" }, { .compatible = "renesas,ether-r8a7790" }, { .compatible = "renesas,ether-r8a7791" }, { .compatible = "renesas,ether-r8a7793" }, diff --git a/drivers/net/sh_eth.h b/drivers/net/sh_eth.h index cd8190062a..e1bbd4913f 100644 --- a/drivers/net/sh_eth.h +++ b/drivers/net/sh_eth.h @@ -228,6 +228,60 @@ static const u16 sh_eth_offset_gigabit[SH_ETH_MAX_REGISTER_OFFSET] = { [RMII_MII] = 0x0790, }; +static const u16 sh_eth_offset_rz[SH_ETH_MAX_REGISTER_OFFSET] = { + [EDSR] = 0x0000, + [EDMR] = 0x0400, + [EDTRR] = 0x0408, + [EDRRR] = 0x0410, + [EESR] = 0x0428, + [EESIPR] = 0x0430, + [TDLAR] = 0x0010, + [TDFAR] = 0x0014, + [TDFXR] = 0x0018, + [TDFFR] = 0x001c, + [RDLAR] = 0x0030, + [RDFAR] = 0x0034, + [RDFXR] = 0x0038, + [RDFFR] = 0x003c, + [TRSCER] = 0x0438, + [RMFCR] = 0x0440, + [TFTR] = 0x0448, + [FDR] = 0x0450, + [RMCR] = 0x0458, + [RPADIR] = 0x0460, + [FCFTR] = 0x0468, + [CSMR] = 0x04E4, + + [ECMR] = 0x0500, + [ECSR] = 0x0510, + [ECSIPR] = 0x0518, + [PIR] = 0x0520, + [PSR] = 0x0528, + [PIPR] = 0x052c, + [RFLR] = 0x0508, + [APR] = 0x0554, + [MPR] = 0x0558, + [PFTCR] = 0x055c, + [PFRCR] = 0x0560, + [TPAUSER] = 0x0564, + [GECMR] = 0x05b0, + [BCULR] = 0x05b4, + [MAHR] = 0x05c0, + [MALR] = 0x05c8, + [TROCR] = 0x0700, + [CDCR] = 0x0708, + [LCCR] = 0x0710, + [CEFCR] = 0x0740, + [FRECR] = 0x0748, + [TSFRCR] = 0x0750, + [TLFRCR] = 0x0758, + [RFCR] = 0x0760, + [CERCR] = 0x0768, + [CEECR] = 0x0770, + [MAFCR] = 0x0778, + [RMII_MII] = 0x0790, +}; + static const u16 sh_eth_offset_fast_sh4[SH_ETH_MAX_REGISTER_OFFSET] = { [ECMR] = 0x0100, [RFLR] = 0x0108, @@ -295,9 +349,6 @@ static const u16 sh_eth_offset_fast_sh4[SH_ETH_MAX_REGISTER_OFFSET] = { #define SH_ETH_TYPE_ETHER #define BASE_IO_ADDR 0xfef00000 #endif -#elif defined(CONFIG_CPU_SH7724) -#define SH_ETH_TYPE_ETHER -#define BASE_IO_ADDR 0xA4600000 #elif defined(CONFIG_R8A7740) #define SH_ETH_TYPE_GETHER #define BASE_IO_ADDR 0xE9A00000 @@ -606,6 +657,8 @@ static inline unsigned long sh_eth_reg_addr(struct sh_eth_info *port, const u16 *reg_offset = sh_eth_offset_gigabit; #elif defined(SH_ETH_TYPE_ETHER) const u16 *reg_offset = sh_eth_offset_fast_sh4; +#elif defined(SH_ETH_TYPE_RZ) + const u16 *reg_offset = sh_eth_offset_rz; #else #error #endif diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 98bd7a5823..c0a440886e 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -138,7 +138,9 @@ struct emac_eth_dev { struct phy_device *phydev; struct mii_dev *bus; struct clk tx_clk; + struct clk ephy_clk; struct reset_ctl tx_rst; + struct reset_ctl ephy_rst; #ifdef CONFIG_DM_GPIO struct gpio_desc reset_gpio; #endif @@ -653,7 +655,6 @@ static int sun8i_eth_write_hwaddr(struct udevice *dev) static int sun8i_emac_board_setup(struct emac_eth_dev *priv) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; int ret; ret = clk_enable(&priv->tx_clk); @@ -670,16 +671,20 @@ static int sun8i_emac_board_setup(struct emac_eth_dev *priv) } } - if (priv->variant == H3_EMAC) { - /* Only H3/H5 have clock controls for internal EPHY */ - if (priv->use_internal_phy) { - /* Set clock gating for ephy */ - setbits_le32(&ccm->bus_gate4, - BIT(AHB_GATE_OFFSET_EPHY)); - - /* Deassert EPHY */ - setbits_le32(&ccm->ahb_reset2_cfg, - BIT(AHB_RESET_OFFSET_EPHY)); + /* Only H3/H5 have clock controls for internal EPHY */ + if (clk_valid(&priv->ephy_clk)) { + ret = clk_enable(&priv->ephy_clk); + if (ret) { + dev_err(dev, "failed to enable EPHY TX clock\n"); + return ret; + } + } + + if (reset_valid(&priv->ephy_rst)) { + ret = reset_deassert(&priv->ephy_rst); + if (ret) { + dev_err(dev, "failed to deassert EPHY TX clock\n"); + return ret; } } @@ -839,6 +844,44 @@ static const struct eth_ops sun8i_emac_eth_ops = { .stop = sun8i_emac_eth_stop, }; +static int sun8i_get_ephy_nodes(struct emac_eth_dev *priv) +{ + int node, ret; + + /* look for mdio-mux node for internal PHY node */ + node = fdt_path_offset(gd->fdt_blob, + "/soc/ethernet@1c30000/mdio-mux/mdio@1/ethernet-phy@1"); + if (node < 0) { + debug("failed to get mdio-mux with internal PHY\n"); + return node; + } + + ret = fdt_node_check_compatible(gd->fdt_blob, node, + "allwinner,sun8i-h3-mdio-internal"); + if (ret < 0) { + debug("failed to find mdio-internal node\n"); + return ret; + } + + ret = clk_get_by_index_nodev(offset_to_ofnode(node), 0, + &priv->ephy_clk); + if (ret) { + dev_err(dev, "failed to get EPHY TX clock\n"); + return ret; + } + + ret = reset_get_by_index_nodev(offset_to_ofnode(node), 0, + &priv->ephy_rst); + if (ret) { + dev_err(dev, "failed to get EPHY TX reset\n"); + return ret; + } + + priv->use_internal_phy = true; + + return 0; +} + static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev) { struct sun8i_eth_pdata *sun8i_pdata = dev_get_platdata(dev); @@ -920,12 +963,9 @@ static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev) } if (priv->variant == H3_EMAC) { - int parent = fdt_parent_offset(gd->fdt_blob, offset); - - if (parent >= 0 && - !fdt_node_check_compatible(gd->fdt_blob, parent, - "allwinner,sun8i-h3-mdio-internal")) - priv->use_internal_phy = true; + ret = sun8i_get_ephy_nodes(priv); + if (ret) + return ret; } priv->interface = pdata->phy_interface; diff --git a/drivers/net/ti/davinci_emac.c b/drivers/net/ti/davinci_emac.c index bb879d8d4f..9d53984973 100644 --- a/drivers/net/ti/davinci_emac.c +++ b/drivers/net/ti/davinci_emac.c @@ -816,55 +816,12 @@ int davinci_emac_initialize(void) phy_id |= tmp & 0x0000ffff; - switch (phy_id) { -#ifdef PHY_KSZ8873 - case PHY_KSZ8873: - sprintf(phy[i].name, "KSZ8873 @ 0x%02x", - active_phy_addr[i]); - phy[i].init = ksz8873_init_phy; - phy[i].is_phy_connected = ksz8873_is_phy_connected; - phy[i].get_link_speed = ksz8873_get_link_speed; - phy[i].auto_negotiate = ksz8873_auto_negotiate; - break; -#endif -#ifdef PHY_LXT972 - case PHY_LXT972: - sprintf(phy[i].name, "LXT972 @ 0x%02x", - active_phy_addr[i]); - phy[i].init = lxt972_init_phy; - phy[i].is_phy_connected = lxt972_is_phy_connected; - phy[i].get_link_speed = lxt972_get_link_speed; - phy[i].auto_negotiate = lxt972_auto_negotiate; - break; -#endif -#ifdef PHY_DP83848 - case PHY_DP83848: - sprintf(phy[i].name, "DP83848 @ 0x%02x", - active_phy_addr[i]); - phy[i].init = dp83848_init_phy; - phy[i].is_phy_connected = dp83848_is_phy_connected; - phy[i].get_link_speed = dp83848_get_link_speed; - phy[i].auto_negotiate = dp83848_auto_negotiate; - break; -#endif -#ifdef PHY_ET1011C - case PHY_ET1011C: - sprintf(phy[i].name, "ET1011C @ 0x%02x", - active_phy_addr[i]); - phy[i].init = gen_init_phy; - phy[i].is_phy_connected = gen_is_phy_connected; - phy[i].get_link_speed = et1011c_get_link_speed; - phy[i].auto_negotiate = gen_auto_negotiate; - break; -#endif - default: - sprintf(phy[i].name, "GENERIC @ 0x%02x", - active_phy_addr[i]); - phy[i].init = gen_init_phy; - phy[i].is_phy_connected = gen_is_phy_connected; - phy[i].get_link_speed = gen_get_link_speed; - phy[i].auto_negotiate = gen_auto_negotiate; - } + sprintf(phy[i].name, "GENERIC @ 0x%02x", + active_phy_addr[i]); + phy[i].init = gen_init_phy; + phy[i].is_phy_connected = gen_is_phy_connected; + phy[i].get_link_speed = gen_get_link_speed; + phy[i].auto_negotiate = gen_auto_negotiate; debug("Ethernet PHY: %s\n", phy[i].name); diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index 1ee0a0aefb..d4965e2ef6 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -577,7 +577,7 @@ static int nvme_get_info_from_identify(struct nvme_dev *dev) int ret; int shift = NVME_CAP_MPSMIN(dev->cap) + 12; - ret = nvme_identify(dev, 0, 1, (dma_addr_t)ctrl); + ret = nvme_identify(dev, 0, 1, (dma_addr_t)(long)ctrl); if (ret) return -EIO; @@ -646,7 +646,7 @@ static int nvme_blk_probe(struct udevice *udev) ns->dev = ndev; /* extract the namespace id from the block device name */ ns->ns_id = trailing_strtol(udev->name) + 1; - if (nvme_identify(ndev, ns->ns_id, 0, (dma_addr_t)id)) + if (nvme_identify(ndev, ns->ns_id, 0, (dma_addr_t)(long)id)) return -EIO; flbas = id->flbas & NVME_NS_FLBAS_LBA_MASK; diff --git a/drivers/nvme/nvme_show.c b/drivers/nvme/nvme_show.c index 395b0618e6..15e459da1a 100644 --- a/drivers/nvme/nvme_show.c +++ b/drivers/nvme/nvme_show.c @@ -111,14 +111,14 @@ int nvme_print_info(struct udevice *udev) ALLOC_CACHE_ALIGN_BUFFER(char, buf_ctrl, sizeof(struct nvme_id_ctrl)); struct nvme_id_ctrl *ctrl = (struct nvme_id_ctrl *)buf_ctrl; - if (nvme_identify(dev, 0, 1, (dma_addr_t)ctrl)) + if (nvme_identify(dev, 0, 1, (dma_addr_t)(long)ctrl)) return -EIO; print_optional_admin_cmd(le16_to_cpu(ctrl->oacs), ns->devnum); print_optional_nvm_cmd(le16_to_cpu(ctrl->oncs), ns->devnum); print_format_nvme_attributes(ctrl->fna, ns->devnum); - if (nvme_identify(dev, ns->ns_id, 0, (dma_addr_t)id)) + if (nvme_identify(dev, ns->ns_id, 0, (dma_addr_t)(long)id)) return -EIO; print_formats(id, ns); diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 1521885bde..429bb836a8 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -69,6 +69,14 @@ config PCI_RCAR_GEN2 Renesas RCar Gen2 SoCs. The PCIe controller on RCar Gen2 is also used to access EHCI USB controller on the SoC. +config PCI_RCAR_GEN3 + bool "Renesas RCar Gen3 PCIe driver" + depends on DM_PCI + depends on RCAR_GEN3 + help + Say Y here if you want to enable PCIe controller support on + Renesas RCar Gen3 SoCs. + config PCI_SANDBOX bool "Sandbox PCI support" depends on SANDBOX && DM_PCI @@ -105,6 +113,14 @@ config PCIE_LAYERSCAPE PCIe controllers. The PCIe may works in RC or EP mode according to RCW[HOST_AGT_PEX] setting. +config PCIE_LAYERSCAPE_GEN4 + bool "Layerscape Gen4 PCIe support" + depends on DM_PCI + help + Support PCIe Gen4 on NXP Layerscape SoCs, which may have one or + several PCIe controllers. The PCIe controller can work in RC or + EP mode according to RCW[HOST_AGT_PEX] setting. + config PCIE_INTEL_FPGA bool "Intel FPGA PCIe support" depends on DM_PCI diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 4923641895..bd392edba1 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_PCIE_IMX) += pcie_imx.o obj-$(CONFIG_FTPCI100) += pci_ftpci100.o obj-$(CONFIG_PCI_MVEBU) += pci_mvebu.o obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o +obj-$(CONFIG_PCI_RCAR_GEN3) += pci-rcar-gen3.o obj-$(CONFIG_SH4_PCI) += pci_sh4.o obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o @@ -32,5 +33,7 @@ obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o obj-$(CONFIG_PCIE_DW_MVEBU) += pcie_dw_mvebu.o obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape.o obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape_fixup.o +obj-$(CONFIG_PCIE_LAYERSCAPE_GEN4) += pcie_layerscape_gen4.o \ + pcie_layerscape_gen4_fixup.o obj-$(CONFIG_PCI_XILINX) += pcie_xilinx.o obj-$(CONFIG_PCIE_INTEL_FPGA) += pcie_intel_fpga.o diff --git a/drivers/pci/pci-rcar-gen3.c b/drivers/pci/pci-rcar-gen3.c new file mode 100644 index 0000000000..52ca13b70c --- /dev/null +++ b/drivers/pci/pci-rcar-gen3.c @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RCar Gen3 PCIEC driver + * + * Copyright (C) 2018-2019 Marek Vasut <marek.vasut@gmail.com> + * + * Based on Linux PCIe driver for Renesas R-Car SoCs + * Copyright (C) 2014 Renesas Electronics Europe Ltd + * + * Based on: + * arch/sh/drivers/pci/pcie-sh7786.c + * arch/sh/drivers/pci/ops-sh7786.c + * Copyright (C) 2009 - 2011 Paul Mundt + * + * Author: Phil Edworthy <phil.edworthy@renesas.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <errno.h> +#include <pci.h> +#include <wait_bit.h> + +#define PCIECAR 0x000010 +#define PCIECCTLR 0x000018 +#define CONFIG_SEND_ENABLE BIT(31) +#define TYPE0 (0 << 8) +#define TYPE1 BIT(8) +#define PCIECDR 0x000020 +#define PCIEMSR 0x000028 +#define PCIEINTXR 0x000400 +#define PCIEPHYSR 0x0007f0 +#define PHYRDY BIT(0) +#define PCIEMSITXR 0x000840 + +/* Transfer control */ +#define PCIETCTLR 0x02000 +#define CFINIT 1 +#define PCIETSTR 0x02004 +#define DATA_LINK_ACTIVE 1 +#define PCIEERRFR 0x02020 +#define UNSUPPORTED_REQUEST BIT(4) +#define PCIEMSIFR 0x02044 +#define PCIEMSIALR 0x02048 +#define MSIFE 1 +#define PCIEMSIAUR 0x0204c +#define PCIEMSIIER 0x02050 + +/* root port address */ +#define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) + +/* local address reg & mask */ +#define PCIELAR(x) (0x02200 + ((x) * 0x20)) +#define PCIELAMR(x) (0x02208 + ((x) * 0x20)) +#define LAM_PREFETCH BIT(3) +#define LAM_64BIT BIT(2) +#define LAR_ENABLE BIT(1) + +/* PCIe address reg & mask */ +#define PCIEPALR(x) (0x03400 + ((x) * 0x20)) +#define PCIEPAUR(x) (0x03404 + ((x) * 0x20)) +#define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) +#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) +#define PAR_ENABLE BIT(31) +#define IO_SPACE BIT(8) + +/* Configuration */ +#define PCICONF(x) (0x010000 + ((x) * 0x4)) +#define PMCAP(x) (0x010040 + ((x) * 0x4)) +#define EXPCAP(x) (0x010070 + ((x) * 0x4)) +#define VCCAP(x) (0x010100 + ((x) * 0x4)) + +/* link layer */ +#define IDSETR1 0x011004 +#define TLCTLR 0x011048 +#define MACSR 0x011054 +#define SPCHGFIN BIT(4) +#define SPCHGFAIL BIT(6) +#define SPCHGSUC BIT(7) +#define LINK_SPEED (0xf << 16) +#define LINK_SPEED_2_5GTS (1 << 16) +#define LINK_SPEED_5_0GTS (2 << 16) +#define MACCTLR 0x011058 +#define SPEED_CHANGE BIT(24) +#define SCRAMBLE_DISABLE BIT(27) +#define MACS2R 0x011078 +#define MACCGSPSETR 0x011084 +#define SPCNGRSN BIT(31) + +/* R-Car H1 PHY */ +#define H1_PCIEPHYADRR 0x04000c +#define WRITE_CMD BIT(16) +#define PHY_ACK BIT(24) +#define RATE_POS 12 +#define LANE_POS 8 +#define ADR_POS 0 +#define H1_PCIEPHYDOUTR 0x040014 + +/* R-Car Gen2 PHY */ +#define GEN2_PCIEPHYADDR 0x780 +#define GEN2_PCIEPHYDATA 0x784 +#define GEN2_PCIEPHYCTRL 0x78c + +#define INT_PCI_MSI_NR 32 + +#define RCONF(x) (PCICONF(0) + (x)) +#define RPMCAP(x) (PMCAP(0) + (x)) +#define REXPCAP(x) (EXPCAP(0) + (x)) +#define RVCCAP(x) (VCCAP(0) + (x)) + +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) + +#define RCAR_PCI_MAX_RESOURCES 4 +#define MAX_NR_INBOUND_MAPS 6 + +#define PCI_EXP_FLAGS 2 /* Capabilities register */ +#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ +#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ +#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ +#define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */ +#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ +#define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */ + +enum { + RCAR_PCI_ACCESS_READ, + RCAR_PCI_ACCESS_WRITE, +}; + +struct rcar_gen3_pcie_priv { + fdt_addr_t regs; +}; + +static void rcar_rmw32(struct udevice *dev, int where, u32 mask, u32 data) +{ + struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev); + int shift = 8 * (where & 3); + + clrsetbits_le32(priv->regs + (where & ~3), + mask << shift, data << shift); +} + +static u32 rcar_read_conf(struct udevice *dev, int where) +{ + struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev); + int shift = 8 * (where & 3); + + return readl(priv->regs + (where & ~3)) >> shift; +} + +static int rcar_pcie_config_access(struct udevice *udev, + unsigned char access_type, + pci_dev_t bdf, int where, ulong *data) +{ + struct rcar_gen3_pcie_priv *priv = dev_get_platdata(udev); + u32 reg = where & ~3; + + /* Clear errors */ + clrbits_le32(priv->regs + PCIEERRFR, 0); + + /* Set the PIO address */ + writel((bdf << 8) | reg, priv->regs + PCIECAR); + + /* Enable the configuration access */ + if (!PCI_BUS(bdf)) + writel(CONFIG_SEND_ENABLE | TYPE0, priv->regs + PCIECCTLR); + else + writel(CONFIG_SEND_ENABLE | TYPE1, priv->regs + PCIECCTLR); + + /* Check for errors */ + if (readl(priv->regs + PCIEERRFR) & UNSUPPORTED_REQUEST) + return -ENODEV; + + /* Check for master and target aborts */ + if (rcar_read_conf(udev, RCONF(PCI_STATUS)) & + (PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT)) + return -ENODEV; + + if (access_type == RCAR_PCI_ACCESS_READ) + *data = readl(priv->regs + PCIECDR); + else + writel(*data, priv->regs + PCIECDR); + + /* Disable the configuration access */ + writel(0, priv->regs + PCIECCTLR); + + return 0; +} + +static int rcar_gen3_pcie_addr_valid(pci_dev_t d, uint where) +{ + u32 slot; + + if (PCI_FUNC(d)) + return -EINVAL; + + slot = PCI_DEV(d); + if (slot != 1) + return -EINVAL; + + return 0; +} + +static int rcar_gen3_pcie_read_config(struct udevice *dev, pci_dev_t bdf, + uint where, ulong *val, + enum pci_size_t size) +{ + ulong reg; + int ret; + + ret = rcar_gen3_pcie_addr_valid(bdf, where); + if (ret) { + *val = pci_get_ff(size); + return 0; + } + + ret = rcar_pcie_config_access(dev, RCAR_PCI_ACCESS_READ, + bdf, where, ®); + if (ret != 0) + reg = 0xffffffffUL; + + *val = pci_conv_32_to_size(reg, where, size); + + return ret; +} + +static int rcar_gen3_pcie_write_config(struct udevice *dev, pci_dev_t bdf, + uint where, ulong val, + enum pci_size_t size) +{ + ulong data; + int ret; + + ret = rcar_gen3_pcie_addr_valid(bdf, where); + if (ret) + return ret; + + data = pci_conv_32_to_size(val, where, size); + + ret = rcar_pcie_config_access(dev, RCAR_PCI_ACCESS_WRITE, + bdf, where, &data); + + return ret; +} + +static int rcar_gen3_pcie_wait_for_phyrdy(struct udevice *dev) +{ + struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev); + + return wait_for_bit_le32((void *)priv->regs + PCIEPHYSR, PHYRDY, + true, 50, false); +} + +static int rcar_gen3_pcie_wait_for_dl(struct udevice *dev) +{ + struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev); + + return wait_for_bit_le32((void *)priv->regs + PCIETSTR, + DATA_LINK_ACTIVE, true, 50, false); +} + +static int rcar_gen3_pcie_hw_init(struct udevice *dev) +{ + struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev); + int ret; + + /* Begin initialization */ + writel(0, priv->regs + PCIETCTLR); + + /* Set mode */ + writel(1, priv->regs + PCIEMSR); + + ret = rcar_gen3_pcie_wait_for_phyrdy(dev); + if (ret) + return ret; + + /* + * Initial header for port config space is type 1, set the device + * class to match. Hardware takes care of propagating the IDSETR + * settings, so there is no need to bother with a quirk. + */ + writel(PCI_CLASS_BRIDGE_PCI << 16, priv->regs + IDSETR1); + + /* + * Setup Secondary Bus Number & Subordinate Bus Number, even though + * they aren't used, to avoid bridge being detected as broken. + */ + rcar_rmw32(dev, RCONF(PCI_SECONDARY_BUS), 0xff, 1); + rcar_rmw32(dev, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1); + + /* Initialize default capabilities. */ + rcar_rmw32(dev, REXPCAP(0), 0xff, PCI_CAP_ID_EXP); + rcar_rmw32(dev, REXPCAP(PCI_EXP_FLAGS), + PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4); + rcar_rmw32(dev, RCONF(PCI_HEADER_TYPE), 0x7f, + PCI_HEADER_TYPE_BRIDGE); + + /* Enable data link layer active state reporting */ + rcar_rmw32(dev, REXPCAP(PCI_EXP_LNKCAP), + PCI_EXP_LNKCAP_DLLLARC, PCI_EXP_LNKCAP_DLLLARC); + + /* Write out the physical slot number = 0 */ + rcar_rmw32(dev, REXPCAP(PCI_EXP_SLTCAP), + PCI_EXP_SLTCAP_PSN, 0); + + /* Set the completion timer timeout to the maximum 50ms. */ + rcar_rmw32(dev, TLCTLR + 1, 0x3f, 50); + + /* Terminate list of capabilities (Next Capability Offset=0) */ + rcar_rmw32(dev, RVCCAP(0), 0xfff00000, 0); + + /* Finish initialization - establish a PCI Express link */ + writel(CFINIT, priv->regs + PCIETCTLR); + + return rcar_gen3_pcie_wait_for_dl(dev); +} + +static int rcar_gen3_pcie_probe(struct udevice *dev) +{ + struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev); + struct pci_controller *hose = dev_get_uclass_priv(dev); + struct clk pci_clk; + u32 mask; + int i, cnt, ret; + + ret = clk_get_by_index(dev, 0, &pci_clk); + if (ret) + return ret; + + ret = clk_enable(&pci_clk); + if (ret) + return ret; + + for (i = 0; i < hose->region_count; i++) { + if (hose->regions[i].flags != PCI_REGION_SYS_MEMORY) + continue; + + if (hose->regions[i].phys_start == 0) + continue; + + mask = (hose->regions[i].size - 1) & ~0xf; + mask |= LAR_ENABLE; + writel(hose->regions[i].phys_start, priv->regs + PCIEPRAR(0)); + writel(hose->regions[i].phys_start, priv->regs + PCIELAR(0)); + writel(mask, priv->regs + PCIELAMR(0)); + break; + } + + writel(0, priv->regs + PCIEPRAR(4)); + writel(0, priv->regs + PCIELAR(4)); + writel(0, priv->regs + PCIELAMR(4)); + + ret = rcar_gen3_pcie_hw_init(dev); + if (ret) + return ret; + + for (i = 0, cnt = 0; i < hose->region_count; i++) { + if (hose->regions[i].flags == PCI_REGION_SYS_MEMORY) + continue; + + writel(0, priv->regs + PCIEPTCTLR(cnt)); + writel((hose->regions[i].size - 1) & ~0x7f, + priv->regs + PCIEPAMR(cnt)); + writel(upper_32_bits(hose->regions[i].phys_start), + priv->regs + PCIEPAUR(cnt)); + writel(lower_32_bits(hose->regions[i].phys_start), + priv->regs + PCIEPALR(cnt)); + mask = PAR_ENABLE; + if (hose->regions[i].flags == PCI_REGION_IO) + mask |= IO_SPACE; + writel(mask, priv->regs + PCIEPTCTLR(cnt)); + + cnt++; + } + + return 0; +} + +static int rcar_gen3_pcie_ofdata_to_platdata(struct udevice *dev) +{ + struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev); + + priv->regs = devfdt_get_addr_index(dev, 0); + if (!priv->regs) + return -EINVAL; + + return 0; +} + +static const struct dm_pci_ops rcar_gen3_pcie_ops = { + .read_config = rcar_gen3_pcie_read_config, + .write_config = rcar_gen3_pcie_write_config, +}; + +static const struct udevice_id rcar_gen3_pcie_ids[] = { + { .compatible = "renesas,pcie-rcar-gen3" }, + { } +}; + +U_BOOT_DRIVER(rcar_gen3_pcie) = { + .name = "rcar_gen3_pcie", + .id = UCLASS_PCI, + .of_match = rcar_gen3_pcie_ids, + .ops = &rcar_gen3_pcie_ops, + .probe = rcar_gen3_pcie_probe, + .ofdata_to_platdata = rcar_gen3_pcie_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct rcar_gen3_pcie_priv), +}; diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index d7237f6eee..1a3bf70834 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -359,7 +359,8 @@ int dm_pciauto_config_device(struct udevice *dev) PCI_DEV(dm_pci_get_bdf(dev))); break; #endif -#if defined(CONFIG_MPC834x) && !defined(CONFIG_VME8349) +#if defined(CONFIG_ARCH_MPC834X) && !defined(CONFIG_TARGET_VME8349) && \ + !defined(CONFIG_TARGET_CADDY2) case PCI_CLASS_BRIDGE_OTHER: /* * The host/PCI bridge 1 seems broken in 8349 - it presents diff --git a/drivers/pci/pci_auto_old.c b/drivers/pci/pci_auto_old.c index e705a3072e..b566705c9d 100644 --- a/drivers/pci/pci_auto_old.c +++ b/drivers/pci/pci_auto_old.c @@ -376,7 +376,8 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev) PCI_DEV(dev)); break; #endif -#if defined(CONFIG_MPC834x) && !defined(CONFIG_VME8349) +#if defined(CONFIG_ARCH_MPC834X) && !defined(CONFIG_TARGET_VME8349) && \ + !defined(CONFIG_TARGET_CADDY2) case PCI_CLASS_BRIDGE_OTHER: /* * The host/PCI bridge 1 seems broken in 8349 - it presents diff --git a/drivers/pci/pci_rom.c b/drivers/pci/pci_rom.c index 7d9b75c2c4..2cede1211b 100644 --- a/drivers/pci/pci_rom.c +++ b/drivers/pci/pci_rom.c @@ -306,7 +306,7 @@ int dm_pci_run_vga_bios(struct udevice *dev, int (*int15_handler)(void), goto err; #endif } else { -#if defined(CONFIG_X86) && CONFIG_IS_ENABLED(X86_32BIT_INIT) +#if defined(CONFIG_X86) && (CONFIG_IS_ENABLED(X86_32BIT_INIT) || CONFIG_TPL) bios_set_interrupt_handler(0x15, int15_handler); bios_run_on_x86(dev, (unsigned long)ram, vesa_mode, diff --git a/drivers/pci/pcie_layerscape_gen4.c b/drivers/pci/pcie_layerscape_gen4.c new file mode 100644 index 0000000000..1fd8761bbc --- /dev/null +++ b/drivers/pci/pcie_layerscape_gen4.c @@ -0,0 +1,572 @@ +// SPDX-License-Identifier: GPL-2.0+ OR X11 +/* + * Copyright 2018-2019 NXP + * + * PCIe Gen4 driver for NXP Layerscape SoCs + * Author: Hou Zhiqiang <Minder.Hou@gmail.com> + */ + +#include <common.h> +#include <asm/arch/fsl_serdes.h> +#include <pci.h> +#include <asm/io.h> +#include <errno.h> +#include <malloc.h> +#include <dm.h> +#include <linux/sizes.h> + +#include "pcie_layerscape_gen4.h" + +DECLARE_GLOBAL_DATA_PTR; + +LIST_HEAD(ls_pcie_g4_list); + +static u64 bar_size[4] = { + PCIE_BAR0_SIZE, + PCIE_BAR1_SIZE, + PCIE_BAR2_SIZE, + PCIE_BAR4_SIZE +}; + +static int ls_pcie_g4_ltssm(struct ls_pcie_g4 *pcie) +{ + u32 state; + + state = pf_ctrl_readl(pcie, PCIE_LTSSM_STA) & LTSSM_STATE_MASK; + + return state; +} + +static int ls_pcie_g4_link_up(struct ls_pcie_g4 *pcie) +{ + int ltssm; + + ltssm = ls_pcie_g4_ltssm(pcie); + if (ltssm != LTSSM_PCIE_L0) + return 0; + + return 1; +} + +static void ls_pcie_g4_ep_enable_cfg(struct ls_pcie_g4 *pcie) +{ + ccsr_writel(pcie, GPEX_CFG_READY, PCIE_CONFIG_READY); +} + +static void ls_pcie_g4_cfg_set_target(struct ls_pcie_g4 *pcie, u32 target) +{ + ccsr_writel(pcie, PAB_AXI_AMAP_PEX_WIN_L(0), target); + ccsr_writel(pcie, PAB_AXI_AMAP_PEX_WIN_H(0), 0); +} + +static int ls_pcie_g4_outbound_win_set(struct ls_pcie_g4 *pcie, int idx, + int type, u64 phys, u64 bus_addr, + pci_size_t size) +{ + u32 val; + u32 size_h, size_l; + + if (idx >= PAB_WINS_NUM) + return -EINVAL; + + size_h = upper_32_bits(~(size - 1)); + size_l = lower_32_bits(~(size - 1)); + + val = ccsr_readl(pcie, PAB_AXI_AMAP_CTRL(idx)); + val &= ~((AXI_AMAP_CTRL_TYPE_MASK << AXI_AMAP_CTRL_TYPE_SHIFT) | + (AXI_AMAP_CTRL_SIZE_MASK << AXI_AMAP_CTRL_SIZE_SHIFT) | + AXI_AMAP_CTRL_EN); + val |= ((type & AXI_AMAP_CTRL_TYPE_MASK) << AXI_AMAP_CTRL_TYPE_SHIFT) | + ((size_l >> AXI_AMAP_CTRL_SIZE_SHIFT) << + AXI_AMAP_CTRL_SIZE_SHIFT) | AXI_AMAP_CTRL_EN; + + ccsr_writel(pcie, PAB_AXI_AMAP_CTRL(idx), val); + + ccsr_writel(pcie, PAB_AXI_AMAP_AXI_WIN(idx), lower_32_bits(phys)); + ccsr_writel(pcie, PAB_EXT_AXI_AMAP_AXI_WIN(idx), upper_32_bits(phys)); + ccsr_writel(pcie, PAB_AXI_AMAP_PEX_WIN_L(idx), lower_32_bits(bus_addr)); + ccsr_writel(pcie, PAB_AXI_AMAP_PEX_WIN_H(idx), upper_32_bits(bus_addr)); + ccsr_writel(pcie, PAB_EXT_AXI_AMAP_SIZE(idx), size_h); + + return 0; +} + +static int ls_pcie_g4_rc_inbound_win_set(struct ls_pcie_g4 *pcie, int idx, + int type, u64 phys, u64 bus_addr, + pci_size_t size) +{ + u32 val; + pci_size_t win_size = ~(size - 1); + + val = ccsr_readl(pcie, PAB_PEX_AMAP_CTRL(idx)); + + val &= ~(PEX_AMAP_CTRL_TYPE_MASK << PEX_AMAP_CTRL_TYPE_SHIFT); + val &= ~(PEX_AMAP_CTRL_EN_MASK << PEX_AMAP_CTRL_EN_SHIFT); + val = (val | (type << PEX_AMAP_CTRL_TYPE_SHIFT)); + val = (val | (1 << PEX_AMAP_CTRL_EN_SHIFT)); + + ccsr_writel(pcie, PAB_PEX_AMAP_CTRL(idx), + val | lower_32_bits(win_size)); + + ccsr_writel(pcie, PAB_EXT_PEX_AMAP_SIZE(idx), upper_32_bits(win_size)); + ccsr_writel(pcie, PAB_PEX_AMAP_AXI_WIN(idx), lower_32_bits(phys)); + ccsr_writel(pcie, PAB_EXT_PEX_AMAP_AXI_WIN(idx), upper_32_bits(phys)); + ccsr_writel(pcie, PAB_PEX_AMAP_PEX_WIN_L(idx), lower_32_bits(bus_addr)); + ccsr_writel(pcie, PAB_PEX_AMAP_PEX_WIN_H(idx), upper_32_bits(bus_addr)); + + return 0; +} + +static void ls_pcie_g4_dump_wins(struct ls_pcie_g4 *pcie, int wins) +{ + int i; + + for (i = 0; i < wins; i++) { + debug("APIO Win%d:\n", i); + debug("\tLOWER PHYS: 0x%08x\n", + ccsr_readl(pcie, PAB_AXI_AMAP_AXI_WIN(i))); + debug("\tUPPER PHYS: 0x%08x\n", + ccsr_readl(pcie, PAB_EXT_AXI_AMAP_AXI_WIN(i))); + debug("\tLOWER BUS: 0x%08x\n", + ccsr_readl(pcie, PAB_AXI_AMAP_PEX_WIN_L(i))); + debug("\tUPPER BUS: 0x%08x\n", + ccsr_readl(pcie, PAB_AXI_AMAP_PEX_WIN_H(i))); + debug("\tSIZE: 0x%08x\n", + ccsr_readl(pcie, PAB_AXI_AMAP_CTRL(i)) & + (AXI_AMAP_CTRL_SIZE_MASK << AXI_AMAP_CTRL_SIZE_SHIFT)); + debug("\tEXT_SIZE: 0x%08x\n", + ccsr_readl(pcie, PAB_EXT_AXI_AMAP_SIZE(i))); + debug("\tPARAM: 0x%08x\n", + ccsr_readl(pcie, PAB_AXI_AMAP_PCI_HDR_PARAM(i))); + debug("\tCTRL: 0x%08x\n", + ccsr_readl(pcie, PAB_AXI_AMAP_CTRL(i))); + } +} + +static void ls_pcie_g4_setup_wins(struct ls_pcie_g4 *pcie) +{ + struct pci_region *io, *mem, *pref; + int idx = 1; + + /* INBOUND WIN */ + ls_pcie_g4_rc_inbound_win_set(pcie, 0, IB_TYPE_MEM_F, 0, 0, SIZE_1T); + + /* OUTBOUND WIN 0: CFG */ + ls_pcie_g4_outbound_win_set(pcie, 0, PAB_AXI_TYPE_CFG, + pcie->cfg_res.start, 0, + fdt_resource_size(&pcie->cfg_res)); + + pci_get_regions(pcie->bus, &io, &mem, &pref); + + if (io) + /* OUTBOUND WIN: IO */ + ls_pcie_g4_outbound_win_set(pcie, idx++, PAB_AXI_TYPE_IO, + io->phys_start, io->bus_start, + io->size); + + if (mem) + /* OUTBOUND WIN: MEM */ + ls_pcie_g4_outbound_win_set(pcie, idx++, PAB_AXI_TYPE_MEM, + mem->phys_start, mem->bus_start, + mem->size); + + if (pref) + /* OUTBOUND WIN: perf MEM */ + ls_pcie_g4_outbound_win_set(pcie, idx++, PAB_AXI_TYPE_MEM, + pref->phys_start, pref->bus_start, + pref->size); + + ls_pcie_g4_dump_wins(pcie, idx); +} + +/* Return 0 if the address is valid, -errno if not valid */ +static int ls_pcie_g4_addr_valid(struct ls_pcie_g4 *pcie, pci_dev_t bdf) +{ + struct udevice *bus = pcie->bus; + + if (pcie->mode == PCI_HEADER_TYPE_NORMAL) + return -ENODEV; + + if (!pcie->enabled) + return -ENXIO; + + if (PCI_BUS(bdf) < bus->seq) + return -EINVAL; + + if ((PCI_BUS(bdf) > bus->seq) && (!ls_pcie_g4_link_up(pcie))) + return -EINVAL; + + if (PCI_BUS(bdf) <= (bus->seq + 1) && (PCI_DEV(bdf) > 0)) + return -EINVAL; + + return 0; +} + +void *ls_pcie_g4_conf_address(struct ls_pcie_g4 *pcie, pci_dev_t bdf, + int offset) +{ + struct udevice *bus = pcie->bus; + u32 target; + + if (PCI_BUS(bdf) == bus->seq) { + if (offset < INDIRECT_ADDR_BNDRY) { + ccsr_set_page(pcie, 0); + return pcie->ccsr + offset; + } + + ccsr_set_page(pcie, OFFSET_TO_PAGE_IDX(offset)); + return pcie->ccsr + OFFSET_TO_PAGE_ADDR(offset); + } + + target = PAB_TARGET_BUS(PCI_BUS(bdf) - bus->seq) | + PAB_TARGET_DEV(PCI_DEV(bdf)) | + PAB_TARGET_FUNC(PCI_FUNC(bdf)); + + ls_pcie_g4_cfg_set_target(pcie, target); + + return pcie->cfg + offset; +} + +static int ls_pcie_g4_read_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong *valuep, + enum pci_size_t size) +{ + struct ls_pcie_g4 *pcie = dev_get_priv(bus); + void *address; + int ret = 0; + + if (ls_pcie_g4_addr_valid(pcie, bdf)) { + *valuep = pci_get_ff(size); + return 0; + } + + address = ls_pcie_g4_conf_address(pcie, bdf, offset); + + switch (size) { + case PCI_SIZE_8: + *valuep = readb(address); + break; + case PCI_SIZE_16: + *valuep = readw(address); + break; + case PCI_SIZE_32: + *valuep = readl(address); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int ls_pcie_g4_write_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + struct ls_pcie_g4 *pcie = dev_get_priv(bus); + void *address; + + if (ls_pcie_g4_addr_valid(pcie, bdf)) + return 0; + + address = ls_pcie_g4_conf_address(pcie, bdf, offset); + + switch (size) { + case PCI_SIZE_8: + writeb(value, address); + return 0; + case PCI_SIZE_16: + writew(value, address); + return 0; + case PCI_SIZE_32: + writel(value, address); + return 0; + default: + return -EINVAL; + } +} + +static void ls_pcie_g4_setup_ctrl(struct ls_pcie_g4 *pcie) +{ + u32 val; + + /* Fix class code */ + val = ccsr_readl(pcie, GPEX_CLASSCODE); + val &= ~(GPEX_CLASSCODE_MASK << GPEX_CLASSCODE_SHIFT); + val |= PCI_CLASS_BRIDGE_PCI << GPEX_CLASSCODE_SHIFT; + ccsr_writel(pcie, GPEX_CLASSCODE, val); + + /* Enable APIO and Memory/IO/CFG Wins */ + val = ccsr_readl(pcie, PAB_AXI_PIO_CTRL(0)); + val |= APIO_EN | MEM_WIN_EN | IO_WIN_EN | CFG_WIN_EN; + ccsr_writel(pcie, PAB_AXI_PIO_CTRL(0), val); + + ls_pcie_g4_setup_wins(pcie); + + pcie->stream_id_cur = 0; +} + +static void ls_pcie_g4_ep_inbound_win_set(struct ls_pcie_g4 *pcie, int pf, + int bar, u64 phys) +{ + u32 val; + + /* PF BAR1 is for MSI-X and only need to enable */ + if (bar == 1) { + ccsr_writel(pcie, PAB_PEX_BAR_AMAP(pf, bar), BAR_AMAP_EN); + return; + } + + val = upper_32_bits(phys); + ccsr_writel(pcie, PAB_EXT_PEX_BAR_AMAP(pf, bar), val); + val = lower_32_bits(phys) | BAR_AMAP_EN; + ccsr_writel(pcie, PAB_PEX_BAR_AMAP(pf, bar), val); +} + +static void ls_pcie_g4_ep_setup_wins(struct ls_pcie_g4 *pcie, int pf) +{ + u64 phys; + int bar; + u32 val; + + if ((!pcie->sriov_support && pf > LS_G4_PF0) || pf > LS_G4_PF1) + return; + + phys = CONFIG_SYS_PCI_EP_MEMORY_BASE + PCIE_BAR_SIZE * 4 * pf; + for (bar = 0; bar < PF_BAR_NUM; bar++) { + ls_pcie_g4_ep_inbound_win_set(pcie, pf, bar, phys); + phys += PCIE_BAR_SIZE; + } + + /* OUTBOUND: map MEM */ + ls_pcie_g4_outbound_win_set(pcie, pf, PAB_AXI_TYPE_MEM, + pcie->cfg_res.start + + CONFIG_SYS_PCI_MEMORY_SIZE * pf, 0x0, + CONFIG_SYS_PCI_MEMORY_SIZE); + + val = ccsr_readl(pcie, PAB_AXI_AMAP_PCI_HDR_PARAM(pf)); + val &= ~FUNC_NUM_PCIE_MASK; + val |= pf; + ccsr_writel(pcie, PAB_AXI_AMAP_PCI_HDR_PARAM(pf), val); +} + +static void ls_pcie_g4_ep_enable_bar(struct ls_pcie_g4 *pcie, int pf, + int bar, bool vf_bar, bool enable) +{ + u32 val; + u32 bar_pos = BAR_POS(bar, pf, vf_bar); + + val = ccsr_readl(pcie, GPEX_BAR_ENABLE); + if (enable) + val |= 1 << bar_pos; + else + val &= ~(1 << bar_pos); + ccsr_writel(pcie, GPEX_BAR_ENABLE, val); +} + +static void ls_pcie_g4_ep_set_bar_size(struct ls_pcie_g4 *pcie, int pf, + int bar, bool vf_bar, u64 size) +{ + u32 bar_pos = BAR_POS(bar, pf, vf_bar); + u32 mask_l = lower_32_bits(~(size - 1)); + u32 mask_h = upper_32_bits(~(size - 1)); + + ccsr_writel(pcie, GPEX_BAR_SELECT, bar_pos); + ccsr_writel(pcie, GPEX_BAR_SIZE_LDW, mask_l); + ccsr_writel(pcie, GPEX_BAR_SIZE_UDW, mask_h); +} + +static void ls_pcie_g4_ep_setup_bar(struct ls_pcie_g4 *pcie, int pf, + int bar, bool vf_bar, u64 size) +{ + bool en = size ? true : false; + + ls_pcie_g4_ep_enable_bar(pcie, pf, bar, vf_bar, en); + ls_pcie_g4_ep_set_bar_size(pcie, pf, bar, vf_bar, size); +} + +static void ls_pcie_g4_ep_setup_bars(struct ls_pcie_g4 *pcie, int pf) +{ + int bar; + + /* Setup PF BARs */ + for (bar = 0; bar < PF_BAR_NUM; bar++) + ls_pcie_g4_ep_setup_bar(pcie, pf, bar, false, bar_size[bar]); + + if (!pcie->sriov_support) + return; + + /* Setup VF BARs */ + for (bar = 0; bar < VF_BAR_NUM; bar++) + ls_pcie_g4_ep_setup_bar(pcie, pf, bar, true, bar_size[bar]); +} + +static void ls_pcie_g4_set_sriov(struct ls_pcie_g4 *pcie, int pf) +{ + unsigned int val; + + val = ccsr_readl(pcie, GPEX_SRIOV_INIT_VFS_TOTAL_VF(pf)); + val &= ~(TTL_VF_MASK << TTL_VF_SHIFT); + val |= PCIE_VF_NUM << TTL_VF_SHIFT; + val &= ~(INI_VF_MASK << INI_VF_SHIFT); + val |= PCIE_VF_NUM << INI_VF_SHIFT; + ccsr_writel(pcie, GPEX_SRIOV_INIT_VFS_TOTAL_VF(pf), val); + + val = ccsr_readl(pcie, PCIE_SRIOV_VF_OFFSET_STRIDE); + val += PCIE_VF_NUM * pf - pf; + ccsr_writel(pcie, GPEX_SRIOV_VF_OFFSET_STRIDE(pf), val); +} + +static void ls_pcie_g4_setup_ep(struct ls_pcie_g4 *pcie) +{ + u32 pf, sriov; + u32 val; + int i; + + /* Enable APIO and Memory Win */ + val = ccsr_readl(pcie, PAB_AXI_PIO_CTRL(0)); + val |= APIO_EN | MEM_WIN_EN; + ccsr_writel(pcie, PAB_AXI_PIO_CTRL(0), val); + + sriov = ccsr_readl(pcie, PCIE_SRIOV_CAPABILITY); + if (PCI_EXT_CAP_ID(sriov) == PCI_EXT_CAP_ID_SRIOV) + pcie->sriov_support = 1; + + pf = pcie->sriov_support ? PCIE_PF_NUM : 1; + + for (i = 0; i < pf; i++) { + ls_pcie_g4_ep_setup_bars(pcie, i); + ls_pcie_g4_ep_setup_wins(pcie, i); + if (pcie->sriov_support) + ls_pcie_g4_set_sriov(pcie, i); + } + + ls_pcie_g4_ep_enable_cfg(pcie); + ls_pcie_g4_dump_wins(pcie, pf); +} + +static int ls_pcie_g4_probe(struct udevice *dev) +{ + struct ls_pcie_g4 *pcie = dev_get_priv(dev); + const void *fdt = gd->fdt_blob; + int node = dev_of_offset(dev); + u32 link_ctrl_sta; + u32 val; + int ret; + + pcie->bus = dev; + + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "ccsr", &pcie->ccsr_res); + if (ret) { + printf("ls-pcie-g4: resource \"ccsr\" not found\n"); + return ret; + } + + pcie->idx = (pcie->ccsr_res.start - PCIE_SYS_BASE_ADDR) / + PCIE_CCSR_SIZE; + + list_add(&pcie->list, &ls_pcie_g4_list); + + pcie->enabled = is_serdes_configured(PCIE_SRDS_PRTCL(pcie->idx)); + if (!pcie->enabled) { + printf("PCIe%d: %s disabled\n", pcie->idx, dev->name); + return 0; + } + + pcie->ccsr = map_physmem(pcie->ccsr_res.start, + fdt_resource_size(&pcie->ccsr_res), + MAP_NOCACHE); + + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "config", &pcie->cfg_res); + if (ret) { + printf("%s: resource \"config\" not found\n", dev->name); + return ret; + } + + pcie->cfg = map_physmem(pcie->cfg_res.start, + fdt_resource_size(&pcie->cfg_res), + MAP_NOCACHE); + + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "lut", &pcie->lut_res); + if (ret) { + printf("ls-pcie-g4: resource \"lut\" not found\n"); + return ret; + } + + pcie->lut = map_physmem(pcie->lut_res.start, + fdt_resource_size(&pcie->lut_res), + MAP_NOCACHE); + + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "pf_ctrl", &pcie->pf_ctrl_res); + if (ret) { + printf("ls-pcie-g4: resource \"pf_ctrl\" not found\n"); + return ret; + } + + pcie->pf_ctrl = map_physmem(pcie->pf_ctrl_res.start, + fdt_resource_size(&pcie->pf_ctrl_res), + MAP_NOCACHE); + + pcie->big_endian = fdtdec_get_bool(fdt, node, "big-endian"); + + debug("%s ccsr:%lx, cfg:0x%lx, big-endian:%d\n", + dev->name, (unsigned long)pcie->ccsr, (unsigned long)pcie->cfg, + pcie->big_endian); + + pcie->mode = readb(pcie->ccsr + PCI_HEADER_TYPE) & 0x7f; + + if (pcie->mode == PCI_HEADER_TYPE_NORMAL) { + printf("PCIe%u: %s %s", pcie->idx, dev->name, "Endpoint"); + ls_pcie_g4_setup_ep(pcie); + } else { + printf("PCIe%u: %s %s", pcie->idx, dev->name, "Root Complex"); + ls_pcie_g4_setup_ctrl(pcie); + } + + /* Enable Amba & PEX PIO */ + val = ccsr_readl(pcie, PAB_CTRL); + val |= PAB_CTRL_APIO_EN | PAB_CTRL_PPIO_EN; + ccsr_writel(pcie, PAB_CTRL, val); + + val = ccsr_readl(pcie, PAB_PEX_PIO_CTRL(0)); + val |= PPIO_EN; + ccsr_writel(pcie, PAB_PEX_PIO_CTRL(0), val); + + if (!ls_pcie_g4_link_up(pcie)) { + /* Let the user know there's no PCIe link */ + printf(": no link\n"); + return 0; + } + + /* Print the negotiated PCIe link width */ + link_ctrl_sta = ccsr_readl(pcie, PCIE_LINK_CTRL_STA); + printf(": x%d gen%d\n", + (link_ctrl_sta >> PCIE_LINK_WIDTH_SHIFT & PCIE_LINK_WIDTH_MASK), + (link_ctrl_sta >> PCIE_LINK_SPEED_SHIFT) & PCIE_LINK_SPEED_MASK); + + return 0; +} + +static const struct dm_pci_ops ls_pcie_g4_ops = { + .read_config = ls_pcie_g4_read_config, + .write_config = ls_pcie_g4_write_config, +}; + +static const struct udevice_id ls_pcie_g4_ids[] = { + { .compatible = "fsl,lx2160a-pcie" }, + { } +}; + +U_BOOT_DRIVER(pcie_layerscape_gen4) = { + .name = "pcie_layerscape_gen4", + .id = UCLASS_PCI, + .of_match = ls_pcie_g4_ids, + .ops = &ls_pcie_g4_ops, + .probe = ls_pcie_g4_probe, + .priv_auto_alloc_size = sizeof(struct ls_pcie_g4), +}; diff --git a/drivers/pci/pcie_layerscape_gen4.h b/drivers/pci/pcie_layerscape_gen4.h new file mode 100644 index 0000000000..27c2d09332 --- /dev/null +++ b/drivers/pci/pcie_layerscape_gen4.h @@ -0,0 +1,264 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2018-2019 NXP + * + * PCIe Gen4 driver for NXP Layerscape SoCs + * Author: Hou Zhiqiang <Minder.Hou@gmail.com> + */ + +#ifndef _PCIE_LAYERSCAPE_GEN4_H_ +#define _PCIE_LAYERSCAPE_GEN4_H_ +#include <pci.h> +#include <dm.h> + +#ifndef CONFIG_SYS_PCI_MEMORY_SIZE +#define CONFIG_SYS_PCI_MEMORY_SIZE (4 * 1024 * 1024 * 1024ULL) +#endif + +#ifndef CONFIG_SYS_PCI_EP_MEMORY_BASE +#define CONFIG_SYS_PCI_EP_MEMORY_BASE CONFIG_SYS_LOAD_ADDR +#endif + +#define PCIE_PF_NUM 2 +#define PCIE_VF_NUM 32 + +#define LS_G4_PF0 0 +#define LS_G4_PF1 1 +#define PF_BAR_NUM 4 +#define VF_BAR_NUM 4 +#define PCIE_BAR_SIZE (8 * 1024) /* 8K */ +#define PCIE_BAR0_SIZE PCIE_BAR_SIZE +#define PCIE_BAR1_SIZE PCIE_BAR_SIZE +#define PCIE_BAR2_SIZE PCIE_BAR_SIZE +#define PCIE_BAR4_SIZE PCIE_BAR_SIZE +#define SIZE_1T (1024 * 1024 * 1024 * 1024ULL) + +/* GPEX CSR */ +#define GPEX_CLASSCODE 0x474 +#define GPEX_CLASSCODE_SHIFT 16 +#define GPEX_CLASSCODE_MASK 0xffff + +#define GPEX_CFG_READY 0x4b0 +#define PCIE_CONFIG_READY BIT(0) + +#define GPEX_BAR_ENABLE 0x4d4 +#define GPEX_BAR_SIZE_LDW 0x4d8 +#define GPEX_BAR_SIZE_UDW 0x4dC +#define GPEX_BAR_SELECT 0x4e0 + +#define BAR_POS(bar, pf, vf_bar) \ + ((bar) + (pf) * PF_BAR_NUM + (vf_bar) * PCIE_PF_NUM * PF_BAR_NUM) + +#define GPEX_SRIOV_INIT_VFS_TOTAL_VF(pf) (0x644 + (pf) * 4) +#define TTL_VF_MASK 0xffff +#define TTL_VF_SHIFT 16 +#define INI_VF_MASK 0xffff +#define INI_VF_SHIFT 0 +#define GPEX_SRIOV_VF_OFFSET_STRIDE(pf) (0x704 + (pf) * 4) + +/* PAB CSR */ +#define PAB_CTRL 0x808 +#define PAB_CTRL_APIO_EN BIT(0) +#define PAB_CTRL_PPIO_EN BIT(1) +#define PAB_CTRL_MAX_BRST_LEN_SHIFT 4 +#define PAB_CTRL_MAX_BRST_LEN_MASK 0x3 +#define PAB_CTRL_PAGE_SEL_SHIFT 13 +#define PAB_CTRL_PAGE_SEL_MASK 0x3f +#define PAB_CTRL_FUNC_SEL_SHIFT 19 +#define PAB_CTRL_FUNC_SEL_MASK 0x1ff + +#define PAB_RST_CTRL 0x820 +#define PAB_BR_STAT 0x80c + +/* AXI PIO Engines */ +#define PAB_AXI_PIO_CTRL(idx) (0x840 + 0x10 * (idx)) +#define APIO_EN BIT(0) +#define MEM_WIN_EN BIT(1) +#define IO_WIN_EN BIT(2) +#define CFG_WIN_EN BIT(3) +#define PAB_AXI_PIO_STAT(idx) (0x844 + 0x10 * (idx)) +#define PAB_AXI_PIO_SL_CMD_STAT(idx) (0x848 + 0x10 * (idx)) +#define PAB_AXI_PIO_SL_ADDR_STAT(idx) (0x84c + 0x10 * (idx)) +#define PAB_AXI_PIO_SL_EXT_ADDR_STAT(idx) (0xb8a0 + 0x4 * (idx)) + +/* PEX PIO Engines */ +#define PAB_PEX_PIO_CTRL(idx) (0x8c0 + 0x10 * (idx)) +#define PPIO_EN BIT(0) +#define PAB_PEX_PIO_STAT(idx) (0x8c4 + 0x10 * (idx)) +#define PAB_PEX_PIO_MT_STAT(idx) (0x8c8 + 0x10 * (idx)) + +#define INDIRECT_ADDR_BNDRY 0xc00 +#define PAGE_IDX_SHIFT 10 +#define PAGE_ADDR_MASK 0x3ff + +#define OFFSET_TO_PAGE_IDX(off) \ + (((off) >> PAGE_IDX_SHIFT) & PAB_CTRL_PAGE_SEL_MASK) + +#define OFFSET_TO_PAGE_ADDR(off) \ + (((off) & PAGE_ADDR_MASK) | INDIRECT_ADDR_BNDRY) + +/* APIO WINs */ +#define PAB_AXI_AMAP_CTRL(idx) (0xba0 + 0x10 * (idx)) +#define PAB_EXT_AXI_AMAP_SIZE(idx) (0xbaf0 + 0x4 * (idx)) +#define PAB_AXI_AMAP_AXI_WIN(idx) (0xba4 + 0x10 * (idx)) +#define PAB_EXT_AXI_AMAP_AXI_WIN(idx) (0x80a0 + 0x4 * (idx)) +#define PAB_AXI_AMAP_PEX_WIN_L(idx) (0xba8 + 0x10 * (idx)) +#define PAB_AXI_AMAP_PEX_WIN_H(idx) (0xbac + 0x10 * (idx)) +#define PAB_AXI_AMAP_PCI_HDR_PARAM(idx) (0x5ba0 + 0x4 * (idx)) +#define FUNC_NUM_PCIE_MASK GENMASK(7, 0) + +#define AXI_AMAP_CTRL_EN BIT(0) +#define AXI_AMAP_CTRL_TYPE_SHIFT 1 +#define AXI_AMAP_CTRL_TYPE_MASK 0x3 +#define AXI_AMAP_CTRL_SIZE_SHIFT 10 +#define AXI_AMAP_CTRL_SIZE_MASK 0x3fffff + +#define PAB_TARGET_BUS(x) (((x) & 0xff) << 24) +#define PAB_TARGET_DEV(x) (((x) & 0x1f) << 19) +#define PAB_TARGET_FUNC(x) (((x) & 0x7) << 16) + +#define PAB_AXI_TYPE_CFG 0x00 +#define PAB_AXI_TYPE_IO 0x01 +#define PAB_AXI_TYPE_MEM 0x02 +#define PAB_AXI_TYPE_ATOM 0x03 + +#define PAB_WINS_NUM 256 + +/* PPIO WINs RC mode */ +#define PAB_PEX_AMAP_CTRL(idx) (0x4ba0 + 0x10 * (idx)) +#define PAB_EXT_PEX_AMAP_SIZE(idx) (0xbef0 + 0x04 * (idx)) +#define PAB_PEX_AMAP_AXI_WIN(idx) (0x4ba4 + 0x10 * (idx)) +#define PAB_EXT_PEX_AMAP_AXI_WIN(idx) (0xb4a0 + 0x04 * (idx)) +#define PAB_PEX_AMAP_PEX_WIN_L(idx) (0x4ba8 + 0x10 * (idx)) +#define PAB_PEX_AMAP_PEX_WIN_H(idx) (0x4bac + 0x10 * (idx)) + +#define IB_TYPE_MEM_F 0x2 +#define IB_TYPE_MEM_NF 0x3 + +#define PEX_AMAP_CTRL_TYPE_SHIFT 0x1 +#define PEX_AMAP_CTRL_EN_SHIFT 0x0 +#define PEX_AMAP_CTRL_TYPE_MASK 0x3 +#define PEX_AMAP_CTRL_EN_MASK 0x1 + +/* PPIO WINs EP mode */ +#define PAB_PEX_BAR_AMAP(pf, bar) \ + (0x1ba0 + 0x20 * (pf) + 4 * (bar)) +#define BAR_AMAP_EN BIT(0) +#define PAB_EXT_PEX_BAR_AMAP(pf, bar) \ + (0x84a0 + 0x20 * (pf) + 4 * (bar)) + +/* CCSR registers */ +#define PCIE_LINK_CTRL_STA 0x5c +#define PCIE_LINK_SPEED_SHIFT 16 +#define PCIE_LINK_SPEED_MASK 0x0f +#define PCIE_LINK_WIDTH_SHIFT 20 +#define PCIE_LINK_WIDTH_MASK 0x3f +#define PCIE_SRIOV_CAPABILITY 0x2a0 +#define PCIE_SRIOV_VF_OFFSET_STRIDE 0x2b4 + +/* LUT registers */ +#define PCIE_LUT_UDR(n) (0x800 + (n) * 8) +#define PCIE_LUT_LDR(n) (0x804 + (n) * 8) +#define PCIE_LUT_ENABLE BIT(31) +#define PCIE_LUT_ENTRY_COUNT 32 + +/* PF control registers */ +#define PCIE_LTSSM_STA 0x7fc +#define LTSSM_STATE_MASK 0x7f +#define LTSSM_PCIE_L0 0x2d /* L0 state */ + +#define PCIE_SRDS_PRTCL(idx) (PCIE1 + (idx)) +#define PCIE_SYS_BASE_ADDR 0x3400000 +#define PCIE_CCSR_SIZE 0x0100000 + +struct ls_pcie_g4 { + int idx; + struct list_head list; + struct udevice *bus; + struct fdt_resource ccsr_res; + struct fdt_resource cfg_res; + struct fdt_resource lut_res; + struct fdt_resource pf_ctrl_res; + void __iomem *ccsr; + void __iomem *cfg; + void __iomem *lut; + void __iomem *pf_ctrl; + bool big_endian; + bool enabled; + int next_lut_index; + struct pci_controller hose; + int stream_id_cur; + int mode; + int sriov_support; +}; + +extern struct list_head ls_pcie_g4_list; + +static inline void lut_writel(struct ls_pcie_g4 *pcie, unsigned int value, + unsigned int offset) +{ + if (pcie->big_endian) + out_be32(pcie->lut + offset, value); + else + out_le32(pcie->lut + offset, value); +} + +static inline u32 lut_readl(struct ls_pcie_g4 *pcie, unsigned int offset) +{ + if (pcie->big_endian) + return in_be32(pcie->lut + offset); + else + return in_le32(pcie->lut + offset); +} + +static inline void ccsr_set_page(struct ls_pcie_g4 *pcie, u8 pg_idx) +{ + u32 val; + + val = in_le32(pcie->ccsr + PAB_CTRL); + val &= ~(PAB_CTRL_PAGE_SEL_MASK << PAB_CTRL_PAGE_SEL_SHIFT); + val |= (pg_idx & PAB_CTRL_PAGE_SEL_MASK) << PAB_CTRL_PAGE_SEL_SHIFT; + + out_le32(pcie->ccsr + PAB_CTRL, val); +} + +static inline unsigned int ccsr_readl(struct ls_pcie_g4 *pcie, u32 offset) +{ + if (offset < INDIRECT_ADDR_BNDRY) { + ccsr_set_page(pcie, 0); + return in_le32(pcie->ccsr + offset); + } + + ccsr_set_page(pcie, OFFSET_TO_PAGE_IDX(offset)); + return in_le32(pcie->ccsr + OFFSET_TO_PAGE_ADDR(offset)); +} + +static inline void ccsr_writel(struct ls_pcie_g4 *pcie, u32 offset, u32 value) +{ + if (offset < INDIRECT_ADDR_BNDRY) { + ccsr_set_page(pcie, 0); + out_le32(pcie->ccsr + offset, value); + } else { + ccsr_set_page(pcie, OFFSET_TO_PAGE_IDX(offset)); + out_le32(pcie->ccsr + OFFSET_TO_PAGE_ADDR(offset), value); + } +} + +static inline unsigned int pf_ctrl_readl(struct ls_pcie_g4 *pcie, u32 offset) +{ + if (pcie->big_endian) + return in_be32(pcie->pf_ctrl + offset); + else + return in_le32(pcie->pf_ctrl + offset); +} + +static inline void pf_ctrl_writel(struct ls_pcie_g4 *pcie, u32 offset, + u32 value) +{ + if (pcie->big_endian) + out_be32(pcie->pf_ctrl + offset, value); + else + out_le32(pcie->pf_ctrl + offset, value); +} + +#endif /* _PCIE_LAYERSCAPE_GEN4_H_ */ diff --git a/drivers/pci/pcie_layerscape_gen4_fixup.c b/drivers/pci/pcie_layerscape_gen4_fixup.c new file mode 100644 index 0000000000..1c9e5750bd --- /dev/null +++ b/drivers/pci/pcie_layerscape_gen4_fixup.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0+ OR X11 +/* + * Copyright 2018-2019 NXP + * + * PCIe Gen4 driver for NXP Layerscape SoCs + * Author: Hou Zhiqiang <Minder.Hou@gmail.com> + * + */ + +#include <common.h> +#include <pci.h> +#include <asm/arch/fsl_serdes.h> +#include <asm/io.h> +#include <errno.h> +#ifdef CONFIG_OF_BOARD_SETUP +#include <linux/libfdt.h> +#include <fdt_support.h> +#ifdef CONFIG_ARM +#include <asm/arch/clock.h> +#endif +#include "pcie_layerscape_gen4.h" + +#if defined(CONFIG_FSL_LSCH3) || defined(CONFIG_FSL_LSCH2) +/* + * Return next available LUT index. + */ +static int ls_pcie_g4_next_lut_index(struct ls_pcie_g4 *pcie) +{ + if (pcie->next_lut_index < PCIE_LUT_ENTRY_COUNT) + return pcie->next_lut_index++; + + return -ENOSPC; /* LUT is full */ +} + +/* returns the next available streamid for pcie, -errno if failed */ +static int ls_pcie_g4_next_streamid(struct ls_pcie_g4 *pcie) +{ + int stream_id = pcie->stream_id_cur; + + if (stream_id > FSL_PEX_STREAM_ID_NUM) + return -EINVAL; + + pcie->stream_id_cur++; + + return stream_id | ((pcie->idx + 1) << 11); +} + +/* + * Program a single LUT entry + */ +static void ls_pcie_g4_lut_set_mapping(struct ls_pcie_g4 *pcie, int index, + u32 devid, u32 streamid) +{ + /* leave mask as all zeroes, want to match all bits */ + lut_writel(pcie, devid << 16, PCIE_LUT_UDR(index)); + lut_writel(pcie, streamid | PCIE_LUT_ENABLE, PCIE_LUT_LDR(index)); +} + +/* + * An msi-map is a property to be added to the pci controller + * node. It is a table, where each entry consists of 4 fields + * e.g.: + * + * msi-map = <[devid] [phandle-to-msi-ctrl] [stream-id] [count] + * [devid] [phandle-to-msi-ctrl] [stream-id] [count]>; + */ +static void fdt_pcie_set_msi_map_entry(void *blob, struct ls_pcie_g4 *pcie, + u32 devid, u32 streamid) +{ + u32 *prop; + u32 phandle; + int nodeoff; + +#ifdef CONFIG_FSL_PCIE_COMPAT + nodeoff = fdt_node_offset_by_compat_reg(blob, CONFIG_FSL_PCIE_COMPAT, + pcie->ccsr_res.start); +#else +#error "No CONFIG_FSL_PCIE_COMPAT defined" +#endif + if (nodeoff < 0) { + debug("%s: ERROR: failed to find pcie compatiable\n", __func__); + return; + } + + /* get phandle to MSI controller */ + prop = (u32 *)fdt_getprop(blob, nodeoff, "msi-parent", 0); + if (!prop) { + debug("\n%s: ERROR: missing msi-parent: PCIe%d\n", + __func__, pcie->idx); + return; + } + phandle = fdt32_to_cpu(*prop); + + /* set one msi-map row */ + fdt_appendprop_u32(blob, nodeoff, "msi-map", devid); + fdt_appendprop_u32(blob, nodeoff, "msi-map", phandle); + fdt_appendprop_u32(blob, nodeoff, "msi-map", streamid); + fdt_appendprop_u32(blob, nodeoff, "msi-map", 1); +} + +/* + * An iommu-map is a property to be added to the pci controller + * node. It is a table, where each entry consists of 4 fields + * e.g.: + * + * iommu-map = <[devid] [phandle-to-iommu-ctrl] [stream-id] [count] + * [devid] [phandle-to-iommu-ctrl] [stream-id] [count]>; + */ +static void fdt_pcie_set_iommu_map_entry(void *blob, struct ls_pcie_g4 *pcie, + u32 devid, u32 streamid) +{ + u32 *prop; + u32 iommu_map[4]; + int nodeoff; + int lenp; + +#ifdef CONFIG_FSL_PCIE_COMPAT + nodeoff = fdt_node_offset_by_compat_reg(blob, CONFIG_FSL_PCIE_COMPAT, + pcie->ccsr_res.start); +#else +#error "No CONFIG_FSL_PCIE_COMPAT defined" +#endif + if (nodeoff < 0) { + debug("%s: ERROR: failed to find pcie compatiable\n", __func__); + return; + } + + /* get phandle to iommu controller */ + prop = fdt_getprop_w(blob, nodeoff, "iommu-map", &lenp); + if (!prop) { + debug("\n%s: ERROR: missing iommu-map: PCIe%d\n", + __func__, pcie->idx); + return; + } + + /* set iommu-map row */ + iommu_map[0] = cpu_to_fdt32(devid); + iommu_map[1] = *++prop; + iommu_map[2] = cpu_to_fdt32(streamid); + iommu_map[3] = cpu_to_fdt32(1); + + if (devid == 0) + fdt_setprop_inplace(blob, nodeoff, "iommu-map", iommu_map, 16); + else + fdt_appendprop(blob, nodeoff, "iommu-map", iommu_map, 16); +} + +static void fdt_fixup_pcie(void *blob) +{ + struct udevice *dev, *bus; + struct ls_pcie_g4 *pcie; + int streamid; + int index; + pci_dev_t bdf; + + /* Scan all known buses */ + for (pci_find_first_device(&dev); dev; pci_find_next_device(&dev)) { + for (bus = dev; device_is_on_pci_bus(bus);) + bus = bus->parent; + pcie = dev_get_priv(bus); + + streamid = ls_pcie_g4_next_streamid(pcie); + if (streamid < 0) { + debug("ERROR: no stream ids free\n"); + continue; + } + + index = ls_pcie_g4_next_lut_index(pcie); + if (index < 0) { + debug("ERROR: no LUT indexes free\n"); + continue; + } + + /* the DT fixup must be relative to the hose first_busno */ + bdf = dm_pci_get_bdf(dev) - PCI_BDF(bus->seq, 0, 0); + /* map PCI b.d.f to streamID in LUT */ + ls_pcie_g4_lut_set_mapping(pcie, index, bdf >> 8, streamid); + /* update msi-map in device tree */ + fdt_pcie_set_msi_map_entry(blob, pcie, bdf >> 8, streamid); + /* update iommu-map in device tree */ + fdt_pcie_set_iommu_map_entry(blob, pcie, bdf >> 8, streamid); + } +} +#endif + +static void ft_pcie_ep_layerscape_gen4_fix(void *blob, struct ls_pcie_g4 *pcie) +{ + int off; + + off = fdt_node_offset_by_compat_reg(blob, "fsl,lx2160a-pcie-ep", + pcie->ccsr_res.start); + + if (off < 0) { + debug("%s: ERROR: failed to find pcie compatiable\n", + __func__); + return; + } + + if (pcie->enabled && pcie->mode == PCI_HEADER_TYPE_NORMAL) + fdt_set_node_status(blob, off, FDT_STATUS_OKAY, 0); + else + fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0); +} + +static void ft_pcie_rc_layerscape_gen4_fix(void *blob, struct ls_pcie_g4 *pcie) +{ + int off; + +#ifdef CONFIG_FSL_PCIE_COMPAT + off = fdt_node_offset_by_compat_reg(blob, CONFIG_FSL_PCIE_COMPAT, + pcie->ccsr_res.start); +#else +#error "No CONFIG_FSL_PCIE_COMPAT defined" +#endif + if (off < 0) { + debug("%s: ERROR: failed to find pcie compatiable\n", __func__); + return; + } + + if (pcie->enabled && pcie->mode == PCI_HEADER_TYPE_BRIDGE) + fdt_set_node_status(blob, off, FDT_STATUS_OKAY, 0); + else + fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0); +} + +static void ft_pcie_layerscape_gen4_setup(void *blob, struct ls_pcie_g4 *pcie) +{ + ft_pcie_rc_layerscape_gen4_fix(blob, pcie); + ft_pcie_ep_layerscape_gen4_fix(blob, pcie); +} + +/* Fixup Kernel DT for PCIe */ +void ft_pci_setup(void *blob, bd_t *bd) +{ + struct ls_pcie_g4 *pcie; + + list_for_each_entry(pcie, &ls_pcie_g4_list, list) + ft_pcie_layerscape_gen4_setup(blob, pcie); + +#if defined(CONFIG_FSL_LSCH3) || defined(CONFIG_FSL_LSCH2) + fdt_fixup_pcie(blob); +#endif +} + +#else /* !CONFIG_OF_BOARD_SETUP */ +void ft_pci_setup(void *blob, bd_t *bd) +{ +} +#endif diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 102fb91fff..957efb3984 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -147,6 +147,14 @@ config MESON_GXL_USB_PHY This is the generic phy driver for the Amlogic Meson GXL USB2 and USB3 PHYS. +config MESON_G12A_USB_PHY + bool "Amlogic Meson G12A USB PHYs" + depends on PHY && ARCH_MESON && MESON_G12A + imply REGMAP + help + This is the generic phy driver for the Amlogic Meson G12A + USB2 and USB3 PHYS. + config MSM8916_USB_PHY bool "Qualcomm MSM8916 USB PHY support" depends on PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index b55917bce1..90646ca55b 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o obj-$(CONFIG_PHY_RCAR_GEN3) += phy-rcar-gen3.o obj-$(CONFIG_PHY_STM32_USBPHYC) += phy-stm32-usbphyc.o obj-$(CONFIG_MESON_GXL_USB_PHY) += meson-gxl-usb2.o meson-gxl-usb3.o +obj-$(CONFIG_MESON_G12A_USB_PHY) += meson-g12a-usb2.o meson-g12a-usb3-pcie.o obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o diff --git a/drivers/phy/meson-g12a-usb2.c b/drivers/phy/meson-g12a-usb2.c new file mode 100644 index 0000000000..ad1a77fcfc --- /dev/null +++ b/drivers/phy/meson-g12a-usb2.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Meson G12A USB2 PHY driver + * + * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + * Copyright (C) 2019 BayLibre, SAS + * Author: Neil Armstrong <narmstron@baylibre.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <bitfield.h> +#include <dm.h> +#include <errno.h> +#include <generic-phy.h> +#include <regmap.h> +#include <power/regulator.h> +#include <reset.h> +#include <clk.h> + +#include <linux/bitops.h> +#include <linux/compat.h> + +#define PHY_CTRL_R0 0x0 +#define PHY_CTRL_R1 0x4 +#define PHY_CTRL_R2 0x8 +#define PHY_CTRL_R3 0xc +#define PHY_CTRL_R4 0x10 +#define PHY_CTRL_R5 0x14 +#define PHY_CTRL_R6 0x18 +#define PHY_CTRL_R7 0x1c +#define PHY_CTRL_R8 0x20 +#define PHY_CTRL_R9 0x24 +#define PHY_CTRL_R10 0x28 +#define PHY_CTRL_R11 0x2c +#define PHY_CTRL_R12 0x30 +#define PHY_CTRL_R13 0x34 +#define PHY_CTRL_R14 0x38 +#define PHY_CTRL_R15 0x3c +#define PHY_CTRL_R16 0x40 +#define PHY_CTRL_R17 0x44 +#define PHY_CTRL_R18 0x48 +#define PHY_CTRL_R19 0x4c +#define PHY_CTRL_R20 0x50 +#define PHY_CTRL_R21 0x54 +#define PHY_CTRL_R22 0x58 +#define PHY_CTRL_R23 0x5c + +#define RESET_COMPLETE_TIME 1000 +#define PLL_RESET_COMPLETE_TIME 100 + +struct phy_meson_g12a_usb2_priv { + struct regmap *regmap; +#if CONFIG_IS_ENABLED(DM_REGULATOR) + struct udevice *phy_supply; +#endif +#if CONFIG_IS_ENABLED(CLK) + struct clk clk; +#endif + struct reset_ctl reset; +}; + + +static int phy_meson_g12a_usb2_power_on(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev); + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + if (priv->phy_supply) { + int ret = regulator_set_enable(priv->phy_supply, true); + if (ret) + return ret; + } +#endif + + return 0; +} + +static int phy_meson_g12a_usb2_power_off(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev); + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + if (priv->phy_supply) { + int ret = regulator_set_enable(priv->phy_supply, false); + if (ret) { + pr_err("Error disabling PHY supply\n"); + return ret; + } + } +#endif + + return 0; +} + +static int phy_meson_g12a_usb2_init(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev); + int ret; + + ret = reset_assert(&priv->reset); + udelay(1); + ret |= reset_deassert(&priv->reset); + if (ret) + return ret; + + udelay(RESET_COMPLETE_TIME); + + /* usb2_otg_aca_en == 0 */ + regmap_update_bits(priv->regmap, PHY_CTRL_R21, BIT(2), 0); + + /* PLL Setup : 24MHz * 20 / 1 = 480MHz */ + regmap_write(priv->regmap, PHY_CTRL_R16, 0x39400414); + regmap_write(priv->regmap, PHY_CTRL_R17, 0x927e0000); + regmap_write(priv->regmap, PHY_CTRL_R18, 0xac5f49e5); + + udelay(PLL_RESET_COMPLETE_TIME); + + /* UnReset PLL */ + regmap_write(priv->regmap, PHY_CTRL_R16, 0x19400414); + + /* PHY Tuning */ + regmap_write(priv->regmap, PHY_CTRL_R20, 0xfe18); + regmap_write(priv->regmap, PHY_CTRL_R4, 0x8000fff); + + /* Tuning Disconnect Threshold */ + regmap_write(priv->regmap, PHY_CTRL_R3, 0x34); + + /* Analog Settings */ + regmap_write(priv->regmap, PHY_CTRL_R14, 0); + regmap_write(priv->regmap, PHY_CTRL_R13, 0x78000); + + return 0; +} + +static int phy_meson_g12a_usb2_exit(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev); + int ret; + + ret = reset_assert(&priv->reset); + if (ret) + return ret; + + return 0; +} + +struct phy_ops meson_g12a_usb2_phy_ops = { + .init = phy_meson_g12a_usb2_init, + .exit = phy_meson_g12a_usb2_exit, + .power_on = phy_meson_g12a_usb2_power_on, + .power_off = phy_meson_g12a_usb2_power_off, +}; + +int meson_g12a_usb2_phy_probe(struct udevice *dev) +{ + struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev); + int ret; + + ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); + if (ret) + return ret; + + ret = reset_get_by_index(dev, 0, &priv->reset); + if (ret == -ENOTSUPP) + return 0; + else if (ret) + return ret; + + ret = reset_deassert(&priv->reset); + if (ret) { + reset_release_all(&priv->reset, 1); + return ret; + } + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret < 0) + return ret; + + ret = clk_enable(&priv->clk); + if (ret && ret != -ENOSYS && ret != -ENOTSUPP) { + pr_err("failed to enable PHY clock\n"); + clk_free(&priv->clk); + return ret; + } +#endif + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + ret = device_get_supply_regulator(dev, "phy-supply", &priv->phy_supply); + if (ret && ret != -ENOENT) { + pr_err("Failed to get PHY regulator\n"); + return ret; + } +#endif + + return 0; +} + +static const struct udevice_id meson_g12a_usb2_phy_ids[] = { + { .compatible = "amlogic,g12a-usb2-phy" }, + { } +}; + +U_BOOT_DRIVER(meson_g12a_usb2_phy) = { + .name = "meson_g12a_usb2_phy", + .id = UCLASS_PHY, + .of_match = meson_g12a_usb2_phy_ids, + .probe = meson_g12a_usb2_phy_probe, + .ops = &meson_g12a_usb2_phy_ops, + .priv_auto_alloc_size = sizeof(struct phy_meson_g12a_usb2_priv), +}; diff --git a/drivers/phy/meson-g12a-usb3-pcie.c b/drivers/phy/meson-g12a-usb3-pcie.c new file mode 100644 index 0000000000..920675dc99 --- /dev/null +++ b/drivers/phy/meson-g12a-usb3-pcie.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Meson G12A USB3+PCIE Combo PHY driver + * + * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + * Copyright (C) 2019 BayLibre, SAS + * Author: Neil Armstrong <narmstron@baylibre.com> + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <regmap.h> +#include <errno.h> +#include <asm/io.h> +#include <reset.h> +#include <bitfield.h> +#include <generic-phy.h> + +#include <linux/bitops.h> +#include <linux/compat.h> +#include <linux/bitfield.h> + +#define PHY_R0 0x00 + #define PHY_R0_PCIE_POWER_STATE GENMASK(4, 0) + #define PHY_R0_PCIE_USB3_SWITCH GENMASK(6, 5) + +#define PHY_R1 0x04 + #define PHY_R1_PHY_TX1_TERM_OFFSET GENMASK(4, 0) + #define PHY_R1_PHY_TX0_TERM_OFFSET GENMASK(9, 5) + #define PHY_R1_PHY_RX1_EQ GENMASK(12, 10) + #define PHY_R1_PHY_RX0_EQ GENMASK(15, 13) + #define PHY_R1_PHY_LOS_LEVEL GENMASK(20, 16) + #define PHY_R1_PHY_LOS_BIAS GENMASK(23, 21) + #define PHY_R1_PHY_REF_CLKDIV2 BIT(24) + #define PHY_R1_PHY_MPLL_MULTIPLIER GENMASK(31, 25) + +#define PHY_R2 0x08 + #define PHY_R2_PCS_TX_DEEMPH_GEN2_6DB GENMASK(5, 0) + #define PHY_R2_PCS_TX_DEEMPH_GEN2_3P5DB GENMASK(11, 6) + #define PHY_R2_PCS_TX_DEEMPH_GEN1 GENMASK(17, 12) + #define PHY_R2_PHY_TX_VBOOST_LVL GENMASK(20, 18) + +#define PHY_R4 0x10 + #define PHY_R4_PHY_CR_WRITE BIT(0) + #define PHY_R4_PHY_CR_READ BIT(1) + #define PHY_R4_PHY_CR_DATA_IN GENMASK(17, 2) + #define PHY_R4_PHY_CR_CAP_DATA BIT(18) + #define PHY_R4_PHY_CR_CAP_ADDR BIT(19) + +#define PHY_R5 0x14 + #define PHY_R5_PHY_CR_DATA_OUT GENMASK(15, 0) + #define PHY_R5_PHY_CR_ACK BIT(16) + #define PHY_R5_PHY_BS_OUT BIT(17) + +struct phy_g12a_usb3_pcie_priv { + struct regmap *regmap; +#if CONFIG_IS_ENABLED(CLK) + struct clk clk; +#endif + struct reset_ctl_bulk resets; +}; + +static int phy_g12a_usb3_pcie_cr_bus_addr(struct phy_g12a_usb3_pcie_priv *priv, + unsigned int addr) +{ + unsigned int val, reg; + int ret; + + reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, addr); + + regmap_write(priv->regmap, PHY_R4, reg); + regmap_write(priv->regmap, PHY_R4, reg); + + regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_ADDR); + + ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val, + (val & PHY_R5_PHY_CR_ACK), + 5, 1000); + if (ret) + return ret; + + regmap_write(priv->regmap, PHY_R4, reg); + + ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val, + !(val & PHY_R5_PHY_CR_ACK), + 5, 1000); + if (ret) + return ret; + + return 0; +} + +static int +phy_g12a_usb3_pcie_cr_bus_read(struct phy_g12a_usb3_pcie_priv *priv, + unsigned int addr, unsigned int *data) +{ + unsigned int val; + int ret; + + ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr); + if (ret) + return ret; + + regmap_write(priv->regmap, PHY_R4, 0); + regmap_write(priv->regmap, PHY_R4, PHY_R4_PHY_CR_READ); + + ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val, + (val & PHY_R5_PHY_CR_ACK), + 5, 1000); + if (ret) + return ret; + + *data = FIELD_GET(PHY_R5_PHY_CR_DATA_OUT, val); + + regmap_write(priv->regmap, PHY_R4, 0); + + ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val, + !(val & PHY_R5_PHY_CR_ACK), + 5, 1000); + if (ret) + return ret; + + return 0; +} + +static int +phy_g12a_usb3_pcie_cr_bus_write(struct phy_g12a_usb3_pcie_priv *priv, + unsigned int addr, unsigned int data) +{ + unsigned int val, reg; + int ret; + + ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr); + if (ret) + return ret; + + reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, data); + + regmap_write(priv->regmap, PHY_R4, reg); + regmap_write(priv->regmap, PHY_R4, reg); + + regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_DATA); + + ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val, + (val & PHY_R5_PHY_CR_ACK), + 5, 1000); + if (ret) + return ret; + + regmap_write(priv->regmap, PHY_R4, reg); + + ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val, + (val & PHY_R5_PHY_CR_ACK) == 0, + 5, 1000); + if (ret) + return ret; + + regmap_write(priv->regmap, PHY_R4, reg); + + regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_WRITE); + + ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val, + (val & PHY_R5_PHY_CR_ACK), + 5, 1000); + if (ret) + return ret; + + regmap_write(priv->regmap, PHY_R4, reg); + + ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val, + (val & PHY_R5_PHY_CR_ACK) == 0, + 5, 1000); + if (ret) + return ret; + + return 0; +} + +static int +phy_g12a_usb3_pcie_cr_bus_update_bits(struct phy_g12a_usb3_pcie_priv *priv, + uint offset, uint mask, uint val) +{ + uint reg; + int ret; + + ret = phy_g12a_usb3_pcie_cr_bus_read(priv, offset, ®); + if (ret) + return ret; + + reg &= ~mask; + + return phy_g12a_usb3_pcie_cr_bus_write(priv, offset, reg | val); +} + +static int phy_meson_g12a_usb3_init(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(dev); + unsigned int data; + int ret; + + /* TOFIX Handle PCIE mode */ + + ret = reset_assert_bulk(&priv->resets); + udelay(1); + ret |= reset_deassert_bulk(&priv->resets); + if (ret) + return ret; + + /* Switch PHY to USB3 */ + regmap_update_bits(priv->regmap, PHY_R0, + PHY_R0_PCIE_USB3_SWITCH, + PHY_R0_PCIE_USB3_SWITCH); + + /* + * WORKAROUND: There is SSPHY suspend bug due to + * which USB enumerates + * in HS mode instead of SS mode. Workaround it by asserting + * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus + * mode + */ + ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x102d, + BIT(7), BIT(7)); + if (ret) + return ret; + + ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x1010, 0xff0, 20); + if (ret) + return ret; + + /* + * Fix RX Equalization setting as follows + * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 + * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 + * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3 + * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 + */ + ret = phy_g12a_usb3_pcie_cr_bus_read(priv, 0x1006, &data); + if (ret) + return ret; + + data &= ~BIT(6); + data |= BIT(7); + data &= ~(0x7 << 8); + data |= (0x3 << 8); + data |= (1 << 11); + ret = phy_g12a_usb3_pcie_cr_bus_write(priv, 0x1006, data); + if (ret) + return ret; + + /* + * Set EQ and TX launch amplitudes as follows + * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22 + * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127 + * LANE0.TX_OVRD_DRV_LO.EN set to 1. + */ + ret = phy_g12a_usb3_pcie_cr_bus_read(priv, 0x1002, &data); + if (ret) + return ret; + + data &= ~0x3f80; + data |= (0x16 << 7); + data &= ~0x7f; + data |= (0x7f | BIT(14)); + ret = phy_g12a_usb3_pcie_cr_bus_write(priv, 0x1002, data); + if (ret) + return ret; + + /* + * MPLL_LOOP_CTL.PROP_CNTRL = 8 + */ + ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x30, + 0xf << 4, 8 << 4); + if (ret) + return ret; + + regmap_update_bits(priv->regmap, PHY_R2, + PHY_R2_PHY_TX_VBOOST_LVL, + FIELD_PREP(PHY_R2_PHY_TX_VBOOST_LVL, 0x4)); + + regmap_update_bits(priv->regmap, PHY_R1, + PHY_R1_PHY_LOS_BIAS | PHY_R1_PHY_LOS_LEVEL, + FIELD_PREP(PHY_R1_PHY_LOS_BIAS, 4) | + FIELD_PREP(PHY_R1_PHY_LOS_LEVEL, 9)); + + return ret; +} + +static int phy_meson_g12a_usb3_exit(struct phy *phy) +{ + struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(phy->dev); + + return reset_assert_bulk(&priv->resets); +} + +struct phy_ops meson_g12a_usb3_pcie_phy_ops = { + .init = phy_meson_g12a_usb3_init, + .exit = phy_meson_g12a_usb3_exit, +}; + +int meson_g12a_usb3_pcie_phy_probe(struct udevice *dev) +{ + struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(dev); + int ret; + + ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); + if (ret) + return ret; + + ret = reset_get_bulk(dev, &priv->resets); + if (ret == -ENOTSUPP) + return 0; + else if (ret) + return ret; + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret < 0) + return ret; + + ret = clk_enable(&priv->clk); + if (ret && ret != -ENOENT && ret != -ENOTSUPP) { + pr_err("failed to enable PHY clock\n"); + clk_free(&priv->clk); + return ret; + } +#endif + + return 0; +} + +static const struct udevice_id meson_g12a_usb3_pcie_phy_ids[] = { + { .compatible = "amlogic,g12a-usb3-pcie-phy" }, + { } +}; + +U_BOOT_DRIVER(meson_g12a_usb3_pcie_phy) = { + .name = "meson_g12a_usb3_pcie_phy", + .id = UCLASS_PHY, + .of_match = meson_g12a_usb3_pcie_phy_ids, + .probe = meson_g12a_usb3_pcie_phy_probe, + .ops = &meson_g12a_usb3_pcie_phy_ops, + .priv_auto_alloc_size = sizeof(struct phy_g12a_usb3_pcie_priv), +}; diff --git a/drivers/pinctrl/nxp/pinctrl-imx8.c b/drivers/pinctrl/nxp/pinctrl-imx8.c index 0738da0ebe..c1b0ca438a 100644 --- a/drivers/pinctrl/nxp/pinctrl-imx8.c +++ b/drivers/pinctrl/nxp/pinctrl-imx8.c @@ -25,6 +25,7 @@ static int imx8_pinctrl_probe(struct udevice *dev) static const struct udevice_id imx8_pinctrl_match[] = { { .compatible = "fsl,imx8qxp-iomuxc", .data = (ulong)&imx8_pinctrl_soc_info }, + { .compatible = "fsl,imx8qm-iomuxc", .data = (ulong)&imx8_pinctrl_soc_info }, { /* sentinel */ } }; diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c index 0e6c559d5e..5b1cd29d86 100644 --- a/drivers/pinctrl/pinctrl-uclass.c +++ b/drivers/pinctrl/pinctrl-uclass.c @@ -116,6 +116,9 @@ static int pinconfig_post_bind(struct udevice *dev) ofnode node; int ret; + if (!dev_of_valid(dev)) + return 0; + dev_for_each_subnode(node, dev) { if (pre_reloc_only && !ofnode_pre_reloc(node)) @@ -169,6 +172,102 @@ static int pinconfig_post_bind(struct udevice *dev) } #endif +static int +pinctrl_gpio_get_pinctrl_and_offset(struct udevice *dev, unsigned offset, + struct udevice **pctldev, + unsigned int *pin_selector) +{ + struct ofnode_phandle_args args; + unsigned gpio_offset, pfc_base, pfc_pins; + int ret; + + ret = dev_read_phandle_with_args(dev, "gpio-ranges", NULL, 3, + 0, &args); + if (ret) { + dev_dbg(dev, "%s: dev_read_phandle_with_args: err=%d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_ofnode(UCLASS_PINCTRL, + args.node, pctldev); + if (ret) { + dev_dbg(dev, + "%s: uclass_get_device_by_of_offset failed: err=%d\n", + __func__, ret); + return ret; + } + + gpio_offset = args.args[0]; + pfc_base = args.args[1]; + pfc_pins = args.args[2]; + + if (offset < gpio_offset || offset > gpio_offset + pfc_pins) { + dev_dbg(dev, + "%s: GPIO can not be mapped to pincontrol pin\n", + __func__); + return -EINVAL; + } + + offset -= gpio_offset; + offset += pfc_base; + *pin_selector = offset; + + return 0; +} + +/** + * pinctrl_gpio_request() - request a single pin to be used as GPIO + * + * @dev: GPIO peripheral device + * @offset: the GPIO pin offset from the GPIO controller + * @return: 0 on success, or negative error code on failure + */ +int pinctrl_gpio_request(struct udevice *dev, unsigned offset) +{ + const struct pinctrl_ops *ops; + struct udevice *pctldev; + unsigned int pin_selector; + int ret; + + ret = pinctrl_gpio_get_pinctrl_and_offset(dev, offset, + &pctldev, &pin_selector); + if (ret) + return ret; + + ops = pinctrl_get_ops(pctldev); + if (!ops || !ops->gpio_request_enable) + return -ENOTSUPP; + + return ops->gpio_request_enable(pctldev, pin_selector); +} + +/** + * pinctrl_gpio_free() - free a single pin used as GPIO + * + * @dev: GPIO peripheral device + * @offset: the GPIO pin offset from the GPIO controller + * @return: 0 on success, or negative error code on failure + */ +int pinctrl_gpio_free(struct udevice *dev, unsigned offset) +{ + const struct pinctrl_ops *ops; + struct udevice *pctldev; + unsigned int pin_selector; + int ret; + + ret = pinctrl_gpio_get_pinctrl_and_offset(dev, offset, + &pctldev, &pin_selector); + if (ret) + return ret; + + ops = pinctrl_get_ops(pctldev); + if (!ops || !ops->gpio_disable_free) + return -ENOTSUPP; + + return ops->gpio_disable_free(pctldev, pin_selector); +} + /** * pinctrl_select_state_simple() - simple implementation of pinctrl_select_state * diff --git a/drivers/pinctrl/renesas/Kconfig b/drivers/pinctrl/renesas/Kconfig index 152414ce31..0ffd7fcfd4 100644 --- a/drivers/pinctrl/renesas/Kconfig +++ b/drivers/pinctrl/renesas/Kconfig @@ -3,6 +3,7 @@ if ARCH_RMOBILE config PINCTRL_PFC bool "Renesas pin control drivers" depends on DM && ARCH_RMOBILE + default n if CPU_RZA1 help Enable support for clock present on Renesas RCar SoCs. @@ -116,4 +117,15 @@ config PINCTRL_PFC_R8A77995 the GPIO definitions and pin control functions for each available multiplex function. +config PINCTRL_PFC_R7S72100 + bool "Renesas RZ/A1 R7S72100 pin control driver" + depends on CPU_RZA1 + default y if CPU_RZA1 + help + Support pin multiplexing control on Renesas RZ/A1 R7S72100 SoCs. + + The driver is controlled by a device tree node which contains both + the GPIO definitions and pin control functions for each available + multiplex function. + endif diff --git a/drivers/pinctrl/renesas/Makefile b/drivers/pinctrl/renesas/Makefile index 596b0023a3..e8703f681e 100644 --- a/drivers/pinctrl/renesas/Makefile +++ b/drivers/pinctrl/renesas/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_PINCTRL_PFC_R8A77965) += pfc-r8a77965.o obj-$(CONFIG_PINCTRL_PFC_R8A77970) += pfc-r8a77970.o obj-$(CONFIG_PINCTRL_PFC_R8A77990) += pfc-r8a77990.o obj-$(CONFIG_PINCTRL_PFC_R8A77995) += pfc-r8a77995.o +obj-$(CONFIG_PINCTRL_PFC_R7S72100) += pfc-r7s72100.o diff --git a/drivers/pinctrl/renesas/pfc-r7s72100.c b/drivers/pinctrl/renesas/pfc-r7s72100.c new file mode 100644 index 0000000000..7e4530d684 --- /dev/null +++ b/drivers/pinctrl/renesas/pfc-r7s72100.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * R7S72100 processor support + * + * Copyright (C) 2019 Marek Vasut <marek.vasut@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <dm/lists.h> +#include <dm/pinctrl.h> +#include <linux/io.h> +#include <linux/err.h> + +#define P(bank) (0x0000 + (bank) * 4) +#define PSR(bank) (0x0100 + (bank) * 4) +#define PPR(bank) (0x0200 + (bank) * 4) +#define PM(bank) (0x0300 + (bank) * 4) +#define PMC(bank) (0x0400 + (bank) * 4) +#define PFC(bank) (0x0500 + (bank) * 4) +#define PFCE(bank) (0x0600 + (bank) * 4) +#define PNOT(bank) (0x0700 + (bank) * 4) +#define PMSR(bank) (0x0800 + (bank) * 4) +#define PMCSR(bank) (0x0900 + (bank) * 4) +#define PFCAE(bank) (0x0A00 + (bank) * 4) +#define PIBC(bank) (0x4000 + (bank) * 4) +#define PBDC(bank) (0x4100 + (bank) * 4) +#define PIPC(bank) (0x4200 + (bank) * 4) + +#define RZA1_PINS_PER_PORT 16 + +DECLARE_GLOBAL_DATA_PTR; + +struct r7s72100_pfc_platdata { + void __iomem *base; +}; + +static void r7s72100_pfc_set_function(struct udevice *dev, u16 bank, u16 line, + u16 func, u16 inbuf, u16 bidir) +{ + struct r7s72100_pfc_platdata *plat = dev_get_platdata(dev); + + clrsetbits_le16(plat->base + PFCAE(bank), BIT(line), + (func & BIT(2)) ? BIT(line) : 0); + clrsetbits_le16(plat->base + PFCE(bank), BIT(line), + (func & BIT(1)) ? BIT(line) : 0); + clrsetbits_le16(plat->base + PFC(bank), BIT(line), + (func & BIT(0)) ? BIT(line) : 0); + + clrsetbits_le16(plat->base + PIBC(bank), BIT(line), + inbuf ? BIT(line) : 0); + clrsetbits_le16(plat->base + PBDC(bank), BIT(line), + bidir ? BIT(line) : 0); + + setbits_le32(plat->base + PMCSR(bank), BIT(line + 16) | BIT(line)); + + setbits_le16(plat->base + PIPC(bank), BIT(line)); +} + +static int r7s72100_pfc_set_state(struct udevice *dev, struct udevice *config) +{ + const void *blob = gd->fdt_blob; + int node = dev_of_offset(config); + u32 cells[32]; + u16 bank, line, func; + int i, count, bidir; + + count = fdtdec_get_int_array_count(blob, node, "pinmux", + cells, ARRAY_SIZE(cells)); + if (count < 0) { + printf("%s: bad pinmux array %d\n", __func__, count); + return -EINVAL; + } + + if (count > ARRAY_SIZE(cells)) { + printf("%s: unsupported pinmux array count %d\n", + __func__, count); + return -EINVAL; + } + + for (i = 0 ; i < count; i++) { + func = (cells[i] >> 16) & 0xf; + if (func == 0 || func > 8) { + printf("Invalid cell %i in node %s!\n", + count, ofnode_get_name(dev_ofnode(config))); + continue; + } + + func = (func - 1) & 0x7; + + bank = (cells[i] / RZA1_PINS_PER_PORT) & 0xff; + line = cells[i] % RZA1_PINS_PER_PORT; + + bidir = 0; + if (bank == 3 && line == 3 && func == 1) + bidir = 1; + + r7s72100_pfc_set_function(dev, bank, line, func, 0, bidir); + } + + return 0; +} + +const struct pinctrl_ops r7s72100_pfc_ops = { + .set_state = r7s72100_pfc_set_state, +}; + +static int r7s72100_pfc_probe(struct udevice *dev) +{ + struct r7s72100_pfc_platdata *plat = dev_get_platdata(dev); + fdt_addr_t addr_base; + ofnode node; + + addr_base = devfdt_get_addr(dev); + if (addr_base == FDT_ADDR_T_NONE) + return -EINVAL; + + plat->base = (void __iomem *)addr_base; + + dev_for_each_subnode(node, dev) { + struct udevice *cdev; + + if (!ofnode_read_bool(node, "gpio-controller")) + continue; + + device_bind_driver_to_node(dev, "r7s72100-gpio", + ofnode_get_name(node), + node, &cdev); + } + + return 0; +} + +static const struct udevice_id r7s72100_pfc_match[] = { + { .compatible = "renesas,r7s72100-ports" }, + {} +}; + +U_BOOT_DRIVER(r7s72100_pfc) = { + .name = "r7s72100_pfc", + .id = UCLASS_PINCTRL, + .of_match = r7s72100_pfc_match, + .probe = r7s72100_pfc_probe, + .platdata_auto_alloc_size = sizeof(struct r7s72100_pfc_platdata), + .ops = &r7s72100_pfc_ops, +}; diff --git a/drivers/pinctrl/renesas/pfc.c b/drivers/pinctrl/renesas/pfc.c index 06359501b7..d1271dad44 100644 --- a/drivers/pinctrl/renesas/pfc.c +++ b/drivers/pinctrl/renesas/pfc.c @@ -459,14 +459,15 @@ static const char *sh_pfc_pinctrl_get_function_name(struct udevice *dev, return priv->pfc.info->functions[selector].name; } -int sh_pfc_config_mux_for_gpio(struct udevice *dev, unsigned pin_selector) +static int sh_pfc_gpio_request_enable(struct udevice *dev, + unsigned pin_selector) { struct sh_pfc_pinctrl_priv *priv = dev_get_priv(dev); struct sh_pfc_pinctrl *pmx = &priv->pmx; struct sh_pfc *pfc = &priv->pfc; struct sh_pfc_pin_config *cfg; const struct sh_pfc_pin *pin = NULL; - int i, idx; + int i, ret, idx; for (i = 1; i < pfc->info->nr_pins; i++) { if (priv->pfc.info->pins[i].pin != pin_selector) @@ -485,7 +486,42 @@ int sh_pfc_config_mux_for_gpio(struct udevice *dev, unsigned pin_selector) if (cfg->type != PINMUX_TYPE_NONE) return -EBUSY; - return sh_pfc_config_mux(pfc, pin->enum_id, PINMUX_TYPE_GPIO); + ret = sh_pfc_config_mux(pfc, pin->enum_id, PINMUX_TYPE_GPIO); + if (ret) + return ret; + + cfg->type = PINMUX_TYPE_GPIO; + + return 0; +} + +static int sh_pfc_gpio_disable_free(struct udevice *dev, + unsigned pin_selector) +{ + struct sh_pfc_pinctrl_priv *priv = dev_get_priv(dev); + struct sh_pfc_pinctrl *pmx = &priv->pmx; + struct sh_pfc *pfc = &priv->pfc; + struct sh_pfc_pin_config *cfg; + const struct sh_pfc_pin *pin = NULL; + int i, idx; + + for (i = 1; i < pfc->info->nr_pins; i++) { + if (priv->pfc.info->pins[i].pin != pin_selector) + continue; + + pin = &priv->pfc.info->pins[i]; + break; + } + + if (!pin) + return -EINVAL; + + idx = sh_pfc_get_pin_index(pfc, pin->pin); + cfg = &pmx->configs[idx]; + + cfg->type = PINMUX_TYPE_NONE; + + return 0; } static int sh_pfc_pinctrl_pin_set(struct udevice *dev, unsigned pin_selector, @@ -746,6 +782,9 @@ static struct pinctrl_ops sh_pfc_pinctrl_ops = { .pinmux_set = sh_pfc_pinctrl_pin_set, .pinmux_group_set = sh_pfc_pinctrl_group_set, .set_state = pinctrl_generic_set_state, + + .gpio_request_enable = sh_pfc_gpio_request_enable, + .gpio_disable_free = sh_pfc_gpio_disable_free, }; static int sh_pfc_map_pins(struct sh_pfc *pfc, struct sh_pfc_pinctrl *pmx) diff --git a/drivers/pinctrl/renesas/sh_pfc.h b/drivers/pinctrl/renesas/sh_pfc.h index 09e11d31b3..6629e1f772 100644 --- a/drivers/pinctrl/renesas/sh_pfc.h +++ b/drivers/pinctrl/renesas/sh_pfc.h @@ -275,7 +275,6 @@ void sh_pfc_write(struct sh_pfc *pfc, u32 reg, u32 data); const struct pinmux_bias_reg * sh_pfc_pin_to_bias_reg(const struct sh_pfc *pfc, unsigned int pin, unsigned int *bit); -int sh_pfc_config_mux_for_gpio(struct udevice *dev, unsigned pin_selector); extern const struct sh_pfc_soc_info r8a7790_pinmux_info; extern const struct sh_pfc_soc_info r8a7791_pinmux_info; diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3036.c b/drivers/pinctrl/rockchip/pinctrl-rk3036.c index 2729b03443..28c905129b 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rk3036.c +++ b/drivers/pinctrl/rockchip/pinctrl-rk3036.c @@ -11,6 +11,30 @@ #include "pinctrl-rockchip.h" +static int rk3036_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, ret, mask, mux_type; + u8 bit; + u32 data; + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? priv->regmap_pmu : priv->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mux_type = bank->iomux[iomux_num].type; + reg = bank->iomux[iomux_num].offset; + reg += rockchip_get_mux_data(mux_type, pin, &bit, &mask); + + data = (mask << (bit + 16)); + data |= (mux & mask) << bit; + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RK3036_PULL_OFFSET 0x118 #define RK3036_PULL_PINS_PER_REG 16 #define RK3036_PULL_BANK_STRIDE 8 @@ -29,6 +53,27 @@ static void rk3036_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, *bit = pin_num % RK3036_PULL_PINS_PER_REG; }; +static int rk3036_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct regmap *regmap; + int reg, ret; + u8 bit; + u32 data; + + if (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT && + pull != PIN_CONFIG_BIAS_DISABLE) + return -ENOTSUPP; + + rk3036_calc_pull_reg_and_bit(bank, pin_num, ®map, ®, &bit); + data = BIT(bit + 16); + if (pull == PIN_CONFIG_BIAS_DISABLE) + data |= BIT(bit); + ret = regmap_write(regmap, reg, data); + + return ret; +} + static struct rockchip_pin_bank rk3036_pin_banks[] = { PIN_BANK(0, 32, "gpio0"), PIN_BANK(1, 32, "gpio1"), @@ -36,12 +81,11 @@ static struct rockchip_pin_bank rk3036_pin_banks[] = { }; static struct rockchip_pin_ctrl rk3036_pin_ctrl = { - .pin_banks = rk3036_pin_banks, - .nr_banks = ARRAY_SIZE(rk3036_pin_banks), - .label = "RK3036-GPIO", - .type = RK3036, - .grf_mux_offset = 0xa8, - .pull_calc_reg = rk3036_calc_pull_reg_and_bit, + .pin_banks = rk3036_pin_banks, + .nr_banks = ARRAY_SIZE(rk3036_pin_banks), + .grf_mux_offset = 0xa8, + .set_mux = rk3036_set_mux, + .set_pull = rk3036_set_pull, }; static const struct udevice_id rk3036_pinctrl_ids[] = { diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3128.c b/drivers/pinctrl/rockchip/pinctrl-rk3128.c index 43a6c173a0..3eb4d952bb 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rk3128.c +++ b/drivers/pinctrl/rockchip/pinctrl-rk3128.c @@ -98,6 +98,42 @@ static struct rockchip_mux_route_data rk3128_mux_route_data[] = { }, }; +static int rk3128_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, ret, mask, mux_type; + u8 bit; + u32 data, route_reg, route_val; + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? priv->regmap_pmu : priv->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mux_type = bank->iomux[iomux_num].type; + reg = bank->iomux[iomux_num].offset; + reg += rockchip_get_mux_data(mux_type, pin, &bit, &mask); + + if (bank->recalced_mask & BIT(pin)) + rockchip_get_recalced_mux(bank, pin, ®, &bit, &mask); + + if (bank->route_mask & BIT(pin)) { + if (rockchip_get_mux_route(bank, pin, mux, &route_reg, + &route_val)) { + ret = regmap_write(regmap, route_reg, route_val); + if (ret) + return ret; + } + } + + data = (mask << (bit + 16)); + data |= (mux & mask) << bit; + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RK3128_PULL_OFFSET 0x118 #define RK3128_PULL_PINS_PER_REG 16 #define RK3128_PULL_BANK_STRIDE 8 @@ -116,6 +152,27 @@ static void rk3128_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, *bit = pin_num % RK3128_PULL_PINS_PER_REG; } +static int rk3128_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct regmap *regmap; + int reg, ret; + u8 bit; + u32 data; + + if (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT && + pull != PIN_CONFIG_BIAS_DISABLE) + return -ENOTSUPP; + + rk3128_calc_pull_reg_and_bit(bank, pin_num, ®map, ®, &bit); + data = BIT(bit + 16); + if (pull == PIN_CONFIG_BIAS_DISABLE) + data |= BIT(bit); + ret = regmap_write(regmap, reg, data); + + return ret; +} + static struct rockchip_pin_bank rk3128_pin_banks[] = { PIN_BANK(0, 32, "gpio0"), PIN_BANK(1, 32, "gpio1"), @@ -126,14 +183,13 @@ static struct rockchip_pin_bank rk3128_pin_banks[] = { static struct rockchip_pin_ctrl rk3128_pin_ctrl = { .pin_banks = rk3128_pin_banks, .nr_banks = ARRAY_SIZE(rk3128_pin_banks), - .label = "RK3128-GPIO", - .type = RK3128, .grf_mux_offset = 0xa8, .iomux_recalced = rk3128_mux_recalced_data, .niomux_recalced = ARRAY_SIZE(rk3128_mux_recalced_data), .iomux_routes = rk3128_mux_route_data, .niomux_routes = ARRAY_SIZE(rk3128_mux_route_data), - .pull_calc_reg = rk3128_calc_pull_reg_and_bit, + .set_mux = rk3128_set_mux, + .set_pull = rk3128_set_pull, }; static const struct udevice_id rk3128_pinctrl_ids[] = { diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3188.c b/drivers/pinctrl/rockchip/pinctrl-rk3188.c index 5ed9aec938..043764fc92 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rk3188.c +++ b/drivers/pinctrl/rockchip/pinctrl-rk3188.c @@ -11,6 +11,30 @@ #include "pinctrl-rockchip.h" +static int rk3188_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, ret, mask, mux_type; + u8 bit; + u32 data; + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? priv->regmap_pmu : priv->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mux_type = bank->iomux[iomux_num].type; + reg = bank->iomux[iomux_num].offset; + reg += rockchip_get_mux_data(mux_type, pin, &bit, &mask); + + data = (mask << (bit + 16)); + data |= (mux & mask) << bit; + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RK3188_PULL_OFFSET 0x164 #define RK3188_PULL_PMU_OFFSET 0x64 @@ -47,6 +71,33 @@ static void rk3188_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, } } +static int rk3188_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct regmap *regmap; + int reg, ret; + u8 bit, type; + u32 data; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + return -ENOTSUPP; + + rk3188_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 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + + return ret; +} + static struct rockchip_pin_bank rk3188_pin_banks[] = { PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_GPIO_ONLY, 0, 0, 0), PIN_BANK(1, 32, "gpio1"), @@ -55,12 +106,11 @@ static struct rockchip_pin_bank rk3188_pin_banks[] = { }; static struct rockchip_pin_ctrl rk3188_pin_ctrl = { - .pin_banks = rk3188_pin_banks, - .nr_banks = ARRAY_SIZE(rk3188_pin_banks), - .label = "RK3188-GPIO", - .type = RK3188, - .grf_mux_offset = 0x60, - .pull_calc_reg = rk3188_calc_pull_reg_and_bit, + .pin_banks = rk3188_pin_banks, + .nr_banks = ARRAY_SIZE(rk3188_pin_banks), + .grf_mux_offset = 0x60, + .set_mux = rk3188_set_mux, + .set_pull = rk3188_set_pull, }; static const struct udevice_id rk3188_pinctrl_ids[] = { diff --git a/drivers/pinctrl/rockchip/pinctrl-rk322x.c b/drivers/pinctrl/rockchip/pinctrl-rk322x.c index d2a6cd7055..c5e4fe30a7 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rk322x.c +++ b/drivers/pinctrl/rockchip/pinctrl-rk322x.c @@ -141,6 +141,39 @@ static struct rockchip_mux_route_data rk3228_mux_route_data[] = { }, }; +static int rk3228_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, ret, mask, mux_type; + u8 bit; + u32 data, route_reg, route_val; + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? priv->regmap_pmu : priv->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mux_type = bank->iomux[iomux_num].type; + reg = bank->iomux[iomux_num].offset; + reg += rockchip_get_mux_data(mux_type, pin, &bit, &mask); + + if (bank->route_mask & BIT(pin)) { + if (rockchip_get_mux_route(bank, pin, mux, &route_reg, + &route_val)) { + ret = regmap_write(regmap, route_reg, route_val); + if (ret) + return ret; + } + } + + data = (mask << (bit + 16)); + data |= (mux & mask) << bit; + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RK3228_PULL_OFFSET 0x100 static void rk3228_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, @@ -158,6 +191,33 @@ static void rk3228_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, *bit *= ROCKCHIP_PULL_BITS_PER_PIN; } +static int rk3228_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct regmap *regmap; + int reg, ret; + u8 bit, type; + u32 data; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + return -ENOTSUPP; + + rk3228_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 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RK3228_DRV_GRF_OFFSET 0x200 static void rk3228_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, @@ -175,6 +235,29 @@ static void rk3228_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, *bit *= ROCKCHIP_DRV_BITS_PER_PIN; } +static int rk3228_set_drive(struct rockchip_pin_bank *bank, + int pin_num, int strength) +{ + struct regmap *regmap; + int reg, ret; + u32 data; + u8 bit; + int type = bank->drv[pin_num / 8].drv_type; + + rk3228_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + ret = rockchip_translate_drive_value(type, strength); + if (ret < 0) { + debug("unsupported driver strength %d\n", strength); + return ret; + } + + /* enable the write to the equivalent lower bits */ + data = ((1 << ROCKCHIP_DRV_BITS_PER_PIN) - 1) << (bit + 16); + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + return ret; +} + static struct rockchip_pin_bank rk3228_pin_banks[] = { PIN_BANK(0, 32, "gpio0"), PIN_BANK(1, 32, "gpio1"), @@ -183,15 +266,14 @@ static struct rockchip_pin_bank rk3228_pin_banks[] = { }; static struct rockchip_pin_ctrl rk3228_pin_ctrl = { - .pin_banks = rk3228_pin_banks, - .nr_banks = ARRAY_SIZE(rk3228_pin_banks), - .label = "RK3228-GPIO", - .type = RK3288, - .grf_mux_offset = 0x0, - .iomux_routes = rk3228_mux_route_data, - .niomux_routes = ARRAY_SIZE(rk3228_mux_route_data), - .pull_calc_reg = rk3228_calc_pull_reg_and_bit, - .drv_calc_reg = rk3228_calc_drv_reg_and_bit, + .pin_banks = rk3228_pin_banks, + .nr_banks = ARRAY_SIZE(rk3228_pin_banks), + .grf_mux_offset = 0x0, + .iomux_routes = rk3228_mux_route_data, + .niomux_routes = ARRAY_SIZE(rk3228_mux_route_data), + .set_mux = rk3228_set_mux, + .set_pull = rk3228_set_pull, + .set_drive = rk3228_set_drive, }; static const struct udevice_id rk3228_pinctrl_ids[] = { diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3288.c b/drivers/pinctrl/rockchip/pinctrl-rk3288.c index 8b6ce11a63..7ae147f304 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rk3288.c +++ b/drivers/pinctrl/rockchip/pinctrl-rk3288.c @@ -7,7 +7,6 @@ #include <dm.h> #include <dm/pinctrl.h> #include <regmap.h> -#include <syscon.h> #include "pinctrl-rockchip.h" @@ -29,6 +28,47 @@ static struct rockchip_mux_route_data rk3288_mux_route_data[] = { }, }; +static int rk3288_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, ret, mask, mux_type; + u8 bit; + u32 data, route_reg, route_val; + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? priv->regmap_pmu : priv->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mux_type = bank->iomux[iomux_num].type; + reg = bank->iomux[iomux_num].offset; + reg += rockchip_get_mux_data(mux_type, pin, &bit, &mask); + + if (bank->route_mask & BIT(pin)) { + if (rockchip_get_mux_route(bank, pin, mux, &route_reg, + &route_val)) { + ret = regmap_write(regmap, route_reg, route_val); + if (ret) + return ret; + } + } + + /* bank0 is special, there are no higher 16 bit writing bits. */ + if (bank->bank_num == 0) { + regmap_read(regmap, reg, &data); + data &= ~(mask << bit); + } else { + /* enable the write to the equivalent lower bits */ + data = (mask << (bit + 16)); + } + + data |= (mux & mask) << bit; + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RK3288_PULL_OFFSET 0x140 #define RK3288_PULL_PMU_OFFSET 0x64 @@ -42,10 +82,6 @@ static void rk3288_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, if (bank->bank_num == 0) { *regmap = priv->regmap_pmu; *reg = RK3288_PULL_PMU_OFFSET; - - *reg += ((pin_num / ROCKCHIP_PULL_PINS_PER_REG) * 4); - *bit = pin_num % ROCKCHIP_PULL_PINS_PER_REG; - *bit *= ROCKCHIP_PULL_BITS_PER_PIN; } else { *regmap = priv->regmap_base; *reg = RK3288_PULL_OFFSET; @@ -53,11 +89,46 @@ static void rk3288_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, /* correct the offset, as we're starting with the 2nd bank */ *reg -= 0x10; *reg += bank->bank_num * ROCKCHIP_PULL_BANK_STRIDE; - *reg += ((pin_num / ROCKCHIP_PULL_PINS_PER_REG) * 4); + } + + *reg += ((pin_num / ROCKCHIP_PULL_PINS_PER_REG) * 4); + + *bit = (pin_num % ROCKCHIP_PULL_PINS_PER_REG); + *bit *= ROCKCHIP_PULL_BITS_PER_PIN; +} + +static int rk3288_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct regmap *regmap; + int reg, ret; + u8 bit, type; + u32 data; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + return -ENOTSUPP; + + rk3288_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; + } - *bit = (pin_num % ROCKCHIP_PULL_PINS_PER_REG); - *bit *= ROCKCHIP_PULL_BITS_PER_PIN; + /* bank0 is special, there are no higher 16 bit writing bits */ + if (bank->bank_num == 0) { + regmap_read(regmap, reg, &data); + data &= ~(((1 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << bit); + } else { + /* enable the write to the equivalent lower bits */ + data = ((1 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); } + + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + + return ret; } #define RK3288_DRV_PMU_OFFSET 0x70 @@ -73,10 +144,6 @@ static void rk3288_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, if (bank->bank_num == 0) { *regmap = priv->regmap_pmu; *reg = RK3288_DRV_PMU_OFFSET; - - *reg += ((pin_num / ROCKCHIP_DRV_PINS_PER_REG) * 4); - *bit = pin_num % ROCKCHIP_DRV_PINS_PER_REG; - *bit *= ROCKCHIP_DRV_BITS_PER_PIN; } else { *regmap = priv->regmap_base; *reg = RK3288_DRV_GRF_OFFSET; @@ -84,27 +151,48 @@ static void rk3288_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, /* correct the offset, as we're starting with the 2nd bank */ *reg -= 0x10; *reg += bank->bank_num * ROCKCHIP_DRV_BANK_STRIDE; - *reg += ((pin_num / ROCKCHIP_DRV_PINS_PER_REG) * 4); + } - *bit = (pin_num % ROCKCHIP_DRV_PINS_PER_REG); - *bit *= ROCKCHIP_DRV_BITS_PER_PIN; + *reg += ((pin_num / ROCKCHIP_DRV_PINS_PER_REG) * 4); + *bit = (pin_num % ROCKCHIP_DRV_PINS_PER_REG); + *bit *= ROCKCHIP_DRV_BITS_PER_PIN; +} + +static int rk3288_set_drive(struct rockchip_pin_bank *bank, + int pin_num, int strength) +{ + struct regmap *regmap; + int reg, ret; + u32 data; + u8 bit; + int type = bank->drv[pin_num / 8].drv_type; + + rk3288_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + ret = rockchip_translate_drive_value(type, strength); + if (ret < 0) { + debug("unsupported driver strength %d\n", strength); + return ret; + } + + /* bank0 is special, there are no higher 16 bit writing bits. */ + if (bank->bank_num == 0) { + regmap_read(regmap, reg, &data); + data &= ~(((1 << ROCKCHIP_DRV_BITS_PER_PIN) - 1) << bit); + } else { + /* enable the write to the equivalent lower bits */ + data = ((1 << ROCKCHIP_DRV_BITS_PER_PIN) - 1) << (bit + 16); } + + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + return ret; } static struct rockchip_pin_bank rk3288_pin_banks[] = { - PIN_BANK_IOMUX_DRV_PULL_FLAGS(0, 24, "gpio0", - IOMUX_SOURCE_PMU | IOMUX_WRITABLE_32BIT, - IOMUX_SOURCE_PMU | IOMUX_WRITABLE_32BIT, - IOMUX_SOURCE_PMU | IOMUX_WRITABLE_32BIT, - IOMUX_UNROUTED, - DRV_TYPE_WRITABLE_32BIT, - DRV_TYPE_WRITABLE_32BIT, - DRV_TYPE_WRITABLE_32BIT, - 0, - PULL_TYPE_WRITABLE_32BIT, - PULL_TYPE_WRITABLE_32BIT, - PULL_TYPE_WRITABLE_32BIT, - 0 + PIN_BANK_IOMUX_FLAGS(0, 24, "gpio0", IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_UNROUTED ), PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_UNROUTED, IOMUX_UNROUTED, @@ -133,16 +221,15 @@ static struct rockchip_pin_bank rk3288_pin_banks[] = { }; static struct rockchip_pin_ctrl rk3288_pin_ctrl = { - .pin_banks = rk3288_pin_banks, - .nr_banks = ARRAY_SIZE(rk3288_pin_banks), - .label = "RK3288-GPIO", - .type = RK3288, - .grf_mux_offset = 0x0, - .pmu_mux_offset = 0x84, - .iomux_routes = rk3288_mux_route_data, - .niomux_routes = ARRAY_SIZE(rk3288_mux_route_data), - .pull_calc_reg = rk3288_calc_pull_reg_and_bit, - .drv_calc_reg = rk3288_calc_drv_reg_and_bit, + .pin_banks = rk3288_pin_banks, + .nr_banks = ARRAY_SIZE(rk3288_pin_banks), + .grf_mux_offset = 0x0, + .pmu_mux_offset = 0x84, + .iomux_routes = rk3288_mux_route_data, + .niomux_routes = ARRAY_SIZE(rk3288_mux_route_data), + .set_mux = rk3288_set_mux, + .set_pull = rk3288_set_pull, + .set_drive = rk3288_set_drive, }; static const struct udevice_id rk3288_pinctrl_ids[] = { diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3328.c b/drivers/pinctrl/rockchip/pinctrl-rk3328.c index f1b3d10dbe..8d37a6f945 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rk3328.c +++ b/drivers/pinctrl/rockchip/pinctrl-rk3328.c @@ -121,6 +121,42 @@ static struct rockchip_mux_route_data rk3328_mux_route_data[] = { }, }; +static int rk3328_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, ret, mask, mux_type; + u8 bit; + u32 data, route_reg, route_val; + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? priv->regmap_pmu : priv->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mux_type = bank->iomux[iomux_num].type; + reg = bank->iomux[iomux_num].offset; + reg += rockchip_get_mux_data(mux_type, pin, &bit, &mask); + + if (bank->recalced_mask & BIT(pin)) + rockchip_get_recalced_mux(bank, pin, ®, &bit, &mask); + + if (bank->route_mask & BIT(pin)) { + if (rockchip_get_mux_route(bank, pin, mux, &route_reg, + &route_val)) { + ret = regmap_write(regmap, route_reg, route_val); + if (ret) + return ret; + } + } + + data = (mask << (bit + 16)); + data |= (mux & mask) << bit; + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RK3328_PULL_OFFSET 0x100 static void rk3328_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, @@ -138,6 +174,33 @@ static void rk3328_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, *bit *= ROCKCHIP_PULL_BITS_PER_PIN; } +static int rk3328_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct regmap *regmap; + int reg, ret; + u8 bit, type; + u32 data; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + return -ENOTSUPP; + + rk3328_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 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RK3328_DRV_GRF_OFFSET 0x200 static void rk3328_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, @@ -155,6 +218,30 @@ static void rk3328_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, *bit *= ROCKCHIP_DRV_BITS_PER_PIN; } +static int rk3328_set_drive(struct rockchip_pin_bank *bank, + int pin_num, int strength) +{ + struct regmap *regmap; + int reg, ret; + u32 data; + u8 bit; + int type = bank->drv[pin_num / 8].drv_type; + + rk3328_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + ret = rockchip_translate_drive_value(type, strength); + if (ret < 0) { + debug("unsupported driver strength %d\n", strength); + return ret; + } + + /* enable the write to the equivalent lower bits */ + data = ((1 << ROCKCHIP_DRV_BITS_PER_PIN) - 1) << (bit + 16); + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RK3328_SCHMITT_BITS_PER_PIN 1 #define RK3328_SCHMITT_PINS_PER_REG 16 #define RK3328_SCHMITT_BANK_STRIDE 8 @@ -177,6 +264,21 @@ static int rk3328_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank, return 0; } +static int rk3328_set_schmitt(struct rockchip_pin_bank *bank, + int pin_num, int enable) +{ + struct regmap *regmap; + int reg; + u8 bit; + u32 data; + + rk3328_calc_schmitt_reg_and_bit(bank, pin_num, ®map, ®, &bit); + /* enable the write to the equivalent lower bits */ + data = BIT(bit + 16) | (enable << bit); + + return regmap_write(regmap, reg, data); +} + static struct rockchip_pin_bank rk3328_pin_banks[] = { PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", 0, 0, 0, 0), PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", 0, 0, 0, 0), @@ -192,18 +294,17 @@ static struct rockchip_pin_bank rk3328_pin_banks[] = { }; static struct rockchip_pin_ctrl rk3328_pin_ctrl = { - .pin_banks = rk3328_pin_banks, - .nr_banks = ARRAY_SIZE(rk3328_pin_banks), - .label = "RK3328-GPIO", - .type = RK3288, - .grf_mux_offset = 0x0, - .iomux_recalced = rk3328_mux_recalced_data, - .niomux_recalced = ARRAY_SIZE(rk3328_mux_recalced_data), - .iomux_routes = rk3328_mux_route_data, - .niomux_routes = ARRAY_SIZE(rk3328_mux_route_data), - .pull_calc_reg = rk3328_calc_pull_reg_and_bit, - .drv_calc_reg = rk3328_calc_drv_reg_and_bit, - .schmitt_calc_reg = rk3328_calc_schmitt_reg_and_bit, + .pin_banks = rk3328_pin_banks, + .nr_banks = ARRAY_SIZE(rk3328_pin_banks), + .grf_mux_offset = 0x0, + .iomux_recalced = rk3328_mux_recalced_data, + .niomux_recalced = ARRAY_SIZE(rk3328_mux_recalced_data), + .iomux_routes = rk3328_mux_route_data, + .niomux_routes = ARRAY_SIZE(rk3328_mux_route_data), + .set_mux = rk3328_set_mux, + .set_pull = rk3328_set_pull, + .set_drive = rk3328_set_drive, + .set_schmitt = rk3328_set_schmitt, }; static const struct udevice_id rk3328_pinctrl_ids[] = { diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3368.c b/drivers/pinctrl/rockchip/pinctrl-rk3368.c index f5cd6ff24e..6cb7bb45d9 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rk3368.c +++ b/drivers/pinctrl/rockchip/pinctrl-rk3368.c @@ -11,6 +11,30 @@ #include "pinctrl-rockchip.h" +static int rk3368_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, ret, mask, mux_type; + u8 bit; + u32 data; + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? priv->regmap_pmu : priv->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mux_type = bank->iomux[iomux_num].type; + reg = bank->iomux[iomux_num].offset; + reg += rockchip_get_mux_data(mux_type, pin, &bit, &mask); + + data = (mask << (bit + 16)); + data |= (mux & mask) << bit; + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RK3368_PULL_GRF_OFFSET 0x100 #define RK3368_PULL_PMU_OFFSET 0x10 @@ -24,10 +48,6 @@ static void rk3368_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, if (bank->bank_num == 0) { *regmap = priv->regmap_pmu; *reg = RK3368_PULL_PMU_OFFSET; - - *reg += ((pin_num / ROCKCHIP_PULL_PINS_PER_REG) * 4); - *bit = pin_num % ROCKCHIP_PULL_PINS_PER_REG; - *bit *= ROCKCHIP_PULL_BITS_PER_PIN; } else { *regmap = priv->regmap_base; *reg = RK3368_PULL_GRF_OFFSET; @@ -35,11 +55,39 @@ static void rk3368_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, /* correct the offset, as we're starting with the 2nd bank */ *reg -= 0x10; *reg += bank->bank_num * ROCKCHIP_PULL_BANK_STRIDE; - *reg += ((pin_num / ROCKCHIP_PULL_PINS_PER_REG) * 4); + } + + *reg += ((pin_num / ROCKCHIP_PULL_PINS_PER_REG) * 4); - *bit = (pin_num % ROCKCHIP_PULL_PINS_PER_REG); - *bit *= ROCKCHIP_PULL_BITS_PER_PIN; + *bit = (pin_num % ROCKCHIP_PULL_PINS_PER_REG); + *bit *= ROCKCHIP_PULL_BITS_PER_PIN; +} + +static int rk3368_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct regmap *regmap; + int reg, ret; + u8 bit, type; + u32 data; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + return -ENOTSUPP; + + rk3368_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 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + + return ret; } #define RK3368_DRV_PMU_OFFSET 0x20 @@ -55,10 +103,6 @@ static void rk3368_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, if (bank->bank_num == 0) { *regmap = priv->regmap_pmu; *reg = RK3368_DRV_PMU_OFFSET; - - *reg += ((pin_num / ROCKCHIP_DRV_PINS_PER_REG) * 4); - *bit = pin_num % ROCKCHIP_DRV_PINS_PER_REG; - *bit *= ROCKCHIP_DRV_BITS_PER_PIN; } else { *regmap = priv->regmap_base; *reg = RK3368_DRV_GRF_OFFSET; @@ -66,11 +110,35 @@ static void rk3368_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, /* correct the offset, as we're starting with the 2nd bank */ *reg -= 0x10; *reg += bank->bank_num * ROCKCHIP_DRV_BANK_STRIDE; - *reg += ((pin_num / ROCKCHIP_DRV_PINS_PER_REG) * 4); + } - *bit = (pin_num % ROCKCHIP_DRV_PINS_PER_REG); - *bit *= ROCKCHIP_DRV_BITS_PER_PIN; + *reg += ((pin_num / ROCKCHIP_DRV_PINS_PER_REG) * 4); + *bit = (pin_num % ROCKCHIP_DRV_PINS_PER_REG); + *bit *= ROCKCHIP_DRV_BITS_PER_PIN; +} + +static int rk3368_set_drive(struct rockchip_pin_bank *bank, + int pin_num, int strength) +{ + struct regmap *regmap; + int reg, ret; + u32 data; + u8 bit; + int type = bank->drv[pin_num / 8].drv_type; + + rk3368_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + ret = rockchip_translate_drive_value(type, strength); + if (ret < 0) { + debug("unsupported driver strength %d\n", strength); + return ret; } + + /* enable the write to the equivalent lower bits */ + data = ((1 << ROCKCHIP_DRV_BITS_PER_PIN) - 1) << (bit + 16); + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + + return ret; } static struct rockchip_pin_bank rk3368_pin_banks[] = { @@ -85,14 +153,13 @@ static struct rockchip_pin_bank rk3368_pin_banks[] = { }; static struct rockchip_pin_ctrl rk3368_pin_ctrl = { - .pin_banks = rk3368_pin_banks, - .nr_banks = ARRAY_SIZE(rk3368_pin_banks), - .label = "RK3368-GPIO", - .type = RK3368, - .grf_mux_offset = 0x0, - .pmu_mux_offset = 0x0, - .pull_calc_reg = rk3368_calc_pull_reg_and_bit, - .drv_calc_reg = rk3368_calc_drv_reg_and_bit, + .pin_banks = rk3368_pin_banks, + .nr_banks = ARRAY_SIZE(rk3368_pin_banks), + .grf_mux_offset = 0x0, + .pmu_mux_offset = 0x0, + .set_mux = rk3368_set_mux, + .set_pull = rk3368_set_pull, + .set_drive = rk3368_set_drive, }; static const struct udevice_id rk3368_pinctrl_ids[] = { diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3399.c b/drivers/pinctrl/rockchip/pinctrl-rk3399.c index c5aab647a5..75634e9f4d 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rk3399.c +++ b/drivers/pinctrl/rockchip/pinctrl-rk3399.c @@ -50,6 +50,39 @@ static struct rockchip_mux_route_data rk3399_mux_route_data[] = { }, }; +static int rk3399_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, ret, mask, mux_type; + u8 bit; + u32 data, route_reg, route_val; + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? priv->regmap_pmu : priv->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mux_type = bank->iomux[iomux_num].type; + reg = bank->iomux[iomux_num].offset; + reg += rockchip_get_mux_data(mux_type, pin, &bit, &mask); + + if (bank->route_mask & BIT(pin)) { + if (rockchip_get_mux_route(bank, pin, mux, &route_reg, + &route_val)) { + ret = regmap_write(regmap, route_reg, route_val); + if (ret) + return ret; + } + } + + data = (mask << (bit + 16)); + data |= (mux & mask) << bit; + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RK3399_PULL_GRF_OFFSET 0xe040 #define RK3399_PULL_PMU_OFFSET 0x40 @@ -65,10 +98,6 @@ static void rk3399_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, *reg = RK3399_PULL_PMU_OFFSET; *reg += bank->bank_num * ROCKCHIP_PULL_BANK_STRIDE; - - *reg += ((pin_num / ROCKCHIP_PULL_PINS_PER_REG) * 4); - *bit = pin_num % ROCKCHIP_PULL_PINS_PER_REG; - *bit *= ROCKCHIP_PULL_BITS_PER_PIN; } else { *regmap = priv->regmap_base; *reg = RK3399_PULL_GRF_OFFSET; @@ -76,11 +105,39 @@ static void rk3399_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, /* correct the offset, as we're starting with the 3rd bank */ *reg -= 0x20; *reg += bank->bank_num * ROCKCHIP_PULL_BANK_STRIDE; - *reg += ((pin_num / ROCKCHIP_PULL_PINS_PER_REG) * 4); + } + + *reg += ((pin_num / ROCKCHIP_PULL_PINS_PER_REG) * 4); + + *bit = (pin_num % ROCKCHIP_PULL_PINS_PER_REG); + *bit *= ROCKCHIP_PULL_BITS_PER_PIN; +} + +static int rk3399_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct regmap *regmap; + int reg, ret; + u8 bit, type; + u32 data; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + return -ENOTSUPP; - *bit = (pin_num % ROCKCHIP_PULL_PINS_PER_REG); - *bit *= ROCKCHIP_PULL_BITS_PER_PIN; + rk3399_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 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + + return ret; } static void rk3399_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, @@ -104,6 +161,79 @@ static void rk3399_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, *bit = (pin_num % 8) * 2; } +static int rk3399_set_drive(struct rockchip_pin_bank *bank, + int pin_num, int strength) +{ + struct regmap *regmap; + int reg, ret; + u32 data, rmask_bits, temp; + u8 bit; + int drv_type = bank->drv[pin_num / 8].drv_type; + + rk3399_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + ret = rockchip_translate_drive_value(drv_type, strength); + if (ret < 0) { + debug("unsupported driver strength %d\n", strength); + return ret; + } + + switch (drv_type) { + case DRV_TYPE_IO_1V8_3V0_AUTO: + case DRV_TYPE_IO_3V3_ONLY: + rmask_bits = ROCKCHIP_DRV_3BITS_PER_PIN; + switch (bit) { + case 0 ... 12: + /* regular case, nothing to do */ + break; + case 15: + /* + * drive-strength offset is special, as it is spread + * over 2 registers, the bit data[15] contains bit 0 + * of the value while temp[1:0] contains bits 2 and 1 + */ + data = (ret & 0x1) << 15; + temp = (ret >> 0x1) & 0x3; + + data |= BIT(31); + ret = regmap_write(regmap, reg, data); + if (ret) + return ret; + + temp |= (0x3 << 16); + reg += 0x4; + ret = regmap_write(regmap, reg, temp); + + return ret; + case 18 ... 21: + /* setting fully enclosed in the second register */ + reg += 4; + bit -= 16; + break; + default: + debug("unsupported bit: %d for pinctrl drive type: %d\n", + bit, drv_type); + return -EINVAL; + } + break; + case DRV_TYPE_IO_DEFAULT: + case DRV_TYPE_IO_1V8_OR_3V0: + case DRV_TYPE_IO_1V8_ONLY: + rmask_bits = ROCKCHIP_DRV_BITS_PER_PIN; + break; + default: + debug("unsupported pinctrl drive type: %d\n", + drv_type); + return -EINVAL; + } + + /* enable the write to the equivalent lower bits */ + data = ((1 << rmask_bits) - 1) << (bit + 16); + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + + return ret; +} + static struct rockchip_pin_bank rk3399_pin_banks[] = { PIN_BANK_IOMUX_FLAGS_DRV_FLAGS_OFFSET_PULL_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU, @@ -158,18 +288,17 @@ static struct rockchip_pin_bank rk3399_pin_banks[] = { }; static struct rockchip_pin_ctrl rk3399_pin_ctrl = { - .pin_banks = rk3399_pin_banks, - .nr_banks = ARRAY_SIZE(rk3399_pin_banks), - .label = "RK3399-GPIO", - .type = RK3399, - .grf_mux_offset = 0xe000, - .pmu_mux_offset = 0x0, - .grf_drv_offset = 0xe100, - .pmu_drv_offset = 0x80, - .iomux_routes = rk3399_mux_route_data, - .niomux_routes = ARRAY_SIZE(rk3399_mux_route_data), - .pull_calc_reg = rk3399_calc_pull_reg_and_bit, - .drv_calc_reg = rk3399_calc_drv_reg_and_bit, + .pin_banks = rk3399_pin_banks, + .nr_banks = ARRAY_SIZE(rk3399_pin_banks), + .grf_mux_offset = 0xe000, + .pmu_mux_offset = 0x0, + .grf_drv_offset = 0xe100, + .pmu_drv_offset = 0x80, + .iomux_routes = rk3399_mux_route_data, + .niomux_routes = ARRAY_SIZE(rk3399_mux_route_data), + .set_mux = rk3399_set_mux, + .set_pull = rk3399_set_pull, + .set_drive = rk3399_set_drive, }; static const struct udevice_id rk3399_pinctrl_ids[] = { diff --git a/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c b/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c index ce935656f0..80dc431d20 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c +++ b/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c @@ -35,8 +35,8 @@ static int rockchip_verify_config(struct udevice *dev, u32 bank, u32 pin) return 0; } -static void rockchip_get_recalced_mux(struct rockchip_pin_bank *bank, int pin, - int *reg, u8 *bit, int *mask) +void rockchip_get_recalced_mux(struct rockchip_pin_bank *bank, int pin, + int *reg, u8 *bit, int *mask) { struct rockchip_pinctrl_priv *priv = bank->priv; struct rockchip_pin_ctrl *ctrl = priv->ctrl; @@ -58,8 +58,8 @@ static void rockchip_get_recalced_mux(struct rockchip_pin_bank *bank, int pin, *bit = data->bit; } -static bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin, - int mux, u32 *reg, u32 *value) +bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin, + int mux, u32 *reg, u32 *value) { struct rockchip_pinctrl_priv *priv = bank->priv; struct rockchip_pin_ctrl *ctrl = priv->ctrl; @@ -82,7 +82,7 @@ static bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin, return true; } -static int rockchip_get_mux_data(int mux_type, int pin, u8 *bit, int *mask) +int rockchip_get_mux_data(int mux_type, int pin, u8 *bit, int *mask) { int offset = 0; @@ -193,11 +193,9 @@ static int rockchip_verify_mux(struct rockchip_pin_bank *bank, static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) { struct rockchip_pinctrl_priv *priv = bank->priv; + struct rockchip_pin_ctrl *ctrl = priv->ctrl; int iomux_num = (pin / 8); - struct regmap *regmap; - int reg, ret, mask, mux_type; - u8 bit; - u32 data, route_reg, route_val; + int ret; ret = rockchip_verify_mux(bank, pin, mux); if (ret < 0) @@ -208,35 +206,10 @@ static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) debug("setting mux of GPIO%d-%d to %d\n", bank->bank_num, pin, mux); - regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) - ? priv->regmap_pmu : priv->regmap_base; + if (!ctrl->set_mux) + return -ENOTSUPP; - /* get basic quadrupel of mux registers and the correct reg inside */ - mux_type = bank->iomux[iomux_num].type; - reg = bank->iomux[iomux_num].offset; - reg += rockchip_get_mux_data(mux_type, pin, &bit, &mask); - - if (bank->recalced_mask & BIT(pin)) - rockchip_get_recalced_mux(bank, pin, ®, &bit, &mask); - - if (bank->route_mask & BIT(pin)) { - if (rockchip_get_mux_route(bank, pin, mux, &route_reg, - &route_val)) { - ret = regmap_write(regmap, route_reg, route_val); - if (ret) - return ret; - } - } - - if (mux_type & IOMUX_WRITABLE_32BIT) { - regmap_read(regmap, reg, &data); - data &= ~(mask << bit); - } else { - data = (mask << (bit + 16)); - } - - data |= (mux & mask) << bit; - ret = regmap_write(regmap, reg, data); + ret = ctrl->set_mux(bank, pin, mux); return ret; } @@ -249,99 +222,37 @@ static int rockchip_perpin_drv_list[DRV_TYPE_MAX][8] = { { 4, 7, 10, 13, 16, 19, 22, 26 } }; -static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank, - int pin_num, int strength) +int rockchip_translate_drive_value(int type, int strength) { - struct rockchip_pinctrl_priv *priv = bank->priv; - struct rockchip_pin_ctrl *ctrl = priv->ctrl; - struct regmap *regmap; - int reg, ret, i; - u32 data, rmask_bits, temp; - u8 bit; - /* Where need to clean the special mask for rockchip_perpin_drv_list */ - int drv_type = bank->drv[pin_num / 8].drv_type & (~DRV_TYPE_IO_MASK); - - debug("setting drive of GPIO%d-%d to %d\n", bank->bank_num, - pin_num, strength); - - ctrl->drv_calc_reg(bank, pin_num, ®map, ®, &bit); + int i, ret; ret = -EINVAL; - for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list[drv_type]); i++) { - if (rockchip_perpin_drv_list[drv_type][i] == strength) { + for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list[type]); i++) { + if (rockchip_perpin_drv_list[type][i] == strength) { ret = i; break; - } else if (rockchip_perpin_drv_list[drv_type][i] < 0) { - ret = rockchip_perpin_drv_list[drv_type][i]; + } else if (rockchip_perpin_drv_list[type][i] < 0) { + ret = rockchip_perpin_drv_list[type][i]; break; } } - if (ret < 0) { - debug("unsupported driver strength %d\n", strength); - return ret; - } - - switch (drv_type) { - case DRV_TYPE_IO_1V8_3V0_AUTO: - case DRV_TYPE_IO_3V3_ONLY: - rmask_bits = ROCKCHIP_DRV_3BITS_PER_PIN; - switch (bit) { - case 0 ... 12: - /* regular case, nothing to do */ - break; - case 15: - /* - * drive-strength offset is special, as it is spread - * over 2 registers, the bit data[15] contains bit 0 - * of the value while temp[1:0] contains bits 2 and 1 - */ - data = (ret & 0x1) << 15; - temp = (ret >> 0x1) & 0x3; - - data |= BIT(31); - ret = regmap_write(regmap, reg, data); - if (ret) - return ret; + return ret; +} - temp |= (0x3 << 16); - reg += 0x4; - ret = regmap_write(regmap, reg, temp); +static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank, + int pin_num, int strength) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + struct rockchip_pin_ctrl *ctrl = priv->ctrl; - return ret; - case 18 ... 21: - /* setting fully enclosed in the second register */ - reg += 4; - bit -= 16; - break; - default: - debug("unsupported bit: %d for pinctrl drive type: %d\n", - bit, drv_type); - return -EINVAL; - } - break; - case DRV_TYPE_IO_DEFAULT: - case DRV_TYPE_IO_1V8_OR_3V0: - case DRV_TYPE_IO_1V8_ONLY: - rmask_bits = ROCKCHIP_DRV_BITS_PER_PIN; - break; - default: - debug("unsupported pinctrl drive type: %d\n", - drv_type); - return -EINVAL; - } + debug("setting drive of GPIO%d-%d to %d\n", bank->bank_num, + pin_num, strength); - if (bank->drv[pin_num / 8].drv_type & DRV_TYPE_WRITABLE_32BIT) { - regmap_read(regmap, reg, &data); - data &= ~(((1 << rmask_bits) - 1) << bit); - } else { - /* enable the write to the equivalent lower bits */ - data = ((1 << rmask_bits) - 1) << (bit + 16); - } + if (!ctrl->set_drive) + return -ENOTSUPP; - data |= (ret << bit); - ret = regmap_write(regmap, reg, data); - return ret; + return ctrl->set_drive(bank, pin_num, strength); } static int rockchip_pull_list[PULL_TYPE_MAX][4] = { @@ -359,70 +270,35 @@ static int rockchip_pull_list[PULL_TYPE_MAX][4] = { }, }; +int rockchip_translate_pull_value(int type, int pull) +{ + int i, ret; + + ret = -EINVAL; + for (i = 0; i < ARRAY_SIZE(rockchip_pull_list[type]); + i++) { + if (rockchip_pull_list[type][i] == pull) { + ret = i; + break; + } + } + + return ret; +} + static int rockchip_set_pull(struct rockchip_pin_bank *bank, int pin_num, int pull) { struct rockchip_pinctrl_priv *priv = bank->priv; struct rockchip_pin_ctrl *ctrl = priv->ctrl; - struct regmap *regmap; - int reg, ret, i, pull_type; - u8 bit; - u32 data; debug("setting pull of GPIO%d-%d to %d\n", bank->bank_num, pin_num, pull); - ctrl->pull_calc_reg(bank, pin_num, ®map, ®, &bit); + if (!ctrl->set_pull) + return -ENOTSUPP; - switch (ctrl->type) { - case RK3036: - case RK3128: - data = BIT(bit + 16); - if (pull == PIN_CONFIG_BIAS_DISABLE) - data |= BIT(bit); - ret = regmap_write(regmap, reg, data); - break; - case RV1108: - case RK3188: - case RK3288: - case RK3368: - case RK3399: - /* - * Where need to clean the special mask for - * rockchip_pull_list. - */ - pull_type = bank->pull_type[pin_num / 8] & (~PULL_TYPE_IO_MASK); - ret = -EINVAL; - for (i = 0; i < ARRAY_SIZE(rockchip_pull_list[pull_type]); - i++) { - if (rockchip_pull_list[pull_type][i] == pull) { - ret = i; - break; - } - } - - if (ret < 0) { - debug("unsupported pull setting %d\n", pull); - return ret; - } - - if (bank->pull_type[pin_num / 8] & PULL_TYPE_WRITABLE_32BIT) { - regmap_read(regmap, reg, &data); - data &= ~(((1 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << bit); - } else { - /* enable the write to the equivalent lower bits */ - data = ((1 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); - } - - data |= (ret << bit); - ret = regmap_write(regmap, reg, data); - break; - default: - debug("unsupported pinctrl type\n"); - return -EINVAL; - } - - return ret; + return ctrl->set_pull(bank, pin_num, pull); } static int rockchip_set_schmitt(struct rockchip_pin_bank *bank, @@ -430,89 +306,40 @@ static int rockchip_set_schmitt(struct rockchip_pin_bank *bank, { struct rockchip_pinctrl_priv *priv = bank->priv; struct rockchip_pin_ctrl *ctrl = priv->ctrl; - struct regmap *regmap; - int reg, ret; - u8 bit; - u32 data; debug("setting input schmitt of GPIO%d-%d to %d\n", bank->bank_num, pin_num, enable); - ret = ctrl->schmitt_calc_reg(bank, pin_num, ®map, ®, &bit); - if (ret) - return ret; - - /* enable the write to the equivalent lower bits */ - data = BIT(bit + 16) | (enable << bit); - - return regmap_write(regmap, reg, data); -} - -/* - * Pinconf_ops handling - */ -static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl, - unsigned int pull) -{ - switch (ctrl->type) { - case RK3036: - case RK3128: - return (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT || - pull == PIN_CONFIG_BIAS_DISABLE); - case RV1108: - case RK3188: - case RK3288: - case RK3368: - case RK3399: - return (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT); - } + if (!ctrl->set_schmitt) + return -ENOTSUPP; - return false; + return ctrl->set_schmitt(bank, pin_num, enable); } /* set the pin config settings for a specified pin */ static int rockchip_pinconf_set(struct rockchip_pin_bank *bank, u32 pin, u32 param, u32 arg) { - struct rockchip_pinctrl_priv *priv = bank->priv; - struct rockchip_pin_ctrl *ctrl = priv->ctrl; int rc; switch (param) { case PIN_CONFIG_BIAS_DISABLE: - rc = rockchip_set_pull(bank, pin, param); - if (rc) - return rc; - break; - case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: case PIN_CONFIG_BIAS_BUS_HOLD: - if (!rockchip_pinconf_pull_valid(ctrl, param)) - return -ENOTSUPP; - - if (!arg) - return -EINVAL; - rc = rockchip_set_pull(bank, pin, param); if (rc) return rc; break; case PIN_CONFIG_DRIVE_STRENGTH: - if (!ctrl->drv_calc_reg) - return -ENOTSUPP; - rc = rockchip_set_drive_perpin(bank, pin, arg); if (rc < 0) return rc; break; case PIN_CONFIG_INPUT_SCHMITT_ENABLE: - if (!ctrl->schmitt_calc_reg) - return -ENOTSUPP; - rc = rockchip_set_schmitt(bank, pin, arg); if (rc < 0) return rc; @@ -530,9 +357,8 @@ static const struct pinconf_param rockchip_conf_params[] = { { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 }, { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, + { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 }, { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 }, - { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 }, - { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 }, { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 }, { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 }, }; diff --git a/drivers/pinctrl/rockchip/pinctrl-rockchip.h b/drivers/pinctrl/rockchip/pinctrl-rockchip.h index 5a6849c996..9651e9c7a6 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rockchip.h +++ b/drivers/pinctrl/rockchip/pinctrl-rockchip.h @@ -8,16 +8,6 @@ #include <linux/types.h> -enum rockchip_pinctrl_type { - RV1108, - RK3036, - RK3128, - RK3188, - RK3288, - RK3368, - RK3399, -}; - /** * Encode variants of iomux registers into a type variable */ @@ -26,7 +16,6 @@ enum rockchip_pinctrl_type { #define IOMUX_SOURCE_PMU BIT(2) #define IOMUX_UNROUTED BIT(3) #define IOMUX_WIDTH_3BIT BIT(4) -#define IOMUX_WRITABLE_32BIT BIT(5) /** * Defined some common pins constants @@ -50,9 +39,6 @@ struct rockchip_iomux { int offset; }; -#define DRV_TYPE_IO_MASK GENMASK(31, 16) -#define DRV_TYPE_WRITABLE_32BIT BIT(31) - /** * enum type index corresponding to rockchip_perpin_drv_list arrays index. */ @@ -65,9 +51,6 @@ enum rockchip_pin_drv_type { DRV_TYPE_MAX }; -#define PULL_TYPE_IO_MASK GENMASK(31, 16) -#define PULL_TYPE_WRITABLE_32BIT BIT(31) - /** * enum type index corresponding to rockchip_pull_list arrays index. */ @@ -207,32 +190,6 @@ struct rockchip_pin_bank { }, \ } -#define PIN_BANK_IOMUX_DRV_PULL_FLAGS(id, pins, label, iom0, iom1, \ - iom2, iom3, drv0, drv1, drv2, \ - drv3, pull0, pull1, pull2, \ - pull3) \ - { \ - .bank_num = id, \ - .nr_pins = pins, \ - .name = label, \ - .iomux = { \ - { .type = iom0, .offset = -1 }, \ - { .type = iom1, .offset = -1 }, \ - { .type = iom2, .offset = -1 }, \ - { .type = iom3, .offset = -1 }, \ - }, \ - .drv = { \ - { .drv_type = drv0, .offset = -1 }, \ - { .drv_type = drv1, .offset = -1 }, \ - { .drv_type = drv2, .offset = -1 }, \ - { .drv_type = drv3, .offset = -1 }, \ - }, \ - .pull_type[0] = pull0, \ - .pull_type[1] = pull1, \ - .pull_type[2] = pull2, \ - .pull_type[3] = pull3, \ - } - #define PIN_BANK_IOMUX_FLAGS_DRV_FLAGS_OFFSET_PULL_FLAGS(id, pins, \ label, iom0, iom1, iom2, \ iom3, drv0, drv1, drv2, \ @@ -299,8 +256,6 @@ struct rockchip_pin_ctrl { struct rockchip_pin_bank *pin_banks; u32 nr_banks; u32 nr_pins; - char *label; - enum rockchip_pinctrl_type type; int grf_mux_offset; int pmu_mux_offset; int grf_drv_offset; @@ -310,15 +265,14 @@ struct rockchip_pin_ctrl { struct rockchip_mux_route_data *iomux_routes; u32 niomux_routes; - void (*pull_calc_reg)(struct rockchip_pin_bank *bank, - int pin_num, struct regmap **regmap, - int *reg, u8 *bit); - void (*drv_calc_reg)(struct rockchip_pin_bank *bank, - int pin_num, struct regmap **regmap, - int *reg, u8 *bit); - int (*schmitt_calc_reg)(struct rockchip_pin_bank *bank, - int pin_num, struct regmap **regmap, - int *reg, u8 *bit); + int (*set_mux)(struct rockchip_pin_bank *bank, + int pin, int mux); + int (*set_pull)(struct rockchip_pin_bank *bank, + int pin_num, int pull); + int (*set_drive)(struct rockchip_pin_bank *bank, + int pin_num, int strength); + int (*set_schmitt)(struct rockchip_pin_bank *bank, + int pin_num, int enable); }; /** @@ -331,5 +285,12 @@ struct rockchip_pinctrl_priv { extern const struct pinctrl_ops rockchip_pinctrl_ops; int rockchip_pinctrl_probe(struct udevice *dev); +void rockchip_get_recalced_mux(struct rockchip_pin_bank *bank, int pin, + int *reg, u8 *bit, int *mask); +bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin, + int mux, u32 *reg, u32 *value); +int rockchip_get_mux_data(int mux_type, int pin, u8 *bit, int *mask); +int rockchip_translate_drive_value(int type, int strength); +int rockchip_translate_pull_value(int type, int pull); #endif /* __DRIVERS_PINCTRL_ROCKCHIP_H */ diff --git a/drivers/pinctrl/rockchip/pinctrl-rv1108.c b/drivers/pinctrl/rockchip/pinctrl-rv1108.c index f4a09a6824..54610a3e90 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rv1108.c +++ b/drivers/pinctrl/rockchip/pinctrl-rv1108.c @@ -75,6 +75,33 @@ static struct rockchip_mux_recalced_data rv1108_mux_recalced_data[] = { }, }; +static int rv1108_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, ret, mask, mux_type; + u8 bit; + u32 data; + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? priv->regmap_pmu : priv->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mux_type = bank->iomux[iomux_num].type; + reg = bank->iomux[iomux_num].offset; + reg += rockchip_get_mux_data(mux_type, pin, &bit, &mask); + + if (bank->recalced_mask & BIT(pin)) + rockchip_get_recalced_mux(bank, pin, ®, &bit, &mask); + + data = (mask << (bit + 16)); + data |= (mux & mask) << bit; + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RV1108_PULL_PMU_OFFSET 0x10 #define RV1108_PULL_OFFSET 0x110 @@ -101,6 +128,34 @@ static void rv1108_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, *bit *= ROCKCHIP_PULL_BITS_PER_PIN; } +static int rv1108_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct regmap *regmap; + int reg, ret; + u8 bit, type; + u32 data; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + return -ENOTSUPP; + + rv1108_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 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); + + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + + return ret; +} + #define RV1108_DRV_PMU_OFFSET 0x20 #define RV1108_DRV_GRF_OFFSET 0x210 @@ -128,6 +183,30 @@ static void rv1108_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, *bit *= ROCKCHIP_DRV_BITS_PER_PIN; } +static int rv1108_set_drive(struct rockchip_pin_bank *bank, + int pin_num, int strength) +{ + struct regmap *regmap; + int reg, ret; + u32 data; + u8 bit; + int type = bank->drv[pin_num / 8].drv_type; + + rv1108_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + ret = rockchip_translate_drive_value(type, strength); + if (ret < 0) { + debug("unsupported driver strength %d\n", strength); + return ret; + } + + /* enable the write to the equivalent lower bits */ + data = ((1 << ROCKCHIP_DRV_BITS_PER_PIN) - 1) << (bit + 16); + + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + return ret; +} + #define RV1108_SCHMITT_PMU_OFFSET 0x30 #define RV1108_SCHMITT_GRF_OFFSET 0x388 #define RV1108_SCHMITT_BANK_STRIDE 8 @@ -158,6 +237,21 @@ static int rv1108_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank, return 0; } +static int rv1108_set_schmitt(struct rockchip_pin_bank *bank, + int pin_num, int enable) +{ + struct regmap *regmap; + int reg; + u8 bit; + u32 data; + + rv1108_calc_schmitt_reg_and_bit(bank, pin_num, ®map, ®, &bit); + /* enable the write to the equivalent lower bits */ + data = BIT(bit + 16) | (enable << bit); + + return regmap_write(regmap, reg, data); +} + static struct rockchip_pin_bank rv1108_pin_banks[] = { PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU, IOMUX_SOURCE_PMU, @@ -171,15 +265,14 @@ static struct rockchip_pin_bank rv1108_pin_banks[] = { static struct rockchip_pin_ctrl rv1108_pin_ctrl = { .pin_banks = rv1108_pin_banks, .nr_banks = ARRAY_SIZE(rv1108_pin_banks), - .label = "RV1108-GPIO", - .type = RV1108, .grf_mux_offset = 0x10, .pmu_mux_offset = 0x0, .iomux_recalced = rv1108_mux_recalced_data, .niomux_recalced = ARRAY_SIZE(rv1108_mux_recalced_data), - .pull_calc_reg = rv1108_calc_pull_reg_and_bit, - .drv_calc_reg = rv1108_calc_drv_reg_and_bit, - .schmitt_calc_reg = rv1108_calc_schmitt_reg_and_bit, + .set_mux = rv1108_set_mux, + .set_pull = rv1108_set_pull, + .set_drive = rv1108_set_drive, + .set_schmitt = rv1108_set_schmitt, }; static const struct udevice_id rv1108_pinctrl_ids[] = { diff --git a/drivers/pwm/rk_pwm.c b/drivers/pwm/rk_pwm.c index 9994cbafbf..88db294cf1 100644 --- a/drivers/pwm/rk_pwm.c +++ b/drivers/pwm/rk_pwm.c @@ -12,7 +12,7 @@ #include <regmap.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/pwm.h> +#include <asm/arch-rockchip/pwm.h> #include <power/regulator.h> struct rk_pwm_priv { diff --git a/drivers/qe/Kconfig b/drivers/qe/Kconfig index 49a6e32b16..864b36b822 100644 --- a/drivers/qe/Kconfig +++ b/drivers/qe/Kconfig @@ -1,6 +1,14 @@ # # QUICC Engine Drivers # +config QE + bool "Enable support for QUICC Engine" + depends on PPC + default y if ARCH_T1040 || ARCH_T1042 || ARCH_T1024 || ARCH_P1021 \ + || ARCH_P1025 + help + Chose this option to add support for the QUICC Engine. + config U_QE bool "Enable support for U QUICC Engine" default y if (ARCH_LS1021A && !SD_BOOT && !NAND_BOOT && !QSPI_BOOT) \ @@ -10,3 +18,28 @@ config U_QE || (TARGET_LS1043ARDB && !SPL_NO_QE && !NAND_BOOT && !QSPI_BOOT) help Choose this option to add support for U QUICC Engine. + +choice + prompt "QUICC Engine FMan ethernet firmware location" + depends on FMAN_ENET || QE + default SYS_QE_FMAN_FW_IN_ROM + +config SYS_QE_FMAN_FW_IN_NOR + bool "NOR flash" + +config SYS_QE_FMAN_FW_IN_NAND + bool "NAND flash" + +config SYS_QE_FMAN_FW_IN_SPIFLASH + bool "SPI flash" + +config SYS_QE_FMAN_FW_IN_MMC + bool "MMC" + +config SYS_QE_FMAN_FW_IN_REMOTE + bool "Remote memory location (PCI)" + +config SYS_QE_FMAN_FW_IN_ROM + bool "Firmware is already in ROM" + +endchoice diff --git a/drivers/qe/qe.c b/drivers/qe/qe.c index 70d02d3f93..505ae9b45f 100644 --- a/drivers/qe/qe.c +++ b/drivers/qe/qe.c @@ -119,7 +119,7 @@ static void qe_sdma_init(void) */ static u8 thread_snum[] = { /* Evthreads 16-29 are not supported in MPC8309 */ -#if !defined(CONFIG_MPC8309) +#if !defined(CONFIG_ARCH_MPC8309) 0x04, 0x05, 0x0c, 0x0d, 0x14, 0x15, 0x1c, 0x1d, 0x24, 0x25, 0x2c, 0x2d, diff --git a/drivers/ram/mpc83xx_sdram.c b/drivers/ram/mpc83xx_sdram.c index 441baeb6f1..f03d0428b2 100644 --- a/drivers/ram/mpc83xx_sdram.c +++ b/drivers/ram/mpc83xx_sdram.c @@ -169,8 +169,8 @@ static int mpc83xx_sdram_static_init(ofnode node, u32 cs, u32 mapaddr, u32 size) odt_rd_cfg = ofnode_read_u32_default(node, "odt_rd_cfg", 0); switch (odt_rd_cfg) { case ODT_RD_ONLY_OTHER_DIMM: - if (!IS_ENABLED(CONFIG_MPC8360) && - !IS_ENABLED(CONFIG_MPC837x)) { + if (!IS_ENABLED(CONFIG_ARCH_MPC8360) && + !IS_ENABLED(CONFIG_ARCH_MPC837X)) { debug("%s: odt_rd_cfg value %d invalid.\n", ofnode_get_name(node), odt_rd_cfg); return -EINVAL; @@ -179,10 +179,10 @@ static int mpc83xx_sdram_static_init(ofnode node, u32 cs, u32 mapaddr, u32 size) case ODT_RD_NEVER: case ODT_RD_ONLY_CURRENT: case ODT_RD_ONLY_OTHER_CS: - if (!IS_ENABLED(CONFIG_MPC830x) && - !IS_ENABLED(CONFIG_MPC831x) && - !IS_ENABLED(CONFIG_MPC8360) && - !IS_ENABLED(CONFIG_MPC837x)) { + if (!IS_ENABLED(CONFIG_ARCH_MPC830X) && + !IS_ENABLED(CONFIG_ARCH_MPC831X) && + !IS_ENABLED(CONFIG_ARCH_MPC8360) && + !IS_ENABLED(CONFIG_ARCH_MPC837X)) { debug("%s: odt_rd_cfg value %d invalid.\n", ofnode_get_name(node), odt_rd_cfg); return -EINVAL; @@ -200,8 +200,8 @@ static int mpc83xx_sdram_static_init(ofnode node, u32 cs, u32 mapaddr, u32 size) odt_wr_cfg = ofnode_read_u32_default(node, "odt_wr_cfg", 0); switch (odt_wr_cfg) { case ODT_WR_ONLY_OTHER_DIMM: - if (!IS_ENABLED(CONFIG_MPC8360) && - !IS_ENABLED(CONFIG_MPC837x)) { + if (!IS_ENABLED(CONFIG_ARCH_MPC8360) && + !IS_ENABLED(CONFIG_ARCH_MPC837X)) { debug("%s: odt_wr_cfg value %d invalid.\n", ofnode_get_name(node), odt_wr_cfg); return -EINVAL; @@ -210,10 +210,10 @@ static int mpc83xx_sdram_static_init(ofnode node, u32 cs, u32 mapaddr, u32 size) case ODT_WR_NEVER: case ODT_WR_ONLY_CURRENT: case ODT_WR_ONLY_OTHER_CS: - if (!IS_ENABLED(CONFIG_MPC830x) && - !IS_ENABLED(CONFIG_MPC831x) && - !IS_ENABLED(CONFIG_MPC8360) && - !IS_ENABLED(CONFIG_MPC837x)) { + if (!IS_ENABLED(CONFIG_ARCH_MPC830X) && + !IS_ENABLED(CONFIG_ARCH_MPC831X) && + !IS_ENABLED(CONFIG_ARCH_MPC8360) && + !IS_ENABLED(CONFIG_ARCH_MPC837X)) { debug("%s: odt_wr_cfg value %d invalid.\n", ofnode_get_name(node), odt_wr_cfg); return -EINVAL; diff --git a/drivers/ram/rockchip/dmc-rk3368.c b/drivers/ram/rockchip/dmc-rk3368.c index 8d1b9faacc..e52fc3baad 100644 --- a/drivers/ram/rockchip/dmc-rk3368.c +++ b/drivers/ram/rockchip/dmc-rk3368.c @@ -12,12 +12,12 @@ #include <regmap.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3368.h> -#include <asm/arch/grf_rk3368.h> -#include <asm/arch/ddr_rk3368.h> -#include <asm/arch/sdram.h> -#include <asm/arch/sdram_common.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3368.h> +#include <asm/arch-rockchip/grf_rk3368.h> +#include <asm/arch-rockchip/ddr_rk3368.h> +#include <asm/arch-rockchip/sdram.h> +#include <asm/arch-rockchip/sdram_common.h> struct dram_info { struct ram_info info; @@ -842,7 +842,11 @@ static int setup_sdram(struct udevice *dev) move_to_access_state(pctl); /* TODO(prt): could detect rank in training... */ +#ifdef CONFIG_TARGET_EVB_PX5 + params->chan.rank = 1; +#else params->chan.rank = 2; +#endif /* TODO(prt): bus width is not auto-detected (yet)... */ params->chan.bw = 2; /* 32bit wide bus */ params->chan.dbw = params->chan.dbw; /* 32bit wide bus */ diff --git a/drivers/ram/rockchip/sdram_rk3128.c b/drivers/ram/rockchip/sdram_rk3128.c index df7b988703..bfabc22a7d 100644 --- a/drivers/ram/rockchip/sdram_rk3128.c +++ b/drivers/ram/rockchip/sdram_rk3128.c @@ -7,9 +7,9 @@ #include <dm.h> #include <ram.h> #include <syscon.h> -#include <asm/arch/clock.h> -#include <asm/arch/grf_rk3128.h> -#include <asm/arch/sdram_common.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/grf_rk3128.h> +#include <asm/arch-rockchip/sdram_common.h> struct dram_info { struct ram_info info; diff --git a/drivers/ram/rockchip/sdram_rk3188.c b/drivers/ram/rockchip/sdram_rk3188.c index fdd500aa47..00e52ec949 100644 --- a/drivers/ram/rockchip/sdram_rk3188.c +++ b/drivers/ram/rockchip/sdram_rk3188.c @@ -15,13 +15,13 @@ #include <regmap.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3188.h> -#include <asm/arch/ddr_rk3188.h> -#include <asm/arch/grf_rk3188.h> -#include <asm/arch/pmu_rk3188.h> -#include <asm/arch/sdram.h> -#include <asm/arch/sdram_common.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3188.h> +#include <asm/arch-rockchip/ddr_rk3188.h> +#include <asm/arch-rockchip/grf_rk3188.h> +#include <asm/arch-rockchip/pmu_rk3188.h> +#include <asm/arch-rockchip/sdram.h> +#include <asm/arch-rockchip/sdram_common.h> #include <linux/err.h> struct chan_info { diff --git a/drivers/ram/rockchip/sdram_rk322x.c b/drivers/ram/rockchip/sdram_rk322x.c index 53835a9cd0..e96ac54c39 100644 --- a/drivers/ram/rockchip/sdram_rk322x.c +++ b/drivers/ram/rockchip/sdram_rk322x.c @@ -11,14 +11,14 @@ #include <regmap.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk322x.h> -#include <asm/arch/grf_rk322x.h> -#include <asm/arch/hardware.h> -#include <asm/arch/sdram_rk322x.h> -#include <asm/arch/timer.h> -#include <asm/arch/uart.h> -#include <asm/arch/sdram_common.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk322x.h> +#include <asm/arch-rockchip/grf_rk322x.h> +#include <asm/arch-rockchip/hardware.h> +#include <asm/arch-rockchip/sdram_rk322x.h> +#include <asm/arch-rockchip/timer.h> +#include <asm/arch-rockchip/uart.h> +#include <asm/arch-rockchip/sdram_common.h> #include <asm/types.h> #include <linux/err.h> @@ -49,7 +49,7 @@ struct rk322x_sdram_params { struct regmap *map; }; -#ifdef CONFIG_SPL_BUILD +#ifdef CONFIG_TPL_BUILD /* * [7:6] bank(n:n bit bank) * [5:4] row(13+n) @@ -750,7 +750,7 @@ static int rk322x_dmc_ofdata_to_platdata(struct udevice *dev) return 0; } -#endif /* CONFIG_SPL_BUILD */ +#endif /* CONFIG_TPL_BUILD */ #if CONFIG_IS_ENABLED(OF_PLATDATA) static int conv_of_platdata(struct udevice *dev) @@ -778,7 +778,7 @@ static int conv_of_platdata(struct udevice *dev) static int rk322x_dmc_probe(struct udevice *dev) { -#ifdef CONFIG_SPL_BUILD +#ifdef CONFIG_TPL_BUILD struct rk322x_sdram_params *plat = dev_get_platdata(dev); int ret; struct udevice *dev_clk; @@ -786,7 +786,7 @@ static int rk322x_dmc_probe(struct udevice *dev) struct dram_info *priv = dev_get_priv(dev); priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); -#ifdef CONFIG_SPL_BUILD +#ifdef CONFIG_TPL_BUILD #if CONFIG_IS_ENABLED(OF_PLATDATA) ret = conv_of_platdata(dev); if (ret) @@ -842,12 +842,12 @@ U_BOOT_DRIVER(dmc_rk322x) = { .id = UCLASS_RAM, .of_match = rk322x_dmc_ids, .ops = &rk322x_dmc_ops, -#ifdef CONFIG_SPL_BUILD +#ifdef CONFIG_TPL_BUILD .ofdata_to_platdata = rk322x_dmc_ofdata_to_platdata, #endif .probe = rk322x_dmc_probe, .priv_auto_alloc_size = sizeof(struct dram_info), -#ifdef CONFIG_SPL_BUILD +#ifdef CONFIG_TPL_BUILD .platdata_auto_alloc_size = sizeof(struct rk322x_sdram_params), #endif }; diff --git a/drivers/ram/rockchip/sdram_rk3288.c b/drivers/ram/rockchip/sdram_rk3288.c index d1e52d84e7..6bb025a851 100644 --- a/drivers/ram/rockchip/sdram_rk3288.c +++ b/drivers/ram/rockchip/sdram_rk3288.c @@ -15,13 +15,13 @@ #include <regmap.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3288.h> -#include <asm/arch/ddr_rk3288.h> -#include <asm/arch/grf_rk3288.h> -#include <asm/arch/pmu_rk3288.h> -#include <asm/arch/sdram.h> -#include <asm/arch/sdram_common.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3288.h> +#include <asm/arch-rockchip/ddr_rk3288.h> +#include <asm/arch-rockchip/grf_rk3288.h> +#include <asm/arch-rockchip/pmu_rk3288.h> +#include <asm/arch-rockchip/sdram.h> +#include <asm/arch-rockchip/sdram_common.h> #include <linux/err.h> #include <power/regulator.h> #include <power/rk8xx_pmic.h> diff --git a/drivers/ram/rockchip/sdram_rk3328.c b/drivers/ram/rockchip/sdram_rk3328.c index e8b234d866..f4e0b18447 100644 --- a/drivers/ram/rockchip/sdram_rk3328.c +++ b/drivers/ram/rockchip/sdram_rk3328.c @@ -7,9 +7,9 @@ #include <dm.h> #include <ram.h> #include <syscon.h> -#include <asm/arch/clock.h> -#include <asm/arch/grf_rk3328.h> -#include <asm/arch/sdram_common.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/grf_rk3328.h> +#include <asm/arch-rockchip/sdram_common.h> struct dram_info { struct ram_info info; diff --git a/drivers/ram/rockchip/sdram_rk3399.c b/drivers/ram/rockchip/sdram_rk3399.c index 94dd01156a..52518656c4 100644 --- a/drivers/ram/rockchip/sdram_rk3399.c +++ b/drivers/ram/rockchip/sdram_rk3399.c @@ -13,12 +13,12 @@ #include <regmap.h> #include <syscon.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/sdram_common.h> -#include <asm/arch/sdram_rk3399.h> -#include <asm/arch/cru_rk3399.h> -#include <asm/arch/grf_rk3399.h> -#include <asm/arch/hardware.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/sdram_common.h> +#include <asm/arch-rockchip/sdram_rk3399.h> +#include <asm/arch-rockchip/cru_rk3399.h> +#include <asm/arch-rockchip/grf_rk3399.h> +#include <asm/arch-rockchip/hardware.h> #include <linux/err.h> #include <time.h> @@ -30,7 +30,8 @@ struct chan_info { }; struct dram_info { -#ifdef CONFIG_SPL_BUILD +#if defined(CONFIG_TPL_BUILD) || \ + (!defined(CONFIG_TPL) && defined(CONFIG_SPL_BUILD)) struct chan_info chan[2]; struct clk ddr_clk; struct rk3399_cru *cru; @@ -55,7 +56,8 @@ struct dram_info { #define PHY_DRV_ODT_40 0xe #define PHY_DRV_ODT_34_3 0xf -#ifdef CONFIG_SPL_BUILD +#if defined(CONFIG_TPL_BUILD) || \ + (!defined(CONFIG_TPL) && defined(CONFIG_SPL_BUILD)) struct rockchip_dmc_plat { #if CONFIG_IS_ENABLED(OF_PLATDATA) @@ -1187,7 +1189,8 @@ static int rk3399_dmc_init(struct udevice *dev) static int rk3399_dmc_probe(struct udevice *dev) { -#ifdef CONFIG_SPL_BUILD +#if defined(CONFIG_TPL_BUILD) || \ + (!defined(CONFIG_TPL) && defined(CONFIG_SPL_BUILD)) if (rk3399_dmc_init(dev)) return 0; #else @@ -1226,12 +1229,14 @@ U_BOOT_DRIVER(dmc_rk3399) = { .id = UCLASS_RAM, .of_match = rk3399_dmc_ids, .ops = &rk3399_dmc_ops, -#ifdef CONFIG_SPL_BUILD +#if defined(CONFIG_TPL_BUILD) || \ + (!defined(CONFIG_TPL) && defined(CONFIG_SPL_BUILD)) .ofdata_to_platdata = rk3399_dmc_ofdata_to_platdata, #endif .probe = rk3399_dmc_probe, .priv_auto_alloc_size = sizeof(struct dram_info), -#ifdef CONFIG_SPL_BUILD +#if defined(CONFIG_TPL_BUILD) || \ + (!defined(CONFIG_TPL) && defined(CONFIG_SPL_BUILD)) .platdata_auto_alloc_size = sizeof(struct rockchip_dmc_plat), #endif }; diff --git a/drivers/ram/stm32mp1/Kconfig b/drivers/ram/stm32mp1/Kconfig index b9c816662c..2fd8c7b7e3 100644 --- a/drivers/ram/stm32mp1/Kconfig +++ b/drivers/ram/stm32mp1/Kconfig @@ -10,3 +10,40 @@ config STM32MP1_DDR family: support for LPDDR2, LPDDR3 and DDR3 the SDRAM parameters for controleur and phy need to be provided in device tree (computed by DDR tuning tools) + +config STM32MP1_DDR_INTERACTIVE + bool "STM32MP1 DDR driver : interactive support" + depends on STM32MP1_DDR + help + activate interactive support in STM32MP1 DDR controller driver + used for DDR tuning tools + to enter in intercative mode type 'd' during SPL DDR driver + initialisation + +config STM32MP1_DDR_INTERACTIVE_FORCE + bool "STM32MP1 DDR driver : force interactive mode" + depends on STM32MP1_DDR_INTERACTIVE + default n + help + force interactive mode in STM32MP1 DDR controller driver + skip the polling of character 'd' in console + useful when SPL is loaded in sysram + directly by programmer + +config STM32MP1_DDR_TESTS + bool "STM32MP1 DDR driver : tests support" + depends on STM32MP1_DDR_INTERACTIVE + default y + help + activate test support for interactive support in + STM32MP1 DDR controller driver: command test + +config STM32MP1_DDR_TUNING + bool "STM32MP1 DDR driver : support of tuning" + depends on STM32MP1_DDR_INTERACTIVE + default y + help + activate tuning command in STM32MP1 DDR interactive mode + used for DDR tuning tools + - DQ Deskew algorithm + - DQS Trimming diff --git a/drivers/ram/stm32mp1/Makefile b/drivers/ram/stm32mp1/Makefile index 79eb028fab..e1e9135603 100644 --- a/drivers/ram/stm32mp1/Makefile +++ b/drivers/ram/stm32mp1/Makefile @@ -5,3 +5,11 @@ obj-y += stm32mp1_ram.o obj-y += stm32mp1_ddr.o + +obj-$(CONFIG_STM32MP1_DDR_INTERACTIVE) += stm32mp1_interactive.o +obj-$(CONFIG_STM32MP1_DDR_TESTS) += stm32mp1_tests.o +obj-$(CONFIG_STM32MP1_DDR_TUNING) += stm32mp1_tuning.o + +ifneq ($(DDR_INTERACTIVE),) +CFLAGS_stm32mp1_interactive.o += -DCONFIG_STM32MP1_DDR_INTERACTIVE_FORCE=y +endif diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr.c b/drivers/ram/stm32mp1/stm32mp1_ddr.c index c7c3ba70a4..d765a46f7c 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ddr.c +++ b/drivers/ram/stm32mp1/stm32mp1_ddr.c @@ -41,8 +41,32 @@ struct reg_desc { offsetof(struct stm32mp1_ddrphy, x),\ offsetof(struct y, x)} +#define DDR_REG_DYN(x) \ + {#x,\ + offsetof(struct stm32mp1_ddrctl, x),\ + INVALID_OFFSET} + +#define DDRPHY_REG_DYN(x) \ + {#x,\ + offsetof(struct stm32mp1_ddrphy, x),\ + INVALID_OFFSET} + +/*********************************************************** + * PARAMETERS: value get from device tree : + * size / order need to be aligned with binding + * modification NOT ALLOWED !!! + ***********************************************************/ +#define DDRCTL_REG_REG_SIZE 25 /* st,ctl-reg */ +#define DDRCTL_REG_TIMING_SIZE 12 /* st,ctl-timing */ +#define DDRCTL_REG_MAP_SIZE 9 /* st,ctl-map */ +#define DDRCTL_REG_PERF_SIZE 17 /* st,ctl-perf */ + +#define DDRPHY_REG_REG_SIZE 11 /* st,phy-reg */ +#define DDRPHY_REG_TIMING_SIZE 10 /* st,phy-timing */ +#define DDRPHY_REG_CAL_SIZE 12 /* st,phy-cal */ + #define DDRCTL_REG_REG(x) DDRCTL_REG(x, stm32mp1_ddrctrl_reg) -static const struct reg_desc ddr_reg[] = { +static const struct reg_desc ddr_reg[DDRCTL_REG_REG_SIZE] = { DDRCTL_REG_REG(mstr), DDRCTL_REG_REG(mrctrl0), DDRCTL_REG_REG(mrctrl1), @@ -71,7 +95,7 @@ static const struct reg_desc ddr_reg[] = { }; #define DDRCTL_REG_TIMING(x) DDRCTL_REG(x, stm32mp1_ddrctrl_timing) -static const struct reg_desc ddr_timing[] = { +static const struct reg_desc ddr_timing[DDRCTL_REG_TIMING_SIZE] = { DDRCTL_REG_TIMING(rfshtmg), DDRCTL_REG_TIMING(dramtmg0), DDRCTL_REG_TIMING(dramtmg1), @@ -87,7 +111,7 @@ static const struct reg_desc ddr_timing[] = { }; #define DDRCTL_REG_MAP(x) DDRCTL_REG(x, stm32mp1_ddrctrl_map) -static const struct reg_desc ddr_map[] = { +static const struct reg_desc ddr_map[DDRCTL_REG_MAP_SIZE] = { DDRCTL_REG_MAP(addrmap1), DDRCTL_REG_MAP(addrmap2), DDRCTL_REG_MAP(addrmap3), @@ -100,7 +124,7 @@ static const struct reg_desc ddr_map[] = { }; #define DDRCTL_REG_PERF(x) DDRCTL_REG(x, stm32mp1_ddrctrl_perf) -static const struct reg_desc ddr_perf[] = { +static const struct reg_desc ddr_perf[DDRCTL_REG_PERF_SIZE] = { DDRCTL_REG_PERF(sched), DDRCTL_REG_PERF(sched1), DDRCTL_REG_PERF(perfhpr1), @@ -121,7 +145,7 @@ static const struct reg_desc ddr_perf[] = { }; #define DDRPHY_REG_REG(x) DDRPHY_REG(x, stm32mp1_ddrphy_reg) -static const struct reg_desc ddrphy_reg[] = { +static const struct reg_desc ddrphy_reg[DDRPHY_REG_REG_SIZE] = { DDRPHY_REG_REG(pgcr), DDRPHY_REG_REG(aciocr), DDRPHY_REG_REG(dxccr), @@ -136,7 +160,7 @@ static const struct reg_desc ddrphy_reg[] = { }; #define DDRPHY_REG_TIMING(x) DDRPHY_REG(x, stm32mp1_ddrphy_timing) -static const struct reg_desc ddrphy_timing[] = { +static const struct reg_desc ddrphy_timing[DDRPHY_REG_TIMING_SIZE] = { DDRPHY_REG_TIMING(ptr0), DDRPHY_REG_TIMING(ptr1), DDRPHY_REG_TIMING(ptr2), @@ -150,7 +174,7 @@ static const struct reg_desc ddrphy_timing[] = { }; #define DDRPHY_REG_CAL(x) DDRPHY_REG(x, stm32mp1_ddrphy_cal) -static const struct reg_desc ddrphy_cal[] = { +static const struct reg_desc ddrphy_cal[DDRPHY_REG_CAL_SIZE] = { DDRPHY_REG_CAL(dx0dllcr), DDRPHY_REG_CAL(dx0dqtr), DDRPHY_REG_CAL(dx0dqstr), @@ -165,6 +189,45 @@ static const struct reg_desc ddrphy_cal[] = { DDRPHY_REG_CAL(dx3dqstr), }; +/************************************************************** + * DYNAMIC REGISTERS: only used for debug purpose (read/modify) + **************************************************************/ +#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE +static const struct reg_desc ddr_dyn[] = { + DDR_REG_DYN(stat), + DDR_REG_DYN(init0), + DDR_REG_DYN(dfimisc), + DDR_REG_DYN(dfistat), + DDR_REG_DYN(swctl), + DDR_REG_DYN(swstat), + DDR_REG_DYN(pctrl_0), + DDR_REG_DYN(pctrl_1), +}; + +#define DDR_REG_DYN_SIZE ARRAY_SIZE(ddr_dyn) + +static const struct reg_desc ddrphy_dyn[] = { + DDRPHY_REG_DYN(pir), + DDRPHY_REG_DYN(pgsr), + DDRPHY_REG_DYN(zq0sr0), + DDRPHY_REG_DYN(zq0sr1), + DDRPHY_REG_DYN(dx0gsr0), + DDRPHY_REG_DYN(dx0gsr1), + DDRPHY_REG_DYN(dx1gsr0), + DDRPHY_REG_DYN(dx1gsr1), + DDRPHY_REG_DYN(dx2gsr0), + DDRPHY_REG_DYN(dx2gsr1), + DDRPHY_REG_DYN(dx3gsr0), + DDRPHY_REG_DYN(dx3gsr1), +}; + +#define DDRPHY_REG_DYN_SIZE ARRAY_SIZE(ddrphy_dyn) + +#endif + +/***************************************************************** + * REGISTERS ARRAY: used to parse device tree and interactive mode + *****************************************************************/ enum reg_type { REG_REG, REG_TIMING, @@ -173,6 +236,13 @@ enum reg_type { REGPHY_REG, REGPHY_TIMING, REGPHY_CAL, +#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE +/* dynamic registers => managed in driver or not changed, + * can be dumped in interactive mode + */ + REG_DYN, + REGPHY_DYN, +#endif REG_TYPE_NB }; @@ -193,19 +263,26 @@ struct ddr_reg_info { const struct ddr_reg_info ddr_registers[REG_TYPE_NB] = { [REG_REG] = { - "static", ddr_reg, ARRAY_SIZE(ddr_reg), DDR_BASE}, + "static", ddr_reg, DDRCTL_REG_REG_SIZE, DDR_BASE}, [REG_TIMING] = { - "timing", ddr_timing, ARRAY_SIZE(ddr_timing), DDR_BASE}, + "timing", ddr_timing, DDRCTL_REG_TIMING_SIZE, DDR_BASE}, [REG_PERF] = { - "perf", ddr_perf, ARRAY_SIZE(ddr_perf), DDR_BASE}, + "perf", ddr_perf, DDRCTL_REG_PERF_SIZE, DDR_BASE}, [REG_MAP] = { - "map", ddr_map, ARRAY_SIZE(ddr_map), DDR_BASE}, + "map", ddr_map, DDRCTL_REG_MAP_SIZE, DDR_BASE}, [REGPHY_REG] = { - "static", ddrphy_reg, ARRAY_SIZE(ddrphy_reg), DDRPHY_BASE}, + "static", ddrphy_reg, DDRPHY_REG_REG_SIZE, DDRPHY_BASE}, [REGPHY_TIMING] = { - "timing", ddrphy_timing, ARRAY_SIZE(ddrphy_timing), DDRPHY_BASE}, + "timing", ddrphy_timing, DDRPHY_REG_TIMING_SIZE, DDRPHY_BASE}, [REGPHY_CAL] = { - "cal", ddrphy_cal, ARRAY_SIZE(ddrphy_cal), DDRPHY_BASE}, + "cal", ddrphy_cal, DDRPHY_REG_CAL_SIZE, DDRPHY_BASE}, +#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE +[REG_DYN] = { + "dyn", ddr_dyn, DDR_REG_DYN_SIZE, DDR_BASE}, +[REGPHY_DYN] = { + "dyn", ddrphy_dyn, DDRPHY_REG_DYN_SIZE, DDRPHY_BASE}, +#endif + }; const char *base_name[] = { @@ -246,6 +323,231 @@ static void set_reg(const struct ddr_info *priv, } } +#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE +static void stm32mp1_dump_reg_desc(u32 base_addr, const struct reg_desc *desc) +{ + unsigned int *ptr; + + ptr = (unsigned int *)(base_addr + desc->offset); + printf("%s= 0x%08x\n", desc->name, readl(ptr)); +} + +static void stm32mp1_dump_param_desc(u32 par_addr, const struct reg_desc *desc) +{ + unsigned int *ptr; + + ptr = (unsigned int *)(par_addr + desc->par_offset); + printf("%s= 0x%08x\n", desc->name, readl(ptr)); +} + +static const struct reg_desc *found_reg(const char *name, enum reg_type *type) +{ + unsigned int i, j; + const struct reg_desc *desc; + + for (i = 0; i < ARRAY_SIZE(ddr_registers); i++) { + desc = ddr_registers[i].desc; + for (j = 0; j < ddr_registers[i].size; j++) { + if (strcmp(name, desc[j].name) == 0) { + *type = i; + return &desc[j]; + } + } + } + *type = REG_TYPE_NB; + return NULL; +} + +int stm32mp1_dump_reg(const struct ddr_info *priv, + const char *name) +{ + unsigned int i, j; + const struct reg_desc *desc; + u32 base_addr; + enum base_type p_base; + enum reg_type type; + const char *p_name; + enum base_type filter = NONE_BASE; + int result = -1; + + if (name) { + if (strcmp(name, base_name[DDR_BASE]) == 0) + filter = DDR_BASE; + else if (strcmp(name, base_name[DDRPHY_BASE]) == 0) + filter = DDRPHY_BASE; + } + + for (i = 0; i < ARRAY_SIZE(ddr_registers); i++) { + p_base = ddr_registers[i].base; + p_name = ddr_registers[i].name; + if (!name || (filter == p_base || !strcmp(name, p_name))) { + result = 0; + desc = ddr_registers[i].desc; + base_addr = get_base_addr(priv, p_base); + printf("==%s.%s==\n", base_name[p_base], p_name); + for (j = 0; j < ddr_registers[i].size; j++) + stm32mp1_dump_reg_desc(base_addr, &desc[j]); + } + } + if (result) { + desc = found_reg(name, &type); + if (desc) { + p_base = ddr_registers[type].base; + base_addr = get_base_addr(priv, p_base); + stm32mp1_dump_reg_desc(base_addr, desc); + result = 0; + } + } + return result; +} + +void stm32mp1_edit_reg(const struct ddr_info *priv, + char *name, char *string) +{ + unsigned long *ptr, value; + enum reg_type type; + enum base_type base; + const struct reg_desc *desc; + u32 base_addr; + + desc = found_reg(name, &type); + + if (!desc) { + printf("%s not found\n", name); + return; + } + if (strict_strtoul(string, 16, &value) < 0) { + printf("invalid value %s\n", string); + return; + } + base = ddr_registers[type].base; + base_addr = get_base_addr(priv, base); + ptr = (unsigned long *)(base_addr + desc->offset); + writel(value, ptr); + printf("%s= 0x%08x\n", desc->name, readl(ptr)); +} + +static u32 get_par_addr(const struct stm32mp1_ddr_config *config, + enum reg_type type) +{ + u32 par_addr = 0x0; + + switch (type) { + case REG_REG: + par_addr = (u32)&config->c_reg; + break; + case REG_TIMING: + par_addr = (u32)&config->c_timing; + break; + case REG_PERF: + par_addr = (u32)&config->c_perf; + break; + case REG_MAP: + par_addr = (u32)&config->c_map; + break; + case REGPHY_REG: + par_addr = (u32)&config->p_reg; + break; + case REGPHY_TIMING: + par_addr = (u32)&config->p_timing; + break; + case REGPHY_CAL: + par_addr = (u32)&config->p_cal; + break; + case REG_DYN: + case REGPHY_DYN: + case REG_TYPE_NB: + par_addr = (u32)NULL; + break; + } + + return par_addr; +} + +int stm32mp1_dump_param(const struct stm32mp1_ddr_config *config, + const char *name) +{ + unsigned int i, j; + const struct reg_desc *desc; + u32 par_addr; + enum base_type p_base; + enum reg_type type; + const char *p_name; + enum base_type filter = NONE_BASE; + int result = -EINVAL; + + if (name) { + if (strcmp(name, base_name[DDR_BASE]) == 0) + filter = DDR_BASE; + else if (strcmp(name, base_name[DDRPHY_BASE]) == 0) + filter = DDRPHY_BASE; + } + + for (i = 0; i < ARRAY_SIZE(ddr_registers); i++) { + par_addr = get_par_addr(config, i); + if (!par_addr) + continue; + p_base = ddr_registers[i].base; + p_name = ddr_registers[i].name; + if (!name || (filter == p_base || !strcmp(name, p_name))) { + result = 0; + desc = ddr_registers[i].desc; + printf("==%s.%s==\n", base_name[p_base], p_name); + for (j = 0; j < ddr_registers[i].size; j++) + stm32mp1_dump_param_desc(par_addr, &desc[j]); + } + } + if (result) { + desc = found_reg(name, &type); + if (desc) { + par_addr = get_par_addr(config, type); + if (par_addr) { + stm32mp1_dump_param_desc(par_addr, desc); + result = 0; + } + } + } + return result; +} + +void stm32mp1_edit_param(const struct stm32mp1_ddr_config *config, + char *name, char *string) +{ + unsigned long *ptr, value; + enum reg_type type; + const struct reg_desc *desc; + u32 par_addr; + + desc = found_reg(name, &type); + if (!desc) { + printf("%s not found\n", name); + return; + } + if (strict_strtoul(string, 16, &value) < 0) { + printf("invalid value %s\n", string); + return; + } + par_addr = get_par_addr(config, type); + if (!par_addr) { + printf("no parameter %s\n", name); + return; + } + ptr = (unsigned long *)(par_addr + desc->par_offset); + writel(value, ptr); + printf("%s= 0x%08x\n", desc->name, readl(ptr)); +} +#endif + +__weak bool stm32mp1_ddr_interactive(void *priv, + enum stm32mp1_ddr_interact_step step, + const struct stm32mp1_ddr_config *config) +{ + return false; +} + +#define INTERACTIVE(step)\ + stm32mp1_ddr_interactive(priv, step, config) + static void ddrphy_idone_wait(struct stm32mp1_ddrphy *phy) { u32 pgsr; @@ -312,7 +614,7 @@ static void wait_operating_mode(struct ddr_info *priv, int mode) /* self-refresh due to software => check also STAT.selfref_type */ if (mode == DDRCTRL_STAT_OPERATING_MODE_SR) { mask |= DDRCTRL_STAT_SELFREF_TYPE_MASK; - stat |= DDRCTRL_STAT_SELFREF_TYPE_SR; + val |= DDRCTRL_STAT_SELFREF_TYPE_SR; } else if (mode == DDRCTRL_STAT_OPERATING_MODE_NORMAL) { /* normal mode: handle also automatic self refresh */ mask2 = DDRCTRL_STAT_OPERATING_MODE_MASK | @@ -355,7 +657,7 @@ void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, } /* board-specific DDR power initializations. */ -__weak int board_ddr_power_init(void) +__weak int board_ddr_power_init(enum ddr_type ddr_type) { return 0; } @@ -365,15 +667,21 @@ void stm32mp1_ddr_init(struct ddr_info *priv, const struct stm32mp1_ddr_config *config) { u32 pir; - int ret; + int ret = -EINVAL; - ret = board_ddr_power_init(); + if (config->c_reg.mstr & DDRCTRL_MSTR_DDR3) + ret = board_ddr_power_init(STM32MP_DDR3); + else if (config->c_reg.mstr & DDRCTRL_MSTR_LPDDR2) + ret = board_ddr_power_init(STM32MP_LPDDR2); + else if (config->c_reg.mstr & DDRCTRL_MSTR_LPDDR3) + ret = board_ddr_power_init(STM32MP_LPDDR3); if (ret) panic("ddr power init failed\n"); +start: debug("name = %s\n", config->info.name); - debug("speed = %d MHz\n", config->info.speed); + debug("speed = %d kHz\n", config->info.speed); debug("size = 0x%x\n", config->info.size); /* * 1. Program the DWC_ddr_umctl2 registers @@ -389,7 +697,7 @@ void stm32mp1_ddr_init(struct ddr_info *priv, /* 1.2. start CLOCK */ if (stm32mp1_ddr_clk_enable(priv, config->info.speed)) - panic("invalid DRAM clock : %d MHz\n", + panic("invalid DRAM clock : %d kHz\n", config->info.speed); /* 1.3. deassert reset */ @@ -401,11 +709,12 @@ void stm32mp1_ddr_init(struct ddr_info *priv, */ clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAPBRST); -/* 1.4. wait 4 cycles for synchronization */ - asm(" nop"); - asm(" nop"); - asm(" nop"); - asm(" nop"); +/* 1.4. wait 128 cycles to permit initialization of end logic */ + udelay(2); + /* for PCLK = 133MHz => 1 us is enough, 2 to allow lower frequency */ + + if (INTERACTIVE(STEP_DDR_RESET)) + goto start; /* 1.5. initialize registers ddr_umctl2 */ /* Stop uMCTL2 before PHY is ready */ @@ -424,6 +733,9 @@ void stm32mp1_ddr_init(struct ddr_info *priv, set_reg(priv, REG_PERF, &config->c_perf); + if (INTERACTIVE(STEP_CTL_INIT)) + goto start; + /* 2. deassert reset signal core_ddrc_rstn, aresetn and presetn */ clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCORERST); clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAXIRST); @@ -436,6 +748,9 @@ void stm32mp1_ddr_init(struct ddr_info *priv, set_reg(priv, REGPHY_TIMING, &config->p_timing); set_reg(priv, REGPHY_CAL, &config->p_cal); + if (INTERACTIVE(STEP_PHY_INIT)) + goto start; + /* 4. Monitor PHY init status by polling PUBL register PGSR.IDONE * Perform DDR PHY DRAM initialization and Gate Training Evaluation */ @@ -492,4 +807,7 @@ void stm32mp1_ddr_init(struct ddr_info *priv, /* enable uMCTL2 AXI port 0 and 1 */ setbits_le32(&priv->ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN); setbits_le32(&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN); + + if (INTERACTIVE(STEP_DDR_READY)) + goto start; } diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr.h b/drivers/ram/stm32mp1/stm32mp1_ddr.h index 3cd0161299..a8eed89e3c 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ddr.h +++ b/drivers/ram/stm32mp1/stm32mp1_ddr.h @@ -157,7 +157,7 @@ struct stm32mp1_ddrphy_cal { struct stm32mp1_ddr_info { const char *name; - u16 speed; /* in MHZ */ + u32 speed; /* in kHZ */ u32 size; /* memory size in byte = col * row * width */ }; @@ -172,7 +172,7 @@ struct stm32mp1_ddr_config { struct stm32mp1_ddrphy_cal p_cal; }; -int stm32mp1_ddr_clk_enable(struct ddr_info *priv, u16 mem_speed); +int stm32mp1_ddr_clk_enable(struct ddr_info *priv, u32 mem_speed); void stm32mp1_ddrphy_init(struct stm32mp1_ddrphy *phy, u32 pir); void stm32mp1_refresh_disable(struct stm32mp1_ddrctl *ctl); void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h b/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h index a606b2bcbe..9d33186b3a 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h +++ b/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h @@ -234,6 +234,8 @@ struct stm32mp1_ddrphy { /* DDRCTRL REGISTERS */ #define DDRCTRL_MSTR_DDR3 BIT(0) +#define DDRCTRL_MSTR_LPDDR2 BIT(2) +#define DDRCTRL_MSTR_LPDDR3 BIT(3) #define DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK GENMASK(13, 12) #define DDRCTRL_MSTR_DATA_BUS_WIDTH_FULL (0 << 12) #define DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF (1 << 12) @@ -330,6 +332,7 @@ struct stm32mp1_ddrphy { #define DDRPHYC_DXNGCR_DXEN BIT(0) +#define DDRPHYC_DXNDLLCR_DLLSRST BIT(30) #define DDRPHYC_DXNDLLCR_DLLDIS BIT(31) #define DDRPHYC_DXNDLLCR_SDPHASE_MASK GENMASK(17, 14) #define DDRPHYC_DXNDLLCR_SDPHASE_SHIFT 14 diff --git a/drivers/ram/stm32mp1/stm32mp1_interactive.c b/drivers/ram/stm32mp1/stm32mp1_interactive.c new file mode 100644 index 0000000000..cc9b2e7c96 --- /dev/null +++ b/drivers/ram/stm32mp1/stm32mp1_interactive.c @@ -0,0 +1,483 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + */ + +#include <common.h> +#include <console.h> +#include <cli.h> +#include <clk.h> +#include <malloc.h> +#include <ram.h> +#include <reset.h> +#include "stm32mp1_ddr.h" +#include "stm32mp1_tests.h" + +DECLARE_GLOBAL_DATA_PTR; + +enum ddr_command { + DDR_CMD_HELP, + DDR_CMD_INFO, + DDR_CMD_FREQ, + DDR_CMD_RESET, + DDR_CMD_PARAM, + DDR_CMD_PRINT, + DDR_CMD_EDIT, + DDR_CMD_STEP, + DDR_CMD_NEXT, + DDR_CMD_GO, + DDR_CMD_TEST, + DDR_CMD_TUNING, + DDR_CMD_UNKNOWN, +}; + +const char *step_str[] = { + [STEP_DDR_RESET] = "DDR_RESET", + [STEP_CTL_INIT] = "DDR_CTRL_INIT_DONE", + [STEP_PHY_INIT] = "DDR PHY_INIT_DONE", + [STEP_DDR_READY] = "DDR_READY", + [STEP_RUN] = "RUN" +}; + +enum ddr_command stm32mp1_get_command(char *cmd, int argc) +{ + const char *cmd_string[DDR_CMD_UNKNOWN] = { + [DDR_CMD_HELP] = "help", + [DDR_CMD_INFO] = "info", + [DDR_CMD_FREQ] = "freq", + [DDR_CMD_RESET] = "reset", + [DDR_CMD_PARAM] = "param", + [DDR_CMD_PRINT] = "print", + [DDR_CMD_EDIT] = "edit", + [DDR_CMD_STEP] = "step", + [DDR_CMD_NEXT] = "next", + [DDR_CMD_GO] = "go", +#ifdef CONFIG_STM32MP1_DDR_TESTS + [DDR_CMD_TEST] = "test", +#endif +#ifdef CONFIG_STM32MP1_DDR_TUNING + [DDR_CMD_TUNING] = "tuning", +#endif + }; + /* min and max number of argument */ + const char cmd_arg[DDR_CMD_UNKNOWN][2] = { + [DDR_CMD_HELP] = { 0, 0 }, + [DDR_CMD_INFO] = { 0, 255 }, + [DDR_CMD_FREQ] = { 0, 1 }, + [DDR_CMD_RESET] = { 0, 0 }, + [DDR_CMD_PARAM] = { 0, 2 }, + [DDR_CMD_PRINT] = { 0, 1 }, + [DDR_CMD_EDIT] = { 2, 2 }, + [DDR_CMD_STEP] = { 0, 1 }, + [DDR_CMD_NEXT] = { 0, 0 }, + [DDR_CMD_GO] = { 0, 0 }, +#ifdef CONFIG_STM32MP1_DDR_TESTS + [DDR_CMD_TEST] = { 0, 255 }, +#endif +#ifdef CONFIG_STM32MP1_DDR_TUNING + [DDR_CMD_TUNING] = { 0, 255 }, +#endif + }; + int i; + + for (i = 0; i < DDR_CMD_UNKNOWN; i++) + if (!strcmp(cmd, cmd_string[i])) { + if (argc - 1 < cmd_arg[i][0]) { + printf("no enought argument (min=%d)\n", + cmd_arg[i][0]); + return DDR_CMD_UNKNOWN; + } else if (argc - 1 > cmd_arg[i][1]) { + printf("too many argument (max=%d)\n", + cmd_arg[i][1]); + return DDR_CMD_UNKNOWN; + } else { + return i; + } + } + + printf("unknown command %s\n", cmd); + return DDR_CMD_UNKNOWN; +} + +static void stm32mp1_do_usage(void) +{ + const char *usage = { + "commands:\n\n" + "help displays help\n" + "info displays DDR information\n" + "info <param> <val> changes DDR information\n" + " with <param> = step, name, size or speed\n" + "freq displays the DDR PHY frequency in kHz\n" + "freq <freq> changes the DDR PHY frequency\n" + "param [type|reg] prints input parameters\n" + "param <reg> <val> edits parameters in step 0\n" + "print [type|reg] dumps registers\n" + "edit <reg> <val> modifies one register\n" + "step lists the available step\n" + "step <n> go to the step <n>\n" + "next goes to the next step\n" + "go continues the U-Boot SPL execution\n" + "reset reboots machine\n" +#ifdef CONFIG_STM32MP1_DDR_TESTS + "test [help] | <n> [...] lists (with help) or executes test <n>\n" +#endif +#ifdef CONFIG_STM32MP1_DDR_TUNING + "tuning [help] | <n> [...] lists (with help) or execute tuning <n>\n" +#endif + "\nwith for [type|reg]:\n" + " all registers if absent\n" + " <type> = ctl, phy\n" + " or one category (static, timing, map, perf, cal, dyn)\n" + " <reg> = name of the register\n" + }; + + puts(usage); +} + +static bool stm32mp1_check_step(enum stm32mp1_ddr_interact_step step, + enum stm32mp1_ddr_interact_step expected) +{ + if (step != expected) { + printf("invalid step %d:%s expecting %d:%s\n", + step, step_str[step], + expected, + step_str[expected]); + return false; + } + return true; +} + +static void stm32mp1_do_info(struct ddr_info *priv, + struct stm32mp1_ddr_config *config, + enum stm32mp1_ddr_interact_step step, + int argc, char * const argv[]) +{ + unsigned long value; + static char *ddr_name; + + if (argc == 1) { + printf("step = %d : %s\n", step, step_str[step]); + printf("name = %s\n", config->info.name); + printf("size = 0x%x\n", config->info.size); + printf("speed = %d kHz\n", config->info.speed); + return; + } + + if (argc < 3) { + printf("no enought parameter\n"); + return; + } + if (!strcmp(argv[1], "name")) { + u32 i, name_len = 0; + + for (i = 2; i < argc; i++) + name_len += strlen(argv[i]) + 1; + if (ddr_name) + free(ddr_name); + ddr_name = malloc(name_len); + config->info.name = ddr_name; + if (!ddr_name) { + printf("alloc error, length %d\n", name_len); + return; + } + strcpy(ddr_name, argv[2]); + for (i = 3; i < argc; i++) { + strcat(ddr_name, " "); + strcat(ddr_name, argv[i]); + } + printf("name = %s\n", ddr_name); + return; + } + if (!strcmp(argv[1], "size")) { + if (strict_strtoul(argv[2], 16, &value) < 0) { + printf("invalid value %s\n", argv[2]); + } else { + config->info.size = value; + printf("size = 0x%x\n", config->info.size); + } + return; + } + if (!strcmp(argv[1], "speed")) { + if (strict_strtoul(argv[2], 10, &value) < 0) { + printf("invalid value %s\n", argv[2]); + } else { + config->info.speed = value; + printf("speed = %d kHz\n", config->info.speed); + value = clk_get_rate(&priv->clk); + printf("DDRPHY = %ld kHz\n", value / 1000); + } + return; + } + printf("argument %s invalid\n", argv[1]); +} + +static bool stm32mp1_do_freq(struct ddr_info *priv, + int argc, char * const argv[]) +{ + unsigned long ddrphy_clk; + + if (argc == 2) { + if (strict_strtoul(argv[1], 0, &ddrphy_clk) < 0) { + printf("invalid argument %s", argv[1]); + return false; + } + if (clk_set_rate(&priv->clk, ddrphy_clk * 1000)) { + printf("ERROR: update failed!\n"); + return false; + } + } + ddrphy_clk = clk_get_rate(&priv->clk); + printf("DDRPHY = %ld kHz\n", ddrphy_clk / 1000); + if (argc == 2) + return true; + return false; +} + +static void stm32mp1_do_param(enum stm32mp1_ddr_interact_step step, + const struct stm32mp1_ddr_config *config, + int argc, char * const argv[]) +{ + switch (argc) { + case 1: + stm32mp1_dump_param(config, NULL); + break; + case 2: + if (stm32mp1_dump_param(config, argv[1])) + printf("invalid argument %s\n", + argv[1]); + break; + case 3: + if (!stm32mp1_check_step(step, STEP_DDR_RESET)) + return; + stm32mp1_edit_param(config, argv[1], argv[2]); + break; + } +} + +static void stm32mp1_do_print(struct ddr_info *priv, + int argc, char * const argv[]) +{ + switch (argc) { + case 1: + stm32mp1_dump_reg(priv, NULL); + break; + case 2: + if (stm32mp1_dump_reg(priv, argv[1])) + printf("invalid argument %s\n", + argv[1]); + break; + } +} + +static int stm32mp1_do_step(enum stm32mp1_ddr_interact_step step, + int argc, char * const argv[]) +{ + int i; + unsigned long value; + + switch (argc) { + case 1: + for (i = 0; i < ARRAY_SIZE(step_str); i++) + printf("%d:%s\n", i, step_str[i]); + break; + + case 2: + if ((strict_strtoul(argv[1], 0, + &value) < 0) || + value >= ARRAY_SIZE(step_str)) { + printf("invalid argument %s\n", + argv[1]); + goto end; + } + + if (value != STEP_DDR_RESET && + value <= step) { + printf("invalid target %d:%s, current step is %d:%s\n", + (int)value, step_str[value], + step, step_str[step]); + goto end; + } + printf("step to %d:%s\n", + (int)value, step_str[value]); + return (int)value; + }; + +end: + return step; +} + +#if defined(CONFIG_STM32MP1_DDR_TESTS) || defined(CONFIG_STM32MP1_DDR_TUNING) +static const char * const s_result[] = { + [TEST_PASSED] = "Pass", + [TEST_FAILED] = "Failed", + [TEST_ERROR] = "Error" +}; + +static void stm32mp1_ddr_subcmd(struct ddr_info *priv, + int argc, char *argv[], + const struct test_desc array[], + const int array_nb) +{ + int i; + unsigned long value; + int result; + char string[50] = ""; + + if (argc == 1) { + printf("%s:%d\n", argv[0], array_nb); + for (i = 0; i < array_nb; i++) + printf("%d:%s:%s\n", + i, array[i].name, array[i].usage); + return; + } + if (argc > 1 && !strcmp(argv[1], "help")) { + printf("%s:%d\n", argv[0], array_nb); + for (i = 0; i < array_nb; i++) + printf("%d:%s:%s:%s\n", i, + array[i].name, array[i].usage, array[i].help); + return; + } + + if ((strict_strtoul(argv[1], 0, &value) < 0) || + value >= array_nb) { + sprintf(string, "invalid argument %s", + argv[1]); + result = TEST_FAILED; + goto end; + } + + if (argc > (array[value].max_args + 2)) { + sprintf(string, "invalid nb of args %d, max %d", + argc - 2, array[value].max_args); + result = TEST_FAILED; + goto end; + } + + printf("execute %d:%s\n", (int)value, array[value].name); + clear_ctrlc(); + result = array[value].fct(priv->ctl, priv->phy, + string, argc - 2, &argv[2]); + +end: + printf("Result: %s [%s]\n", s_result[result], string); +} +#endif + +bool stm32mp1_ddr_interactive(void *priv, + enum stm32mp1_ddr_interact_step step, + const struct stm32mp1_ddr_config *config) +{ + const char *prompt = "DDR>"; + char buffer[CONFIG_SYS_CBSIZE]; + char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ + int argc; + static int next_step = -1; + + if (next_step < 0 && step == STEP_DDR_RESET) { +#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE_FORCE + gd->flags &= ~(GD_FLG_SILENT | + GD_FLG_DISABLE_CONSOLE); + next_step = STEP_DDR_RESET; +#else + unsigned long start = get_timer(0); + + while (1) { + if (tstc() && (getc() == 'd')) { + next_step = STEP_DDR_RESET; + break; + } + if (get_timer(start) > 100) + break; + } +#endif + } + + debug("** step %d ** %s / %d\n", step, step_str[step], next_step); + + if (next_step < 0) + return false; + + if (step < 0 || step > ARRAY_SIZE(step_str)) { + printf("** step %d ** INVALID\n", step); + return false; + } + + printf("%d:%s\n", step, step_str[step]); + printf("%s\n", prompt); + + if (next_step > step) + return false; + + while (next_step == step) { + cli_readline_into_buffer(prompt, buffer, 0); + argc = cli_simple_parse_line(buffer, argv); + if (!argc) + continue; + + switch (stm32mp1_get_command(argv[0], argc)) { + case DDR_CMD_HELP: + stm32mp1_do_usage(); + break; + + case DDR_CMD_INFO: + stm32mp1_do_info(priv, + (struct stm32mp1_ddr_config *)config, + step, argc, argv); + break; + + case DDR_CMD_FREQ: + if (stm32mp1_do_freq(priv, argc, argv)) + next_step = STEP_DDR_RESET; + break; + + case DDR_CMD_RESET: + do_reset(NULL, 0, 0, NULL); + break; + + case DDR_CMD_PARAM: + stm32mp1_do_param(step, config, argc, argv); + break; + + case DDR_CMD_PRINT: + stm32mp1_do_print(priv, argc, argv); + break; + + case DDR_CMD_EDIT: + stm32mp1_edit_reg(priv, argv[1], argv[2]); + break; + + case DDR_CMD_GO: + next_step = STEP_RUN; + break; + + case DDR_CMD_NEXT: + next_step = step + 1; + break; + + case DDR_CMD_STEP: + next_step = stm32mp1_do_step(step, argc, argv); + break; + +#ifdef CONFIG_STM32MP1_DDR_TESTS + case DDR_CMD_TEST: + if (!stm32mp1_check_step(step, STEP_DDR_READY)) + continue; + stm32mp1_ddr_subcmd(priv, argc, argv, test, test_nb); + break; +#endif + +#ifdef CONFIG_STM32MP1_DDR_TUNING + case DDR_CMD_TUNING: + if (!stm32mp1_check_step(step, STEP_DDR_READY)) + continue; + stm32mp1_ddr_subcmd(priv, argc, argv, + tuning, tuning_nb); + break; +#endif + + default: + break; + } + } + return next_step == STEP_DDR_RESET; +} diff --git a/drivers/ram/stm32mp1/stm32mp1_ram.c b/drivers/ram/stm32mp1/stm32mp1_ram.c index e45a3b2658..84e39d093b 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ram.c +++ b/drivers/ram/stm32mp1/stm32mp1_ram.c @@ -20,7 +20,7 @@ static const char *const clkname[] = { "ddrphyc" /* LAST clock => used for get_rate() */ }; -int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint16_t mem_speed) +int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint32_t mem_speed) { unsigned long ddrphy_clk; unsigned long ddr_clk; @@ -43,13 +43,13 @@ int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint16_t mem_speed) priv->clk = clk; ddrphy_clk = clk_get_rate(&priv->clk); - debug("DDR: mem_speed (%d MHz), RCC %d MHz\n", - mem_speed, (u32)(ddrphy_clk / 1000 / 1000)); + debug("DDR: mem_speed (%d kHz), RCC %d kHz\n", + mem_speed, (u32)(ddrphy_clk / 1000)); /* max 10% frequency delta */ - ddr_clk = abs(ddrphy_clk - mem_speed * 1000 * 1000); - if (ddr_clk > (mem_speed * 1000 * 100)) { - pr_err("DDR expected freq %d MHz, current is %d MHz\n", - mem_speed, (u32)(ddrphy_clk / 1000 / 1000)); + ddr_clk = abs(ddrphy_clk - mem_speed * 1000); + if (ddr_clk > (mem_speed * 100)) { + pr_err("DDR expected freq %d kHz, current is %d kHz\n", + mem_speed, (u32)(ddrphy_clk / 1000)); return -EINVAL; } @@ -102,8 +102,8 @@ static __maybe_unused int stm32mp1_ddr_setup(struct udevice *dev) debug("%s: %s[0x%x] = %d\n", __func__, param[idx].name, param[idx].size, ret); if (ret) { - pr_err("%s: Cannot read %s\n", - __func__, param[idx].name); + pr_err("%s: Cannot read %s, error=%d\n", + __func__, param[idx].name, ret); return -EINVAL; } } diff --git a/drivers/ram/stm32mp1/stm32mp1_tests.c b/drivers/ram/stm32mp1/stm32mp1_tests.c new file mode 100644 index 0000000000..b6fb2a9c58 --- /dev/null +++ b/drivers/ram/stm32mp1/stm32mp1_tests.c @@ -0,0 +1,1426 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + */ +#include <common.h> +#include <console.h> +#include <asm/io.h> +#include <linux/log2.h> +#include "stm32mp1_tests.h" + +#define ADDR_INVALID 0xFFFFFFFF + +DECLARE_GLOBAL_DATA_PTR; + +static int get_bufsize(char *string, int argc, char *argv[], int arg_nb, + size_t *bufsize, size_t default_size) +{ + unsigned long value; + + if (argc > arg_nb) { + if (strict_strtoul(argv[arg_nb], 0, &value) < 0) { + sprintf(string, "invalid %d parameter %s", + arg_nb, argv[arg_nb]); + return -1; + } + if (value > STM32_DDR_SIZE || value == 0) { + sprintf(string, "invalid size %s", argv[arg_nb]); + return -1; + } + if (value & 0x3) { + sprintf(string, "unaligned size %s", + argv[arg_nb]); + return -1; + } + *bufsize = value; + } else { + if (default_size != STM32_DDR_SIZE) + *bufsize = default_size; + else + *bufsize = get_ram_size((long *)STM32_DDR_BASE, + STM32_DDR_SIZE); + } + return 0; +} + +static int get_nb_loop(char *string, int argc, char *argv[], int arg_nb, + u32 *nb_loop, u32 default_nb_loop) +{ + unsigned long value; + + if (argc > arg_nb) { + if (strict_strtoul(argv[arg_nb], 0, &value) < 0) { + sprintf(string, "invalid %d parameter %s", + arg_nb, argv[arg_nb]); + return -1; + } + if (value == 0) + printf("WARNING: infinite loop requested\n"); + *nb_loop = value; + } else { + *nb_loop = default_nb_loop; + } + + return 0; +} + +static int get_addr(char *string, int argc, char *argv[], int arg_nb, + u32 *addr) +{ + unsigned long value; + + if (argc > arg_nb) { + if (strict_strtoul(argv[arg_nb], 16, &value) < 0) { + sprintf(string, "invalid %d parameter %s", + arg_nb, argv[arg_nb]); + return -1; + } + if (value < STM32_DDR_BASE) { + sprintf(string, "too low address %s", argv[arg_nb]); + return -1; + } + if (value & 0x3 && value != ADDR_INVALID) { + sprintf(string, "unaligned address %s", + argv[arg_nb]); + return -1; + } + *addr = value; + } else { + *addr = STM32_DDR_BASE; + } + + return 0; +} + +static int get_pattern(char *string, int argc, char *argv[], int arg_nb, + u32 *pattern, u32 default_pattern) +{ + unsigned long value; + + if (argc > arg_nb) { + if (strict_strtoul(argv[arg_nb], 16, &value) < 0) { + sprintf(string, "invalid %d parameter %s", + arg_nb, argv[arg_nb]); + return -1; + } + *pattern = value; + } else { + *pattern = default_pattern; + } + + return 0; +} + +static u32 check_addr(u32 addr, u32 value) +{ + u32 data = readl(addr); + + if (value != data) { + printf("0x%08x: 0x%08x <=> 0x%08x", addr, data, value); + data = readl(addr); + printf("(2nd read: 0x%08x)", data); + if (value == data) + printf("- read error"); + else + printf("- write error"); + printf("\n"); + return -1; + } + return 0; +} + +static int progress(u32 offset) +{ + if (!(offset & 0xFFFFFF)) { + putc('.'); + if (ctrlc()) { + printf("\ntest interrupted!\n"); + return 1; + } + } + return 0; +} + +static int test_loop_end(u32 *loop, u32 nb_loop, u32 progress) +{ + (*loop)++; + if (nb_loop && *loop >= nb_loop) + return 1; + if ((*loop) % progress) + return 0; + /* allow to interrupt the test only for progress step */ + if (ctrlc()) { + printf("test interrupted!\n"); + return 1; + } + printf("loop #%d\n", *loop); + return 0; +} + +/********************************************************************** + * + * Function: memTestDataBus() + * + * Description: Test the data bus wiring in a memory region by + * performing a walking 1's test at a fixed address + * within that region. The address is selected + * by the caller. + * + * Notes: + * + * Returns: 0 if the test succeeds. + * A non-zero result is the first pattern that failed. + * + **********************************************************************/ +static u32 databus(u32 *address) +{ + u32 pattern; + u32 read_value; + + /* Perform a walking 1's test at the given address. */ + for (pattern = 1; pattern != 0; pattern <<= 1) { + /* Write the test pattern. */ + writel(pattern, address); + + /* Read it back (immediately is okay for this test). */ + read_value = readl(address); + debug("%x: %x <=> %x\n", + (u32)address, read_value, pattern); + + if (read_value != pattern) + return pattern; + } + + return 0; +} + +/********************************************************************** + * + * Function: memTestAddressBus() + * + * Description: Test the address bus wiring in a memory region by + * performing a walking 1's test on the relevant bits + * of the address and checking for aliasing. This test + * will find single-bit address failures such as stuck + * -high, stuck-low, and shorted pins. The base address + * and size of the region are selected by the caller. + * + * Notes: For best results, the selected base address should + * have enough LSB 0's to guarantee single address bit + * changes. For example, to test a 64-Kbyte region, + * select a base address on a 64-Kbyte boundary. Also, + * select the region size as a power-of-two--if at all + * possible. + * + * Returns: NULL if the test succeeds. + * A non-zero result is the first address at which an + * aliasing problem was uncovered. By examining the + * contents of memory, it may be possible to gather + * additional information about the problem. + * + **********************************************************************/ +static u32 *addressbus(u32 *address, u32 nb_bytes) +{ + u32 mask = (nb_bytes / sizeof(u32) - 1); + u32 offset; + u32 test_offset; + u32 read_value; + + u32 pattern = 0xAAAAAAAA; + u32 antipattern = 0x55555555; + + /* Write the default pattern at each of the power-of-two offsets. */ + for (offset = 1; (offset & mask) != 0; offset <<= 1) + writel(pattern, &address[offset]); + + /* Check for address bits stuck high. */ + test_offset = 0; + writel(antipattern, &address[test_offset]); + + for (offset = 1; (offset & mask) != 0; offset <<= 1) { + read_value = readl(&address[offset]); + debug("%x: %x <=> %x\n", + (u32)&address[offset], read_value, pattern); + if (read_value != pattern) + return &address[offset]; + } + + writel(pattern, &address[test_offset]); + + /* Check for address bits stuck low or shorted. */ + for (test_offset = 1; (test_offset & mask) != 0; test_offset <<= 1) { + writel(antipattern, &address[test_offset]); + if (readl(&address[0]) != pattern) + return &address[test_offset]; + + for (offset = 1; (offset & mask) != 0; offset <<= 1) { + if (readl(&address[offset]) != pattern && + offset != test_offset) + return &address[test_offset]; + } + writel(pattern, &address[test_offset]); + } + + return NULL; +} + +/********************************************************************** + * + * Function: memTestDevice() + * + * Description: Test the integrity of a physical memory device by + * performing an increment/decrement test over the + * entire region. In the process every storage bit + * in the device is tested as a zero and a one. The + * base address and the size of the region are + * selected by the caller. + * + * Notes: + * + * Returns: NULL if the test succeeds. + * + * A non-zero result is the first address at which an + * incorrect value was read back. By examining the + * contents of memory, it may be possible to gather + * additional information about the problem. + * + **********************************************************************/ +static u32 *memdevice(u32 *address, u32 nb_bytes) +{ + u32 offset; + u32 nb_words = nb_bytes / sizeof(u32); + + u32 pattern; + u32 antipattern; + + puts("Fill with pattern"); + /* Fill memory with a known pattern. */ + for (pattern = 1, offset = 0; offset < nb_words; pattern++, offset++) { + writel(pattern, &address[offset]); + if (progress(offset)) + return NULL; + } + + puts("\nCheck and invert pattern"); + /* Check each location and invert it for the second pass. */ + for (pattern = 1, offset = 0; offset < nb_words; pattern++, offset++) { + if (readl(&address[offset]) != pattern) + return &address[offset]; + + antipattern = ~pattern; + writel(antipattern, &address[offset]); + if (progress(offset)) + return NULL; + } + + puts("\nCheck inverted pattern"); + /* Check each location for the inverted pattern and zero it. */ + for (pattern = 1, offset = 0; offset < nb_words; pattern++, offset++) { + antipattern = ~pattern; + if (readl(&address[offset]) != antipattern) + return &address[offset]; + if (progress(offset)) + return NULL; + } + printf("\n"); + + return NULL; +} + +static enum test_result databuswalk0(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + int i; + u32 loop = 0, nb_loop; + u32 addr; + u32 error = 0; + u32 data; + + if (get_nb_loop(string, argc, argv, 0, &nb_loop, 100)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 1, &addr)) + return TEST_ERROR; + + printf("running %d loops at 0x%x\n", nb_loop, addr); + while (!error) { + for (i = 0; i < 32; i++) + writel(~(1 << i), addr + 4 * i); + for (i = 0; i < 32; i++) { + data = readl(addr + 4 * i); + if (~(1 << i) != data) { + error |= 1 << i; + debug("%x: error %x expected %x => error:%x\n", + addr + 4 * i, data, ~(1 << i), error); + } + } + if (test_loop_end(&loop, nb_loop, 1000)) + break; + for (i = 0; i < 32; i++) + writel(0, addr + 4 * i); + } + if (error) { + sprintf(string, "loop %d: error for bits 0x%x", + loop, error); + return TEST_FAILED; + } + sprintf(string, "no error for %d loops", loop); + return TEST_PASSED; +} + +static enum test_result databuswalk1(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + int i; + u32 loop = 0, nb_loop; + u32 addr; + u32 error = 0; + u32 data; + + if (get_nb_loop(string, argc, argv, 0, &nb_loop, 100)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 1, &addr)) + return TEST_ERROR; + printf("running %d loops at 0x%x\n", nb_loop, addr); + while (!error) { + for (i = 0; i < 32; i++) + writel(1 << i, addr + 4 * i); + for (i = 0; i < 32; i++) { + data = readl(addr + 4 * i); + if ((1 << i) != data) { + error |= 1 << i; + debug("%x: error %x expected %x => error:%x\n", + addr + 4 * i, data, (1 << i), error); + } + } + if (test_loop_end(&loop, nb_loop, 1000)) + break; + for (i = 0; i < 32; i++) + writel(0, addr + 4 * i); + } + if (error) { + sprintf(string, "loop %d: error for bits 0x%x", + loop, error); + return TEST_FAILED; + } + sprintf(string, "no error for %d loops", loop); + return TEST_PASSED; +} + +static enum test_result test_databus(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + u32 addr; + u32 error; + + if (get_addr(string, argc, argv, 0, &addr)) + return TEST_ERROR; + error = databus((u32 *)addr); + if (error) { + sprintf(string, "0x%x: error for bits 0x%x", + addr, error); + return TEST_FAILED; + } + sprintf(string, "address 0x%x", addr); + return TEST_PASSED; +} + +static enum test_result test_addressbus(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + u32 addr; + u32 bufsize; + u32 error; + + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + return TEST_ERROR; + if (!is_power_of_2(bufsize)) { + sprintf(string, "size 0x%x is not a power of 2", + (u32)bufsize); + return TEST_ERROR; + } + if (get_addr(string, argc, argv, 1, &addr)) + return TEST_ERROR; + + error = (u32)addressbus((u32 *)addr, bufsize); + if (error) { + sprintf(string, "0x%x: error for address 0x%x", + addr, error); + return TEST_FAILED; + } + sprintf(string, "address 0x%x, size 0x%x", + addr, bufsize); + return TEST_PASSED; +} + +static enum test_result test_memdevice(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + u32 addr; + size_t bufsize; + u32 error; + + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 1, &addr)) + return TEST_ERROR; + error = (u32)memdevice((u32 *)addr, (unsigned long)bufsize); + if (error) { + sprintf(string, "0x%x: error for address 0x%x", + addr, error); + return TEST_FAILED; + } + sprintf(string, "address 0x%x, size 0x%x", + addr, bufsize); + return TEST_PASSED; +} + +/********************************************************************** + * + * Function: sso + * + * Description: Test the Simultaneous Switching Output. + * Verifies succes sive reads and writes to the same memory word, + * holding one bit constant while toggling all other data bits + * simultaneously + * => stress the data bus over an address range + * + * The CPU writes to each address in the given range. + * For each bit, first the CPU holds the bit at 1 while + * toggling the other bits, and then the CPU holds the bit at 0 + * while toggling the other bits. + * After each write, the CPU reads the address that was written + * to verify that it contains the correct data + * + **********************************************************************/ +static enum test_result test_sso(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + int i, j; + u32 addr, bufsize, remaining, offset; + u32 error = 0; + u32 data; + + if (get_bufsize(string, argc, argv, 0, &bufsize, 4)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 1, &addr)) + return TEST_ERROR; + + printf("running sso at 0x%x length 0x%x", addr, bufsize); + offset = addr; + remaining = bufsize; + while (remaining) { + for (i = 0; i < 32; i++) { + /* write pattern. */ + for (j = 0; j < 6; j++) { + switch (j) { + case 0: + case 2: + data = 1 << i; + break; + case 3: + case 5: + data = ~(1 << i); + break; + case 1: + data = ~0x0; + break; + case 4: + data = 0x0; + break; + } + + writel(data, offset); + error = check_addr(offset, data); + if (error) + goto end; + } + } + offset += 4; + remaining -= 4; + if (progress(offset << 7)) + goto end; + } + puts("\n"); + +end: + if (error) { + sprintf(string, "error for pattern 0x%x @0x%x", + data, offset); + return TEST_FAILED; + } + sprintf(string, "no error for sso at 0x%x length 0x%x", addr, bufsize); + return TEST_PASSED; +} + +/********************************************************************** + * + * Function: Random + * + * Description: Verifies r/w with pseudo-ramdom value on one region + * + write the region (individual access) + * + memcopy to the 2nd region (try to use burst) + * + verify the 2 regions + * + **********************************************************************/ +static enum test_result test_random(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + u32 addr, offset, value = 0; + size_t bufsize; + u32 loop = 0, nb_loop; + u32 error = 0; + unsigned int seed; + + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + return TEST_ERROR; + if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 2, &addr)) + return TEST_ERROR; + + printf("running %d loops at 0x%x\n", nb_loop, addr); + while (!error) { + seed = rand(); + for (offset = addr; offset < addr + bufsize; offset += 4) + writel(rand(), offset); + + memcpy((void *)addr + bufsize, (void *)addr, bufsize); + + srand(seed); + for (offset = addr; offset < addr + 2 * bufsize; offset += 4) { + if (offset == (addr + bufsize)) + srand(seed); + value = rand(); + error = check_addr(offset, value); + if (error) + break; + if (progress(offset)) + return TEST_FAILED; + } + if (test_loop_end(&loop, nb_loop, 100)) + break; + } + + if (error) { + sprintf(string, + "loop %d: error for address 0x%x: 0x%x expected 0x%x", + loop, offset, readl(offset), value); + return TEST_FAILED; + } + sprintf(string, "no error for %d loops, size 0x%x", + loop, bufsize); + return TEST_PASSED; +} + +/********************************************************************** + * + * Function: noise + * + * Description: Verifies r/w while forcing switching of all data bus lines. + * optimised 4 iteration write/read/write/read cycles... + * for pattern and inversed pattern + * + **********************************************************************/ +void do_noise(u32 addr, u32 pattern, u32 *result) +{ + __asm__("push {R0-R11}"); + __asm__("mov r0, %0" : : "r" (addr)); + __asm__("mov r1, %0" : : "r" (pattern)); + __asm__("mov r11, %0" : : "r" (result)); + + __asm__("mvn r2, r1"); + + __asm__("str r1, [r0]"); + __asm__("ldr r3, [r0]"); + __asm__("str r2, [r0]"); + __asm__("ldr r4, [r0]"); + + __asm__("str r1, [r0]"); + __asm__("ldr r5, [r0]"); + __asm__("str r2, [r0]"); + __asm__("ldr r6, [r0]"); + + __asm__("str r1, [r0]"); + __asm__("ldr r7, [r0]"); + __asm__("str r2, [r0]"); + __asm__("ldr r8, [r0]"); + + __asm__("str r1, [r0]"); + __asm__("ldr r9, [r0]"); + __asm__("str r2, [r0]"); + __asm__("ldr r10, [r0]"); + + __asm__("stmia R11!, {R3-R10}"); + + __asm__("pop {R0-R11}"); +} + +static enum test_result test_noise(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + u32 addr, pattern; + u32 result[8]; + int i; + enum test_result res = TEST_PASSED; + + if (get_pattern(string, argc, argv, 0, &pattern, 0xFFFFFFFF)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 1, &addr)) + return TEST_ERROR; + + printf("running noise for 0x%x at 0x%x\n", pattern, addr); + + do_noise(addr, pattern, result); + + for (i = 0; i < 0x8;) { + if (check_addr((u32)&result[i++], pattern)) + res = TEST_FAILED; + if (check_addr((u32)&result[i++], ~pattern)) + res = TEST_FAILED; + } + + return res; +} + +/********************************************************************** + * + * Function: noise_burst + * + * Description: Verifies r/w while forcing switching of all data bus lines. + * optimised write loop witrh store multiple to use burst + * for pattern and inversed pattern + * + **********************************************************************/ +void do_noise_burst(u32 addr, u32 pattern, size_t bufsize) +{ + __asm__("push {R0-R9}"); + __asm__("mov r0, %0" : : "r" (addr)); + __asm__("mov r1, %0" : : "r" (pattern)); + __asm__("mov r9, %0" : : "r" (bufsize)); + + __asm__("mvn r2, r1"); + __asm__("mov r3, r1"); + __asm__("mov r4, r2"); + __asm__("mov r5, r1"); + __asm__("mov r6, r2"); + __asm__("mov r7, r1"); + __asm__("mov r8, r2"); + + __asm__("loop1:"); + __asm__("stmia R0!, {R1-R8}"); + __asm__("stmia R0!, {R1-R8}"); + __asm__("stmia R0!, {R1-R8}"); + __asm__("stmia R0!, {R1-R8}"); + __asm__("subs r9, r9, #128"); + __asm__("bge loop1"); + __asm__("pop {R0-R9}"); +} + +/* chunk size enough to allow interruption with Ctrl-C*/ +#define CHUNK_SIZE 0x8000000 +static enum test_result test_noise_burst(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + u32 addr, offset, pattern; + size_t bufsize, remaining, size; + int i; + enum test_result res = TEST_PASSED; + + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + return TEST_ERROR; + if (get_pattern(string, argc, argv, 1, &pattern, 0xFFFFFFFF)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 2, &addr)) + return TEST_ERROR; + + printf("running noise burst for 0x%x at 0x%x + 0x%x", + pattern, addr, bufsize); + + offset = addr; + remaining = bufsize; + size = CHUNK_SIZE; + while (remaining) { + if (remaining < size) + size = remaining; + do_noise_burst(offset, pattern, size); + remaining -= size; + offset += size; + if (progress(offset)) { + res = TEST_FAILED; + goto end; + } + } + puts("\ncheck buffer"); + for (i = 0; i < bufsize;) { + if (check_addr(addr + i, pattern)) + res = TEST_FAILED; + i += 4; + if (check_addr(addr + i, ~pattern)) + res = TEST_FAILED; + i += 4; + if (progress(i)) { + res = TEST_FAILED; + goto end; + } + } +end: + puts("\n"); + return res; +} + +/********************************************************************** + * + * Function: pattern test + * + * Description: optimized loop for read/write pattern (array of 8 u32) + * + **********************************************************************/ +#define PATTERN_SIZE 8 +static enum test_result test_loop(const u32 *pattern, u32 *address, + const u32 bufsize) +{ + int i; + int j; + enum test_result res = TEST_PASSED; + u32 *offset, testsize, remaining; + + offset = address; + remaining = bufsize; + while (remaining) { + testsize = bufsize > 0x1000000 ? 0x1000000 : bufsize; + + __asm__("push {R0-R10}"); + __asm__("mov r0, %0" : : "r" (pattern)); + __asm__("mov r1, %0" : : "r" (offset)); + __asm__("mov r2, %0" : : "r" (testsize)); + __asm__("ldmia r0!, {R3-R10}"); + + __asm__("loop2:"); + __asm__("stmia r1!, {R3-R10}"); + __asm__("stmia r1!, {R3-R10}"); + __asm__("stmia r1!, {R3-R10}"); + __asm__("stmia r1!, {R3-R10}"); + __asm__("subs r2, r2, #8"); + __asm__("bge loop2"); + __asm__("pop {R0-R10}"); + + offset += testsize; + remaining -= testsize; + if (progress((u32)offset)) { + res = TEST_FAILED; + goto end; + } + } + + puts("\ncheck buffer"); + for (i = 0; i < bufsize; i += PATTERN_SIZE * 4) { + for (j = 0; j < PATTERN_SIZE; j++, address++) + if (check_addr((u32)address, pattern[j])) { + res = TEST_FAILED; + goto end; + } + if (progress(i)) { + res = TEST_FAILED; + goto end; + } + } + +end: + puts("\n"); + return res; +} + +const u32 pattern_div1_x16[PATTERN_SIZE] = { + 0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF, + 0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF +}; + +const u32 pattern_div2_x16[PATTERN_SIZE] = { + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000 +}; + +const u32 pattern_div4_x16[PATTERN_SIZE] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000 +}; + +const u32 pattern_div4_x32[PATTERN_SIZE] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +const u32 pattern_mostly_zero_x16[PATTERN_SIZE] = { + 0x00000000, 0x00000000, 0x00000000, 0x0000FFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +const u32 pattern_mostly_zero_x32[PATTERN_SIZE] = { + 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +const u32 pattern_mostly_one_x16[PATTERN_SIZE] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +const u32 pattern_mostly_one_x32[PATTERN_SIZE] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +#define NB_PATTERN 5 +static enum test_result test_freq_pattern(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + const u32 * const patterns_x16[NB_PATTERN] = { + pattern_div1_x16, + pattern_div2_x16, + pattern_div4_x16, + pattern_mostly_zero_x16, + pattern_mostly_one_x16, + }; + const u32 * const patterns_x32[NB_PATTERN] = { + pattern_div2_x16, + pattern_div4_x16, + pattern_div4_x32, + pattern_mostly_zero_x32, + pattern_mostly_one_x32 + }; + const char *patterns_comments[NB_PATTERN] = { + "switching at frequency F/1", + "switching at frequency F/2", + "switching at frequency F/4", + "mostly zero", + "mostly one" + }; + + enum test_result res = TEST_PASSED, pattern_res; + int i, bus_width; + const u32 **patterns; + u32 bufsize; + + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + return TEST_ERROR; + + switch (readl(&ctl->mstr) & DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK) { + case DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF: + case DDRCTRL_MSTR_DATA_BUS_WIDTH_QUARTER: + bus_width = 16; + break; + default: + bus_width = 32; + break; + } + + printf("running test pattern at 0x%08x length 0x%x width = %d\n", + STM32_DDR_BASE, bufsize, bus_width); + + patterns = + (const u32 **)(bus_width == 16 ? patterns_x16 : patterns_x32); + + for (i = 0; i < NB_PATTERN; i++) { + printf("test data pattern %s:", patterns_comments[i]); + pattern_res = test_loop(patterns[i], (u32 *)STM32_DDR_BASE, + bufsize); + if (pattern_res != TEST_PASSED) { + printf("Failed\n"); + return pattern_res; + } + printf("Passed\n"); + } + + return res; +} + +/********************************************************************** + * + * Function: pattern test with size + * + * Description: loop for write pattern + * + **********************************************************************/ + +static enum test_result test_loop_size(const u32 *pattern, u32 size, + u32 *address, + const u32 bufsize) +{ + int i, j; + enum test_result res = TEST_PASSED; + u32 *p = address; + + for (i = 0; i < bufsize; i += size * 4) { + for (j = 0; j < size ; j++, p++) + *p = pattern[j]; + if (progress(i)) { + res = TEST_FAILED; + goto end; + } + } + + puts("\ncheck buffer"); + p = address; + for (i = 0; i < bufsize; i += size * 4) { + for (j = 0; j < size; j++, p++) + if (check_addr((u32)p, pattern[j])) { + res = TEST_FAILED; + goto end; + } + if (progress(i)) { + res = TEST_FAILED; + goto end; + } + } + +end: + puts("\n"); + return res; +} + +static enum test_result test_checkboard(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + enum test_result res = TEST_PASSED; + u32 bufsize, nb_loop, loop = 0, addr; + int i; + + u32 checkboard[2] = {0x55555555, 0xAAAAAAAA}; + + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + return TEST_ERROR; + if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 2, &addr)) + return TEST_ERROR; + + printf("running %d loops at 0x%08x length 0x%x\n", + nb_loop, addr, bufsize); + while (1) { + for (i = 0; i < 2; i++) { + res = test_loop_size(checkboard, 2, (u32 *)addr, + bufsize); + if (res) + return res; + checkboard[0] = ~checkboard[0]; + checkboard[1] = ~checkboard[1]; + } + if (test_loop_end(&loop, nb_loop, 1)) + break; + } + sprintf(string, "no error for %d loops at 0x%08x length 0x%x", + loop, addr, bufsize); + + return res; +} + +static enum test_result test_blockseq(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + enum test_result res = TEST_PASSED; + u32 bufsize, nb_loop, loop = 0, addr, value; + int i; + + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + return TEST_ERROR; + if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 2, &addr)) + return TEST_ERROR; + + printf("running %d loops at 0x%08x length 0x%x\n", + nb_loop, addr, bufsize); + while (1) { + for (i = 0; i < 256; i++) { + value = i | i << 8 | i << 16 | i << 24; + printf("pattern = %08x", value); + res = test_loop_size(&value, 1, (u32 *)addr, bufsize); + if (res != TEST_PASSED) + return res; + } + if (test_loop_end(&loop, nb_loop, 1)) + break; + } + sprintf(string, "no error for %d loops at 0x%08x length 0x%x", + loop, addr, bufsize); + + return res; +} + +static enum test_result test_walkbit0(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + enum test_result res = TEST_PASSED; + u32 bufsize, nb_loop, loop = 0, addr, value; + int i; + + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + return TEST_ERROR; + if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 2, &addr)) + return TEST_ERROR; + + printf("running %d loops at 0x%08x length 0x%x\n", + nb_loop, addr, bufsize); + while (1) { + for (i = 0; i < 64; i++) { + if (i < 32) + value = 1 << i; + else + value = 1 << (63 - i); + + printf("pattern = %08x", value); + res = test_loop_size(&value, 1, (u32 *)addr, bufsize); + if (res != TEST_PASSED) + return res; + } + if (test_loop_end(&loop, nb_loop, 1)) + break; + } + sprintf(string, "no error for %d loops at 0x%08x length 0x%x", + loop, addr, bufsize); + + return res; +} + +static enum test_result test_walkbit1(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + enum test_result res = TEST_PASSED; + u32 bufsize, nb_loop, loop = 0, addr, value; + int i; + + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + return TEST_ERROR; + if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 2, &addr)) + return TEST_ERROR; + + printf("running %d loops at 0x%08x length 0x%x\n", + nb_loop, addr, bufsize); + while (1) { + for (i = 0; i < 64; i++) { + if (i < 32) + value = ~(1 << i); + else + value = ~(1 << (63 - i)); + + printf("pattern = %08x", value); + res = test_loop_size(&value, 1, (u32 *)addr, bufsize); + if (res != TEST_PASSED) + return res; + } + if (test_loop_end(&loop, nb_loop, 1)) + break; + } + sprintf(string, "no error for %d loops at 0x%08x length 0x%x", + loop, addr, bufsize); + + return res; +} + +/* + * try to catch bad bits which are dependent on the current values of + * surrounding bits in either the same word32 + */ +static enum test_result test_bitspread(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + enum test_result res = TEST_PASSED; + u32 bufsize, nb_loop, loop = 0, addr, bitspread[4]; + int i, j; + + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + return TEST_ERROR; + if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 2, &addr)) + return TEST_ERROR; + + printf("running %d loops at 0x%08x length 0x%x\n", + nb_loop, addr, bufsize); + while (1) { + for (i = 1; i < 32; i++) { + for (j = 0; j < i; j++) { + if (i < 32) + bitspread[0] = (1 << i) | (1 << j); + else + bitspread[0] = (1 << (63 - i)) | + (1 << (63 - j)); + bitspread[1] = bitspread[0]; + bitspread[2] = ~bitspread[0]; + bitspread[3] = ~bitspread[0]; + printf("pattern = %08x", bitspread[0]); + + res = test_loop_size(bitspread, 4, (u32 *)addr, + bufsize); + if (res != TEST_PASSED) + return res; + } + } + if (test_loop_end(&loop, nb_loop, 1)) + break; + } + sprintf(string, "no error for %d loops at 0x%08x length 0x%x", + loop, addr, bufsize); + + return res; +} + +static enum test_result test_bitflip(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + enum test_result res = TEST_PASSED; + u32 bufsize, nb_loop, loop = 0, addr; + int i; + + u32 bitflip[4]; + + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + return TEST_ERROR; + if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 2, &addr)) + return TEST_ERROR; + + printf("running %d loops at 0x%08x length 0x%x\n", + nb_loop, addr, bufsize); + while (1) { + for (i = 0; i < 32; i++) { + bitflip[0] = 1 << i; + bitflip[1] = bitflip[0]; + bitflip[2] = ~bitflip[0]; + bitflip[3] = bitflip[2]; + printf("pattern = %08x", bitflip[0]); + + res = test_loop_size(bitflip, 4, (u32 *)addr, bufsize); + if (res != TEST_PASSED) + return res; + } + if (test_loop_end(&loop, nb_loop, 1)) + break; + } + sprintf(string, "no error for %d loops at 0x%08x length 0x%x", + loop, addr, bufsize); + + return res; +} + +/********************************************************************** + * + * Function: infinite read access to DDR + * + * Description: continuous read the same pattern at the same address + * + **********************************************************************/ +static enum test_result test_read(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + u32 *addr; + u32 data; + u32 loop = 0; + bool random = false; + + if (get_addr(string, argc, argv, 0, (u32 *)&addr)) + return TEST_ERROR; + + if ((u32)addr == ADDR_INVALID) { + printf("random "); + random = true; + } + + printf("running at 0x%08x\n", (u32)addr); + + while (1) { + if (random) + addr = (u32 *)(STM32_DDR_BASE + + (rand() & (STM32_DDR_SIZE - 1) & ~0x3)); + data = readl(addr); + if (test_loop_end(&loop, 0, 1000)) + break; + } + sprintf(string, "0x%x: %x", (u32)addr, data); + + return TEST_PASSED; +} + +/********************************************************************** + * + * Function: infinite write access to DDR + * + * Description: continuous write the same pattern at the same address + * + **********************************************************************/ +static enum test_result test_write(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + u32 *addr; + u32 data = 0xA5A5AA55; + u32 loop = 0; + bool random = false; + + if (get_addr(string, argc, argv, 0, (u32 *)&addr)) + return TEST_ERROR; + + if ((u32)addr == ADDR_INVALID) { + printf("random "); + random = true; + } + + printf("running at 0x%08x\n", (u32)addr); + + while (1) { + if (random) { + addr = (u32 *)(STM32_DDR_BASE + + (rand() & (STM32_DDR_SIZE - 1) & ~0x3)); + data = rand(); + } + writel(data, addr); + if (test_loop_end(&loop, 0, 1000)) + break; + } + sprintf(string, "0x%x: %x", (u32)addr, data); + + return TEST_PASSED; +} + +#define NB_TEST_INFINITE 2 +static enum test_result test_all(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + enum test_result res = TEST_PASSED, result; + int i, nb_error = 0; + u32 loop = 0, nb_loop; + + if (get_nb_loop(string, argc, argv, 0, &nb_loop, 1)) + return TEST_ERROR; + + while (!nb_error) { + /* execute all the test except the lasts which are infinite */ + for (i = 1; i < test_nb - NB_TEST_INFINITE; i++) { + printf("execute %d:%s\n", (int)i, test[i].name); + result = test[i].fct(ctl, phy, string, 0, NULL); + printf("result %d:%s = ", (int)i, test[i].name); + if (result != TEST_PASSED) { + nb_error++; + res = TEST_FAILED; + puts("Failed"); + } else { + puts("Passed"); + } + puts("\n\n"); + } + printf("loop %d: %d/%d test failed\n\n\n", + loop + 1, nb_error, test_nb - NB_TEST_INFINITE); + if (test_loop_end(&loop, nb_loop, 1)) + break; + } + if (res != TEST_PASSED) { + sprintf(string, "loop %d: %d/%d test failed", loop, nb_error, + test_nb - NB_TEST_INFINITE); + } else { + sprintf(string, "loop %d: %d tests passed", loop, + test_nb - NB_TEST_INFINITE); + } + return res; +} + +/**************************************************************** + * TEST Description + ****************************************************************/ + +const struct test_desc test[] = { + {test_all, "All", "[loop]", "Execute all tests", 1 }, + {test_databus, "Simple DataBus", "[addr]", + "Verifies each data line by walking 1 on fixed address", + 1 + }, + {databuswalk0, "DataBusWalking0", "[loop] [addr]", + "Verifies each data bus signal can be driven low (32 word burst)", + 2 + }, + {databuswalk1, "DataBusWalking1", "[loop] [addr]", + "Verifies each data bus signal can be driven high (32 word burst)", + 2 + }, + {test_addressbus, "AddressBus", "[size] [addr]", + "Verifies each relevant bits of the address and checking for aliasing", + 2 + }, + {test_memdevice, "MemDevice", "[size] [addr]", + "Test the integrity of a physical memory (test every storage bit in the region)", + 2 + }, + {test_sso, "SimultaneousSwitchingOutput", "[size] [addr] ", + "Stress the data bus over an address range", + 2 + }, + {test_noise, "Noise", "[pattern] [addr]", + "Verifies r/w while forcing switching of all data bus lines.", + 3 + }, + {test_noise_burst, "NoiseBurst", "[size] [pattern] [addr]", + "burst transfers while forcing switching of the data bus lines", + 3 + }, + {test_random, "Random", "[size] [loop] [addr]", + "Verifies r/w and memcopy(burst for pseudo random value.", + 3 + }, + {test_freq_pattern, "FrequencySelectivePattern ", "[size]", + "write & test patterns: Mostly Zero, Mostly One and F/n", + 1 + }, + {test_blockseq, "BlockSequential", "[size] [loop] [addr]", + "test incremental pattern", + 3 + }, + {test_checkboard, "Checkerboard", "[size] [loop] [addr]", + "test checker pattern", + 3 + }, + {test_bitspread, "BitSpread", "[size] [loop] [addr]", + "test Bit Spread pattern", + 3 + }, + {test_bitflip, "BitFlip", "[size] [loop] [addr]", + "test Bit Flip pattern", + 3 + }, + {test_walkbit0, "WalkingOnes", "[size] [loop] [addr]", + "test Walking Ones pattern", + 3 + }, + {test_walkbit1, "WalkingZeroes", "[size] [loop] [addr]", + "test Walking Zeroes pattern", + 3 + }, + /* need to the the 2 last one (infinite) : skipped for test all */ + {test_read, "infinite read", "[addr]", + "basic test : infinite read access", 1}, + {test_write, "infinite write", "[addr]", + "basic test : infinite write access", 1}, +}; + +const int test_nb = ARRAY_SIZE(test); diff --git a/drivers/ram/stm32mp1/stm32mp1_tests.h b/drivers/ram/stm32mp1/stm32mp1_tests.h new file mode 100644 index 0000000000..55f5d6d93b --- /dev/null +++ b/drivers/ram/stm32mp1/stm32mp1_tests.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + */ + +#ifndef _RAM_STM32MP1_TESTS_H_ +#define _RAM_STM32MP1_TESTS_H_ + +#include "stm32mp1_ddr_regs.h" + +enum test_result { + TEST_PASSED, + TEST_FAILED, + TEST_ERROR +}; + +struct test_desc { + enum test_result (*fct)(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, + int argc, char *argv[]); + const char *name; + const char *usage; + const char *help; + u8 max_args; +}; + +extern const struct test_desc test[]; +extern const int test_nb; + +extern const struct test_desc tuning[]; +extern const int tuning_nb; + +#endif diff --git a/drivers/ram/stm32mp1/stm32mp1_tuning.c b/drivers/ram/stm32mp1/stm32mp1_tuning.c new file mode 100644 index 0000000000..4e1c1fab54 --- /dev/null +++ b/drivers/ram/stm32mp1/stm32mp1_tuning.c @@ -0,0 +1,1380 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + */ +#include <common.h> +#include <console.h> +#include <clk.h> +#include <ram.h> +#include <reset.h> +#include <asm/io.h> + +#include "stm32mp1_ddr_regs.h" +#include "stm32mp1_ddr.h" +#include "stm32mp1_tests.h" + +#define MAX_DQS_PHASE_IDX _144deg +#define MAX_DQS_UNIT_IDX 7 +#define MAX_GSL_IDX 5 +#define MAX_GPS_IDX 3 + +/* Number of bytes used in this SW. ( min 1--> max 4). */ +#define NUM_BYTES 4 + +enum dqs_phase_enum { + _36deg = 0, + _54deg = 1, + _72deg = 2, + _90deg = 3, + _108deg = 4, + _126deg = 5, + _144deg = 6 +}; + +/* BIST Result struct */ +struct BIST_result { + /* Overall test result: + * 0 Fail (any bit failed) , + * 1 Success (All bits success) + */ + bool test_result; + /* 1: true, all fail / 0: False, not all bits fail */ + bool all_bits_fail; + bool bit_i_test_result[8]; /* 0 fail / 1 success */ +}; + +/* a struct that defines tuning parameters of a byte. */ +struct tuning_position { + u8 phase; /* DQS phase */ + u8 unit; /* DQS unit delay */ + u32 bits_delay; /* Bits deskew in this byte */ +}; + +/* 36deg, 54deg, 72deg, 90deg, 108deg, 126deg, 144deg */ +const u8 dx_dll_phase[7] = {3, 2, 1, 0, 14, 13, 12}; + +static u8 BIST_error_max = 1; +static u32 BIST_seed = 0x1234ABCD; + +static u8 get_nb_bytes(struct stm32mp1_ddrctl *ctl) +{ + u32 data_bus = readl(&ctl->mstr) & DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK; + u8 nb_bytes = NUM_BYTES; + + switch (data_bus) { + case DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF: + nb_bytes /= 2; + break; + case DDRCTRL_MSTR_DATA_BUS_WIDTH_QUARTER: + nb_bytes /= 4; + break; + default: + break; + } + + return nb_bytes; +} + +static void itm_soft_reset(struct stm32mp1_ddrphy *phy) +{ + stm32mp1_ddrphy_init(phy, DDRPHYC_PIR_ITMSRST); +} + +/* Read DQ unit delay register and provides the retrieved value for DQS + * We are assuming that we have the same delay when clocking + * by DQS and when clocking by DQSN + */ +static u8 DQ_unit_index(struct stm32mp1_ddrphy *phy, u8 byte, u8 bit) +{ + u32 index; + u32 addr = DXNDQTR(phy, byte); + + /* We are assuming that we have the same delay when clocking by DQS + * and when clocking by DQSN : use only the low bits + */ + index = (readl(addr) >> DDRPHYC_DXNDQTR_DQDLY_SHIFT(bit)) + & DDRPHYC_DXNDQTR_DQDLY_LOW_MASK; + + pr_debug("%s: [%x]: %x => DQ unit index = %x\n", + __func__, addr, readl(addr), index); + + return index; +} + +/* Sets the DQS phase delay for a byte lane. + *phase delay is specified by giving the index of the desired delay + * in the dx_dll_phase array. + */ +static void DQS_phase_delay(struct stm32mp1_ddrphy *phy, u8 byte, u8 phase_idx) +{ + u8 sdphase_val = 0; + + /* Write DXNDLLCR.SDPHASE = dx_dll_phase(phase_index); */ + sdphase_val = dx_dll_phase[phase_idx]; + clrsetbits_le32(DXNDLLCR(phy, byte), + DDRPHYC_DXNDLLCR_SDPHASE_MASK, + sdphase_val << DDRPHYC_DXNDLLCR_SDPHASE_SHIFT); +} + +/* Sets the DQS unit delay for a byte lane. + * unit delay is specified by giving the index of the desired delay + * for dgsdly and dqsndly (same value). + */ +static void DQS_unit_delay(struct stm32mp1_ddrphy *phy, + u8 byte, u8 unit_dly_idx) +{ + /* Write the same value in DXNDQSTR.DQSDLY and DXNDQSTR.DQSNDLY */ + clrsetbits_le32(DXNDQSTR(phy, byte), + DDRPHYC_DXNDQSTR_DQSDLY_MASK | + DDRPHYC_DXNDQSTR_DQSNDLY_MASK, + (unit_dly_idx << DDRPHYC_DXNDQSTR_DQSDLY_SHIFT) | + (unit_dly_idx << DDRPHYC_DXNDQSTR_DQSNDLY_SHIFT)); + + /* After changing this value, an ITM soft reset (PIR.ITMSRST=1, + * plus PIR.INIT=1) must be issued. + */ + stm32mp1_ddrphy_init(phy, DDRPHYC_PIR_ITMSRST); +} + +/* Sets the DQ unit delay for a bit line in particular byte lane. + * unit delay is specified by giving the desired delay + */ +static void set_DQ_unit_delay(struct stm32mp1_ddrphy *phy, + u8 byte, u8 bit, + u8 dq_delay_index) +{ + u8 dq_bit_delay_val = dq_delay_index | (dq_delay_index << 2); + + /* same value on delay for clock DQ an DQS_b */ + clrsetbits_le32(DXNDQTR(phy, byte), + DDRPHYC_DXNDQTR_DQDLY_MASK + << DDRPHYC_DXNDQTR_DQDLY_SHIFT(bit), + dq_bit_delay_val << DDRPHYC_DXNDQTR_DQDLY_SHIFT(bit)); +} + +static void set_r0dgsl_delay(struct stm32mp1_ddrphy *phy, + u8 byte, u8 r0dgsl_idx) +{ + clrsetbits_le32(DXNDQSTR(phy, byte), + DDRPHYC_DXNDQSTR_R0DGSL_MASK, + r0dgsl_idx << DDRPHYC_DXNDQSTR_R0DGSL_SHIFT); +} + +static void set_r0dgps_delay(struct stm32mp1_ddrphy *phy, + u8 byte, u8 r0dgps_idx) +{ + clrsetbits_le32(DXNDQSTR(phy, byte), + DDRPHYC_DXNDQSTR_R0DGPS_MASK, + r0dgps_idx << DDRPHYC_DXNDQSTR_R0DGPS_SHIFT); +} + +/* Basic BIST configuration for data lane tests. */ +static void config_BIST(struct stm32mp1_ddrphy *phy) +{ + /* Selects the SDRAM bank address to be used during BIST. */ + u32 bbank = 0; + /* Selects the SDRAM row address to be used during BIST. */ + u32 brow = 0; + /* Selects the SDRAM column address to be used during BIST. */ + u32 bcol = 0; + /* Selects the value by which the SDRAM address is incremented + * for each write/read access. + */ + u32 bainc = 0x00000008; + /* Specifies the maximum SDRAM rank to be used during BIST. + * The default value is set to maximum ranks minus 1. + * must be 0 with single rank + */ + u32 bmrank = 0; + /* Selects the SDRAM rank to be used during BIST. + * must be 0 with single rank + */ + u32 brank = 0; + /* Specifies the maximum SDRAM bank address to be used during + * BIST before the address & increments to the next rank. + */ + u32 bmbank = 1; + /* Specifies the maximum SDRAM row address to be used during + * BIST before the address & increments to the next bank. + */ + u32 bmrow = 0x7FFF; /* To check */ + /* Specifies the maximum SDRAM column address to be used during + * BIST before the address & increments to the next row. + */ + u32 bmcol = 0x3FF; /* To check */ + u32 bmode_conf = 0x00000001; /* DRam mode */ + u32 bdxen_conf = 0x00000001; /* BIST on Data byte */ + u32 bdpat_conf = 0x00000002; /* Select LFSR pattern */ + + /*Setup BIST for DRAM mode, and LFSR-random data pattern.*/ + /*Write BISTRR.BMODE = 1?b1;*/ + /*Write BISTRR.BDXEN = 1?b1;*/ + /*Write BISTRR.BDPAT = 2?b10;*/ + + /* reset BIST */ + writel(0x3, &phy->bistrr); + + writel((bmode_conf << 3) | (bdxen_conf << 14) | (bdpat_conf << 17), + &phy->bistrr); + + /*Setup BIST Word Count*/ + /*Write BISTWCR.BWCNT = 16?b0008;*/ + writel(0x00000200, &phy->bistwcr); /* A multiple of BL/2 */ + + writel(bcol | (brow << 12) | (bbank << 28), &phy->bistar0); + writel(brank | (bmrank << 2) | (bainc << 4), &phy->bistar1); + + /* To check this line : */ + writel(bmcol | (bmrow << 12) | (bmbank << 28), &phy->bistar2); +} + +/* Select the Byte lane to be tested by BIST. */ +static void BIST_datx8_sel(struct stm32mp1_ddrphy *phy, u8 datx8) +{ + clrsetbits_le32(&phy->bistrr, + DDRPHYC_BISTRR_BDXSEL_MASK, + datx8 << DDRPHYC_BISTRR_BDXSEL_SHIFT); + + /*(For example, selecting Byte Lane 3, BISTRR.BDXSEL = 4?b0011)*/ + /* Write BISTRR.BDXSEL = datx8; */ +} + +/* Perform BIST Write_Read test on a byte lane and return test result. */ +static void BIST_test(struct stm32mp1_ddrphy *phy, u8 byte, + struct BIST_result *bist) +{ + bool result = true; /* BIST_SUCCESS */ + u32 cnt = 0; + u32 error = 0; + + bist->test_result = true; + +run: + itm_soft_reset(phy); + + /*Perform BIST Reset*/ + /* Write BISTRR.BINST = 3?b011; */ + clrsetbits_le32(&phy->bistrr, + 0x00000007, + 0x00000003); + + /*Re-seed LFSR*/ + /* Write BISTLSR.SEED = 32'h1234ABCD; */ + if (BIST_seed) + writel(BIST_seed, &phy->bistlsr); + else + writel(rand(), &phy->bistlsr); + + /* some delay to reset BIST */ + mdelay(1); + + /*Perform BIST Run*/ + clrsetbits_le32(&phy->bistrr, + 0x00000007, + 0x00000001); + /* Write BISTRR.BINST = 3?b001; */ + + /* Wait for a number of CTL clocks before reading BIST register*/ + /* Wait 300 ctl_clk cycles; ... IS it really needed?? */ + /* Perform BIST Instruction Stop*/ + /* Write BISTRR.BINST = 3?b010;*/ + + /* poll on BISTGSR.BDONE. If 0, wait. ++TODO Add timeout */ + while (!(readl(&phy->bistgsr) & DDRPHYC_BISTGSR_BDDONE)) + ; + + /*Check if received correct number of words*/ + /* if (Read BISTWCSR.DXWCNT = Read BISTWCR.BWCNT) */ + if (((readl(&phy->bistwcsr)) >> DDRPHYC_BISTWCSR_DXWCNT_SHIFT) == + readl(&phy->bistwcr)) { + /*Determine if there is a data comparison error*/ + /* if (Read BISTGSR.BDXERR = 1?b0) */ + if (readl(&phy->bistgsr) & DDRPHYC_BISTGSR_BDXERR) + result = false; /* BIST_FAIL; */ + else + result = true; /* BIST_SUCCESS; */ + } else { + result = false; /* BIST_FAIL; */ + } + + /* loop while success */ + cnt++; + if (result && cnt != 1000) + goto run; + + if (!result) + error++; + + if (error < BIST_error_max) { + if (cnt != 1000) + goto run; + bist->test_result = true; + } else { + bist->test_result = false; + } +} + +/* After running the deskew algo, this function applies the new DQ delays + * by reading them from the array "deskew_delay"and writing in PHY registers. + * The bits that are not deskewed parfectly (too much skew on them, + * or data eye very wide) are marked in the array deskew_non_converge. + */ +static void apply_deskew_results(struct stm32mp1_ddrphy *phy, u8 byte, + u8 deskew_delay[NUM_BYTES][8], + u8 deskew_non_converge[NUM_BYTES][8]) +{ + u8 bit_i; + u8 index; + + for (bit_i = 0; bit_i < 8; bit_i++) { + set_DQ_unit_delay(phy, byte, bit_i, deskew_delay[byte][bit_i]); + index = DQ_unit_index(phy, byte, bit_i); + pr_debug("Byte %d ; bit %d : The new DQ delay (%d) index=%d [delta=%d, 3 is the default]", + byte, bit_i, deskew_delay[byte][bit_i], + index, index - 3); + printf("Byte %d, bit %d, DQ delay = %d", + byte, bit_i, deskew_delay[byte][bit_i]); + if (deskew_non_converge[byte][bit_i] == 1) + pr_debug(" - not converged : still more skew"); + printf("\n"); + } +} + +/* DQ Bit de-skew algorithm. + * Deskews data lines as much as possible. + * 1. Add delay to DQS line until finding the failure + * (normally a hold time violation) + * 2. Reduce DQS line by small steps until finding the very first time + * we go back to "Pass" condition. + * 3. For each DQ line, Reduce DQ delay until finding the very first failure + * (normally a hold time fail) + * 4. When all bits are at their first failure delay, we can consider them + * aligned. + * Handle conrer situation (Can't find Pass-fail, or fail-pass transitions + * at any step) + * TODO Provide a return Status. Improve doc + */ +static enum test_result bit_deskew(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, char *string) +{ + /* New DQ delay value (index), set during Deskew algo */ + u8 deskew_delay[NUM_BYTES][8]; + /*If there is still skew on a bit, mark this bit. */ + u8 deskew_non_converge[NUM_BYTES][8]; + struct BIST_result result; + s8 dqs_unit_delay_index = 0; + u8 datx8 = 0; + u8 bit_i = 0; + s8 phase_idx = 0; + s8 bit_i_delay_index = 0; + u8 success = 0; + struct tuning_position last_right_ok; + u8 force_stop = 0; + u8 fail_found; + u8 error = 0; + u8 nb_bytes = get_nb_bytes(ctl); + /* u8 last_pass_dqs_unit = 0; */ + + memset(deskew_delay, 0, sizeof(deskew_delay)); + memset(deskew_non_converge, 0, sizeof(deskew_non_converge)); + + /*Disable DQS Drift Compensation*/ + clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_DFTCMP); + /*Disable all bytes*/ + /* Disable automatic power down of DLL and IOs when disabling + * a byte (To avoid having to add programming and delay + * for a DLL re-lock when later re-enabling a disabled Byte Lane) + */ + clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_PDDISDX); + + /* Disable all data bytes */ + clrbits_le32(&phy->dx0gcr, DDRPHYC_DXNGCR_DXEN); + clrbits_le32(&phy->dx1gcr, DDRPHYC_DXNGCR_DXEN); + clrbits_le32(&phy->dx2gcr, DDRPHYC_DXNGCR_DXEN); + clrbits_le32(&phy->dx3gcr, DDRPHYC_DXNGCR_DXEN); + + /* Config the BIST block */ + config_BIST(phy); + pr_debug("BIST Config done.\n"); + + /* Train each byte */ + for (datx8 = 0; datx8 < nb_bytes; datx8++) { + if (ctrlc()) { + sprintf(string, "interrupted at byte %d/%d, error=%d", + datx8 + 1, nb_bytes, error); + return TEST_FAILED; + } + pr_debug("\n======================\n"); + pr_debug("Start deskew byte %d .\n", datx8); + pr_debug("======================\n"); + /* Enable Byte (DXNGCR, bit DXEN) */ + setbits_le32(DXNGCR(phy, datx8), DDRPHYC_DXNGCR_DXEN); + + /* Select the byte lane for comparison of read data */ + BIST_datx8_sel(phy, datx8); + + /* Set all DQDLYn to maximum value. All bits within the byte + * will be delayed with DQSTR = 2 instead of max = 3 + * to avoid inter bits fail influence + */ + writel(0xAAAAAAAA, DXNDQTR(phy, datx8)); + + /* Set the DQS phase delay to 90 DEG (default). + * What is defined here is the index of the desired config + * in the PHASE array. + */ + phase_idx = _90deg; + + /* Set DQS unit delay to the max value. */ + dqs_unit_delay_index = MAX_DQS_UNIT_IDX; + DQS_unit_delay(phy, datx8, dqs_unit_delay_index); + DQS_phase_delay(phy, datx8, phase_idx); + + /* Issue a DLL soft reset */ + clrbits_le32(DXNDLLCR(phy, datx8), DDRPHYC_DXNDLLCR_DLLSRST); + setbits_le32(DXNDLLCR(phy, datx8), DDRPHYC_DXNDLLCR_DLLSRST); + + /* Test this typical init condition */ + BIST_test(phy, datx8, &result); + success = result.test_result; + + /* If the test pass in this typical condition, + * start the algo with it. + * Else, look for Pass init condition + */ + if (!success) { + pr_debug("Fail at init condtion. Let's look for a good init condition.\n"); + success = 0; /* init */ + /* Make sure we start with a PASS condition before + * looking for a fail condition. + * Find the first PASS PHASE condition + */ + + /* escape if we find a PASS */ + pr_debug("increase Phase idx\n"); + while (!success && (phase_idx <= MAX_DQS_PHASE_IDX)) { + DQS_phase_delay(phy, datx8, phase_idx); + BIST_test(phy, datx8, &result); + success = result.test_result; + phase_idx++; + } + /* if ended with success + * ==>> Restore the fist success condition + */ + if (success) + phase_idx--; /* because it ended with ++ */ + } + if (ctrlc()) { + sprintf(string, "interrupted at byte %d/%d, error=%d", + datx8 + 1, nb_bytes, error); + return TEST_FAILED; + } + /* We couldn't find a successful condition, its seems + * we have hold violation, lets try reduce DQS_unit Delay + */ + if (!success) { + /* We couldn't find a successful condition, its seems + * we have hold violation, lets try reduce DQS_unit + * Delay + */ + pr_debug("Still fail. Try decrease DQS Unit delay\n"); + + phase_idx = 0; + dqs_unit_delay_index = 0; + DQS_phase_delay(phy, datx8, phase_idx); + + /* escape if we find a PASS */ + while (!success && + (dqs_unit_delay_index <= + MAX_DQS_UNIT_IDX)) { + DQS_unit_delay(phy, datx8, + dqs_unit_delay_index); + BIST_test(phy, datx8, &result); + success = result.test_result; + dqs_unit_delay_index++; + } + if (success) { + /* Restore the first success condition*/ + dqs_unit_delay_index--; + /* last_pass_dqs_unit = dqs_unit_delay_index;*/ + DQS_unit_delay(phy, datx8, + dqs_unit_delay_index); + } else { + /* No need to continue, + * there is no pass region. + */ + force_stop = 1; + } + } + + /* There is an initial PASS condition + * Look for the first failing condition by PHASE stepping. + * This part of the algo can finish without converging. + */ + if (force_stop) { + printf("Result: Failed "); + printf("[Cannot Deskew lines, "); + printf("there is no PASS region]\n"); + error++; + continue; + } + if (ctrlc()) { + sprintf(string, "interrupted at byte %d/%d, error=%d", + datx8 + 1, nb_bytes, error); + return TEST_FAILED; + } + + pr_debug("there is a pass region for phase idx %d\n", + phase_idx); + pr_debug("Step1: Find the first failing condition\n"); + /* Look for the first failing condition by PHASE stepping. + * This part of the algo can finish without converging. + */ + + /* escape if we find a fail (hold time violation) + * condition at any bit or if out of delay range. + */ + while (success && (phase_idx <= MAX_DQS_PHASE_IDX)) { + DQS_phase_delay(phy, datx8, phase_idx); + BIST_test(phy, datx8, &result); + success = result.test_result; + phase_idx++; + } + if (ctrlc()) { + sprintf(string, "interrupted at byte %d/%d, error=%d", + datx8 + 1, nb_bytes, error); + return TEST_FAILED; + } + + /* if the loop ended with a failing condition at any bit, + * lets look for the first previous success condition by unit + * stepping (minimal delay) + */ + if (!success) { + pr_debug("Fail region (PHASE) found phase idx %d\n", + phase_idx); + pr_debug("Let's look for first success by DQS Unit steps\n"); + /* This part, the algo always converge */ + phase_idx--; + + /* escape if we find a success condition + * or if out of delay range. + */ + while (!success && dqs_unit_delay_index >= 0) { + DQS_unit_delay(phy, datx8, + dqs_unit_delay_index); + BIST_test(phy, datx8, &result); + success = result.test_result; + dqs_unit_delay_index--; + } + /* if the loop ended with a success condition, + * the last delay Right OK (before hold violation) + * condition is then defined as following: + */ + if (success) { + /* Hold the dely parameters of the the last + * delay Right OK condition. + * -1 to get back to current condition + */ + last_right_ok.phase = phase_idx; + /*+1 to get back to current condition */ + last_right_ok.unit = dqs_unit_delay_index + 1; + last_right_ok.bits_delay = 0xFFFFFFFF; + pr_debug("Found %d\n", dqs_unit_delay_index); + } else { + /* the last OK condition is then with the + * previous phase_idx. + * -2 instead of -1 because at the last + * iteration of the while(), + * we incremented phase_idx + */ + last_right_ok.phase = phase_idx - 1; + /* Nominal+1. Because we want the previous + * delay after reducing the phase delay. + */ + last_right_ok.unit = 1; + last_right_ok.bits_delay = 0xFFFFFFFF; + pr_debug("Not Found : try previous phase %d\n", + phase_idx - 1); + + DQS_phase_delay(phy, datx8, phase_idx - 1); + dqs_unit_delay_index = 0; + success = true; + while (success && + (dqs_unit_delay_index < + MAX_DQS_UNIT_IDX)) { + DQS_unit_delay(phy, datx8, + dqs_unit_delay_index); + BIST_test(phy, datx8, &result); + success = result.test_result; + dqs_unit_delay_index++; + pr_debug("dqs_unit_delay_index = %d, result = %d\n", + dqs_unit_delay_index, success); + } + + if (!success) { + last_right_ok.unit = + dqs_unit_delay_index - 1; + } else { + last_right_ok.unit = 0; + pr_debug("ERROR: failed region not FOUND"); + } + } + } else { + /* we can't find a failing condition at all bits + * ==> Just hold the last test condition + * (the max DQS delay) + * which is the most likely, + * the closest to a hold violation + * If we can't find a Fail condition after + * the Pass region, stick at this position + * In order to have max chances to find a fail + * when reducing DQ delays. + */ + last_right_ok.phase = MAX_DQS_PHASE_IDX; + last_right_ok.unit = MAX_DQS_UNIT_IDX; + last_right_ok.bits_delay = 0xFFFFFFFF; + pr_debug("Can't find the a fail condition\n"); + } + + /* step 2: + * if we arrive at this stage, it means that we found the last + * Right OK condition (by tweeking the DQS delay). Or we simply + * pushed DQS delay to the max + * This means that by reducing the delay on some DQ bits, + * we should find a failing condition. + */ + printf("Byte %d, DQS unit = %d, phase = %d\n", + datx8, last_right_ok.unit, last_right_ok.phase); + pr_debug("Step2, unit = %d, phase = %d, bits delay=%x\n", + last_right_ok.unit, last_right_ok.phase, + last_right_ok.bits_delay); + + /* Restore the last_right_ok condtion. */ + DQS_unit_delay(phy, datx8, last_right_ok.unit); + DQS_phase_delay(phy, datx8, last_right_ok.phase); + writel(last_right_ok.bits_delay, DXNDQTR(phy, datx8)); + + /* train each bit + * reduce delay on each bit, and perform a write/read test + * and stop at the very first time it fails. + * the goal is the find the first failing condition + * for each bit. + * When we achieve this condition< for all the bits, + * we are sure they are aligned (+/- step resolution) + */ + fail_found = 0; + for (bit_i = 0; bit_i < 8; bit_i++) { + if (ctrlc()) { + sprintf(string, + "interrupted at byte %d/%d, error=%d", + datx8 + 1, nb_bytes, error); + return error; + } + pr_debug("deskewing bit %d:\n", bit_i); + success = 1; /* init */ + /* Set all DQDLYn to maximum value. + * Only bit_i will be down-delayed + * ==> if we have a fail, it will be definitely + * from bit_i + */ + writel(0xFFFFFFFF, DXNDQTR(phy, datx8)); + /* Arriving at this stage, + * we have a success condition with delay = 3; + */ + bit_i_delay_index = 3; + + /* escape if bit delay is out of range or + * if a fatil occurs + */ + while ((bit_i_delay_index >= 0) && success) { + set_DQ_unit_delay(phy, datx8, + bit_i, + bit_i_delay_index); + BIST_test(phy, datx8, &result); + success = result.test_result; + bit_i_delay_index--; + } + + /* if escape with a fail condition + * ==> save this position for bit_i + */ + if (!success) { + /* save the delay position. + * Add 1 because the while loop ended with a --, + * and that we need to hold the last success + * delay + */ + deskew_delay[datx8][bit_i] = + bit_i_delay_index + 2; + if (deskew_delay[datx8][bit_i] > 3) + deskew_delay[datx8][bit_i] = 3; + + /* A flag that states we found at least a fail + * at one bit. + */ + fail_found = 1; + pr_debug("Fail found on bit %d, for delay = %d => deskew[%d][%d] = %d\n", + bit_i, bit_i_delay_index + 1, + datx8, bit_i, + deskew_delay[datx8][bit_i]); + } else { + /* if we can find a success condition by + * back-delaying this bit, just set the delay + * to 0 (the best deskew + * possible) and mark the bit. + */ + deskew_delay[datx8][bit_i] = 0; + /* set a flag that will be used later + * in the report. + */ + deskew_non_converge[datx8][bit_i] = 1; + pr_debug("Fail not found on bit %d => deskew[%d][%d] = %d\n", + bit_i, datx8, bit_i, + deskew_delay[datx8][bit_i]); + } + } + pr_debug("**********byte %d tuning complete************\n", + datx8); + /* If we can't find any failure by back delaying DQ lines, + * hold the default values + */ + if (!fail_found) { + for (bit_i = 0; bit_i < 8; bit_i++) + deskew_delay[datx8][bit_i] = 0; + pr_debug("The Deskew algorithm can't converge, there is too much margin in your design. Good job!\n"); + } + + apply_deskew_results(phy, datx8, deskew_delay, + deskew_non_converge); + /* Restore nominal value for DQS delay */ + DQS_phase_delay(phy, datx8, 3); + DQS_unit_delay(phy, datx8, 3); + /* disable byte after byte bits deskew */ + clrbits_le32(DXNGCR(phy, datx8), DDRPHYC_DXNGCR_DXEN); + } /* end of byte deskew */ + + /* re-enable all data bytes */ + setbits_le32(&phy->dx0gcr, DDRPHYC_DXNGCR_DXEN); + setbits_le32(&phy->dx1gcr, DDRPHYC_DXNGCR_DXEN); + setbits_le32(&phy->dx2gcr, DDRPHYC_DXNGCR_DXEN); + setbits_le32(&phy->dx3gcr, DDRPHYC_DXNGCR_DXEN); + + if (error) { + sprintf(string, "error = %d", error); + return TEST_FAILED; + } + + return TEST_PASSED; +} /* end function */ + +/* Trim DQS timings and set it in the centre of data eye. + * Look for a PPPPF region, then look for a FPPP region and finally select + * the mid of the FPPPPPF region + */ +static enum test_result eye_training(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, char *string) +{ + /*Stores the DQS trim values (PHASE index, unit index) */ + u8 eye_training_val[NUM_BYTES][2]; + u8 byte = 0; + struct BIST_result result; + s8 dqs_unit_delay_index = 0; + s8 phase_idx = 0; + s8 dqs_unit_delay_index_pass = 0; + s8 phase_idx_pass = 0; + u8 success = 0; + u8 left_phase_bound_found, right_phase_bound_found; + u8 left_unit_bound_found, right_unit_bound_found; + u8 left_bound_found, right_bound_found; + struct tuning_position left_bound, right_bound; + u8 error = 0; + u8 nb_bytes = get_nb_bytes(ctl); + + /*Disable DQS Drift Compensation*/ + clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_DFTCMP); + /*Disable all bytes*/ + /* Disable automatic power down of DLL and IOs when disabling a byte + * (To avoid having to add programming and delay + * for a DLL re-lock when later re-enabling a disabled Byte Lane) + */ + clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_PDDISDX); + + /*Disable all data bytes */ + clrbits_le32(&phy->dx0gcr, DDRPHYC_DXNGCR_DXEN); + clrbits_le32(&phy->dx1gcr, DDRPHYC_DXNGCR_DXEN); + clrbits_le32(&phy->dx2gcr, DDRPHYC_DXNGCR_DXEN); + clrbits_le32(&phy->dx3gcr, DDRPHYC_DXNGCR_DXEN); + + /* Config the BIST block */ + config_BIST(phy); + + for (byte = 0; byte < nb_bytes; byte++) { + if (ctrlc()) { + sprintf(string, "interrupted at byte %d/%d, error=%d", + byte + 1, nb_bytes, error); + return TEST_FAILED; + } + right_bound.phase = 0; + right_bound.unit = 0; + + left_bound.phase = 0; + left_bound.unit = 0; + + left_phase_bound_found = 0; + right_phase_bound_found = 0; + + left_unit_bound_found = 0; + right_unit_bound_found = 0; + + left_bound_found = 0; + right_bound_found = 0; + + /* Enable Byte (DXNGCR, bit DXEN) */ + setbits_le32(DXNGCR(phy, byte), DDRPHYC_DXNGCR_DXEN); + + /* Select the byte lane for comparison of read data */ + BIST_datx8_sel(phy, byte); + + /* Set DQS phase delay to the nominal value. */ + phase_idx = _90deg; + phase_idx_pass = phase_idx; + + /* Set DQS unit delay to the nominal value. */ + dqs_unit_delay_index = 3; + dqs_unit_delay_index_pass = dqs_unit_delay_index; + success = 0; + + pr_debug("STEP0: Find Init delay\n"); + /* STEP0: Find Init delay: a delay that put the system + * in a "Pass" condition then (TODO) update + * dqs_unit_delay_index_pass & phase_idx_pass + */ + DQS_unit_delay(phy, byte, dqs_unit_delay_index); + DQS_phase_delay(phy, byte, phase_idx); + BIST_test(phy, byte, &result); + success = result.test_result; + /* If we have a fail in the nominal condition */ + if (!success) { + /* Look at the left */ + while (phase_idx >= 0 && !success) { + phase_idx--; + DQS_phase_delay(phy, byte, phase_idx); + BIST_test(phy, byte, &result); + success = result.test_result; + } + } + if (!success) { + /* if we can't find pass condition, + * then look at the right + */ + phase_idx = _90deg; + while (phase_idx <= MAX_DQS_PHASE_IDX && + !success) { + phase_idx++; + DQS_phase_delay(phy, byte, + phase_idx); + BIST_test(phy, byte, &result); + success = result.test_result; + } + } + /* save the pass condition */ + if (success) { + phase_idx_pass = phase_idx; + } else { + printf("Result: Failed "); + printf("[Cannot DQS timings, "); + printf("there is no PASS region]\n"); + error++; + continue; + } + + if (ctrlc()) { + sprintf(string, "interrupted at byte %d/%d, error=%d", + byte + 1, nb_bytes, error); + return TEST_FAILED; + } + pr_debug("STEP1: Find LEFT PHASE DQS Bound\n"); + /* STEP1: Find LEFT PHASE DQS Bound */ + while ((phase_idx >= 0) && + (phase_idx <= MAX_DQS_PHASE_IDX) && + !left_phase_bound_found) { + DQS_unit_delay(phy, byte, + dqs_unit_delay_index); + DQS_phase_delay(phy, byte, + phase_idx); + BIST_test(phy, byte, &result); + success = result.test_result; + + /*TODO: Manage the case were at the beginning + * there is already a fail + */ + if (!success) { + /* the last pass condition */ + left_bound.phase = ++phase_idx; + left_phase_bound_found = 1; + } else if (success) { + phase_idx--; + } + } + if (!left_phase_bound_found) { + left_bound.phase = 0; + phase_idx = 0; + } + /* If not found, lets take 0 */ + + if (ctrlc()) { + sprintf(string, "interrupted at byte %d/%d, error=%d", + byte + 1, nb_bytes, error); + return TEST_FAILED; + } + pr_debug("STEP2: Find UNIT left bound\n"); + /* STEP2: Find UNIT left bound */ + while ((dqs_unit_delay_index >= 0) && + !left_unit_bound_found) { + DQS_unit_delay(phy, byte, + dqs_unit_delay_index); + DQS_phase_delay(phy, byte, phase_idx); + BIST_test(phy, byte, &result); + success = result.test_result; + if (!success) { + left_bound.unit = + ++dqs_unit_delay_index; + left_unit_bound_found = 1; + left_bound_found = 1; + } else if (success) { + dqs_unit_delay_index--; + } + } + + /* If not found, lets take 0 */ + if (!left_unit_bound_found) + left_bound.unit = 0; + + if (ctrlc()) { + sprintf(string, "interrupted at byte %d/%d, error=%d", + byte + 1, nb_bytes, error); + return TEST_FAILED; + } + pr_debug("STEP3: Find PHase right bound\n"); + /* STEP3: Find PHase right bound, start with "pass" + * condition + */ + + /* Set DQS phase delay to the pass value. */ + phase_idx = phase_idx_pass; + + /* Set DQS unit delay to the pass value. */ + dqs_unit_delay_index = dqs_unit_delay_index_pass; + + while ((phase_idx <= MAX_DQS_PHASE_IDX) && + !right_phase_bound_found) { + DQS_unit_delay(phy, byte, + dqs_unit_delay_index); + DQS_phase_delay(phy, byte, phase_idx); + BIST_test(phy, byte, &result); + success = result.test_result; + if (!success) { + /* the last pass condition */ + right_bound.phase = --phase_idx; + right_phase_bound_found = 1; + } else if (success) { + phase_idx++; + } + } + + /* If not found, lets take the max value */ + if (!right_phase_bound_found) { + right_bound.phase = MAX_DQS_PHASE_IDX; + phase_idx = MAX_DQS_PHASE_IDX; + } + + if (ctrlc()) { + sprintf(string, "interrupted at byte %d/%d, error=%d", + byte + 1, nb_bytes, error); + return TEST_FAILED; + } + pr_debug("STEP4: Find UNIT right bound\n"); + /* STEP4: Find UNIT right bound */ + while ((dqs_unit_delay_index <= MAX_DQS_UNIT_IDX) && + !right_unit_bound_found) { + DQS_unit_delay(phy, byte, + dqs_unit_delay_index); + DQS_phase_delay(phy, byte, phase_idx); + BIST_test(phy, byte, &result); + success = result.test_result; + if (!success) { + right_bound.unit = + --dqs_unit_delay_index; + right_unit_bound_found = 1; + right_bound_found = 1; + } else if (success) { + dqs_unit_delay_index++; + } + } + /* If not found, lets take the max value */ + if (!right_unit_bound_found) + right_bound.unit = MAX_DQS_UNIT_IDX; + + /* If we found a regular FAil Pass FAil pattern + * FFPPPPPPFF + * OR PPPPPFF Or FFPPPPP + */ + + if (left_bound_found || right_bound_found) { + eye_training_val[byte][0] = (right_bound.phase + + left_bound.phase) / 2; + eye_training_val[byte][1] = (right_bound.unit + + left_bound.unit) / 2; + + /* If we already lost 1/2PHASE Tuning, + * let's try to recover by ++ on unit + */ + if (((right_bound.phase + left_bound.phase) % 2 == 1) && + eye_training_val[byte][1] != MAX_DQS_UNIT_IDX) + eye_training_val[byte][1]++; + pr_debug("** found phase : %d - %d & unit %d - %d\n", + right_bound.phase, left_bound.phase, + right_bound.unit, left_bound.unit); + pr_debug("** calculating mid region: phase: %d unit: %d (nominal is 3)\n", + eye_training_val[byte][0], + eye_training_val[byte][1]); + } else { + /* PPPPPPPPPP, we're already good. + * Set nominal values. + */ + eye_training_val[byte][0] = 3; + eye_training_val[byte][1] = 3; + } + DQS_phase_delay(phy, byte, eye_training_val[byte][0]); + DQS_unit_delay(phy, byte, eye_training_val[byte][1]); + + printf("Byte %d, DQS unit = %d, phase = %d\n", + byte, + eye_training_val[byte][1], + eye_training_val[byte][0]); + } + + if (error) { + sprintf(string, "error = %d", error); + return TEST_FAILED; + } + + return TEST_PASSED; +} + +static void display_reg_results(struct stm32mp1_ddrphy *phy, u8 byte) +{ + u8 i = 0; + + printf("Byte %d Dekew result, bit0 delay, bit1 delay...bit8 delay\n ", + byte); + + for (i = 0; i < 8; i++) + printf("%d ", DQ_unit_index(phy, byte, i)); + printf("\n"); + + printf("dxndllcr: [%08x] val:%08x\n", + DXNDLLCR(phy, byte), + readl(DXNDLLCR(phy, byte))); + printf("dxnqdstr: [%08x] val:%08x\n", + DXNDQSTR(phy, byte), + readl(DXNDQSTR(phy, byte))); + printf("dxndqtr: [%08x] val:%08x\n", + DXNDQTR(phy, byte), + readl(DXNDQTR(phy, byte))); +} + +/* analyse the dgs gating log table, and determine the midpoint.*/ +static u8 set_midpoint_read_dqs_gating(struct stm32mp1_ddrphy *phy, u8 byte, + u8 dqs_gating[NUM_BYTES] + [MAX_GSL_IDX + 1] + [MAX_GPS_IDX + 1]) +{ + /* stores the dqs gate values (gsl index, gps index) */ + u8 dqs_gate_values[NUM_BYTES][2]; + u8 gsl_idx, gps_idx = 0; + u8 left_bound_idx[2] = {0, 0}; + u8 right_bound_idx[2] = {0, 0}; + u8 left_bound_found = 0; + u8 right_bound_found = 0; + u8 intermittent = 0; + u8 value; + + for (gsl_idx = 0; gsl_idx <= MAX_GSL_IDX; gsl_idx++) { + for (gps_idx = 0; gps_idx <= MAX_GPS_IDX; gps_idx++) { + value = dqs_gating[byte][gsl_idx][gps_idx]; + if (value == 1 && left_bound_found == 0) { + left_bound_idx[0] = gsl_idx; + left_bound_idx[1] = gps_idx; + left_bound_found = 1; + } else if (value == 0 && + left_bound_found == 1 && + !right_bound_found) { + if (gps_idx == 0) { + right_bound_idx[0] = gsl_idx - 1; + right_bound_idx[1] = MAX_GPS_IDX; + } else { + right_bound_idx[0] = gsl_idx; + right_bound_idx[1] = gps_idx - 1; + } + right_bound_found = 1; + } else if (value == 1 && + right_bound_found == 1) { + intermittent = 1; + } + } + } + + /* if only ppppppp is found, there is no mid region. */ + if (left_bound_idx[0] == 0 && left_bound_idx[1] == 0 && + right_bound_idx[0] == 0 && right_bound_idx[1] == 0) + intermittent = 1; + + /*if we found a regular fail pass fail pattern ffppppppff + * or pppppff or ffppppp + */ + if (!intermittent) { + /*if we found a regular fail pass fail pattern ffppppppff + * or pppppff or ffppppp + */ + if (left_bound_found || right_bound_found) { + pr_debug("idx0(%d): %d %d idx1(%d) : %d %d\n", + left_bound_found, + right_bound_idx[0], left_bound_idx[0], + right_bound_found, + right_bound_idx[1], left_bound_idx[1]); + dqs_gate_values[byte][0] = + (right_bound_idx[0] + left_bound_idx[0]) / 2; + dqs_gate_values[byte][1] = + (right_bound_idx[1] + left_bound_idx[1]) / 2; + /* if we already lost 1/2gsl tuning, + * let's try to recover by ++ on gps + */ + if (((right_bound_idx[0] + + left_bound_idx[0]) % 2 == 1) && + dqs_gate_values[byte][1] != MAX_GPS_IDX) + dqs_gate_values[byte][1]++; + /* if we already lost 1/2gsl tuning and gps is on max*/ + else if (((right_bound_idx[0] + + left_bound_idx[0]) % 2 == 1) && + dqs_gate_values[byte][1] == MAX_GPS_IDX) { + dqs_gate_values[byte][1] = 0; + dqs_gate_values[byte][0]++; + } + /* if we have gsl left and write limit too close + * (difference=1) + */ + if (((right_bound_idx[0] - left_bound_idx[0]) == 1)) { + dqs_gate_values[byte][1] = (left_bound_idx[1] + + right_bound_idx[1] + + 4) / 2; + if (dqs_gate_values[byte][1] >= 4) { + dqs_gate_values[byte][0] = + right_bound_idx[0]; + dqs_gate_values[byte][1] -= 4; + } else { + dqs_gate_values[byte][0] = + left_bound_idx[0]; + } + } + pr_debug("*******calculating mid region: system latency: %d phase: %d********\n", + dqs_gate_values[byte][0], + dqs_gate_values[byte][1]); + pr_debug("*******the nominal values were system latency: 0 phase: 2*******\n"); + set_r0dgsl_delay(phy, byte, dqs_gate_values[byte][0]); + set_r0dgps_delay(phy, byte, dqs_gate_values[byte][1]); + } + } else { + /* if intermitant, restore defaut values */ + pr_debug("dqs gating:no regular fail/pass/fail found. defaults values restored.\n"); + set_r0dgsl_delay(phy, byte, 0); + set_r0dgps_delay(phy, byte, 2); + } + + /* return 0 if intermittent or if both left_bound + * and right_bound are not found + */ + return !(intermittent || (left_bound_found && right_bound_found)); +} + +static enum test_result read_dqs_gating(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string) +{ + /* stores the log of pass/fail */ + u8 dqs_gating[NUM_BYTES][MAX_GSL_IDX + 1][MAX_GPS_IDX + 1]; + u8 byte, gsl_idx, gps_idx = 0; + struct BIST_result result; + u8 success = 0; + u8 nb_bytes = get_nb_bytes(ctl); + + memset(dqs_gating, 0x0, sizeof(dqs_gating)); + + /*disable dqs drift compensation*/ + clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_DFTCMP); + /*disable all bytes*/ + /* disable automatic power down of dll and ios when disabling a byte + * (to avoid having to add programming and delay + * for a dll re-lock when later re-enabling a disabled byte lane) + */ + clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_PDDISDX); + + /* disable all data bytes */ + clrbits_le32(&phy->dx0gcr, DDRPHYC_DXNGCR_DXEN); + clrbits_le32(&phy->dx1gcr, DDRPHYC_DXNGCR_DXEN); + clrbits_le32(&phy->dx2gcr, DDRPHYC_DXNGCR_DXEN); + clrbits_le32(&phy->dx3gcr, DDRPHYC_DXNGCR_DXEN); + + /* config the bist block */ + config_BIST(phy); + + for (byte = 0; byte < nb_bytes; byte++) { + if (ctrlc()) { + sprintf(string, "interrupted at byte %d/%d", + byte + 1, nb_bytes); + return TEST_FAILED; + } + /* enable byte x (dxngcr, bit dxen) */ + setbits_le32(DXNGCR(phy, byte), DDRPHYC_DXNGCR_DXEN); + + /* select the byte lane for comparison of read data */ + BIST_datx8_sel(phy, byte); + for (gsl_idx = 0; gsl_idx <= MAX_GSL_IDX; gsl_idx++) { + for (gps_idx = 0; gps_idx <= MAX_GPS_IDX; gps_idx++) { + if (ctrlc()) { + sprintf(string, + "interrupted at byte %d/%d", + byte + 1, nb_bytes); + return TEST_FAILED; + } + /* write cfg to dxndqstr */ + set_r0dgsl_delay(phy, byte, gsl_idx); + set_r0dgps_delay(phy, byte, gps_idx); + + BIST_test(phy, byte, &result); + success = result.test_result; + if (success) + dqs_gating[byte][gsl_idx][gps_idx] = 1; + itm_soft_reset(phy); + } + } + set_midpoint_read_dqs_gating(phy, byte, dqs_gating); + /* dummy reads */ + readl(0xc0000000); + readl(0xc0000000); + } + + /* re-enable drift compensation */ + /* setbits_le32(&phy->pgcr, DDRPHYC_PGCR_DFTCMP); */ + return TEST_PASSED; +} + +/**************************************************************** + * TEST + **************************************************************** + */ +static enum test_result do_read_dqs_gating(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, + char *argv[]) +{ + u32 rfshctl3 = readl(&ctl->rfshctl3); + u32 pwrctl = readl(&ctl->pwrctl); + enum test_result res; + + stm32mp1_refresh_disable(ctl); + res = read_dqs_gating(ctl, phy, string); + stm32mp1_refresh_restore(ctl, rfshctl3, pwrctl); + + return res; +} + +static enum test_result do_bit_deskew(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + u32 rfshctl3 = readl(&ctl->rfshctl3); + u32 pwrctl = readl(&ctl->pwrctl); + enum test_result res; + + stm32mp1_refresh_disable(ctl); + res = bit_deskew(ctl, phy, string); + stm32mp1_refresh_restore(ctl, rfshctl3, pwrctl); + + return res; +} + +static enum test_result do_eye_training(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + u32 rfshctl3 = readl(&ctl->rfshctl3); + u32 pwrctl = readl(&ctl->pwrctl); + enum test_result res; + + stm32mp1_refresh_disable(ctl); + res = eye_training(ctl, phy, string); + stm32mp1_refresh_restore(ctl, rfshctl3, pwrctl); + + return res; +} + +static enum test_result do_display(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + int byte; + u8 nb_bytes = get_nb_bytes(ctl); + + for (byte = 0; byte < nb_bytes; byte++) + display_reg_results(phy, byte); + + return TEST_PASSED; +} + +static enum test_result do_bist_config(struct stm32mp1_ddrctl *ctl, + struct stm32mp1_ddrphy *phy, + char *string, int argc, char *argv[]) +{ + unsigned long value; + + if (argc > 0) { + if (strict_strtoul(argv[0], 0, &value) < 0) { + sprintf(string, "invalid nbErr %s", argv[0]); + return TEST_FAILED; + } + BIST_error_max = value; + } + if (argc > 1) { + if (strict_strtoul(argv[1], 0, &value) < 0) { + sprintf(string, "invalid Seed %s", argv[1]); + return TEST_FAILED; + } + BIST_seed = value; + } + printf("Bist.nbErr = %d\n", BIST_error_max); + if (BIST_seed) + printf("Bist.Seed = 0x%x\n", BIST_seed); + else + printf("Bist.Seed = random\n"); + + return TEST_PASSED; +} + +/**************************************************************** + * TEST Description + **************************************************************** + */ + +const struct test_desc tuning[] = { + {do_read_dqs_gating, "Read DQS gating", + "software read DQS Gating", "", 0 }, + {do_bit_deskew, "Bit de-skew", "", "", 0 }, + {do_eye_training, "Eye Training", "or DQS training", "", 0 }, + {do_display, "Display registers", "", "", 0 }, + {do_bist_config, "Bist config", "[nbErr] [seed]", + "configure Bist test", 2}, +}; + +const int tuning_nb = ARRAY_SIZE(tuning); diff --git a/drivers/remoteproc/k3_system_controller.c b/drivers/remoteproc/k3_system_controller.c index 214ea18d8a..44e56c759f 100644 --- a/drivers/remoteproc/k3_system_controller.c +++ b/drivers/remoteproc/k3_system_controller.c @@ -301,7 +301,7 @@ static int k3_sysctrler_probe(struct udevice *dev) static const struct k3_sysctrler_desc k3_sysctrler_am654_desc = { .host_id = 4, /* HOST_ID_R5_1 */ - .max_rx_timeout_us = 400000, + .max_rx_timeout_us = 800000, .max_msg_size = 60, }; diff --git a/drivers/reset/reset-rockchip.c b/drivers/reset/reset-rockchip.c index af07134049..3871fc00d0 100644 --- a/drivers/reset/reset-rockchip.c +++ b/drivers/reset/reset-rockchip.c @@ -7,7 +7,7 @@ #include <dm.h> #include <reset-uclass.h> #include <linux/io.h> -#include <asm/arch/hardware.h> +#include <asm/arch-rockchip/hardware.h> #include <dm/lists.h> /* * Each reg has 16 bits reset signal for devices diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c index cb8312619f..ee4cbcb02f 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c @@ -107,14 +107,12 @@ static const struct reset_ops socfpga_reset_ops = { static int socfpga_reset_probe(struct udevice *dev) { struct socfpga_reset_data *data = dev_get_priv(dev); - const void *blob = gd->fdt_blob; - int node = dev_of_offset(dev); u32 modrst_offset; void __iomem *membase; membase = devfdt_get_addr_ptr(dev); - modrst_offset = fdtdec_get_int(blob, node, "altr,modrst-offset", 0x10); + modrst_offset = dev_read_u32_default(dev, "altr,modrst-offset", 0x10); data->modrst_base = membase + modrst_offset; return 0; diff --git a/drivers/reset/reset-uclass.c b/drivers/reset/reset-uclass.c index 89e39c6b5a..ee1a423ffb 100644 --- a/drivers/reset/reset-uclass.c +++ b/drivers/reset/reset-uclass.c @@ -29,41 +29,34 @@ static int reset_of_xlate_default(struct reset_ctl *reset_ctl, return 0; } -int reset_get_by_index(struct udevice *dev, int index, - struct reset_ctl *reset_ctl) +static int reset_get_by_index_tail(int ret, ofnode node, + struct ofnode_phandle_args *args, + const char *list_name, int index, + struct reset_ctl *reset_ctl) { - struct ofnode_phandle_args args; - int ret; struct udevice *dev_reset; struct reset_ops *ops; - debug("%s(dev=%p, index=%d, reset_ctl=%p)\n", __func__, dev, index, - reset_ctl); + assert(reset_ctl); reset_ctl->dev = NULL; - - ret = dev_read_phandle_with_args(dev, "resets", "#reset-cells", 0, - index, &args); - if (ret) { - debug("%s: fdtdec_parse_phandle_with_args() failed: %d\n", - __func__, ret); + if (ret) return ret; - } - ret = uclass_get_device_by_ofnode(UCLASS_RESET, args.node, + ret = uclass_get_device_by_ofnode(UCLASS_RESET, args->node, &dev_reset); if (ret) { debug("%s: uclass_get_device_by_ofnode() failed: %d\n", __func__, ret); - debug("%s %d\n", ofnode_get_name(args.node), args.args[0]); + debug("%s %d\n", ofnode_get_name(args->node), args->args[0]); return ret; } ops = reset_dev_ops(dev_reset); reset_ctl->dev = dev_reset; if (ops->of_xlate) - ret = ops->of_xlate(reset_ctl, &args); + ret = ops->of_xlate(reset_ctl, args); else - ret = reset_of_xlate_default(reset_ctl, &args); + ret = reset_of_xlate_default(reset_ctl, args); if (ret) { debug("of_xlate() failed: %d\n", ret); return ret; @@ -78,6 +71,32 @@ int reset_get_by_index(struct udevice *dev, int index, return 0; } +int reset_get_by_index(struct udevice *dev, int index, + struct reset_ctl *reset_ctl) +{ + struct ofnode_phandle_args args; + int ret; + + ret = dev_read_phandle_with_args(dev, "resets", "#reset-cells", 0, + index, &args); + + return reset_get_by_index_tail(ret, dev_ofnode(dev), &args, "resets", + index > 0, reset_ctl); +} + +int reset_get_by_index_nodev(ofnode node, int index, + struct reset_ctl *reset_ctl) +{ + struct ofnode_phandle_args args; + int ret; + + ret = ofnode_parse_phandle_with_args(node, "resets", "#reset-cells", 0, + index > 0, &args); + + return reset_get_by_index_tail(ret, node, &args, "resets", + index > 0, reset_ctl); +} + int reset_get_bulk(struct udevice *dev, struct reset_ctl_bulk *bulk) { int i, ret, err, count; diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c index 6528ddfebb..1f7bdade29 100644 --- a/drivers/rtc/rtc-lib.c +++ b/drivers/rtc/rtc-lib.c @@ -23,7 +23,7 @@ static const unsigned char rtc_days_in_month[] = { /* * The number of days in the month. */ -static int rtc_month_days(unsigned int month, unsigned int year) +int rtc_month_days(unsigned int month, unsigned int year) { return rtc_days_in_month[month] + (is_leap_year(year) && month == 1); } diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index fcbb0a81ed..8a447fd6e3 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -559,6 +559,14 @@ config MVEBU_A3700_UART Choose this option to add support for UART driver on the Marvell Armada 3700 SoC. The base address is configured via DT. +config MCFUART + bool "Freescale ColdFire UART support" + help + Choose this option to add support for UART driver on the ColdFire + SoC's family. The serial communication channel provides a full-duplex + asynchronous/synchronous receiver and transmitter deriving an + operating frequency from the internal bus clock or an external clock. + config MXC_UART bool "IMX serial port support" depends on MX5 || MX6 diff --git a/drivers/serial/altera_uart.c b/drivers/serial/altera_uart.c index 67d47199aa..436cf2331d 100644 --- a/drivers/serial/altera_uart.c +++ b/drivers/serial/altera_uart.c @@ -10,8 +10,6 @@ #include <serial.h> #include <asm/io.h> -DECLARE_GLOBAL_DATA_PTR; - /* status register */ #define ALTERA_UART_TMT BIT(5) /* tx empty */ #define ALTERA_UART_TRDY BIT(6) /* tx ready */ @@ -91,8 +89,7 @@ static int altera_uart_ofdata_to_platdata(struct udevice *dev) plat->regs = map_physmem(devfdt_get_addr(dev), sizeof(struct altera_uart_regs), MAP_NOCACHE); - plat->uartclk = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), - "clock-frequency", 0); + plat->uartclk = dev_read_u32_default(dev, "clock-frequency", 0); return 0; } diff --git a/drivers/serial/mcfuart.c b/drivers/serial/mcfuart.c index 1371049de2..066e5a18d8 100644 --- a/drivers/serial/mcfuart.c +++ b/drivers/serial/mcfuart.c @@ -5,6 +5,9 @@ * * Modified to add device model (DM) support * (C) Copyright 2015 Angelo Dureghello <angelo@sysam.it> + * + * Modified to add DM and fdt support, removed non DM code + * (C) Copyright 2018 Angelo Dureghello <angelo@sysam.it> */ /* @@ -78,83 +81,6 @@ static void mcf_serial_setbrg_common(uart_t *uart, int baudrate) writeb(UART_UCR_RX_ENABLED | UART_UCR_TX_ENABLED, &uart->ucr); } -#ifndef CONFIG_DM_SERIAL - -static int mcf_serial_init(void) -{ - uart_t *uart_base; - int port_idx; - - uart_base = (uart_t *)CONFIG_SYS_UART_BASE; - port_idx = CONFIG_SYS_UART_PORT; - - return mcf_serial_init_common(uart_base, port_idx, gd->baudrate); -} - -static void mcf_serial_putc(const char c) -{ - uart_t *uart = (uart_t *)CONFIG_SYS_UART_BASE; - - if (c == '\n') - serial_putc('\r'); - - /* Wait for last character to go. */ - while (!(readb(&uart->usr) & UART_USR_TXRDY)) - ; - - writeb(c, &uart->utb); -} - -static int mcf_serial_getc(void) -{ - uart_t *uart = (uart_t *)CONFIG_SYS_UART_BASE; - - /* Wait for a character to arrive. */ - while (!(readb(&uart->usr) & UART_USR_RXRDY)) - ; - - return readb(&uart->urb); -} - -static void mcf_serial_setbrg(void) -{ - uart_t *uart = (uart_t *)CONFIG_SYS_UART_BASE; - - mcf_serial_setbrg_common(uart, gd->baudrate); -} - -static int mcf_serial_tstc(void) -{ - uart_t *uart = (uart_t *)CONFIG_SYS_UART_BASE; - - return readb(&uart->usr) & UART_USR_RXRDY; -} - -static struct serial_device mcf_serial_drv = { - .name = "mcf_serial", - .start = mcf_serial_init, - .stop = NULL, - .setbrg = mcf_serial_setbrg, - .putc = mcf_serial_putc, - .puts = default_serial_puts, - .getc = mcf_serial_getc, - .tstc = mcf_serial_tstc, -}; - -void mcf_serial_initialize(void) -{ - serial_register(&mcf_serial_drv); -} - -__weak struct serial_device *default_serial_console(void) -{ - return &mcf_serial_drv; -} - -#endif - -#ifdef CONFIG_DM_SERIAL - static int coldfire_serial_probe(struct udevice *dev) { struct coldfire_serial_platdata *plat = dev->platdata; @@ -212,6 +138,23 @@ static int coldfire_serial_pending(struct udevice *dev, bool input) return 0; } +static int coldfire_ofdata_to_platdata(struct udevice *dev) +{ + struct coldfire_serial_platdata *plat = dev_get_platdata(dev); + fdt_addr_t addr_base; + + addr_base = devfdt_get_addr(dev); + if (addr_base == FDT_ADDR_T_NONE) + return -ENODEV; + + plat->base = (uint32_t)addr_base; + + plat->port = dev->seq; + plat->baudrate = gd->baudrate; + + return 0; +} + static const struct dm_serial_ops coldfire_serial_ops = { .putc = coldfire_serial_putc, .pending = coldfire_serial_pending, @@ -219,11 +162,18 @@ static const struct dm_serial_ops coldfire_serial_ops = { .setbrg = coldfire_serial_setbrg, }; +static const struct udevice_id coldfire_serial_ids[] = { + { .compatible = "fsl,mcf-uart" }, + { } +}; + U_BOOT_DRIVER(serial_coldfire) = { .name = "serial_coldfire", .id = UCLASS_SERIAL, + .of_match = coldfire_serial_ids, + .ofdata_to_platdata = coldfire_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct coldfire_serial_platdata), .probe = coldfire_serial_probe, .ops = &coldfire_serial_ops, .flags = DM_FLAG_PRE_RELOC, }; -#endif diff --git a/drivers/serial/serial_rockchip.c b/drivers/serial/serial_rockchip.c index 35fefd74c6..b1718f72d1 100644 --- a/drivers/serial/serial_rockchip.c +++ b/drivers/serial/serial_rockchip.c @@ -9,7 +9,7 @@ #include <dt-structs.h> #include <ns16550.h> #include <serial.h> -#include <asm/arch/clock.h> +#include <asm/arch-rockchip/clock.h> #if defined(CONFIG_ROCKCHIP_RK3188) struct rockchip_uart_platdata { diff --git a/drivers/serial/serial_sh.c b/drivers/serial/serial_sh.c index c934d5f25a..acfcc2954a 100644 --- a/drivers/serial/serial_sh.c +++ b/drivers/serial/serial_sh.c @@ -19,10 +19,7 @@ DECLARE_GLOBAL_DATA_PTR; -#if defined(CONFIG_CPU_SH7760) || \ - defined(CONFIG_CPU_SH7780) || \ - defined(CONFIG_CPU_SH7785) || \ - defined(CONFIG_CPU_SH7786) +#if defined(CONFIG_CPU_SH7780) static int scif_rxfill(struct uart_port *port) { return sci_in(port, SCRFDR) & 0xff; @@ -39,14 +36,6 @@ static int scif_rxfill(struct uart_port *port) return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; } } -#elif defined(CONFIG_ARCH_SH7372) -static int scif_rxfill(struct uart_port *port) -{ - if (port->type == PORT_SCIFA) - return sci_in(port, SCFDR) & SCIF_RFDC_MASK; - else - return sci_in(port, SCRFDR); -} #else static int scif_rxfill(struct uart_port *port) { @@ -63,6 +52,9 @@ static void sh_serial_init_generic(struct uart_port *port) sci_out(port, SCFCR, SCFCR_RFRST|SCFCR_TFRST); sci_in(port, SCFCR); sci_out(port, SCFCR, 0); +#if defined(CONFIG_RZA1) + sci_out(port, SCSPTR, 0x0003); +#endif } static void diff --git a/drivers/serial/serial_sh.h b/drivers/serial/serial_sh.h index deb4b647c6..11deaa9511 100644 --- a/drivers/serial/serial_sh.h +++ b/drivers/serial/serial_sh.h @@ -12,53 +12,16 @@ struct uart_port { enum sh_clk_mode clk_mode; /* clock mode */ }; -#if defined(CONFIG_H83007) || defined(CONFIG_H83068) -#include <asm/regs306x.h> -#endif -#if defined(CONFIG_H8S2678) -#include <asm/regs267x.h> -#endif - -#if defined(CONFIG_CPU_SH7706) || \ - defined(CONFIG_CPU_SH7707) || \ - defined(CONFIG_CPU_SH7708) || \ - defined(CONFIG_CPU_SH7709) -# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */ -# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */ -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ -#elif defined(CONFIG_CPU_SH7705) -# define SCIF0 0xA4400000 -# define SCIF2 0xA4410000 -# define SCSMR_Ir 0xA44A0000 -# define IRDA_SCIF SCIF0 -# define SCPCR 0xA4000116 -# define SCPDR 0xA4000136 - -/* Set the clock source, - * SCIF2 (0xA4410000) -> External clock, SCK pin used as clock input - * SCIF0 (0xA4400000) -> Internal clock, SCK pin as serial clock output - */ -# define SCSCR_INIT(port) (port->mapbase == SCIF2) ? 0xF3 : 0xF0 -#elif defined(CONFIG_CPU_SH7720) || \ - defined(CONFIG_CPU_SH7721) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) || \ +#if defined(CONFIG_CPU_SH7721) || \ defined(CONFIG_SH73A0) || \ defined(CONFIG_R8A7740) # define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */ # define PORT_PTCR 0xA405011EUL # define PORT_PVCR 0xA4050122UL # define SCIF_ORER 0x0200 /* overrun error bit */ -#elif defined(CONFIG_SH_RTS7751R2D) -# define SCSPTR1 0xFFE0001C /* 8 bit SCIF */ -# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ #elif defined(CONFIG_CPU_SH7750) || \ defined(CONFIG_CPU_SH7750R) || \ defined(CONFIG_CPU_SH7750S) || \ - defined(CONFIG_CPU_SH7091) || \ defined(CONFIG_CPU_SH7751) || \ defined(CONFIG_CPU_SH7751R) # define SCSPTR1 0xffe0001c /* 8 bit SCI */ @@ -67,24 +30,6 @@ struct uart_port { # define SCSCR_INIT(port) (((port)->type == PORT_SCI) ? \ 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */) -#elif defined(CONFIG_CPU_SH7760) -# define SCSPTR0 0xfe600024 /* 16 bit SCIF */ -# define SCSPTR1 0xfe610024 /* 16 bit SCIF */ -# define SCSPTR2 0xfe620024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SH7710) || defined(CONFIG_CPU_SH7712) -# define SCSPTR0 0xA4400000 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define PACR 0xa4050100 -# define PBCR 0xa4050102 -# define SCSCR_INIT(port) 0x3B -#elif defined(CONFIG_CPU_SH7343) -# define SCSPTR0 0xffe00010 /* 16 bit SCIF */ -# define SCSPTR1 0xffe10010 /* 16 bit SCIF */ -# define SCSPTR2 0xffe20010 /* 16 bit SCIF */ -# define SCSPTR3 0xffe30010 /* 16 bit SCIF */ -# define SCSCR_INIT(port) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0,CKE=1 */ #elif defined(CONFIG_CPU_SH7722) # define PADR 0xA4050120 # undef PSDR @@ -93,11 +38,6 @@ struct uart_port { # define PSCR 0xA405011E # define SCIF_ORER 0x0001 /* overrun error bit */ # define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SH7366) -# define SCPDR0 0xA405013E /* 16 bit SCIF0 PSDR */ -# define SCSPTR0 SCPDR0 -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ #elif defined(CONFIG_CPU_SH7723) # define SCSPTR0 0xa4050160 # define SCSPTR1 0xa405013e @@ -107,11 +47,6 @@ struct uart_port { # define SCSPTR5 0xa4050128 # define SCIF_ORER 0x0001 /* overrun error bit */ # define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SH7724) -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) ((port)->type == PORT_SCIFA ? \ - 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ - 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */) #elif defined(CONFIG_CPU_SH7734) # define SCSPTR0 0xFFE40020 # define SCSPTR1 0xFFE41020 @@ -121,26 +56,6 @@ struct uart_port { # define SCSPTR5 0xFFE45020 # define SCIF_ORER 0x0001 /* overrun error bit */ # define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SH4_202) -# define SCSPTR2 0xffe80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SH5_101) || defined(CONFIG_CPU_SH5_103) -# define SCIF_BASE_ADDR 0x01030000 -# define SCIF_ADDR_SH5 (PHYS_PERIPHERAL_BLOCK+SCIF_BASE_ADDR) -# define SCIF_PTR2_OFFS 0x0000020 -# define SCIF_LSR2_OFFS 0x0000024 -# define SCSPTR\ - ((port->mapbase)+SCIF_PTR2_OFFS) /* 16 bit SCIF */ -# define SCLSR2\ - ((port->mapbase)+SCIF_LSR2_OFFS) /* 16 bit SCIF */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0, TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_H83007) || defined(CONFIG_H83068) -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ -# define H8300_SCI_DR(ch) (*(volatile char *)(P1DR + h8300_sci_pins[ch].port)) -#elif defined(CONFIG_H8S2678) -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ -# define H8300_SCI_DR(ch) (*(volatile char *)(P1DR + h8300_sci_pins[ch].port)) #elif defined(CONFIG_CPU_SH7757) || \ defined(CONFIG_CPU_SH7752) || \ defined(CONFIG_CPU_SH7753) @@ -156,52 +71,15 @@ struct uart_port { # define SCSPTR2 0xffe10020 /* 16 bit SCIF/IRDA */ # define SCIF_ORER 0x0001 /* overrun error bit */ # define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SH7770) -# define SCSPTR0 0xff923020 /* 16 bit SCIF */ -# define SCSPTR1 0xff924020 /* 16 bit SCIF */ -# define SCSPTR2 0xff925020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x3c /* TIE=0,RIE=0,TE=1,RE=1,REIE=1,cke=2 */ #elif defined(CONFIG_CPU_SH7780) # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ # define SCSPTR1 0xffe10024 /* 16 bit SCIF */ # define SCIF_ORER 0x0001 /* Overrun error bit */ -#if defined(CONFIG_SH_SH2007) -/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=0 */ -# define SCSCR_INIT(port) 0x38 -#else /* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=1 */ # define SCSCR_INIT(port) 0x3a -#endif -#elif defined(CONFIG_CPU_SH7785) || \ - defined(CONFIG_CPU_SH7786) -# define SCSPTR0 0xffea0024 /* 16 bit SCIF */ -# define SCSPTR1 0xffeb0024 /* 16 bit SCIF */ -# define SCSPTR2 0xffec0024 /* 16 bit SCIF */ -# define SCSPTR3 0xffed0024 /* 16 bit SCIF */ -# define SCSPTR4 0xffee0024 /* 16 bit SCIF */ -# define SCSPTR5 0xffef0024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* Overrun error bit */ -# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SH7201) || \ - defined(CONFIG_CPU_SH7203) || \ - defined(CONFIG_CPU_SH7206) || \ - defined(CONFIG_CPU_SH7263) || \ - defined(CONFIG_CPU_SH7264) -# define SCSPTR0 0xfffe8020 /* 16 bit SCIF */ -# define SCSPTR1 0xfffe8820 /* 16 bit SCIF */ -# define SCSPTR2 0xfffe9020 /* 16 bit SCIF */ -# define SCSPTR3 0xfffe9820 /* 16 bit SCIF */ -# if defined(CONFIG_CPU_SH7201) -# define SCSPTR4 0xfffeA020 /* 16 bit SCIF */ -# define SCSPTR5 0xfffeA820 /* 16 bit SCIF */ -# define SCSPTR6 0xfffeB020 /* 16 bit SCIF */ -# define SCSPTR7 0xfffeB820 /* 16 bit SCIF */ -# endif -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SH7269) +#elif defined(CONFIG_RZA1) # define SCSPTR0 0xe8007020 /* 16 bit SCIF */ # define SCSPTR1 0xe8007820 /* 16 bit SCIF */ # define SCSPTR2 0xe8008020 /* 16 bit SCIF */ @@ -211,19 +89,7 @@ struct uart_port { # define SCSPTR6 0xe800a020 /* 16 bit SCIF */ # define SCSPTR7 0xe800a820 /* 16 bit SCIF */ # define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SH7619) -# define SCSPTR0 0xf8400020 /* 16 bit SCIF */ -# define SCSPTR1 0xf8410020 /* 16 bit SCIF */ -# define SCSPTR2 0xf8420020 /* 16 bit SCIF */ # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SHX3) -# define SCSPTR0 0xffc30020 /* 16 bit SCIF */ -# define SCSPTR1 0xffc40020 /* 16 bit SCIF */ -# define SCSPTR2 0xffc50020 /* 16 bit SCIF */ -# define SCSPTR3 0xffc60020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* Overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ #elif defined(CONFIG_RCAR_GEN2) || defined(CONFIG_RCAR_GEN3) || \ defined(CONFIG_R7S72100) # if defined(CONFIG_SCIF_A) @@ -243,7 +109,6 @@ struct uart_port { #define SCI_CTRL_FLAGS_TE 0x20 /* all */ #define SCI_CTRL_FLAGS_RE 0x10 /* all */ #if defined(CONFIG_CPU_SH7750) || \ - defined(CONFIG_CPU_SH7091) || \ defined(CONFIG_CPU_SH7750R) || \ defined(CONFIG_CPU_SH7722) || \ defined(CONFIG_CPU_SH7734) || \ @@ -251,13 +116,8 @@ struct uart_port { defined(CONFIG_CPU_SH7751) || \ defined(CONFIG_CPU_SH7751R) || \ defined(CONFIG_CPU_SH7763) || \ - defined(CONFIG_CPU_SH7780) || \ - defined(CONFIG_CPU_SH7785) || \ - defined(CONFIG_CPU_SH7786) || \ - defined(CONFIG_CPU_SHX3) + defined(CONFIG_CPU_SH7780) #define SCI_CTRL_FLAGS_REIE 0x08 /* 7750 SCIF */ -#elif defined(CONFIG_CPU_SH7724) -#define SCI_CTRL_FLAGS_REIE ((port)->type == PORT_SCIFA ? 0 : 8) #else #define SCI_CTRL_FLAGS_REIE 0 #endif @@ -288,12 +148,7 @@ struct uart_port { #define SCIF_RDF 0x0002 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ #define SCIF_DR 0x0001 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#if defined(CONFIG_CPU_SH7705) || \ - defined(CONFIG_CPU_SH7720) || \ - defined(CONFIG_CPU_SH7721) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) || \ +#if defined(CONFIG_CPU_SH7721) || \ defined(CONFIG_SH73A0) || \ defined(CONFIG_R8A7740) # define SCIF_ORER 0x0200 @@ -341,12 +196,7 @@ struct uart_port { #define SCxSR_ORER(port)\ (((port)->type == PORT_SCI) ? SCI_ORER : SCIF_ORER) -#if defined(CONFIG_CPU_SH7705) || \ - defined(CONFIG_CPU_SH7720) || \ - defined(CONFIG_CPU_SH7721) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) || \ +#if defined(CONFIG_CPU_SH7721) || \ defined(CONFIG_SH73A0) || \ defined(CONFIG_R8A7740) # define SCxSR_RDxF_CLEAR(port) (sci_in(port, SCxSR) & 0xfffc) @@ -410,16 +260,6 @@ static inline void sci_##name##_out(struct uart_port *port,\ }\ } -#ifdef CONFIG_H8300 -/* h8300 don't have SCIF */ -#define CPU_SCIF_FNS(name) \ - static inline unsigned int sci_##name##_in(struct uart_port *port) {\ - return 0;\ - }\ - static inline void sci_##name##_out(struct uart_port *port,\ - unsigned int value) {\ - } -#else #define CPU_SCIF_FNS(name, scif_offset, scif_size) \ static inline unsigned int sci_##name##_in(struct uart_port *port) {\ SCI_IN(scif_size, scif_offset);\ @@ -428,7 +268,6 @@ static inline void sci_##name##_out(struct uart_port *port,\ unsigned int value) {\ SCI_OUT(scif_size, scif_offset, value);\ } -#endif #define CPU_SCI_FNS(name, sci_offset, sci_size)\ static inline unsigned int sci_##name##_in(struct uart_port *port) {\ @@ -439,33 +278,13 @@ static inline void sci_##name##_out(struct uart_port *port,\ SCI_OUT(sci_size, sci_offset, value);\ } -#if defined(CONFIG_CPU_SH3) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) || \ - defined(CONFIG_SH73A0) || \ +#if defined(CONFIG_SH73A0) || \ defined(CONFIG_R8A7740) -#if defined(CONFIG_CPU_SH7710) || defined(CONFIG_CPU_SH7712) -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size,\ - sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, \ - sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ - CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size,\ - sh4_scif_offset, sh4_scif_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size,\ - sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) -#elif defined(CONFIG_CPU_SH7705) || \ - defined(CONFIG_CPU_SH7720) || \ - defined(CONFIG_CPU_SH7721) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ +#if defined(CONFIG_CPU_SH7721) || \ defined(CONFIG_SH73A0) #define SCIF_FNS(name, scif_offset, scif_size) \ CPU_SCIF_FNS(name, scif_offset, scif_size) -#elif defined(CONFIG_ARCH_SH7372) || \ - defined(CONFIG_R8A7740) +#elif defined(CONFIG_R8A7740) #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size,\ sh4_scifb_offset, sh4_scifb_size) \ CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size,\ @@ -484,17 +303,7 @@ static inline void sci_##name##_out(struct uart_port *port,\ sh4_scif_offset, sh4_scif_size) \ CPU_SCIF_FNS(name, sh3_scif_offset, sh3_scif_size) #endif -#elif defined(__H8300H__) || defined(__H8300S__) -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size,\ - sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size,\ - sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ - CPU_SCI_FNS(name, h8_sci_offset, h8_sci_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size,\ - sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name) -#elif defined(CONFIG_CPU_SH7723) || defined(CONFIG_CPU_SH7724) +#elif defined(CONFIG_CPU_SH7723) #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size,\ sh4_scif_offset, sh4_scif_size) \ CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size,\ @@ -514,11 +323,7 @@ static inline void sci_##name##_out(struct uart_port *port,\ CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) #endif -#if defined(CONFIG_CPU_SH7705) || \ - defined(CONFIG_CPU_SH7720) || \ - defined(CONFIG_CPU_SH7721) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ +#if defined(CONFIG_CPU_SH7721) || \ defined(CONFIG_SH73A0) SCIF_FNS(SCSMR, 0x00, 16) @@ -533,8 +338,7 @@ SCIF_FNS(SCxTDR, 0x20, 8) SCIF_FNS(SCxRDR, 0x24, 8) SCIF_FNS(SCLSR, 0x00, 0) SCIF_FNS(DL, 0x00, 0) /* dummy */ -#elif defined(CONFIG_ARCH_SH7372) || \ - defined(CONFIG_R8A7740) +#elif defined(CONFIG_R8A7740) SCIF_FNS(SCSMR, 0x00, 16) SCIF_FNS(SCBRR, 0x04, 8) SCIF_FNS(SCSCR, 0x08, 16) @@ -549,8 +353,7 @@ SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8) SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8) SCIF_FNS(SCLSR, 0x00, 0) SCIF_FNS(DL, 0x00, 0) /* dummy */ -#elif defined(CONFIG_CPU_SH7723) ||\ - defined(CONFIG_CPU_SH7724) +#elif defined(CONFIG_CPU_SH7723) SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16) SCIx_FNS(SCBRR, 0x04, 8, 0x04, 8) SCIx_FNS(SCSCR, 0x08, 16, 0x08, 16) @@ -592,10 +395,7 @@ SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8, 0x03, 8) SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16, 0x04, 8) SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8, 0x05, 8) SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16) -#if defined(CONFIG_CPU_SH7760) || \ - defined(CONFIG_CPU_SH7780) || \ - defined(CONFIG_CPU_SH7785) || \ - defined(CONFIG_CPU_SH7786) +#if defined(CONFIG_CPU_SH7780) SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) @@ -624,76 +424,17 @@ SCIF_FNS(DL, 0, 0, 0x0, 0) /* dummy */ #define sci_in(port, reg) sci_##reg##_in(port) #define sci_out(port, reg, value) sci_##reg##_out(port, value) -/* H8/300 series SCI pins assignment */ -#if defined(__H8300H__) || defined(__H8300S__) -static const struct __attribute__((packed)) { - int port; /* GPIO port no */ - unsigned short rx, tx; /* GPIO bit no */ -} h8300_sci_pins[] = { -#if defined(CONFIG_H83007) || defined(CONFIG_H83068) - { /* SCI0 */ - .port = H8300_GPIO_P9, - .rx = H8300_GPIO_B2, - .tx = H8300_GPIO_B0, - }, - { /* SCI1 */ - .port = H8300_GPIO_P9, - .rx = H8300_GPIO_B3, - .tx = H8300_GPIO_B1, - }, - { /* SCI2 */ - .port = H8300_GPIO_PB, - .rx = H8300_GPIO_B7, - .tx = H8300_GPIO_B6, - } -#elif defined(CONFIG_H8S2678) - { /* SCI0 */ - .port = H8300_GPIO_P3, - .rx = H8300_GPIO_B2, - .tx = H8300_GPIO_B0, - }, - { /* SCI1 */ - .port = H8300_GPIO_P3, - .rx = H8300_GPIO_B3, - .tx = H8300_GPIO_B1, - }, - { /* SCI2 */ - .port = H8300_GPIO_P5, - .rx = H8300_GPIO_B1, - .tx = H8300_GPIO_B0, - } -#endif -}; -#endif - -#if defined(CONFIG_CPU_SH7706) || \ - defined(CONFIG_CPU_SH7707) || \ - defined(CONFIG_CPU_SH7708) || \ - defined(CONFIG_CPU_SH7709) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xfffffe80) - return __raw_readb(SCPDR)&0x01 ? 1 : 0; /* SCI */ - return 1; -} -#elif defined(CONFIG_CPU_SH7750) || \ +#if defined(CONFIG_CPU_SH7750) || \ defined(CONFIG_CPU_SH7751) || \ defined(CONFIG_CPU_SH7751R) || \ defined(CONFIG_CPU_SH7750R) || \ - defined(CONFIG_CPU_SH7750S) || \ - defined(CONFIG_CPU_SH7091) + defined(CONFIG_CPU_SH7750S) static inline int sci_rxd_in(struct uart_port *port) { if (port->mapbase == 0xffe00000) return __raw_readb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */ return 1; } -#elif defined(__H8300H__) || defined(__H8300S__) -static inline int sci_rxd_in(struct uart_port *port) -{ - int ch = (port->mapbase - SMR0) >> 3; - return (H8300_SCI_DR(ch) & h8300_sci_pins[ch].rx) ? 1 : 0; -} #else /* default case for non-SCI processors */ static inline int sci_rxd_in(struct uart_port *port) { @@ -733,22 +474,13 @@ static inline int sci_rxd_in(struct uart_port *port) * -- Mitch Davis - 15 Jul 2000 */ -#if (defined(CONFIG_CPU_SH7780) || \ - defined(CONFIG_CPU_SH7785) || \ - defined(CONFIG_CPU_SH7786)) && \ - !defined(CONFIG_SH_SH2007) +#if defined(CONFIG_CPU_SH7780) #define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1) -#elif defined(CONFIG_CPU_SH7705) || \ - defined(CONFIG_CPU_SH7720) || \ - defined(CONFIG_CPU_SH7721) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) || \ +#elif defined(CONFIG_CPU_SH7721) || \ defined(CONFIG_SH73A0) || \ defined(CONFIG_R8A7740) #define SCBRR_VALUE(bps, clk) (((clk*2)+16*bps)/(32*bps)-1) -#elif defined(CONFIG_CPU_SH7723) ||\ - defined(CONFIG_CPU_SH7724) +#elif defined(CONFIG_CPU_SH7723) static inline int scbrr_calc(struct uart_port *port, int bps, int clk) { if (port->type == PORT_SCIF) @@ -757,8 +489,6 @@ static inline int scbrr_calc(struct uart_port *port, int bps, int clk) return ((clk*2)+16*bps)/(16*bps)-1; } #define SCBRR_VALUE(bps, clk) scbrr_calc(port, bps, clk) -#elif defined(__H8300H__) || defined(__H8300S__) -#define SCBRR_VALUE(bps, clk) (((clk*1000/32)/bps)-1) #elif defined(CONFIG_RCAR_GEN2) #define DL_VALUE(bps, clk) (clk / bps / 16) /* External Clock */ #if defined(CONFIG_SCIF_A) diff --git a/drivers/serial/serial_sifive.c b/drivers/serial/serial_sifive.c index 537bc7a975..fdfef69aaa 100644 --- a/drivers/serial/serial_sifive.c +++ b/drivers/serial/serial_sifive.c @@ -3,8 +3,8 @@ * Copyright (C) 2018 Anup Patel <anup@brainfault.org> */ -#include <clk.h> #include <common.h> +#include <clk.h> #include <debug_uart.h> #include <dm.h> #include <errno.h> diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c index e31c87b9ac..cca8b707ac 100644 --- a/drivers/serial/serial_stm32.c +++ b/drivers/serial/serial_stm32.c @@ -269,7 +269,6 @@ static inline void _debug_uart_init(void) _stm32_serial_setbrg(base, uart_info, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); - printf("DEBUG done\n"); } static inline void _debug_uart_putc(int c) @@ -278,7 +277,7 @@ static inline void _debug_uart_putc(int c) struct stm32_uart_info *uart_info = _debug_uart_info(); while (_stm32_serial_putc(base, uart_info, c) == -EAGAIN) - WATCHDOG_RESET(); + ; } DEBUG_UART_FUNCS diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig index 6e9dcefcb9..4ebc719be2 100644 --- a/drivers/sound/Kconfig +++ b/drivers/sound/Kconfig @@ -71,6 +71,15 @@ config SOUND_IVYBRIDGE sometimes called Azalia. The audio codec is detected using a semi-automatic mechanism. +config I2S_TEGRA + bool "Enable I2S support for Nvidia Tegra SoCs" + depends on I2S + select TEGRA124_DMA + help + Nvidia Tegra SoCs support several I2S interfaces for sending audio + data to an audio codec. This option enables support for this, + using one of the available audio codec drivers. + config SOUND_MAX98088 bool "Support Maxim max98088 audio codec" depends on I2S diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index e155041ff5..73ed7fe53c 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_I2S_SAMSUNG) += samsung-i2s.o obj-$(CONFIG_SOUND_SANDBOX) += sandbox.o obj-$(CONFIG_I2S_ROCKCHIP) += rockchip_i2s.o rockchip_sound.o obj-$(CONFIG_I2S_SAMSUNG) += samsung_sound.o +obj-$(CONFIG_I2S_TEGRA) += tegra_ahub.o tegra_i2s.o tegra_sound.o obj-$(CONFIG_SOUND_WM8994) += wm8994.o obj-$(CONFIG_SOUND_MAX98088) += max98088.o maxim_codec.o obj-$(CONFIG_SOUND_MAX98090) += max98090.o maxim_codec.o diff --git a/drivers/sound/rockchip_sound.c b/drivers/sound/rockchip_sound.c index e7fb9fb164..a092dbc445 100644 --- a/drivers/sound/rockchip_sound.c +++ b/drivers/sound/rockchip_sound.c @@ -13,7 +13,7 @@ #include <i2s.h> #include <misc.h> #include <sound.h> -#include <asm/arch/periph.h> +#include <asm/arch-rockchip/periph.h> #include <dm/pinctrl.h> static int rockchip_sound_setup(struct udevice *dev) diff --git a/drivers/sound/tegra_ahub.c b/drivers/sound/tegra_ahub.c new file mode 100644 index 0000000000..c71fce9bb1 --- /dev/null +++ b/drivers/sound/tegra_ahub.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0+159 +/* + * Take from dc tegra_ahub.c + * + * Copyright 2018 Google LLC + */ + +#define LOG_CATEGORY UCLASS_MISC + +#include <common.h> +#include <dm.h> +#include <i2s.h> +#include <misc.h> +#include <asm/io.h> +#include <asm/arch-tegra/tegra_ahub.h> +#include <asm/arch-tegra/tegra_i2s.h> +#include "tegra_i2s_priv.h" + +struct tegra_ahub_priv { + struct apbif_regs *apbif_regs; + struct xbar_regs *xbar_regs; + u32 full_mask; + int capacity_words; /* FIFO capacity in words */ + + /* + * This is unset intially, but is set by tegra_ahub_ioctl() called + * from the misc_ioctl() in tegra_sound_probe() + */ + struct udevice *i2s; + struct udevice *dma; +}; + +static int tegra_ahub_xbar_enable_i2s(struct xbar_regs *regs, int i2s_id) +{ + /* + * Enables I2S as the receiver of APBIF by writing APBIF_TX0 (0x01) to + * the rx0 register + */ + switch (i2s_id) { + case 0: + writel(1, ®s->i2s0_rx0); + break; + case 1: + writel(1, ®s->i2s1_rx0); + break; + case 2: + writel(1, ®s->i2s2_rx0); + break; + case 3: + writel(1, ®s->i2s3_rx0); + break; + case 4: + writel(1, ®s->i2s4_rx0); + break; + default: + log_err("Invalid I2S component id: %d\n", i2s_id); + return -EINVAL; + } + return 0; +} + +static int tegra_ahub_apbif_is_full(struct udevice *dev) +{ + struct tegra_ahub_priv *priv = dev_get_priv(dev); + + return readl(&priv->apbif_regs->apbdma_live_stat) & priv->full_mask; +} + +/** + * tegra_ahub_wait_for_space() - Wait for space in the FIFO + * + * @return 0 if OK, -ETIMEDOUT if no space was available in time + */ +static int tegra_ahub_wait_for_space(struct udevice *dev) +{ + int i = 100000; + ulong start; + + /* Busy-wait initially, since this should take almost no time */ + while (i--) { + if (!tegra_ahub_apbif_is_full(dev)) + return 0; + } + + /* Failed, so do a slower loop for 100ms */ + start = get_timer(0); + while (tegra_ahub_apbif_is_full(dev)) { + if (get_timer(start) > 100) + return -ETIMEDOUT; + } + + return 0; +} + +static int tegra_ahub_apbif_send(struct udevice *dev, int offset, + const void *buf, int len) +{ + struct tegra_ahub_priv *priv = dev_get_priv(dev); + const u32 *data = (const u32 *)buf; + ssize_t written = 0; + + if (len % sizeof(*data)) { + log_err("Data size (%zd) must be aligned to %zd.\n", len, + sizeof(*data)); + return -EFAULT; + } + while (written < len) { + int ret = tegra_ahub_wait_for_space(dev); + + if (ret) + return ret; + + writel(*data++, &priv->apbif_regs->channel0_txfifo); + written += sizeof(*data); + } + + return written; +} + +static void tegra_ahub_apbif_set_cif(struct udevice *dev, u32 value) +{ + struct tegra_ahub_priv *priv = dev_get_priv(dev); + + writel(value, &priv->apbif_regs->channel0_cif_tx0_ctrl); +} + +static void tegra_ahub_apbif_enable_channel0(struct udevice *dev, + int fifo_threshold) +{ + struct tegra_ahub_priv *priv = dev_get_priv(dev); + + u32 ctrl = TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_EN | + TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_16 | + TEGRA_AHUB_CHANNEL_CTRL_TX_EN; + + fifo_threshold--; /* fifo_threshold starts from 1 */ + ctrl |= (fifo_threshold << TEGRA_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT); + writel(ctrl, &priv->apbif_regs->channel0_ctrl); +} + +static u32 tegra_ahub_get_cif(bool is_receive, uint channels, + uint bits_per_sample, uint fifo_threshold) +{ + uint audio_bits = (bits_per_sample >> 2) - 1; + u32 val; + + channels--; /* Channels in CIF starts from 1 */ + fifo_threshold--; /* FIFO threshold starts from 1 */ + /* Assume input and output are always using same channel / bits */ + val = channels << TEGRA_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT | + channels << TEGRA_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT | + audio_bits << TEGRA_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT | + audio_bits << TEGRA_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT | + fifo_threshold << TEGRA_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT | + (is_receive ? TEGRA_AUDIOCIF_DIRECTION_RX << + TEGRA_AUDIOCIF_CTRL_DIRECTION_SHIFT : 0); + + return val; +} + +static int tegra_ahub_enable(struct udevice *dev) +{ + struct tegra_ahub_priv *priv = dev_get_priv(dev); + struct i2s_uc_priv *uc_priv = dev_get_uclass_priv(priv->i2s); + u32 cif_ctrl = 0; + int ret; + + /* We use APBIF channel0 as a sender */ + priv->full_mask = TEGRA_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_FULL; + priv->capacity_words = 8; + + /* + * FIFO is inactive until (fifo_threshold) of words are sent. For + * better performance, we want to set it to half of capacity. + */ + u32 fifo_threshold = priv->capacity_words / 2; + + /* + * Setup audio client interface (ACIF): APBIF (channel0) as sender and + * I2S as receiver + */ + cif_ctrl = tegra_ahub_get_cif(true, uc_priv->channels, + uc_priv->bitspersample, fifo_threshold); + tegra_i2s_set_cif_tx_ctrl(priv->i2s, cif_ctrl); + + cif_ctrl = tegra_ahub_get_cif(false, uc_priv->channels, + uc_priv->bitspersample, fifo_threshold); + tegra_ahub_apbif_set_cif(dev, cif_ctrl); + tegra_ahub_apbif_enable_channel0(dev, fifo_threshold); + + ret = tegra_ahub_xbar_enable_i2s(priv->xbar_regs, uc_priv->id); + if (ret) + return ret; + log_debug("ahub: channels=%d, bitspersample=%d, cif_ctrl=%x, fifo_threshold=%d, id=%d\n", + uc_priv->channels, uc_priv->bitspersample, cif_ctrl, + fifo_threshold, uc_priv->id); + + return 0; +} + +static int tegra_ahub_ioctl(struct udevice *dev, unsigned long request, + void *buf) +{ + struct tegra_ahub_priv *priv = dev_get_priv(dev); + + if (request != AHUB_MISCOP_SET_I2S) + return -ENOSYS; + + priv->i2s = *(struct udevice **)buf; + log_debug("i2s set to '%s'\n", priv->i2s->name); + + return tegra_ahub_enable(dev); +} + +static int tegra_ahub_probe(struct udevice *dev) +{ + struct tegra_ahub_priv *priv = dev_get_priv(dev); + ulong addr; + + addr = dev_read_addr_index(dev, 0); + if (addr == FDT_ADDR_T_NONE) { + log_debug("Invalid apbif address\n"); + return -EINVAL; + } + priv->apbif_regs = (struct apbif_regs *)addr; + + addr = dev_read_addr_index(dev, 1); + if (addr == FDT_ADDR_T_NONE) { + log_debug("Invalid xbar address\n"); + return -EINVAL; + } + priv->xbar_regs = (struct xbar_regs *)addr; + log_debug("ahub apbif_regs=%p, xbar_regs=%p\n", priv->apbif_regs, + priv->xbar_regs); + + return 0; +} + +static struct misc_ops tegra_ahub_ops = { + .write = tegra_ahub_apbif_send, + .ioctl = tegra_ahub_ioctl, +}; + +static const struct udevice_id tegra_ahub_ids[] = { + { .compatible = "nvidia,tegra124-ahub" }, + { } +}; + +U_BOOT_DRIVER(tegra_ahub) = { + .name = "tegra_ahub", + .id = UCLASS_MISC, + .of_match = tegra_ahub_ids, + .ops = &tegra_ahub_ops, + .probe = tegra_ahub_probe, + .priv_auto_alloc_size = sizeof(struct tegra_ahub_priv), +}; diff --git a/drivers/sound/tegra_i2s.c b/drivers/sound/tegra_i2s.c new file mode 100644 index 0000000000..8022dbba64 --- /dev/null +++ b/drivers/sound/tegra_i2s.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ +#define LOG_CATEGORY UCLASS_I2S +#define LOG_DEBUG + +#include <common.h> +#include <dm.h> +#include <i2s.h> +#include <misc.h> +#include <sound.h> +#include <asm/io.h> +#include <asm/arch-tegra/tegra_i2s.h> +#include "tegra_i2s_priv.h" + +int tegra_i2s_set_cif_tx_ctrl(struct udevice *dev, u32 value) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + struct i2s_ctlr *regs = (struct i2s_ctlr *)priv->base_address; + + writel(value, ®s->cif_tx_ctrl); + + return 0; +} + +static void tegra_i2s_transmit_enable(struct i2s_ctlr *regs, int on) +{ + clrsetbits_le32(®s->ctrl, I2S_CTRL_XFER_EN_TX, + on ? I2S_CTRL_XFER_EN_TX : 0); +} + +static int i2s_tx_init(struct i2s_uc_priv *pi2s_tx) +{ + struct i2s_ctlr *regs = (struct i2s_ctlr *)pi2s_tx->base_address; + u32 audio_bits = (pi2s_tx->bitspersample >> 2) - 1; + u32 ctrl = readl(®s->ctrl); + + /* Set format to LRCK / Left Low */ + ctrl &= ~(I2S_CTRL_FRAME_FORMAT_MASK | I2S_CTRL_LRCK_MASK); + ctrl |= I2S_CTRL_FRAME_FORMAT_LRCK; + ctrl |= I2S_CTRL_LRCK_L_LOW; + + /* Disable all transmission until we are ready to transfer */ + ctrl &= ~(I2S_CTRL_XFER_EN_TX | I2S_CTRL_XFER_EN_RX); + + /* Serve as master */ + ctrl |= I2S_CTRL_MASTER_ENABLE; + + /* Configure audio bits size */ + ctrl &= ~I2S_CTRL_BIT_SIZE_MASK; + ctrl |= audio_bits << I2S_CTRL_BIT_SIZE_SHIFT; + writel(ctrl, ®s->ctrl); + + /* Timing in LRCK mode: */ + writel(pi2s_tx->bitspersample, ®s->timing); + + /* I2S mode has [TX/RX]_DATA_OFFSET both set to 1 */ + writel(((1 << I2S_OFFSET_RX_DATA_OFFSET_SHIFT) | + (1 << I2S_OFFSET_TX_DATA_OFFSET_SHIFT)), ®s->offset); + + /* FSYNC_WIDTH = 2 clocks wide, TOTAL_SLOTS = 2 slots per fsync */ + writel((2 - 1) << I2S_CH_CTRL_FSYNC_WIDTH_SHIFT, ®s->ch_ctrl); + + return 0; +} + +static int tegra_i2s_tx_data(struct udevice *dev, void *data, uint data_size) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + struct i2s_ctlr *regs = (struct i2s_ctlr *)priv->base_address; + int ret; + + tegra_i2s_transmit_enable(regs, 1); + ret = misc_write(dev_get_parent(dev), 0, data, data_size); + tegra_i2s_transmit_enable(regs, 0); + if (ret < 0) + return ret; + else if (ret < data_size) + return -EIO; + + return 0; +} + +static int tegra_i2s_probe(struct udevice *dev) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + ulong base; + + base = dev_read_addr(dev); + if (base == FDT_ADDR_T_NONE) { + debug("%s: Missing i2s base\n", __func__); + return -EINVAL; + } + priv->base_address = base; + priv->id = 1; + priv->audio_pll_clk = 4800000; + priv->samplingrate = 48000; + priv->bitspersample = 16; + priv->channels = 2; + priv->rfs = 256; + priv->bfs = 32; + + return i2s_tx_init(priv); +} + +static const struct i2s_ops tegra_i2s_ops = { + .tx_data = tegra_i2s_tx_data, +}; + +static const struct udevice_id tegra_i2s_ids[] = { + { .compatible = "nvidia,tegra124-i2s" }, + { } +}; + +U_BOOT_DRIVER(tegra_i2s) = { + .name = "tegra_i2s", + .id = UCLASS_I2S, + .of_match = tegra_i2s_ids, + .probe = tegra_i2s_probe, + .ops = &tegra_i2s_ops, +}; diff --git a/drivers/sound/tegra_i2s_priv.h b/drivers/sound/tegra_i2s_priv.h new file mode 100644 index 0000000000..7cd3fc808c --- /dev/null +++ b/drivers/sound/tegra_i2s_priv.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#ifndef __TEGRA_I2S_PRIV_H +#define __TEGRA_I2S_PRIV_H + +enum { + /* Set i2s device (in buf) */ + AHUB_MISCOP_SET_I2S, +}; + +/* + * tegra_i2s_set_cif_tx_ctrl() - Set the I2C port to send to + * + * The CIF is not really part of I2S -- it's for Audio Hub to control + * the interface between I2S and Audio Hub. However since it's put in + * the I2S registers domain instead of the Audio Hub, we need to export + * this as a function. + * + * @dev: I2S device + * @value: Value to write to CIF_TX_CTRL register + * @return 0 + */ +int tegra_i2s_set_cif_tx_ctrl(struct udevice *dev, u32 value); + +#endif diff --git a/drivers/sound/tegra_sound.c b/drivers/sound/tegra_sound.c new file mode 100644 index 0000000000..7c2ed53f5a --- /dev/null +++ b/drivers/sound/tegra_sound.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google, LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#define LOG_CATEGORY UCLASS_I2S + +#include <common.h> +#include <audio_codec.h> +#include <dm.h> +#include <i2s.h> +#include <misc.h> +#include <sound.h> +#include <asm/gpio.h> +#include "tegra_i2s_priv.h" + +static int tegra_sound_setup(struct udevice *dev) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct i2s_uc_priv *i2c_priv = dev_get_uclass_priv(uc_priv->i2s); + int ret; + + if (uc_priv->setup_done) + return -EALREADY; + ret = audio_codec_set_params(uc_priv->codec, i2c_priv->id, + i2c_priv->samplingrate, + i2c_priv->samplingrate * i2c_priv->rfs, + i2c_priv->bitspersample, + i2c_priv->channels); + if (ret) + return ret; + uc_priv->setup_done = true; + + return 0; +} + +static int tegra_sound_play(struct udevice *dev, void *data, uint data_size) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + + return i2s_tx_data(uc_priv->i2s, data, data_size); +} + +static int tegra_sound_probe(struct udevice *dev) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct gpio_desc en_gpio; + struct udevice *ahub; + int ret; + + ret = gpio_request_by_name(dev, "codec-enable-gpio", 0, &en_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + + ret = uclass_get_device_by_phandle(UCLASS_AUDIO_CODEC, dev, + "nvidia,audio-codec", + &uc_priv->codec); + if (ret) { + log_debug("Failed to probe audio codec\n"); + return ret; + } + ret = uclass_get_device_by_phandle(UCLASS_I2S, dev, + "nvidia,i2s-controller", + &uc_priv->i2s); + if (ret) { + log_debug("Cannot find i2s: %d\n", ret); + return ret; + } + + /* Set up the audio hub, telling it the currect i2s to use */ + ahub = dev_get_parent(uc_priv->i2s); + ret = misc_ioctl(ahub, AHUB_MISCOP_SET_I2S, &uc_priv->i2s); + if (ret) { + log_debug("Cannot set i2c: %d\n", ret); + return ret; + } + + log_debug("Probed sound '%s' with codec '%s' and i2s '%s'\n", dev->name, + uc_priv->codec->name, uc_priv->i2s->name); + + return 0; +} + +static const struct sound_ops tegra_sound_ops = { + .setup = tegra_sound_setup, + .play = tegra_sound_play, +}; + +static const struct udevice_id tegra_sound_ids[] = { + { .compatible = "nvidia,tegra-audio-max98090-nyan-big" }, + { } +}; + +U_BOOT_DRIVER(tegra_sound) = { + .name = "tegra_sound", + .id = UCLASS_SOUND, + .of_match = tegra_sound_ids, + .probe = tegra_sound_probe, + .ops = &tegra_sound_ops, +}; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index fb794adae7..7044da35d6 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -87,6 +87,12 @@ config CADENCE_QSPI used to access the SPI NOR flash on platforms embedding this Cadence IP core. +config CF_SPI + bool "ColdFire SPI driver" + help + Enable the ColdFire SPI driver. This driver can be used on + some m68k SoCs. + config DESIGNWARE_SPI bool "Designware SPI driver" help @@ -133,7 +139,7 @@ config MPC8XX_SPI config MT7621_SPI bool "MediaTek MT7621 SPI driver" - depends on ARCH_MT7620 + depends on SOC_MT7628 help Enable the MT7621 SPI driver. This driver can be used to access the SPI NOR flash on platforms embedding this Ralink / MediaTek @@ -173,7 +179,7 @@ config PL022_SPI config RENESAS_RPC_SPI bool "Renesas RPC SPI driver" - depends on RCAR_GEN3 + depends on RCAR_GEN3 || RZA1 imply SPI_FLASH_BAR help Enable the Renesas RPC SPI driver, used to access SPI NOR flash @@ -222,7 +228,7 @@ config SPI_SUNXI config STM32_QSPI bool "STM32F7 QSPI driver" - depends on STM32F7 || ARCH_STM32MP + depends on STM32F4 || STM32F7 || ARCH_STM32MP help Enable the STM32F7 Quad-SPI (QSPI) driver. This driver can be used to access the SPI NOR flash chips on platforms embedding diff --git a/drivers/spi/atcspi200_spi.c b/drivers/spi/atcspi200_spi.c index af96c6d21e..e0cc323444 100644 --- a/drivers/spi/atcspi200_spi.c +++ b/drivers/spi/atcspi200_spi.c @@ -6,8 +6,8 @@ * Author: Rick Chen (rick@andestech.com) */ -#include <clk.h> #include <common.h> +#include <clk.h> #include <malloc.h> #include <spi.h> #include <asm/io.h> diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index 41c87004d8..e2e54cd277 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -18,8 +18,6 @@ #define CQSPI_INDIRECT_READ 2 #define CQSPI_INDIRECT_WRITE 3 -DECLARE_GLOBAL_DATA_PTR; - static int cadence_spi_write_speed(struct udevice *bus, uint hz) { struct cadence_spi_platdata *plat = bus->platdata; @@ -295,36 +293,37 @@ static int cadence_spi_xfer(struct udevice *dev, unsigned int bitlen, static int cadence_spi_ofdata_to_platdata(struct udevice *bus) { struct cadence_spi_platdata *plat = bus->platdata; - const void *blob = gd->fdt_blob; - int node = dev_of_offset(bus); - int subnode; + ofnode subnode; plat->regbase = (void *)devfdt_get_addr_index(bus, 0); plat->ahbbase = (void *)devfdt_get_addr_index(bus, 1); - plat->is_decoded_cs = fdtdec_get_bool(blob, node, "cdns,is-decoded-cs"); - plat->fifo_depth = fdtdec_get_uint(blob, node, "cdns,fifo-depth", 128); - plat->fifo_width = fdtdec_get_uint(blob, node, "cdns,fifo-width", 4); - plat->trigger_address = fdtdec_get_uint(blob, node, - "cdns,trigger-address", 0); + plat->is_decoded_cs = dev_read_bool(bus, "cdns,is-decoded-cs"); + plat->fifo_depth = dev_read_u32_default(bus, "cdns,fifo-depth", 128); + plat->fifo_width = dev_read_u32_default(bus, "cdns,fifo-width", 4); + plat->trigger_address = dev_read_u32_default(bus, + "cdns,trigger-address", + 0); /* All other paramters are embedded in the child node */ - subnode = fdt_first_subnode(blob, node); - if (subnode < 0) { + subnode = dev_read_first_subnode(bus); + if (!ofnode_valid(subnode)) { printf("Error: subnode with SPI flash config missing!\n"); return -ENODEV; } /* Use 500 KHz as a suitable default */ - plat->max_hz = fdtdec_get_uint(blob, subnode, "spi-max-frequency", - 500000); + plat->max_hz = ofnode_read_u32_default(subnode, "spi-max-frequency", + 500000); /* Read other parameters from DT */ - plat->page_size = fdtdec_get_uint(blob, subnode, "page-size", 256); - plat->block_size = fdtdec_get_uint(blob, subnode, "block-size", 16); - plat->tshsl_ns = fdtdec_get_uint(blob, subnode, "cdns,tshsl-ns", 200); - plat->tsd2d_ns = fdtdec_get_uint(blob, subnode, "cdns,tsd2d-ns", 255); - plat->tchsh_ns = fdtdec_get_uint(blob, subnode, "cdns,tchsh-ns", 20); - plat->tslch_ns = fdtdec_get_uint(blob, subnode, "cdns,tslch-ns", 20); + plat->page_size = ofnode_read_u32_default(subnode, "page-size", 256); + plat->block_size = ofnode_read_u32_default(subnode, "block-size", 16); + plat->tshsl_ns = ofnode_read_u32_default(subnode, "cdns,tshsl-ns", + 200); + plat->tsd2d_ns = ofnode_read_u32_default(subnode, "cdns,tsd2d-ns", + 255); + plat->tchsh_ns = ofnode_read_u32_default(subnode, "cdns,tchsh-ns", 20); + plat->tslch_ns = ofnode_read_u32_default(subnode, "cdns,tslch-ns", 20); debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n", __func__, plat->regbase, plat->ahbbase, plat->max_hz, diff --git a/drivers/spi/cf_spi.c b/drivers/spi/cf_spi.c index 522631cbbf..923ff6f311 100644 --- a/drivers/spi/cf_spi.c +++ b/drivers/spi/cf_spi.c @@ -6,23 +6,28 @@ * * Copyright (C) 2004-2009 Freescale Semiconductor, Inc. * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * + * Support for DM and DT, non-DM code removed. + * Copyright (C) 2018 Angelo Dureghello <angelo@sysam.it> + * + * TODO: fsl_dspi.c should work as a driver for the DSPI module. */ #include <common.h> +#include <dm.h> +#include <dm/platform_data/spi_coldfire.h> #include <spi.h> #include <malloc.h> -#include <asm/immap.h> +#include <asm/coldfire/dspi.h> +#include <asm/io.h> -struct cf_spi_slave { - struct spi_slave slave; +struct coldfire_spi_priv { + struct dspi *regs; uint baudrate; + int mode; int charbit; }; -extern void cfspi_port_conf(void); -extern int cfspi_claim_bus(uint bus, uint cs); -extern void cfspi_release_bus(uint bus, uint cs); - DECLARE_GLOBAL_DATA_PTR; #ifndef CONFIG_SPI_IDLE_VAL @@ -33,163 +38,193 @@ DECLARE_GLOBAL_DATA_PTR; #endif #endif -#if defined(CONFIG_CF_DSPI) -/* DSPI specific mode */ -#define SPI_MODE_MOD 0x00200000 -#define SPI_DBLRATE 0x00100000 - -static inline struct cf_spi_slave *to_cf_spi_slave(struct spi_slave *slave) +/* + * DSPI specific mode + * + * bit 31 - 28: Transfer size 3 to 16 bits + * 27 - 26: PCS to SCK delay prescaler + * 25 - 24: After SCK delay prescaler + * 23 - 22: Delay after transfer prescaler + * 21 : Allow overwrite for bit 31-22 and bit 20-8 + * 20 : Double baud rate + * 19 - 16: PCS to SCK delay scaler + * 15 - 12: After SCK delay scaler + * 11 - 8: Delay after transfer scaler + * 7 - 0: SPI_CPHA, SPI_CPOL, SPI_LSB_FIRST + */ +#define SPI_MODE_MOD 0x00200000 +#define SPI_MODE_DBLRATE 0x00100000 + +#define SPI_MODE_XFER_SZ_MASK 0xf0000000 +#define SPI_MODE_DLY_PRE_MASK 0x0fc00000 +#define SPI_MODE_DLY_SCA_MASK 0x000fff00 + +#define MCF_FRM_SZ_16BIT DSPI_CTAR_TRSZ(0xf) +#define MCF_DSPI_SPEED_BESTMATCH 0x7FFFFFFF +#define MCF_DSPI_MAX_CTAR_REGS 8 + +/* Default values */ +#define MCF_DSPI_DEFAULT_SCK_FREQ 10000000 +#define MCF_DSPI_DEFAULT_MAX_CS 4 +#define MCF_DSPI_DEFAULT_MODE 0 + +#define MCF_DSPI_DEFAULT_CTAR (DSPI_CTAR_TRSZ(7) | \ + DSPI_CTAR_PCSSCK_1CLK | \ + DSPI_CTAR_PASC(0) | \ + DSPI_CTAR_PDT(0) | \ + DSPI_CTAR_CSSCK(0) | \ + DSPI_CTAR_ASC(0) | \ + DSPI_CTAR_DT(1) | \ + DSPI_CTAR_BR(6)) + +#define MCF_CTAR_MODE_MASK (MCF_FRM_SZ_16BIT | \ + DSPI_CTAR_PCSSCK(3) | \ + DSPI_CTAR_PASC_7CLK | \ + DSPI_CTAR_PDT(3) | \ + DSPI_CTAR_CSSCK(0x0f) | \ + DSPI_CTAR_ASC(0x0f) | \ + DSPI_CTAR_DT(0x0f)) + +#define setup_ctrl(ctrl, cs) ((ctrl & 0xFF000000) | ((1 << cs) << 16)) + +static inline void cfspi_tx(struct coldfire_spi_priv *cfspi, + u32 ctrl, u16 data) { - return container_of(slave, struct cf_spi_slave, slave); + /* + * Need to check fifo level here + */ + while ((readl(&cfspi->regs->sr) & 0x0000F000) >= 0x4000) + ; + + writel(ctrl | data, &cfspi->regs->tfr); } -static void cfspi_init(void) +static inline u16 cfspi_rx(struct coldfire_spi_priv *cfspi) { - volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI; - cfspi_port_conf(); /* port configuration */ - - dspi->mcr = DSPI_MCR_MSTR | DSPI_MCR_CSIS7 | DSPI_MCR_CSIS6 | - DSPI_MCR_CSIS5 | DSPI_MCR_CSIS4 | DSPI_MCR_CSIS3 | - DSPI_MCR_CSIS2 | DSPI_MCR_CSIS1 | DSPI_MCR_CSIS0 | - DSPI_MCR_CRXF | DSPI_MCR_CTXF; + while ((readl(&cfspi->regs->sr) & 0x000000F0) == 0) + ; - /* Default setting in platform configuration */ -#ifdef CONFIG_SYS_DSPI_CTAR0 - dspi->ctar[0] = CONFIG_SYS_DSPI_CTAR0; -#endif -#ifdef CONFIG_SYS_DSPI_CTAR1 - dspi->ctar[1] = CONFIG_SYS_DSPI_CTAR1; -#endif -#ifdef CONFIG_SYS_DSPI_CTAR2 - dspi->ctar[2] = CONFIG_SYS_DSPI_CTAR2; -#endif -#ifdef CONFIG_SYS_DSPI_CTAR3 - dspi->ctar[3] = CONFIG_SYS_DSPI_CTAR3; -#endif -#ifdef CONFIG_SYS_DSPI_CTAR4 - dspi->ctar[4] = CONFIG_SYS_DSPI_CTAR4; -#endif -#ifdef CONFIG_SYS_DSPI_CTAR5 - dspi->ctar[5] = CONFIG_SYS_DSPI_CTAR5; -#endif -#ifdef CONFIG_SYS_DSPI_CTAR6 - dspi->ctar[6] = CONFIG_SYS_DSPI_CTAR6; -#endif -#ifdef CONFIG_SYS_DSPI_CTAR7 - dspi->ctar[7] = CONFIG_SYS_DSPI_CTAR7; -#endif + return readw(&cfspi->regs->rfr); } -static void cfspi_tx(u32 ctrl, u16 data) +static int coldfire_spi_claim_bus(struct udevice *dev) { - volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI; + struct udevice *bus = dev->parent; + struct coldfire_spi_priv *cfspi = dev_get_priv(bus); + struct dspi *dspi = cfspi->regs; + struct dm_spi_slave_platdata *slave_plat = + dev_get_parent_platdata(dev); - while ((dspi->sr & 0x0000F000) >= 4) ; + if ((in_be32(&dspi->sr) & DSPI_SR_TXRXS) != DSPI_SR_TXRXS) + return -1; - dspi->tfr = (ctrl | data); + /* Clear FIFO and resume transfer */ + clrbits_be32(&dspi->mcr, DSPI_MCR_CTXF | DSPI_MCR_CRXF); + + dspi_chip_select(slave_plat->cs); + + return 0; } -static u16 cfspi_rx(void) +static int coldfire_spi_release_bus(struct udevice *dev) { - volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI; + struct udevice *bus = dev->parent; + struct coldfire_spi_priv *cfspi = dev_get_priv(bus); + struct dspi *dspi = cfspi->regs; + struct dm_spi_slave_platdata *slave_plat = + dev_get_parent_platdata(dev); - while ((dspi->sr & 0x000000F0) == 0) ; + /* Clear FIFO */ + clrbits_be32(&dspi->mcr, DSPI_MCR_CTXF | DSPI_MCR_CRXF); - return (dspi->rfr & 0xFFFF); + dspi_chip_unselect(slave_plat->cs); + + return 0; } -static int cfspi_xfer(struct spi_slave *slave, uint bitlen, const void *dout, - void *din, ulong flags) +static int coldfire_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, + unsigned long flags) { - struct cf_spi_slave *cfslave = to_cf_spi_slave(slave); + struct udevice *bus = dev_get_parent(dev); + struct coldfire_spi_priv *cfspi = dev_get_priv(bus); + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); u16 *spi_rd16 = NULL, *spi_wr16 = NULL; u8 *spi_rd = NULL, *spi_wr = NULL; - static u32 ctrl = 0; + static u32 ctrl; uint len = bitlen >> 3; - if (cfslave->charbit == 16) { + if (cfspi->charbit == 16) { bitlen >>= 1; - spi_wr16 = (u16 *) dout; - spi_rd16 = (u16 *) din; + spi_wr16 = (u16 *)dout; + spi_rd16 = (u16 *)din; } else { - spi_wr = (u8 *) dout; - spi_rd = (u8 *) din; + spi_wr = (u8 *)dout; + spi_rd = (u8 *)din; } if ((flags & SPI_XFER_BEGIN) == SPI_XFER_BEGIN) ctrl |= DSPI_TFR_CONT; - ctrl = (ctrl & 0xFF000000) | ((1 << slave->cs) << 16); + ctrl = setup_ctrl(ctrl, slave_plat->cs); if (len > 1) { int tmp_len = len - 1; + while (tmp_len--) { - if (dout != NULL) { - if (cfslave->charbit == 16) - cfspi_tx(ctrl, *spi_wr16++); + if (dout) { + if (cfspi->charbit == 16) + cfspi_tx(cfspi, ctrl, *spi_wr16++); else - cfspi_tx(ctrl, *spi_wr++); - cfspi_rx(); + cfspi_tx(cfspi, ctrl, *spi_wr++); + cfspi_rx(cfspi); } - if (din != NULL) { - cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL); - if (cfslave->charbit == 16) - *spi_rd16++ = cfspi_rx(); + if (din) { + cfspi_tx(cfspi, ctrl, CONFIG_SPI_IDLE_VAL); + if (cfspi->charbit == 16) + *spi_rd16++ = cfspi_rx(cfspi); else - *spi_rd++ = cfspi_rx(); + *spi_rd++ = cfspi_rx(cfspi); } } len = 1; /* remaining byte */ } - if ((flags & SPI_XFER_END) == SPI_XFER_END) + if (flags & SPI_XFER_END) ctrl &= ~DSPI_TFR_CONT; if (len) { - if (dout != NULL) { - if (cfslave->charbit == 16) - cfspi_tx(ctrl, *spi_wr16); + if (dout) { + if (cfspi->charbit == 16) + cfspi_tx(cfspi, ctrl, *spi_wr16); else - cfspi_tx(ctrl, *spi_wr); - cfspi_rx(); + cfspi_tx(cfspi, ctrl, *spi_wr); + cfspi_rx(cfspi); } - if (din != NULL) { - cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL); - if (cfslave->charbit == 16) - *spi_rd16 = cfspi_rx(); + if (din) { + cfspi_tx(cfspi, ctrl, CONFIG_SPI_IDLE_VAL); + if (cfspi->charbit == 16) + *spi_rd16 = cfspi_rx(cfspi); else - *spi_rd = cfspi_rx(); + *spi_rd = cfspi_rx(cfspi); } } else { /* dummy read */ - cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL); - cfspi_rx(); + cfspi_tx(cfspi, ctrl, CONFIG_SPI_IDLE_VAL); + cfspi_rx(cfspi); } return 0; } -static struct spi_slave *cfspi_setup_slave(struct cf_spi_slave *cfslave, - uint mode) +static int coldfire_spi_set_speed(struct udevice *bus, uint max_hz) { - /* - * bit definition for mode: - * bit 31 - 28: Transfer size 3 to 16 bits - * 27 - 26: PCS to SCK delay prescaler - * 25 - 24: After SCK delay prescaler - * 23 - 22: Delay after transfer prescaler - * 21 : Allow overwrite for bit 31-22 and bit 20-8 - * 20 : Double baud rate - * 19 - 16: PCS to SCK delay scaler - * 15 - 12: After SCK delay scaler - * 11 - 8: Delay after transfer scaler - * 7 - 0: SPI_CPHA, SPI_CPOL, SPI_LSB_FIRST - */ - volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI; + struct coldfire_spi_priv *cfspi = dev_get_priv(bus); + struct dspi *dspi = cfspi->regs; int prescaler[] = { 2, 3, 5, 7 }; int scaler[] = { 2, 4, 6, 8, @@ -198,57 +233,41 @@ static struct spi_slave *cfspi_setup_slave(struct cf_spi_slave *cfslave, 4096, 8192, 16384, 32768 }; int i, j, pbrcnt, brcnt, diff, tmp, dbr = 0; - int best_i, best_j, bestmatch = 0x7FFFFFFF, baud_speed; - u32 bus_setup = 0; + int best_i, best_j, bestmatch = MCF_DSPI_SPEED_BESTMATCH, baud_speed; + u32 bus_setup; + + cfspi->baudrate = max_hz; + + /* Read current setup */ + bus_setup = readl(&dspi->ctar[bus->seq]); tmp = (prescaler[3] * scaler[15]); /* Maximum and minimum baudrate it can handle */ - if ((cfslave->baudrate > (gd->bus_clk >> 1)) || - (cfslave->baudrate < (gd->bus_clk / tmp))) { + if ((cfspi->baudrate > (gd->bus_clk >> 1)) || + (cfspi->baudrate < (gd->bus_clk / tmp))) { printf("Exceed baudrate limitation: Max %d - Min %d\n", (int)(gd->bus_clk >> 1), (int)(gd->bus_clk / tmp)); - return NULL; + return -1; } /* Activate Double Baud when it exceed 1/4 the bus clk */ - if ((CONFIG_SYS_DSPI_CTAR0 & DSPI_CTAR_DBR) || - (cfslave->baudrate > (gd->bus_clk / (prescaler[0] * scaler[0])))) { + if ((bus_setup & DSPI_CTAR_DBR) || + (cfspi->baudrate > (gd->bus_clk / (prescaler[0] * scaler[0])))) { bus_setup |= DSPI_CTAR_DBR; dbr = 1; } - if (mode & SPI_CPOL) - bus_setup |= DSPI_CTAR_CPOL; - if (mode & SPI_CPHA) - bus_setup |= DSPI_CTAR_CPHA; - if (mode & SPI_LSB_FIRST) - bus_setup |= DSPI_CTAR_LSBFE; - /* Overwrite default value set in platform configuration file */ - if (mode & SPI_MODE_MOD) { - - if ((mode & 0xF0000000) == 0) - bus_setup |= - dspi->ctar[cfslave->slave.bus] & 0x78000000; - else - bus_setup |= ((mode & 0xF0000000) >> 1); - + if (cfspi->mode & SPI_MODE_MOD) { /* * Check to see if it is enabled by default in platform * config, or manual setting passed by mode parameter */ - if (mode & SPI_DBLRATE) { + if (cfspi->mode & SPI_MODE_DBLRATE) { bus_setup |= DSPI_CTAR_DBR; dbr = 1; } - bus_setup |= (mode & 0x0FC00000) >> 4; /* PSCSCK, PASC, PDT */ - bus_setup |= (mode & 0x000FFF00) >> 4; /* CSSCK, ASC, DT */ - } else - bus_setup |= (dspi->ctar[cfslave->slave.bus] & 0x78FCFFF0); - - cfslave->charbit = - ((dspi->ctar[cfslave->slave.bus] & 0x78000000) == - 0x78000000) ? 16 : 8; + } pbrcnt = sizeof(prescaler) / sizeof(int); brcnt = sizeof(scaler) / sizeof(int); @@ -259,10 +278,10 @@ static struct spi_slave *cfspi_setup_slave(struct cf_spi_slave *cfslave, for (j = 0; j < brcnt; j++) { tmp = (baud_speed / scaler[j]) * (1 + dbr); - if (tmp > cfslave->baudrate) - diff = tmp - cfslave->baudrate; + if (tmp > cfspi->baudrate) + diff = tmp - cfspi->baudrate; else - diff = cfslave->baudrate - tmp; + diff = cfspi->baudrate - tmp; if (diff < bestmatch) { bestmatch = diff; @@ -271,65 +290,174 @@ static struct spi_slave *cfspi_setup_slave(struct cf_spi_slave *cfslave, } } } + + bus_setup &= ~(DSPI_CTAR_PBR(0x03) | DSPI_CTAR_BR(0x0f)); bus_setup |= (DSPI_CTAR_PBR(best_i) | DSPI_CTAR_BR(best_j)); - dspi->ctar[cfslave->slave.bus] = bus_setup; + writel(bus_setup, &dspi->ctar[bus->seq]); - return &cfslave->slave; + return 0; } -#endif /* CONFIG_CF_DSPI */ -#ifdef CONFIG_CMD_SPI -int spi_cs_is_valid(unsigned int bus, unsigned int cs) +static int coldfire_spi_set_mode(struct udevice *bus, uint mode) { - if (((cs >= 0) && (cs < 8)) && ((bus >= 0) && (bus < 8))) - return 1; - else - return 0; -} + struct coldfire_spi_priv *cfspi = dev_get_priv(bus); + struct dspi *dspi = cfspi->regs; + u32 bus_setup = 0; -void spi_init(void) -{ - cfspi_init(); -} + cfspi->mode = mode; -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) -{ - struct cf_spi_slave *cfslave; + if (cfspi->mode & SPI_CPOL) + bus_setup |= DSPI_CTAR_CPOL; + if (cfspi->mode & SPI_CPHA) + bus_setup |= DSPI_CTAR_CPHA; + if (cfspi->mode & SPI_LSB_FIRST) + bus_setup |= DSPI_CTAR_LSBFE; - if (!spi_cs_is_valid(bus, cs)) - return NULL; + /* Overwrite default value set in platform configuration file */ + if (cfspi->mode & SPI_MODE_MOD) { + if ((cfspi->mode & SPI_MODE_XFER_SZ_MASK) == 0) + bus_setup |= + readl(&dspi->ctar[bus->seq]) & MCF_FRM_SZ_16BIT; + else + bus_setup |= + ((cfspi->mode & SPI_MODE_XFER_SZ_MASK) >> 1); - cfslave = spi_alloc_slave(struct cf_spi_slave, bus, cs); - if (!cfslave) - return NULL; + /* PSCSCK, PASC, PDT */ + bus_setup |= (cfspi->mode & SPI_MODE_DLY_PRE_MASK) >> 4; + /* CSSCK, ASC, DT */ + bus_setup |= (cfspi->mode & SPI_MODE_DLY_SCA_MASK) >> 4; + } else { + bus_setup |= + (readl(&dspi->ctar[bus->seq]) & MCF_CTAR_MODE_MASK); + } + + cfspi->charbit = + ((readl(&dspi->ctar[bus->seq]) & MCF_FRM_SZ_16BIT) == + MCF_FRM_SZ_16BIT) ? 16 : 8; - cfslave->baudrate = max_hz; + setbits_be32(&dspi->ctar[bus->seq], bus_setup); - /* specific setup */ - return cfspi_setup_slave(cfslave, mode); + return 0; } -void spi_free_slave(struct spi_slave *slave) +static int coldfire_spi_probe(struct udevice *bus) { - struct cf_spi_slave *cfslave = to_cf_spi_slave(slave); + struct coldfire_spi_platdata *plat = dev_get_platdata(bus); + struct coldfire_spi_priv *cfspi = dev_get_priv(bus); + struct dspi *dspi = cfspi->regs; + int i; - free(cfslave); -} + cfspi->regs = (struct dspi *)plat->regs_addr; -int spi_claim_bus(struct spi_slave *slave) -{ - return cfspi_claim_bus(slave->bus, slave->cs); + cfspi->baudrate = plat->speed_hz; + cfspi->mode = plat->mode; + + for (i = 0; i < MCF_DSPI_MAX_CTAR_REGS; i++) { + unsigned int ctar = 0; + + if (plat->ctar[i][0] == 0) + break; + + ctar = DSPI_CTAR_TRSZ(plat->ctar[i][0]) | + DSPI_CTAR_PCSSCK(plat->ctar[i][1]) | + DSPI_CTAR_PASC(plat->ctar[i][2]) | + DSPI_CTAR_PDT(plat->ctar[i][3]) | + DSPI_CTAR_CSSCK(plat->ctar[i][4]) | + DSPI_CTAR_ASC(plat->ctar[i][5]) | + DSPI_CTAR_DT(plat->ctar[i][6]) | + DSPI_CTAR_BR(plat->ctar[i][7]); + + writel(ctar, &cfspi->regs->ctar[i]); + } + + /* Default CTARs */ + for (i = 0; i < MCF_DSPI_MAX_CTAR_REGS; i++) + writel(MCF_DSPI_DEFAULT_CTAR, &dspi->ctar[i]); + + dspi->mcr = DSPI_MCR_MSTR | DSPI_MCR_CSIS7 | DSPI_MCR_CSIS6 | + DSPI_MCR_CSIS5 | DSPI_MCR_CSIS4 | DSPI_MCR_CSIS3 | + DSPI_MCR_CSIS2 | DSPI_MCR_CSIS1 | DSPI_MCR_CSIS0 | + DSPI_MCR_CRXF | DSPI_MCR_CTXF; + + return 0; } -void spi_release_bus(struct spi_slave *slave) +void spi_init(void) { - cfspi_release_bus(slave->bus, slave->cs); } -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, - void *din, unsigned long flags) +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) +static int coldfire_dspi_ofdata_to_platdata(struct udevice *bus) { - return cfspi_xfer(slave, bitlen, dout, din, flags); + fdt_addr_t addr; + struct coldfire_spi_platdata *plat = bus->platdata; + const void *blob = gd->fdt_blob; + int node = dev_of_offset(bus); + int *ctar, len; + + addr = devfdt_get_addr(bus); + if (addr == FDT_ADDR_T_NONE) + return -ENOMEM; + + plat->regs_addr = addr; + + plat->num_cs = fdtdec_get_int(blob, node, "num-cs", + MCF_DSPI_DEFAULT_MAX_CS); + + plat->speed_hz = fdtdec_get_int(blob, node, "spi-max-frequency", + MCF_DSPI_DEFAULT_SCK_FREQ); + + plat->mode = fdtdec_get_int(blob, node, "spi-mode", + MCF_DSPI_DEFAULT_MODE); + + memset(plat->ctar, 0, sizeof(plat->ctar)); + + ctar = (int *)fdt_getprop(blob, node, "ctar-params", &len); + + if (ctar && len) { + int i, q, ctar_regs; + + ctar_regs = len / sizeof(unsigned int) / MAX_CTAR_FIELDS; + + if (ctar_regs > MAX_CTAR_REGS) + ctar_regs = MAX_CTAR_REGS; + + for (i = 0; i < ctar_regs; i++) { + for (q = 0; q < MAX_CTAR_FIELDS; q++) + plat->ctar[i][q] = *ctar++; + } + } + + debug("DSPI: regs=%pa, max-frequency=%d, num-cs=%d, mode=%d\n", + (void *)plat->regs_addr, + plat->speed_hz, plat->num_cs, plat->mode); + + return 0; } -#endif /* CONFIG_CMD_SPI */ + +static const struct udevice_id coldfire_spi_ids[] = { + { .compatible = "fsl,mcf-dspi" }, + { } +}; +#endif + +static const struct dm_spi_ops coldfire_spi_ops = { + .claim_bus = coldfire_spi_claim_bus, + .release_bus = coldfire_spi_release_bus, + .xfer = coldfire_spi_xfer, + .set_speed = coldfire_spi_set_speed, + .set_mode = coldfire_spi_set_mode, +}; + +U_BOOT_DRIVER(coldfire_spi) = { + .name = "spi_coldfire", + .id = UCLASS_SPI, +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + .of_match = coldfire_spi_ids, + .ofdata_to_platdata = coldfire_dspi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct coldfire_spi_platdata), +#endif + .probe = coldfire_spi_probe, + .ops = &coldfire_spi_ops, + .priv_auto_alloc_size = sizeof(struct coldfire_spi_priv), +}; diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c index dadb6fa18b..7d58cfae55 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -22,8 +22,6 @@ #include <linux/iopoll.h> #include <asm/io.h> -DECLARE_GLOBAL_DATA_PTR; - /* Register offsets */ #define DW_SPI_CTRL0 0x00 #define DW_SPI_CTRL1 0x04 @@ -155,14 +153,12 @@ static int request_gpio_cs(struct udevice *bus) static int dw_spi_ofdata_to_platdata(struct udevice *bus) { struct dw_spi_platdata *plat = bus->platdata; - const void *blob = gd->fdt_blob; - int node = dev_of_offset(bus); plat->regs = (struct dw_spi *)devfdt_get_addr(bus); /* Use 500KHz as a suitable default */ - plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", - 500000); + plat->frequency = dev_read_u32_default(bus, "spi-max-frequency", + 500000); debug("%s: regs=%p max-frequency=%d\n", __func__, plat->regs, plat->frequency); diff --git a/drivers/spi/renesas_rpc_spi.c b/drivers/spi/renesas_rpc_spi.c index bec9095ff4..bb2e7748fe 100644 --- a/drivers/spi/renesas_rpc_spi.c +++ b/drivers/spi/renesas_rpc_spi.c @@ -409,27 +409,30 @@ static int rpc_spi_probe(struct udevice *dev) priv->regs = plat->regs; priv->extr = plat->extr; - +#if CONFIG_IS_ENABLED(CLK) clk_enable(&priv->clk); - +#endif return 0; } static int rpc_spi_ofdata_to_platdata(struct udevice *bus) { struct rpc_spi_platdata *plat = dev_get_platdata(bus); - struct rpc_spi_priv *priv = dev_get_priv(bus); - int ret; plat->regs = dev_read_addr_index(bus, 0); plat->extr = dev_read_addr_index(bus, 1); +#if CONFIG_IS_ENABLED(CLK) + struct rpc_spi_priv *priv = dev_get_priv(bus); + int ret; + ret = clk_get_by_index(bus, 0, &priv->clk); if (ret < 0) { printf("%s: Could not get clock for %s: %d\n", __func__, bus->name, ret); return ret; } +#endif plat->freq = dev_read_u32_default(bus, "spi-max-freq", 50000000); @@ -448,6 +451,7 @@ static const struct udevice_id rpc_spi_ids[] = { { .compatible = "renesas,rpc-r8a77965" }, { .compatible = "renesas,rpc-r8a77970" }, { .compatible = "renesas,rpc-r8a77995" }, + { .compatible = "renesas,rpc-r7s72100" }, { } }; diff --git a/drivers/spi/rk_spi.c b/drivers/spi/rk_spi.c index 14437c0a9a..a68553b75b 100644 --- a/drivers/spi/rk_spi.c +++ b/drivers/spi/rk_spi.c @@ -2,6 +2,8 @@ /* * spi driver for rockchip * + * (C) 2019 Theobroma Systems Design und Consulting GmbH + * * (C) Copyright 2015 Google, Inc * * (C) Copyright 2008-2013 Rockchip Electronics @@ -16,14 +18,19 @@ #include <spi.h> #include <linux/errno.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/periph.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/periph.h> #include <dm/pinctrl.h> #include "rk_spi.h" /* Change to 1 to output registers at the start of each transaction */ #define DEBUG_RK_SPI 0 +struct rockchip_spi_params { + /* RXFIFO overruns and TXFIFO underruns stop the master clock */ + bool master_manages_fifo; +}; + struct rockchip_spi_platdata { #if CONFIG_IS_ENABLED(OF_PLATDATA) struct dtd_rockchip_rk3288_spi of_plat; @@ -40,11 +47,8 @@ struct rockchip_spi_priv { unsigned int max_freq; unsigned int mode; ulong last_transaction_us; /* Time of last transaction end */ - u8 bits_per_word; /* max 16 bits per word */ - u8 n_bytes; unsigned int speed_hz; unsigned int last_speed_hz; - unsigned int tmode; uint input_rate; }; @@ -130,8 +134,13 @@ static void spi_cs_activate(struct udevice *dev, uint cs) if (plat->deactivate_delay_us && priv->last_transaction_us) { ulong delay_us; /* The delay completed so far */ delay_us = timer_get_us() - priv->last_transaction_us; - if (delay_us < plat->deactivate_delay_us) - udelay(plat->deactivate_delay_us - delay_us); + if (delay_us < plat->deactivate_delay_us) { + ulong additional_delay_us = + plat->deactivate_delay_us - delay_us; + debug("%s: delaying by %ld us\n", + __func__, additional_delay_us); + udelay(additional_delay_us); + } } debug("activate cs%u\n", cs); @@ -263,8 +272,6 @@ static int rockchip_spi_probe(struct udevice *bus) } priv->input_rate = ret; debug("%s: rate = %u\n", __func__, priv->input_rate); - priv->bits_per_word = 8; - priv->tmode = TMOD_TR; /* Tx & Rx */ return 0; } @@ -274,28 +281,10 @@ static int rockchip_spi_claim_bus(struct udevice *dev) struct udevice *bus = dev->parent; struct rockchip_spi_priv *priv = dev_get_priv(bus); struct rockchip_spi *regs = priv->regs; - u8 spi_dfs, spi_tf; uint ctrlr0; /* Disable the SPI hardware */ - rkspi_enable_chip(regs, 0); - - switch (priv->bits_per_word) { - case 8: - priv->n_bytes = 1; - spi_dfs = DFS_8BIT; - spi_tf = HALF_WORD_OFF; - break; - case 16: - priv->n_bytes = 2; - spi_dfs = DFS_16BIT; - spi_tf = HALF_WORD_ON; - break; - default: - debug("%s: unsupported bits: %dbits\n", __func__, - priv->bits_per_word); - return -EPROTONOSUPPORT; - } + rkspi_enable_chip(regs, false); if (priv->speed_hz != priv->last_speed_hz) rkspi_set_clk(priv, priv->speed_hz); @@ -304,7 +293,7 @@ static int rockchip_spi_claim_bus(struct udevice *dev) ctrlr0 = OMOD_MASTER << OMOD_SHIFT; /* Data Frame Size */ - ctrlr0 |= spi_dfs << DFS_SHIFT; + ctrlr0 |= DFS_8BIT << DFS_SHIFT; /* set SPI mode 0..3 */ if (priv->mode & SPI_CPOL) @@ -325,7 +314,7 @@ static int rockchip_spi_claim_bus(struct udevice *dev) ctrlr0 |= FBM_MSB << FBM_SHIFT; /* Byte and Halfword Transform */ - ctrlr0 |= spi_tf << HALF_WORD_TX_SHIFT; + ctrlr0 |= HALF_WORD_OFF << HALF_WORD_TX_SHIFT; /* Rxd Sample Delay */ ctrlr0 |= 0 << RXDSD_SHIFT; @@ -334,7 +323,7 @@ static int rockchip_spi_claim_bus(struct udevice *dev) ctrlr0 |= FRF_SPI << FRF_SHIFT; /* Tx and Rx mode */ - ctrlr0 |= (priv->tmode & TMOD_MASK) << TMOD_SHIFT; + ctrlr0 |= TMOD_TR << TMOD_SHIFT; writel(ctrlr0, ®s->ctrlr0); @@ -351,6 +340,83 @@ static int rockchip_spi_release_bus(struct udevice *dev) return 0; } +static inline int rockchip_spi_16bit_reader(struct udevice *dev, + u8 **din, int *len) +{ + struct udevice *bus = dev->parent; + const struct rockchip_spi_params * const data = + (void *)dev_get_driver_data(bus); + struct rockchip_spi_priv *priv = dev_get_priv(bus); + struct rockchip_spi *regs = priv->regs; + const u32 saved_ctrlr0 = readl(®s->ctrlr0); +#if defined(DEBUG) + u32 statistics_rxlevels[33] = { }; +#endif + u32 frames = *len / 2; + u8 *in = (u8 *)(*din); + u32 max_chunk_size = SPI_FIFO_DEPTH; + + if (!frames) + return 0; + + /* + * If we know that the hardware will manage RXFIFO overruns + * (i.e. stop the SPI clock until there's space in the FIFO), + * we the allow largest possible chunk size that can be + * represented in CTRLR1. + */ + if (data && data->master_manages_fifo) + max_chunk_size = 0x10000; + + // rockchip_spi_configure(dev, mode, size) + rkspi_enable_chip(regs, false); + clrsetbits_le32(®s->ctrlr0, + TMOD_MASK << TMOD_SHIFT, + TMOD_RO << TMOD_SHIFT); + /* 16bit data frame size */ + clrsetbits_le32(®s->ctrlr0, DFS_MASK, DFS_16BIT); + + /* Update caller's context */ + const u32 bytes_to_process = 2 * frames; + *din += bytes_to_process; + *len -= bytes_to_process; + + /* Process our frames */ + while (frames) { + u32 chunk_size = min(frames, max_chunk_size); + + frames -= chunk_size; + + writew(chunk_size - 1, ®s->ctrlr1); + rkspi_enable_chip(regs, true); + + do { + u32 rx_level = readw(®s->rxflr); +#if defined(DEBUG) + statistics_rxlevels[rx_level]++; +#endif + chunk_size -= rx_level; + while (rx_level--) { + u16 val = readw(regs->rxdr); + *in++ = val & 0xff; + *in++ = val >> 8; + } + } while (chunk_size); + + rkspi_enable_chip(regs, false); + } + +#if defined(DEBUG) + debug("%s: observed rx_level during processing:\n", __func__); + for (int i = 0; i <= 32; ++i) + if (statistics_rxlevels[i]) + debug("\t%2d: %d\n", i, statistics_rxlevels[i]); +#endif + /* Restore the original transfer setup and return error-free. */ + writel(saved_ctrlr0, ®s->ctrlr0); + return 0; +} + static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { @@ -362,7 +428,7 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen, const u8 *out = dout; u8 *in = din; int toread, towrite; - int ret; + int ret = 0; debug("%s: dout=%p, din=%p, len=%x, flags=%lx\n", __func__, dout, din, len, flags); @@ -373,8 +439,18 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen, if (flags & SPI_XFER_BEGIN) spi_cs_activate(dev, slave_plat->cs); + /* + * To ensure fast loading of firmware images (e.g. full U-Boot + * stage, ATF, Linux kernel) from SPI flash, we optimise the + * case of read-only transfers by using the full 16bits of each + * FIFO element. + */ + if (!out) + ret = rockchip_spi_16bit_reader(dev, &in, &len); + + /* This is the original 8bit reader/writer code */ while (len > 0) { - int todo = min(len, 0xffff); + int todo = min(len, 0x10000); rkspi_enable_chip(regs, false); writel(todo - 1, ®s->ctrlr1); @@ -397,9 +473,18 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen, toread--; } } - ret = rkspi_wait_till_not_busy(regs); - if (ret) - break; + + /* + * In case that there's a transmit-component, we need to wait + * until the control goes idle before we can disable the SPI + * control logic (as this will implictly flush the FIFOs). + */ + if (out) { + ret = rkspi_wait_till_not_busy(regs); + if (ret) + break; + } + len -= todo; } @@ -446,10 +531,16 @@ static const struct dm_spi_ops rockchip_spi_ops = { */ }; +const struct rockchip_spi_params rk3399_spi_params = { + .master_manages_fifo = true, +}; + static const struct udevice_id rockchip_spi_ids[] = { { .compatible = "rockchip,rk3288-spi" }, - { .compatible = "rockchip,rk3368-spi" }, - { .compatible = "rockchip,rk3399-spi" }, + { .compatible = "rockchip,rk3368-spi", + .data = (ulong)&rk3399_spi_params }, + { .compatible = "rockchip,rk3399-spi", + .data = (ulong)&rk3399_spi_params }, { } }; diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig index 8ce3e2e207..30aed2c4c1 100644 --- a/drivers/sysreset/Kconfig +++ b/drivers/sysreset/Kconfig @@ -13,11 +13,29 @@ config SYSRESET to effect a reset. The uclass will try all available drivers when reset_walk() is called. +config SPL_SYSRESET + bool "Enable support for system reset drivers in SPL mode" + depends on SYSRESET && SPL_DM + help + Enable system reset drivers which can be used to reset the CPU or + board. Each driver can provide a reset method which will be called + to effect a reset. The uclass will try all available drivers when + reset_walk() is called. + +config TPL_SYSRESET + bool "Enable support for system reset drivers in TPL mode" + depends on SYSRESET && TPL_DM + help + Enable system reset drivers which can be used to reset the CPU or + board. Each driver can provide a reset method which will be called + to effect a reset. The uclass will try all available drivers when + reset_walk() is called. + if SYSRESET config SYSRESET_GPIO bool "Enable support for GPIO reset driver" - select GPIO + select DM_GPIO help Reset support via GPIO pin connected reset logic. This is used for example on Microblaze where reset logic can be controlled via GPIO diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile index b3728ac17f..8e1c845dfe 100644 --- a/drivers/sysreset/Makefile +++ b/drivers/sysreset/Makefile @@ -2,7 +2,7 @@ # # (C) Copyright 2016 Cadence Design Systems Inc. -obj-$(CONFIG_SYSRESET) += sysreset-uclass.o +obj-$(CONFIG_$(SPL_TPL_)SYSRESET) += sysreset-uclass.o obj-$(CONFIG_ARCH_ASPEED) += sysreset_ast.o obj-$(CONFIG_ARCH_ROCKCHIP) += sysreset_rockchip.o obj-$(CONFIG_ARCH_STI) += sysreset_sti.o diff --git a/drivers/sysreset/sysreset_rockchip.c b/drivers/sysreset/sysreset_rockchip.c index 93d7cfe463..0fc6b683f2 100644 --- a/drivers/sysreset/sysreset_rockchip.c +++ b/drivers/sysreset/sysreset_rockchip.c @@ -8,9 +8,9 @@ #include <errno.h> #include <sysreset.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3328.h> -#include <asm/arch/hardware.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3328.h> +#include <asm/arch-rockchip/hardware.h> #include <linux/err.h> int rockchip_sysreset_request(struct udevice *dev, enum sysreset_t type) diff --git a/drivers/sysreset/sysreset_x86.c b/drivers/sysreset/sysreset_x86.c index 009f376602..072f7948ef 100644 --- a/drivers/sysreset/sysreset_x86.c +++ b/drivers/sysreset/sysreset_x86.c @@ -7,15 +7,75 @@ #include <common.h> #include <dm.h> +#include <efi_loader.h> +#include <pch.h> #include <sysreset.h> +#include <asm/acpi_s3.h> #include <asm/io.h> #include <asm/processor.h> -#include <efi_loader.h> -static __efi_runtime int x86_sysreset_request(struct udevice *dev, - enum sysreset_t type) +struct x86_sysreset_platdata { + struct udevice *pch; +}; + +/* + * Power down the machine by using the power management sleep control + * of the chipset. This will currently only work on Intel chipsets. + * However, adapting it to new chipsets is fairly simple. You will + * have to find the IO address of the power management register block + * in your southbridge, and look up the appropriate SLP_TYP_S5 value + * from your southbridge's data sheet. + * + * This function never returns. + */ +int pch_sysreset_power_off(struct udevice *dev) +{ + struct x86_sysreset_platdata *plat = dev_get_platdata(dev); + struct pch_pmbase_info pm; + u32 reg32; + int ret; + + if (!plat->pch) + return -ENOENT; + ret = pch_ioctl(plat->pch, PCH_REQ_PMBASE_INFO, &pm, sizeof(pm)); + if (ret) + return ret; + + /* + * Mask interrupts or system might stay in a coma, not executing code + * anymore, but not powered off either. + */ + asm("cli"); + + /* + * Avoid any GPI waking the system from S5* or the system might stay in + * a coma + */ + outl(0x00000000, pm.base + pm.gpio0_en_ofs); + + /* Clear Power Button Status */ + outw(PWRBTN_STS, pm.base + pm.pm1_sts_ofs); + + /* PMBASE + 4, Bit 10-12, Sleeping Type, * set to 111 -> S5, soft_off */ + reg32 = inl(pm.base + pm.pm1_cnt_ofs); + + /* Set Sleeping Type to S5 (poweroff) */ + reg32 &= ~(SLP_EN | SLP_TYP); + reg32 |= SLP_TYP_S5; + outl(reg32, pm.base + pm.pm1_cnt_ofs); + + /* Now set the Sleep Enable bit */ + reg32 |= SLP_EN; + outl(reg32, pm.base + pm.pm1_cnt_ofs); + + for (;;) + asm("hlt"); +} + +static int x86_sysreset_request(struct udevice *dev, enum sysreset_t type) { int value; + int ret; switch (type) { case SYSRESET_WARM: @@ -24,6 +84,11 @@ static __efi_runtime int x86_sysreset_request(struct udevice *dev, case SYSRESET_COLD: value = SYS_RST | RST_CPU | FULL_RST; break; + case SYSRESET_POWER_OFF: + ret = pch_sysreset_power_off(dev); + if (ret) + return ret; + return -EINPROGRESS; default: return -ENOSYS; } @@ -33,17 +98,29 @@ static __efi_runtime int x86_sysreset_request(struct udevice *dev, return -EINPROGRESS; } +static int x86_sysreset_get_last(struct udevice *dev) +{ + return SYSRESET_POWER; +} + #ifdef CONFIG_EFI_LOADER void __efi_runtime EFIAPI efi_reset_system( enum efi_reset_type reset_type, efi_status_t reset_status, unsigned long data_size, void *reset_data) { + int value; + + /* + * inline this code since we are not caused in the context of a + * udevice and passing NULL to x86_sysreset_request() is too horrible. + */ if (reset_type == EFI_RESET_COLD || reset_type == EFI_RESET_PLATFORM_SPECIFIC) - x86_sysreset_request(NULL, SYSRESET_COLD); - else if (reset_type == EFI_RESET_WARM) - x86_sysreset_request(NULL, SYSRESET_WARM); + value = SYS_RST | RST_CPU | FULL_RST; + else /* assume EFI_RESET_WARM since we cannot return an error */ + value = SYS_RST | RST_CPU; + outb(value, IO_PORT_RESET); /* TODO EFI_RESET_SHUTDOWN */ @@ -51,6 +128,15 @@ void __efi_runtime EFIAPI efi_reset_system( } #endif +static int x86_sysreset_probe(struct udevice *dev) +{ + struct x86_sysreset_platdata *plat = dev_get_platdata(dev); + + /* Locate the PCH if there is one. It isn't essential */ + uclass_first_device(UCLASS_PCH, &plat->pch); + + return 0; +} static const struct udevice_id x86_sysreset_ids[] = { { .compatible = "x86,reset" }, @@ -59,6 +145,7 @@ static const struct udevice_id x86_sysreset_ids[] = { static struct sysreset_ops x86_sysreset_ops = { .request = x86_sysreset_request, + .get_last = x86_sysreset_get_last, }; U_BOOT_DRIVER(x86_sysreset) = { @@ -66,4 +153,6 @@ U_BOOT_DRIVER(x86_sysreset) = { .id = UCLASS_SYSRESET, .of_match = x86_sysreset_ids, .ops = &x86_sysreset_ops, + .probe = x86_sysreset_probe, + .platdata_auto_alloc_size = sizeof(struct x86_sysreset_platdata), }; diff --git a/drivers/tee/sandbox.c b/drivers/tee/sandbox.c index a136bc9609..2f3355c7b7 100644 --- a/drivers/tee/sandbox.c +++ b/drivers/tee/sandbox.c @@ -178,7 +178,7 @@ static u32 ta_avb_invoke_func(struct udevice *dev, u32 func, uint num_params, if (!ep) return TEE_ERROR_ITEM_NOT_FOUND; - value_sz = strlen(ep->data); + value_sz = strlen(ep->data) + 1; memcpy(value, ep->data, value_sz); return TEE_SUCCESS; diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index df37a798bd..5f4bc6edb6 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -110,6 +110,13 @@ config MPC83XX_TIMER Select this to enable support for the timer found on devices based on the MPC83xx family of SoCs. +config RENESAS_OSTM_TIMER + bool "Renesas RZ/A1 R7S72100 OSTM Timer" + depends on TIMER + help + Enables support for the Renesas OSTM Timer driver. + This timer is present on Renesas RZ/A1 R7S72100 SoCs. + config X86_TSC_TIMER_EARLY_FREQ int "x86 TSC timer frequency in MHz when used as the early timer" depends on X86_TSC_TIMER diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile index d0bf218b11..fa35bea6c5 100644 --- a/drivers/timer/Makefile +++ b/drivers/timer/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence-ttc.o obj-$(CONFIG_DESIGNWARE_APB_TIMER) += dw-apb-timer.o obj-$(CONFIG_MPC83XX_TIMER) += mpc83xx_timer.o obj-$(CONFIG_OMAP_TIMER) += omap-timer.o +obj-$(CONFIG_RENESAS_OSTM_TIMER) += ostm_timer.o obj-$(CONFIG_RISCV_TIMER) += riscv_timer.o obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o obj-$(CONFIG_SANDBOX_TIMER) += sandbox_timer.o diff --git a/drivers/timer/dw-apb-timer.c b/drivers/timer/dw-apb-timer.c index cb48801af1..86312b8dc7 100644 --- a/drivers/timer/dw-apb-timer.c +++ b/drivers/timer/dw-apb-timer.c @@ -17,8 +17,6 @@ #define DW_APB_CURR_VAL 0x4 #define DW_APB_CTRL 0x8 -DECLARE_GLOBAL_DATA_PTR; - struct dw_apb_timer_priv { fdt_addr_t regs; }; diff --git a/drivers/timer/ostm_timer.c b/drivers/timer/ostm_timer.c new file mode 100644 index 0000000000..f0e25093ca --- /dev/null +++ b/drivers/timer/ostm_timer.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Renesas RZ/A1 R7S72100 OSTM Timer driver + * + * Copyright (C) 2019 Marek Vasut <marek.vasut@gmail.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <dm.h> +#include <clk.h> +#include <timer.h> + +#define OSTM_CMP 0x00 +#define OSTM_CNT 0x04 +#define OSTM_TE 0x10 +#define OSTM_TS 0x14 +#define OSTM_TT 0x18 +#define OSTM_CTL 0x20 +#define OSTM_CTL_D BIT(1) + +DECLARE_GLOBAL_DATA_PTR; + +struct ostm_priv { + fdt_addr_t regs; +}; + +static int ostm_get_count(struct udevice *dev, u64 *count) +{ + struct ostm_priv *priv = dev_get_priv(dev); + + *count = timer_conv_64(readl(priv->regs + OSTM_CNT)); + + return 0; +} + +static int ostm_probe(struct udevice *dev) +{ + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct ostm_priv *priv = dev_get_priv(dev); +#if CONFIG_IS_ENABLED(CLK) + struct clk clk; + int ret; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret) + return ret; + + uc_priv->clock_rate = clk_get_rate(&clk); + + clk_free(&clk); +#else + uc_priv->clock_rate = CONFIG_SYS_CLK_FREQ / 2; +#endif + + readb(priv->regs + OSTM_CTL); + writeb(OSTM_CTL_D, priv->regs + OSTM_CTL); + + setbits_8(priv->regs + OSTM_TT, BIT(0)); + writel(0xffffffff, priv->regs + OSTM_CMP); + setbits_8(priv->regs + OSTM_TS, BIT(0)); + + return 0; +} + +static int ostm_ofdata_to_platdata(struct udevice *dev) +{ + struct ostm_priv *priv = dev_get_priv(dev); + + priv->regs = dev_read_addr(dev); + + return 0; +} + +static const struct timer_ops ostm_ops = { + .get_count = ostm_get_count, +}; + +static const struct udevice_id ostm_ids[] = { + { .compatible = "renesas,ostm" }, + {} +}; + +U_BOOT_DRIVER(ostm_timer) = { + .name = "ostm-timer", + .id = UCLASS_TIMER, + .ops = &ostm_ops, + .probe = ostm_probe, + .of_match = ostm_ids, + .ofdata_to_platdata = ostm_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct ostm_priv), +}; diff --git a/drivers/timer/rockchip_timer.c b/drivers/timer/rockchip_timer.c index 69019740b0..54956e557a 100644 --- a/drivers/timer/rockchip_timer.c +++ b/drivers/timer/rockchip_timer.c @@ -7,7 +7,7 @@ #include <dm.h> #include <dm/ofnode.h> #include <mapmem.h> -#include <asm/arch/timer.h> +#include <asm/arch-rockchip/timer.h> #include <dt-structs.h> #include <timer.h> #include <asm/io.h> diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index bbd8105c06..25e1a38aee 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -44,6 +44,14 @@ config USB_DWC3_GENERIC Select this for Xilinx ZynqMP and similar Platforms. This wrapper supports Host and Peripheral operation modes. +config USB_DWC3_MESON_G12A + bool "Amlogic Meson G12A USB wrapper" + depends on DM_USB && USB_DWC3 && ARCH_MESON + imply PHY + help + Select this for Amlogic Meson G12A Platforms. + This wrapper supports Host and Peripheral operation modes. + config USB_DWC3_UNIPHIER bool "DesignWare USB3 Host Support on UniPhier Platforms" depends on ARCH_UNIPHIER && USB_XHCI_DWC3 diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 60b5515a67..0b652a6f36 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -7,6 +7,7 @@ dwc3-y := core.o obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o +obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o obj-$(CONFIG_USB_DWC3_GENERIC) += dwc3-generic.o obj-$(CONFIG_USB_DWC3_UNIPHIER) += dwc3-uniphier.o obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c new file mode 100644 index 0000000000..832bcd70ff --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Amlogic G12A DWC3 Glue layer + * + * Copyright (C) 2019 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#include <common.h> +#include <asm-generic/io.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dwc3-uboot.h> +#include <generic-phy.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <malloc.h> +#include <regmap.h> +#include <usb.h> +#include "core.h" +#include "gadget.h" +#include <reset.h> +#include <clk.h> +#include <power/regulator.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/compat.h> + +/* USB2 Ports Control Registers */ + +#define U2P_REG_SIZE 0x20 + +#define U2P_R0 0x0 + #define U2P_R0_HOST_DEVICE BIT(0) + #define U2P_R0_POWER_OK BIT(1) + #define U2P_R0_HAST_MODE BIT(2) + #define U2P_R0_POWER_ON_RESET BIT(3) + #define U2P_R0_ID_PULLUP BIT(4) + #define U2P_R0_DRV_VBUS BIT(5) + +#define U2P_R1 0x4 + #define U2P_R1_PHY_READY BIT(0) + #define U2P_R1_ID_DIG BIT(1) + #define U2P_R1_OTG_SESSION_VALID BIT(2) + #define U2P_R1_VBUS_VALID BIT(3) + +/* USB Glue Control Registers */ + +#define USB_R0 0x80 + #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) + #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) + #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) + #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) + #define USB_R0_U2D_ACT BIT(31) + +#define USB_R1 0x84 + #define USB_R1_U3H_BIGENDIAN_GS BIT(0) + #define USB_R1_U3H_PME_ENABLE BIT(1) + #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2) + #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(9, 7) + #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(13, 12) + #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) + #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) + #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) + #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) + #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) + +#define USB_R2 0x88 + #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) + #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) + +#define USB_R3 0x8c + #define USB_R3_P30_SSC_ENABLE BIT(0) + #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) + #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) + #define USB_R3_P30_REF_SSP_EN BIT(13) + +#define USB_R4 0x90 + #define USB_R4_P21_PORT_RESET_0 BIT(0) + #define USB_R4_P21_SLEEP_M0 BIT(1) + #define USB_R4_MEM_PD_MASK GENMASK(3, 2) + #define USB_R4_P21_ONLY BIT(4) + +#define USB_R5 0x94 + #define USB_R5_ID_DIG_SYNC BIT(0) + #define USB_R5_ID_DIG_REG BIT(1) + #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) + #define USB_R5_ID_DIG_EN_0 BIT(4) + #define USB_R5_ID_DIG_EN_1 BIT(5) + #define USB_R5_ID_DIG_CURR BIT(6) + #define USB_R5_ID_DIG_IRQ BIT(7) + #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) + #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) + +enum { + USB2_HOST_PHY = 0, + USB2_OTG_PHY, + USB3_HOST_PHY, + PHY_COUNT, +}; + +static const char *phy_names[PHY_COUNT] = { + "usb2-phy0", "usb2-phy1", "usb3-phy0", +}; + +struct dwc3_meson_g12a { + struct udevice *dev; + struct regmap *regmap; + struct clk clk; + struct reset_ctl reset; + struct phy phys[PHY_COUNT]; + enum usb_dr_mode otg_mode; + enum usb_dr_mode otg_phy_mode; + unsigned int usb2_ports; + unsigned int usb3_ports; +#if CONFIG_IS_ENABLED(DM_REGULATOR) + struct udevice *vbus_supply; +#endif +}; + +#define U2P_REG_SIZE 0x20 +#define USB_REG_OFFSET 0x80 + +static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, + int i, enum usb_dr_mode mode) +{ + switch (mode) { + case USB_DR_MODE_HOST: + case USB_DR_MODE_OTG: + case USB_DR_MODE_UNKNOWN: + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, + U2P_R0_HOST_DEVICE); + break; + + case USB_DR_MODE_PERIPHERAL: + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, 0); + break; + } +} + +static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv) +{ + int i; + + if (priv->otg_mode == USB_DR_MODE_PERIPHERAL) + priv->otg_phy_mode = USB_DR_MODE_PERIPHERAL; + else + priv->otg_phy_mode = USB_DR_MODE_HOST; + + for (i = 0 ; i < USB3_HOST_PHY ; ++i) { + if (!priv->phys[i].dev) + continue; + + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_POWER_ON_RESET, + U2P_R0_POWER_ON_RESET); + + if (i == USB2_OTG_PHY) { + regmap_update_bits(priv->regmap, + U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, + U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS); + + dwc3_meson_g12a_usb2_set_mode(priv, i, + priv->otg_phy_mode); + } else + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_HOST); + + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_POWER_ON_RESET, 0); + } + + return 0; +} + +static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv) +{ + regmap_update_bits(priv->regmap, USB_R3, + USB_R3_P30_SSC_RANGE_MASK | + USB_R3_P30_REF_SSP_EN, + USB_R3_P30_SSC_ENABLE | + FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) | + USB_R3_P30_REF_SSP_EN); + udelay(2); + + regmap_update_bits(priv->regmap, USB_R2, + USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, + FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15)); + + regmap_update_bits(priv->regmap, USB_R2, + USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, + FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20)); + + udelay(2); + + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT); + + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_P30_PCS_TX_SWING_FULL_MASK, + FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127)); +} + +static void dwc3_meson_g12a_usb_init_mode(struct dwc3_meson_g12a *priv) +{ + if (priv->otg_phy_mode == USB_DR_MODE_PERIPHERAL) { + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, USB_R0_U2D_ACT); + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); + } else { + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, 0); + } +} + +static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv) +{ + int ret; + + ret = dwc3_meson_g12a_usb2_init(priv); + if (ret) + return ret; + + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_U3H_FLADJ_30MHZ_REG_MASK, + FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); + + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_0, + USB_R5_ID_DIG_EN_0); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_1, + USB_R5_ID_DIG_EN_1); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_TH_MASK, + FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff)); + + /* If we have an actual SuperSpeed port, initialize it */ + if (priv->usb3_ports) + dwc3_meson_g12a_usb3_init(priv); + + dwc3_meson_g12a_usb_init_mode(priv); + + return 0; +} + +int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode) +{ + struct dwc3_meson_g12a *priv = dev_get_platdata(dev); + + if (!priv) + return -EINVAL; + + if (mode != USB_DR_MODE_HOST && mode != USB_DR_MODE_PERIPHERAL) + return -EINVAL; + + if (!priv->phys[USB2_OTG_PHY].dev) + return -EINVAL; + + if (mode == priv->otg_mode) + return 0; + + if (mode == USB_DR_MODE_HOST) + debug("%s: switching to Host Mode\n", __func__); + else + debug("%s: switching to Device Mode\n", __func__); + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + if (priv->vbus_supply) { + int ret = regulator_set_enable(priv->vbus_supply, + (mode == USB_DR_MODE_PERIPHERAL)); + if (ret) + return ret; + } +#endif + priv->otg_phy_mode = mode; + + dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode); + + dwc3_meson_g12a_usb_init_mode(priv); + + return 0; +} + +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) +{ + int i, ret; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = generic_phy_get_by_name(priv->dev, phy_names[i], + &priv->phys[i]); + if (ret == -ENOENT) + continue; + + if (ret) + return ret; + + if (i == USB3_HOST_PHY) + priv->usb3_ports++; + else + priv->usb2_ports++; + } + + debug("%s: usb2 ports: %d\n", __func__, priv->usb2_ports); + debug("%s: usb3 ports: %d\n", __func__, priv->usb3_ports); + + return 0; +} + +static int dwc3_meson_g12a_reset_init(struct dwc3_meson_g12a *priv) +{ + int ret; + + ret = reset_get_by_index(priv->dev, 0, &priv->reset); + if (ret) + return ret; + + ret = reset_assert(&priv->reset); + udelay(1); + ret |= reset_deassert(&priv->reset); + if (ret) { + reset_free(&priv->reset); + return ret; + } + + return 0; +} + +static int dwc3_meson_g12a_clk_init(struct dwc3_meson_g12a *priv) +{ + int ret; + + ret = clk_get_by_index(priv->dev, 0, &priv->clk); + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_enable(&priv->clk); + if (ret) { + clk_free(&priv->clk); + return ret; + } +#endif + + return 0; +} + +static int dwc3_meson_g12a_probe(struct udevice *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_platdata(dev); + int ret, i; + + priv->dev = dev; + + ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); + if (ret) + return ret; + + ret = dwc3_meson_g12a_clk_init(priv); + if (ret) + return ret; + + ret = dwc3_meson_g12a_reset_init(priv); + if (ret) + return ret; + + ret = dwc3_meson_g12a_get_phys(priv); + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + ret = device_get_supply_regulator(dev, "vbus-supply", + &priv->vbus_supply); + if (ret && ret != -ENOENT) { + pr_err("Failed to get PHY regulator\n"); + return ret; + } + + if (priv->vbus_supply) { + ret = regulator_set_enable(priv->vbus_supply, true); + if (ret) + return ret; + } +#endif + + priv->otg_mode = usb_get_dr_mode(dev_of_offset(dev)); + + ret = dwc3_meson_g12a_usb_init(priv); + if (ret) + return ret; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + if (!priv->phys[i].dev) + continue; + + ret = generic_phy_init(&priv->phys[i]); + if (ret) + goto err_phy_init; + } + + return 0; + +err_phy_init: + for (i = 0 ; i < PHY_COUNT ; ++i) { + if (!priv->phys[i].dev) + continue; + + generic_phy_exit(&priv->phys[i]); + } + + return ret; +} + +static int dwc3_meson_g12a_remove(struct udevice *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_platdata(dev); + int i; + + reset_release_all(&priv->reset, 1); + + clk_release_all(&priv->clk, 1); + + for (i = 0 ; i < PHY_COUNT ; ++i) { + if (!priv->phys[i].dev) + continue; + + generic_phy_exit(&priv->phys[i]); + } + + return dm_scan_fdt_dev(dev); +} + +static const struct udevice_id dwc3_meson_g12a_ids[] = { + { .compatible = "amlogic,meson-g12a-usb-ctrl" }, + { } +}; + +U_BOOT_DRIVER(dwc3_generic_wrapper) = { + .name = "dwc3-meson-g12a", + .id = UCLASS_SIMPLE_BUS, + .of_match = dwc3_meson_g12a_ids, + .probe = dwc3_meson_g12a_probe, + .remove = dwc3_meson_g12a_remove, + .platdata_auto_alloc_size = sizeof(struct dwc3_meson_g12a), + +}; diff --git a/drivers/usb/gadget/f_rockusb.c b/drivers/usb/gadget/f_rockusb.c index e81eb164b0..f3d24772cd 100644 --- a/drivers/usb/gadget/f_rockusb.c +++ b/drivers/usb/gadget/f_rockusb.c @@ -15,7 +15,7 @@ #include <linux/compiler.h> #include <version.h> #include <g_dnl.h> -#include <asm/arch/f_rockusb.h> +#include <asm/arch-rockchip/f_rockusb.h> static inline struct f_rockusb *func_to_rockusb(struct usb_function *f) { diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 0fbc115801..b1188bcbf5 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -132,6 +132,13 @@ config USB_EHCI_MARVELL ---help--- Enables support for the on-chip EHCI controller on MVEBU SoCs. +config USB_EHCI_MX5 + bool "Support for i.MX5 on-chip EHCI USB controller" + depends on ARCH_MX5 + default n + help + Enables support for the on-chip EHCI controller on i.MX5 SoCs. + config USB_EHCI_MX6 bool "Support for i.MX6 on-chip EHCI USB controller" depends on ARCH_MX6 @@ -239,6 +246,11 @@ config USB_OHCI_GENERIC ---help--- Enables support for generic OHCI controller. +config USB_OHCI_DA8XX + bool "Support for da850 OHCI USB controller" + help + Enable support for the da850 USB controller. + endif # USB_OHCI_HCD config USB_UHCI_HCD diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 23e7e7125f..b8f8e7a794 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -75,8 +75,12 @@ static int ehci_fsl_init_after_reset(struct ehci_ctrl *ctrl) struct usb_ehci *ehci = NULL; struct ehci_fsl_priv *priv = container_of(ctrl, struct ehci_fsl_priv, ehci); - +#ifdef CONFIG_PPC + ehci = (struct usb_ehci *)lower_32_bits(priv->hcd_base); +#else ehci = (struct usb_ehci *)priv->hcd_base; +#endif + if (ehci_fsl_init(priv, ehci, priv->ehci.hccr, priv->ehci.hcor) < 0) return -ENXIO; @@ -103,7 +107,11 @@ static int ehci_fsl_probe(struct udevice *dev) debug("Can't get the EHCI register base address\n"); return -ENXIO; } +#ifdef CONFIG_PPC + ehci = (struct usb_ehci *)lower_32_bits(priv->hcd_base); +#else ehci = (struct usb_ehci *)priv->hcd_base; +#endif hccr = (struct ehci_hccr *)(&ehci->caplength); hcor = (struct ehci_hcor *) ((void *)hccr + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index 60f1470860..0b32728c57 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -12,6 +12,8 @@ #include <asm/io.h> #include <asm/arch/imx-regs.h> #include <asm/arch/clock.h> +#include <dm.h> +#include <power/regulator.h> #include "ehci.h" @@ -223,6 +225,7 @@ __weak void mx5_ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, mdelay(50); } +#if !CONFIG_IS_ENABLED(DM_USB) static const struct ehci_ops mx5_ehci_ops = { .powerup_fixup = mx5_ehci_powerup_fixup, }; @@ -267,3 +270,103 @@ int ehci_hcd_stop(int index) { return 0; } +#else /* CONFIG_IS_ENABLED(DM_USB) */ +struct ehci_mx5_priv_data { + struct ehci_ctrl ctrl; + struct usb_ehci *ehci; + struct udevice *vbus_supply; + enum usb_init_type init_type; + int portnr; +}; + +static const struct ehci_ops mx5_ehci_ops = { + .powerup_fixup = mx5_ehci_powerup_fixup, +}; + +static int ehci_usb_ofdata_to_platdata(struct udevice *dev) +{ + struct usb_platdata *plat = dev_get_platdata(dev); + const char *mode; + + mode = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "dr_mode", NULL); + if (mode) { + if (strcmp(mode, "peripheral") == 0) + plat->init_type = USB_INIT_DEVICE; + else if (strcmp(mode, "host") == 0) + plat->init_type = USB_INIT_HOST; + else + return -EINVAL; + } + + return 0; +} + +static int ehci_usb_probe(struct udevice *dev) +{ + struct usb_platdata *plat = dev_get_platdata(dev); + struct usb_ehci *ehci = (struct usb_ehci *)devfdt_get_addr(dev); + struct ehci_mx5_priv_data *priv = dev_get_priv(dev); + enum usb_init_type type = plat->init_type; + struct ehci_hccr *hccr; + struct ehci_hcor *hcor; + int ret; + + set_usboh3_clk(); + enable_usboh3_clk(true); + set_usb_phy_clk(); + enable_usb_phy1_clk(true); + enable_usb_phy2_clk(true); + mdelay(1); + + priv->ehci = ehci; + priv->portnr = dev->seq; + priv->init_type = type; + + ret = device_get_supply_regulator(dev, "vbus-supply", + &priv->vbus_supply); + if (ret) + debug("%s: No vbus supply\n", dev->name); + + if (!ret && priv->vbus_supply) { + ret = regulator_set_enable(priv->vbus_supply, + (type == USB_INIT_DEVICE) ? + false : true); + if (ret) { + puts("Error enabling VBUS supply\n"); + return ret; + } + } + + hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); + hcor = (struct ehci_hcor *)((uint32_t)hccr + + HC_LENGTH(ehci_readl(&(hccr)->cr_capbase))); + setbits_le32(&ehci->usbmode, CM_HOST); + + __raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc); + setbits_le32(&ehci->portsc, USB_EN); + + mxc_set_usbcontrol(priv->portnr, CONFIG_MXC_USB_FLAGS); + mdelay(10); + + return ehci_register(dev, hccr, hcor, &mx5_ehci_ops, 0, + priv->init_type); +} + +static const struct udevice_id mx5_usb_ids[] = { + { .compatible = "fsl,imx53-usb" }, + { } +}; + +U_BOOT_DRIVER(usb_mx5) = { + .name = "ehci_mx5", + .id = UCLASS_USB, + .of_match = mx5_usb_ids, + .ofdata_to_platdata = ehci_usb_ofdata_to_platdata, + .probe = ehci_usb_probe, + .remove = ehci_deregister, + .ops = &ehci_usb_ops, + .platdata_auto_alloc_size = sizeof(struct usb_platdata), + .priv_auto_alloc_size = sizeof(struct ehci_mx5_priv_data), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; +#endif /* !CONFIG_IS_ENABLED(DM_USB) */ diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 47ad3f34d5..233df57b4d 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -4,9 +4,54 @@ */ #include <common.h> - +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/ofnode.h> +#include <generic-phy.h> +#include <reset.h> +#include "ohci.h" #include <asm/arch/da8xx-usb.h> +struct da8xx_ohci { + ohci_t ohci; + struct clk *clocks; /* clock list */ + struct phy phy; + int clock_count; /* number of clock in clock list */ +}; + +static int usb_phy_on(void) +{ + unsigned long timeout; + + clrsetbits_le32(&davinci_syscfg_regs->cfgchip2, + (CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | + CFGCHIP2_OTGPWRDN | CFGCHIP2_OTGMODE | + CFGCHIP2_REFFREQ | CFGCHIP2_USB1PHYCLKMUX), + (CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN | + CFGCHIP2_PHY_PLLON | CFGCHIP2_REFFREQ_24MHZ | + CFGCHIP2_USB2PHYCLKMUX | CFGCHIP2_USB1SUSPENDM)); + + /* wait until the usb phy pll locks */ + timeout = get_timer(0); + while (get_timer(timeout) < 10) { + if (readl(&davinci_syscfg_regs->cfgchip2) & CFGCHIP2_PHYCLKGD) + return 1; + } + + /* USB phy was not turned on */ + return 0; +} + +static void usb_phy_off(void) +{ + /* Power down the on-chip PHY. */ + clrsetbits_le32(&davinci_syscfg_regs->cfgchip2, + CFGCHIP2_PHY_PLLON | CFGCHIP2_USB1SUSPENDM, + CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN | + CFGCHIP2_RESET); +} + int usb_cpu_init(void) { /* enable psc for usb2.0 */ @@ -37,3 +82,95 @@ int usb_cpu_init_fail(void) { return usb_cpu_stop(); } + +#if CONFIG_IS_ENABLED(DM_USB) +static int ohci_da8xx_probe(struct udevice *dev) +{ + struct ohci_regs *regs = (struct ohci_regs *)devfdt_get_addr(dev); + struct da8xx_ohci *priv = dev_get_priv(dev); + int i, err, ret, clock_nb; + + err = 0; + priv->clock_count = 0; + clock_nb = dev_count_phandle_with_args(dev, "clocks", "#clock-cells"); + + if (clock_nb < 0) + return clock_nb; + + if (clock_nb > 0) { + priv->clocks = devm_kcalloc(dev, clock_nb, sizeof(struct clk), + GFP_KERNEL); + if (!priv->clocks) + return -ENOMEM; + + for (i = 0; i < clock_nb; i++) { + err = clk_get_by_index(dev, i, &priv->clocks[i]); + if (err < 0) + break; + + err = clk_enable(&priv->clocks[i]); + if (err) { + dev_err(dev, "failed to enable clock %d\n", i); + clk_free(&priv->clocks[i]); + goto clk_err; + } + priv->clock_count++; + } + } + + err = usb_cpu_init(); + + if (err) + goto clk_err; + + err = ohci_register(dev, regs); + if (err) + goto phy_err; + + return 0; + +phy_err: + ret = usb_cpu_stop(); + if (ret) + dev_err(dev, "failed to shutdown usb phy\n"); + +clk_err: + ret = clk_release_all(priv->clocks, priv->clock_count); + if (ret) + dev_err(dev, "failed to disable all clocks\n"); + + return err; +} + +static int ohci_da8xx_remove(struct udevice *dev) +{ + struct da8xx_ohci *priv = dev_get_priv(dev); + int ret; + + ret = ohci_deregister(dev); + if (ret) + return ret; + + ret = usb_cpu_stop(); + if (ret) + return ret; + + return clk_release_all(priv->clocks, priv->clock_count); +} + +static const struct udevice_id da8xx_ohci_ids[] = { + { .compatible = "ti,da830-ohci" }, + { } +}; + +U_BOOT_DRIVER(ohci_generic) = { + .name = "ohci-da8xx", + .id = UCLASS_USB, + .of_match = da8xx_ohci_ids, + .probe = ohci_da8xx_probe, + .remove = ohci_da8xx_remove, + .ops = &ohci_usb_ops, + .priv_auto_alloc_size = sizeof(struct da8xx_ohci), + .flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_OS_PREPARE, +}; +#endif diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 3b6f889f7b..2b0df88f49 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1545,10 +1545,8 @@ static int submit_common_msg(ohci_t *ohci, struct usb_device *dev, return -1; } -#if 0 mdelay(10); /* ohci_dump_status(ohci); */ -#endif timeout = USB_TIMEOUT_MS(pipe); diff --git a/drivers/usb/musb/musb_hcd.c b/drivers/usb/musb/musb_hcd.c index 2ee0f23b7e..1f2805270a 100644 --- a/drivers/usb/musb/musb_hcd.c +++ b/drivers/usb/musb/musb_hcd.c @@ -327,9 +327,7 @@ static int ctrlreq_out_data_phase(struct usb_device *dev, u32 len, void *buffer) csr = readw(&musbr->txcsr); csr |= MUSB_CSR0_TXPKTRDY; -#if !defined(CONFIG_SOC_DM365) csr |= MUSB_CSR0_H_DIS_PING; -#endif writew(csr, &musbr->txcsr); result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); if (result < 0) @@ -352,9 +350,7 @@ static int ctrlreq_out_status_phase(struct usb_device *dev) /* Set the StatusPkt bit */ csr = readw(&musbr->txcsr); csr |= (MUSB_CSR0_TXPKTRDY | MUSB_CSR0_H_STATUSPKT); -#if !defined(CONFIG_SOC_DM365) csr |= MUSB_CSR0_H_DIS_PING; -#endif writew(csr, &musbr->txcsr); /* Wait until TXPKTRDY bit is cleared */ @@ -372,9 +368,7 @@ static int ctrlreq_in_status_phase(struct usb_device *dev) /* Set the StatusPkt bit and ReqPkt bit */ csr = MUSB_CSR0_H_REQPKT | MUSB_CSR0_H_STATUSPKT; -#if !defined(CONFIG_SOC_DM365) csr |= MUSB_CSR0_H_DIS_PING; -#endif writew(csr, &musbr->txcsr); result = wait_until_ep0_ready(dev, MUSB_CSR0_H_REQPKT); diff --git a/drivers/video/imx/mxc_ipuv3_fb.c b/drivers/video/imx/mxc_ipuv3_fb.c index 3e38d4bdcc..29ecac40a2 100644 --- a/drivers/video/imx/mxc_ipuv3_fb.c +++ b/drivers/video/imx/mxc_ipuv3_fb.c @@ -678,13 +678,14 @@ static int ipuv3_video_bind(struct udevice *dev) struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * - (1 << LCD_MAX_LOG2_BPP) / 8; + (1 << VIDEO_BPP32) / 8; return 0; } static const struct udevice_id ipuv3_video_ids[] = { { .compatible = "fsl,imx6q-ipu" }, + { .compatible = "fsl,imx53-ipu" }, { } }; diff --git a/drivers/video/rockchip/rk3288_hdmi.c b/drivers/video/rockchip/rk3288_hdmi.c index eb3692c387..315d3adf27 100644 --- a/drivers/video/rockchip/rk3288_hdmi.c +++ b/drivers/video/rockchip/rk3288_hdmi.c @@ -13,9 +13,9 @@ #include <syscon.h> #include <asm/gpio.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/hardware.h> -#include <asm/arch/grf_rk3288.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/hardware.h> +#include <asm/arch-rockchip/grf_rk3288.h> #include <power/regulator.h> #include "rk_hdmi.h" diff --git a/drivers/video/rockchip/rk3288_mipi.c b/drivers/video/rockchip/rk3288_mipi.c index d268b46514..7c4a4cc53b 100644 --- a/drivers/video/rockchip/rk3288_mipi.c +++ b/drivers/video/rockchip/rk3288_mipi.c @@ -14,14 +14,14 @@ #include "rk_mipi.h" #include <syscon.h> #include <asm/gpio.h> -#include <asm/hardware.h> #include <asm/io.h> #include <dm/uclass-internal.h> #include <linux/kernel.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3288.h> -#include <asm/arch/grf_rk3288.h> -#include <asm/arch/rockchip_mipi_dsi.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3288.h> +#include <asm/arch-rockchip/grf_rk3288.h> +#include <asm/arch-rockchip/hardware.h> +#include <asm/arch-rockchip/rockchip_mipi_dsi.h> #define MHz 1000000 diff --git a/drivers/video/rockchip/rk3288_vop.c b/drivers/video/rockchip/rk3288_vop.c index 7e953a628c..0f91dab1f2 100644 --- a/drivers/video/rockchip/rk3288_vop.c +++ b/drivers/video/rockchip/rk3288_vop.c @@ -11,10 +11,10 @@ #include <regmap.h> #include <syscon.h> #include <video.h> -#include <asm/hardware.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/grf_rk3288.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/grf_rk3288.h> +#include <asm/arch-rockchip/hardware.h> #include "rk_vop.h" DECLARE_GLOBAL_DATA_PTR; diff --git a/drivers/video/rockchip/rk3399_hdmi.c b/drivers/video/rockchip/rk3399_hdmi.c index b75efe6fc3..a62be98327 100644 --- a/drivers/video/rockchip/rk3399_hdmi.c +++ b/drivers/video/rockchip/rk3399_hdmi.c @@ -13,9 +13,9 @@ #include <syscon.h> #include <asm/gpio.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/hardware.h> -#include <asm/arch/grf_rk3399.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/hardware.h> +#include <asm/arch-rockchip/grf_rk3399.h> #include <power/regulator.h> #include "rk_hdmi.h" diff --git a/drivers/video/rockchip/rk3399_mipi.c b/drivers/video/rockchip/rk3399_mipi.c index bb9007bf36..a93b73400b 100644 --- a/drivers/video/rockchip/rk3399_mipi.c +++ b/drivers/video/rockchip/rk3399_mipi.c @@ -14,14 +14,14 @@ #include "rk_mipi.h" #include <syscon.h> #include <asm/gpio.h> -#include <asm/hardware.h> #include <asm/io.h> #include <dm/uclass-internal.h> #include <linux/kernel.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3399.h> -#include <asm/arch/grf_rk3399.h> -#include <asm/arch/rockchip_mipi_dsi.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3399.h> +#include <asm/arch-rockchip/grf_rk3399.h> +#include <asm/arch-rockchip/hardware.h> +#include <asm/arch-rockchip/rockchip_mipi_dsi.h> /* Select mipi dsi source, big or little vop */ static int rk_mipi_dsi_source_select(struct udevice *dev) diff --git a/drivers/video/rockchip/rk3399_vop.c b/drivers/video/rockchip/rk3399_vop.c index 7a02221ae0..81c122d7a9 100644 --- a/drivers/video/rockchip/rk3399_vop.c +++ b/drivers/video/rockchip/rk3399_vop.c @@ -10,7 +10,7 @@ #include <dm.h> #include <regmap.h> #include <video.h> -#include <asm/hardware.h> +#include <asm/arch-rockchip/hardware.h> #include <asm/io.h> #include "rk_vop.h" diff --git a/drivers/video/rockchip/rk_edp.c b/drivers/video/rockchip/rk_edp.c index e074107632..4330725a25 100644 --- a/drivers/video/rockchip/rk_edp.c +++ b/drivers/video/rockchip/rk_edp.c @@ -14,9 +14,9 @@ #include <syscon.h> #include <asm/gpio.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/edp_rk3288.h> -#include <asm/arch/grf_rk3288.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/edp_rk3288.h> +#include <asm/arch-rockchip/grf_rk3288.h> #include <dt-bindings/clock/rk3288-cru.h> #define MAX_CR_LOOP 5 diff --git a/drivers/video/rockchip/rk_hdmi.c b/drivers/video/rockchip/rk_hdmi.c index 13d07ee304..51931ceefa 100644 --- a/drivers/video/rockchip/rk_hdmi.c +++ b/drivers/video/rockchip/rk_hdmi.c @@ -14,10 +14,9 @@ #include <regmap.h> #include <syscon.h> #include <asm/gpio.h> -#include <asm/hardware.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/hardware.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/hardware.h> #include "rk_hdmi.h" #include "rk_vop.h" /* for rk_vop_probe_regulators */ diff --git a/drivers/video/rockchip/rk_lvds.c b/drivers/video/rockchip/rk_lvds.c index f0a528c0d6..cf5c0439b1 100644 --- a/drivers/video/rockchip/rk_lvds.c +++ b/drivers/video/rockchip/rk_lvds.c @@ -12,9 +12,9 @@ #include <syscon.h> #include <asm/gpio.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/lvds_rk3288.h> -#include <asm/arch/grf_rk3288.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/lvds_rk3288.h> +#include <asm/arch-rockchip/grf_rk3288.h> #include <dt-bindings/clock/rk3288-cru.h> #include <dt-bindings/video/rk3288.h> diff --git a/drivers/video/rockchip/rk_mipi.c b/drivers/video/rockchip/rk_mipi.c index 4f1a0f3a5f..bcd039b7bc 100644 --- a/drivers/video/rockchip/rk_mipi.c +++ b/drivers/video/rockchip/rk_mipi.c @@ -14,14 +14,13 @@ #include "rk_mipi.h" #include <syscon.h> #include <asm/gpio.h> -#include <asm/hardware.h> #include <asm/io.h> #include <dm/uclass-internal.h> #include <linux/kernel.h> -#include <asm/arch/clock.h> -#include <asm/arch/cru_rk3399.h> -#include <asm/arch/grf_rk3399.h> -#include <asm/arch/rockchip_mipi_dsi.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_rk3399.h> +#include <asm/arch-rockchip/grf_rk3399.h> +#include <asm/arch-rockchip/rockchip_mipi_dsi.h> DECLARE_GLOBAL_DATA_PTR; diff --git a/drivers/video/rockchip/rk_vop.c b/drivers/video/rockchip/rk_vop.c index faf4f24db0..b56c3f336c 100644 --- a/drivers/video/rockchip/rk_vop.c +++ b/drivers/video/rockchip/rk_vop.c @@ -13,11 +13,10 @@ #include <syscon.h> #include <video.h> #include <asm/gpio.h> -#include <asm/hardware.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/edp_rk3288.h> -#include <asm/arch/vop_rk3288.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/edp_rk3288.h> +#include <asm/arch-rockchip/vop_rk3288.h> #include <dm/device-internal.h> #include <dm/uclass-internal.h> #include <power/regulator.h> diff --git a/drivers/video/rockchip/rk_vop.h b/drivers/video/rockchip/rk_vop.h index 828974d394..8fa2f38939 100644 --- a/drivers/video/rockchip/rk_vop.h +++ b/drivers/video/rockchip/rk_vop.h @@ -6,7 +6,7 @@ #ifndef __RK_VOP_H__ #define __RK_VOP_H__ -#include <asm/arch/vop_rk3288.h> +#include <asm/arch-rockchip/vop_rk3288.h> struct rk_vop_priv { void *grf; diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index c31303b56e..af88588904 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -529,6 +529,20 @@ int vidconsole_put_char(struct udevice *dev, char ch) return 0; } +int vidconsole_put_string(struct udevice *dev, const char *str) +{ + const char *s; + int ret; + + for (s = str; *s; s++) { + ret = vidconsole_put_char(dev, *s); + if (ret) + return ret; + } + + return 0; +} + static void vidconsole_putc(struct stdio_dev *sdev, const char ch) { struct udevice *dev = sdev->priv; @@ -541,8 +555,7 @@ static void vidconsole_puts(struct stdio_dev *sdev, const char *s) { struct udevice *dev = sdev->priv; - while (*s) - vidconsole_put_char(dev, *s++); + vidconsole_put_string(dev, s); video_sync(dev->parent, false); } diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index 14aac88d6d..b19bfb4f2f 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -149,7 +149,7 @@ void video_sync(struct udevice *vid, bool force) * architectures do not actually implement it. Is there a way to find * out whether it exists? For now, ARM is safe. */ -#if defined(CONFIG_ARM) && !defined(CONFIG_SYS_DCACHE_OFF) +#if defined(CONFIG_ARM) && !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) struct video_priv *priv = dev_get_uclass_priv(vid); if (priv->flush_dcache) { diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 3bce0aa0b8..b01dbc446d 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -55,7 +55,7 @@ config WDT help Enable driver model for watchdog timer. At the moment the API is very simple and only supports four operations: - start, restart, stop and reset (expire immediately). + start, stop, reset and expire_now (expire immediately). What exactly happens when the timer expires is up to a particular device/driver. @@ -103,6 +103,13 @@ config WDT_ORION Select this to enable Orion watchdog timer, which can be found on some Marvell Armada chips. +config WDT_SP805 + bool "SP805 watchdog timer support" + depends on WDT + help + Select this to enable SP805 watchdog timer, which can be found on some + nxp layerscape chips. + config WDT_CDNS bool "Cadence watchdog timer support" depends on WDT @@ -143,7 +150,7 @@ config WDT_AT91 config WDT_MT7621 bool "MediaTek MT7621 watchdog timer support" - depends on WDT && ARCH_MT7620 + depends on WDT && SOC_MT7628 help Select this to enable Ralink / Mediatek watchdog timer, which can be found on some MediaTek chips. diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 40b2f4bc66..6f20e73810 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o obj-$(CONFIG_WDT_MTK) += mtk_wdt.o +obj-$(CONFIG_WDT_SP805) += sp805_wdt.o diff --git a/drivers/watchdog/bcm6345_wdt.c b/drivers/watchdog/bcm6345_wdt.c index 44f5662038..9f14e7d777 100644 --- a/drivers/watchdog/bcm6345_wdt.c +++ b/drivers/watchdog/bcm6345_wdt.c @@ -10,6 +10,7 @@ #include <common.h> #include <dm.h> #include <wdt.h> +#include <clk.h> #include <asm/io.h> /* WDT Value register */ @@ -26,6 +27,7 @@ struct bcm6345_wdt_priv { void __iomem *regs; + unsigned long clk_rate; }; static int bcm6345_wdt_reset(struct udevice *dev) @@ -41,16 +43,17 @@ static int bcm6345_wdt_reset(struct udevice *dev) static int bcm6345_wdt_start(struct udevice *dev, u64 timeout, ulong flags) { struct bcm6345_wdt_priv *priv = dev_get_priv(dev); + u32 val = priv->clk_rate / 1000 * timeout; - if (timeout < WDT_VAL_MIN) { + if (val < WDT_VAL_MIN) { debug("watchdog won't fire with less than 2 ticks\n"); - timeout = WDT_VAL_MIN; - } else if (timeout > WDT_VAL_MAX) { + val = WDT_VAL_MIN; + } else if (val > WDT_VAL_MAX) { debug("maximum watchdog timeout exceeded\n"); - timeout = WDT_VAL_MAX; + val = WDT_VAL_MAX; } - writel(timeout, priv->regs + WDT_VAL_REG); + writel(val, priv->regs + WDT_VAL_REG); return bcm6345_wdt_reset(dev); } @@ -85,11 +88,19 @@ static const struct udevice_id bcm6345_wdt_ids[] = { static int bcm6345_wdt_probe(struct udevice *dev) { struct bcm6345_wdt_priv *priv = dev_get_priv(dev); + struct clk clk; + int ret; priv->regs = dev_remap_addr(dev); if (!priv->regs) return -EINVAL; + ret = clk_get_by_index(dev, 0, &clk); + if (!ret) + priv->clk_rate = clk_get_rate(&clk); + else + return -EINVAL; + bcm6345_wdt_stop(dev); return 0; diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c new file mode 100644 index 0000000000..966128216f --- /dev/null +++ b/drivers/watchdog/sp805_wdt.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Watchdog driver for SP805 on some Layerscape SoC + * + * Copyright 2019 NXP + */ + +#include <asm/io.h> +#include <common.h> +#include <dm/device.h> +#include <dm/fdtaddr.h> +#include <dm/read.h> +#include <linux/bitops.h> +#include <watchdog.h> +#include <wdt.h> + +#define WDTLOAD 0x000 +#define WDTCONTROL 0x008 +#define WDTINTCLR 0x00C +#define WDTLOCK 0xC00 + +#define TIME_OUT_MIN_MSECS 1 +#define TIME_OUT_MAX_MSECS 120000 +#define SYS_FSL_WDT_CLK_DIV 16 +#define INT_ENABLE BIT(0) +#define RESET_ENABLE BIT(1) +#define DISABLE 0 +#define UNLOCK 0x1ACCE551 +#define LOCK 0x00000001 +#define INT_MASK BIT(0) + +DECLARE_GLOBAL_DATA_PTR; + +struct sp805_wdt_priv { + void __iomem *reg; +}; + +static int sp805_wdt_reset(struct udevice *dev) +{ + struct sp805_wdt_priv *priv = dev_get_priv(dev); + + writel(UNLOCK, priv->reg + WDTLOCK); + writel(INT_MASK, priv->reg + WDTINTCLR); + writel(LOCK, priv->reg + WDTLOCK); + readl(priv->reg + WDTLOCK); + + return 0; +} + +static int sp805_wdt_start(struct udevice *dev, u64 timeout, ulong flags) +{ + u32 load_value; + u32 load_time; + struct sp805_wdt_priv *priv = dev_get_priv(dev); + + load_time = (u32)timeout; + if (timeout < TIME_OUT_MIN_MSECS) + load_time = TIME_OUT_MIN_MSECS; + else if (timeout > TIME_OUT_MAX_MSECS) + load_time = TIME_OUT_MAX_MSECS; + /* sp805 runs counter with given value twice, so when the max timeout is + * set 120s, the gd->bus_clk is less than 1145MHz, the load_value will + * not overflow. + */ + load_value = (gd->bus_clk) / + (2 * 1000 * SYS_FSL_WDT_CLK_DIV) * load_time; + + writel(UNLOCK, priv->reg + WDTLOCK); + writel(load_value, priv->reg + WDTLOAD); + writel(INT_MASK, priv->reg + WDTINTCLR); + writel(INT_ENABLE | RESET_ENABLE, priv->reg + WDTCONTROL); + writel(LOCK, priv->reg + WDTLOCK); + readl(priv->reg + WDTLOCK); + + return 0; +} + +static int sp805_wdt_stop(struct udevice *dev) +{ + struct sp805_wdt_priv *priv = dev_get_priv(dev); + + writel(UNLOCK, priv->reg + WDTLOCK); + writel(DISABLE, priv->reg + WDTCONTROL); + writel(LOCK, priv->reg + WDTLOCK); + readl(priv->reg + WDTLOCK); + + return 0; +} + +static int sp805_wdt_probe(struct udevice *dev) +{ + debug("%s: Probing wdt%u\n", __func__, dev->seq); + + return 0; +} + +static int sp805_wdt_ofdata_to_platdata(struct udevice *dev) +{ + struct sp805_wdt_priv *priv = dev_get_priv(dev); + + priv->reg = (void __iomem *)dev_read_addr(dev); + if (IS_ERR(priv->reg)) + return PTR_ERR(priv->reg); + + return 0; +} + +static const struct wdt_ops sp805_wdt_ops = { + .start = sp805_wdt_start, + .reset = sp805_wdt_reset, + .stop = sp805_wdt_stop, +}; + +static const struct udevice_id sp805_wdt_ids[] = { + { .compatible = "arm,sp805-wdt" }, + {} +}; + +U_BOOT_DRIVER(sp805_wdt) = { + .name = "sp805_wdt", + .id = UCLASS_WDT, + .of_match = sp805_wdt_ids, + .probe = sp805_wdt_probe, + .priv_auto_alloc_size = sizeof(struct sp805_wdt_priv), + .ofdata_to_platdata = sp805_wdt_ofdata_to_platdata, + .ops = &sp805_wdt_ops, +}; |