diff options
Diffstat (limited to 'drivers')
56 files changed, 7421 insertions, 1516 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 87636ae30f..fe589d3aa8 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -87,6 +87,7 @@ config DWC_AHSATA_AHCI config FSL_SATA bool "Enable Freescale SATA controller driver support" select LIBATA + select AHCI if BLK help Enable this driver to support the SATA controller found in some Freescale PowerPC SoCs. @@ -109,6 +110,7 @@ config SATA_MV config SATA_SIL bool "Enable Silicon Image SIL3131 / SIL3132 / SIL3124 SATA driver support" select LIBATA + select AHCI if BLK help Enable this driver to support the SIL3131, SIL3132 and SIL3124 SATA controllers. diff --git a/drivers/ata/fsl_sata.c b/drivers/ata/fsl_sata.c index e70a515eff..3261c10f91 100644 --- a/drivers/ata/fsl_sata.c +++ b/drivers/ata/fsl_sata.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2008,2010 Freescale Semiconductor, Inc. - * Dave Liu <daveliu@freescale.com> + * Copyright 2019 NXP + * Author: Dave Liu <daveliu@freescale.com> */ #include <common.h> @@ -16,6 +17,11 @@ #include <sata.h> #include "fsl_sata.h" +#if CONFIG_IS_ENABLED(BLK) +#include <dm.h> +#include <ahci.h> +#include <blk.h> +#else #ifndef CONFIG_SYS_SATA1_FLAGS #define CONFIG_SYS_SATA1_FLAGS FLAGS_DMA #endif @@ -35,6 +41,7 @@ static struct fsl_sata_info fsl_sata_info[] = { {0, 0}, #endif }; +#endif static inline void sdelay(unsigned long sec) { @@ -74,7 +81,11 @@ static int ata_wait_register(unsigned __iomem *addr, u32 mask, return (i < timeout_msec) ? 0 : -1; } +#if !CONFIG_IS_ENABLED(BLK) int init_sata(int dev) +#else +static int init_sata(struct fsl_ata_priv *priv, int dev) +#endif { u32 length, align; cmd_hdr_tbl_t *cmd_hdr; @@ -110,13 +121,18 @@ int init_sata(int dev) /* Zero all of the device driver struct */ memset((void *)sata, 0, sizeof(fsl_sata_t)); - /* Save the private struct to block device struct */ - sata_dev_desc[dev].priv = (void *)sata; - - snprintf(sata->name, 12, "SATA%d", dev); + snprintf(sata->name, 12, "SATA%d:\n", dev); /* Set the controller register base address to device struct */ +#if !CONFIG_IS_ENABLED(BLK) + sata_dev_desc[dev].priv = (void *)sata; reg = (fsl_sata_reg_t *)(fsl_sata_info[dev].sata_reg_base); + sata->dma_flag = fsl_sata_info[dev].flags; +#else + reg = (fsl_sata_reg_t *)(priv->base + priv->offset * dev); + sata->dma_flag = priv->flag; + priv->fsl_sata = sata; +#endif sata->reg_base = reg; /* Allocate the command header table, 4 bytes aligned */ @@ -479,34 +495,16 @@ static int fsl_sata_exec_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis, return -1; } -static void fsl_sata_identify(int dev, u16 *id) -{ - fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; - 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(int dev, u16 *id) +static void fsl_sata_xfer_mode(fsl_sata_t *sata, u16 *id) { - fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; - 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_set_features(int dev) +static void fsl_sata_set_features(fsl_sata_t *sata) { - fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; struct sata_fis_h2d h2d, *cfis = &h2d; u8 udma_cap; @@ -533,9 +531,9 @@ static void fsl_sata_set_features(int dev) fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0); } -static u32 fsl_sata_rw_cmd(int dev, u32 start, u32 blkcnt, u8 *buffer, int is_write) +static u32 fsl_sata_rw_cmd(fsl_sata_t *sata, u32 start, u32 blkcnt, u8 *buffer, + int is_write) { - fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; struct sata_fis_h2d h2d, *cfis = &h2d; u32 block; @@ -558,9 +556,8 @@ static u32 fsl_sata_rw_cmd(int dev, u32 start, u32 blkcnt, u8 *buffer, int is_wr return blkcnt; } -static void fsl_sata_flush_cache(int dev) +static void fsl_sata_flush_cache(fsl_sata_t *sata) { - fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; struct sata_fis_h2d h2d, *cfis = &h2d; memset(cfis, 0, sizeof(struct sata_fis_h2d)); @@ -572,9 +569,9 @@ static void fsl_sata_flush_cache(int dev) fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0); } -static u32 fsl_sata_rw_cmd_ext(int dev, u32 start, u32 blkcnt, u8 *buffer, int is_write) +static u32 fsl_sata_rw_cmd_ext(fsl_sata_t *sata, u32 start, u32 blkcnt, + u8 *buffer, int is_write) { - fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; struct sata_fis_h2d h2d, *cfis = &h2d; u64 block; @@ -602,10 +599,9 @@ static u32 fsl_sata_rw_cmd_ext(int dev, u32 start, u32 blkcnt, u8 *buffer, int i return blkcnt; } -static u32 fsl_sata_rw_ncq_cmd(int dev, u32 start, u32 blkcnt, u8 *buffer, - int is_write) +static u32 fsl_sata_rw_ncq_cmd(fsl_sata_t *sata, u32 start, u32 blkcnt, + u8 *buffer, int is_write) { - fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; struct sata_fis_h2d h2d, *cfis = &h2d; int ncq_channel; u64 block; @@ -646,9 +642,8 @@ static u32 fsl_sata_rw_ncq_cmd(int dev, u32 start, u32 blkcnt, u8 *buffer, return blkcnt; } -static void fsl_sata_flush_cache_ext(int dev) +static void fsl_sata_flush_cache_ext(fsl_sata_t *sata) { - fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; struct sata_fis_h2d h2d, *cfis = &h2d; memset(cfis, 0, sizeof(struct sata_fis_h2d)); @@ -660,10 +655,8 @@ static void fsl_sata_flush_cache_ext(int dev) fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0); } -static void fsl_sata_init_wcache(int dev, u16 *id) +static void fsl_sata_init_wcache(fsl_sata_t *sata, u16 *id) { - fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; - if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id)) sata->wcache = 1; if (ata_id_has_flush(id)) @@ -672,26 +665,8 @@ static void fsl_sata_init_wcache(int dev, u16 *id) sata->flush_ext = 1; } -static int fsl_sata_get_wcache(int dev) -{ - fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; - return sata->wcache; -} - -static int fsl_sata_get_flush(int dev) -{ - fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; - return sata->flush; -} - -static int fsl_sata_get_flush_ext(int dev) -{ - fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; - return sata->flush_ext; -} - -static u32 ata_low_level_rw_lba48(int dev, u32 blknr, lbaint_t blkcnt, - const void *buffer, int is_write) +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; @@ -704,18 +679,22 @@ static u32 ata_low_level_rw_lba48(int dev, u32 blknr, lbaint_t blkcnt, max_blks = ATA_MAX_SECTORS_LBA48; do { if (blks > max_blks) { - if (fsl_sata_info[dev].flags != FLAGS_FPDMA) - fsl_sata_rw_cmd_ext(dev, start, max_blks, addr, is_write); + if (sata->dma_flag != FLAGS_FPDMA) + fsl_sata_rw_cmd_ext(sata, start, max_blks, addr, + is_write); else - fsl_sata_rw_ncq_cmd(dev, start, max_blks, addr, is_write); + 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 (fsl_sata_info[dev].flags != FLAGS_FPDMA) - fsl_sata_rw_cmd_ext(dev, start, blks, addr, is_write); + if (sata->dma_flag != FLAGS_FPDMA) + fsl_sata_rw_cmd_ext(sata, start, blks, addr, + is_write); else - fsl_sata_rw_ncq_cmd(dev, start, blks, addr, is_write); + fsl_sata_rw_ncq_cmd(sata, start, blks, addr, + is_write); start += blks; blks = 0; addr += ATA_SECT_SIZE * blks; @@ -725,7 +704,7 @@ static u32 ata_low_level_rw_lba48(int dev, u32 blknr, lbaint_t blkcnt, return blkcnt; } -static u32 ata_low_level_rw_lba28(int dev, u32 blknr, u32 blkcnt, +static u32 ata_low_level_rw_lba28(fsl_sata_t *sata, u32 blknr, u32 blkcnt, const void *buffer, int is_write) { u32 start, blks; @@ -739,12 +718,12 @@ static u32 ata_low_level_rw_lba28(int dev, u32 blknr, u32 blkcnt, max_blks = ATA_MAX_SECTORS; do { if (blks > max_blks) { - fsl_sata_rw_cmd(dev, start, max_blks, addr, is_write); + 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(dev, start, blks, addr, is_write); + fsl_sata_rw_cmd(sata, start, blks, addr, is_write); start += blks; blks = 0; addr += ATA_SECT_SIZE * blks; @@ -757,38 +736,81 @@ static u32 ata_low_level_rw_lba28(int dev, u32 blknr, u32 blkcnt, /* * SATA interface between low level driver and command layer */ +#if !CONFIG_IS_ENABLED(BLK) ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer) { - u32 rc; fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; +#else +static ulong sata_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, + void *buffer) +{ + struct fsl_ata_priv *priv = dev_get_platdata(dev); + fsl_sata_t *sata = priv->fsl_sata; +#endif + u32 rc; if (sata->lba48) - rc = ata_low_level_rw_lba48(dev, blknr, blkcnt, buffer, READ_CMD); + rc = ata_low_level_rw_lba48(sata, blknr, blkcnt, buffer, + READ_CMD); else - rc = ata_low_level_rw_lba28(dev, blknr, blkcnt, buffer, READ_CMD); + rc = ata_low_level_rw_lba28(sata, blknr, blkcnt, buffer, + READ_CMD); return rc; } +#if !CONFIG_IS_ENABLED(BLK) ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, const void *buffer) { - u32 rc; fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; +#else +static ulong sata_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, + const void *buffer) +{ + struct fsl_ata_priv *priv = dev_get_platdata(dev); + fsl_sata_t *sata = priv->fsl_sata; +#endif + u32 rc; if (sata->lba48) { - rc = ata_low_level_rw_lba48(dev, blknr, blkcnt, buffer, WRITE_CMD); - if (fsl_sata_get_wcache(dev) && fsl_sata_get_flush_ext(dev)) - fsl_sata_flush_cache_ext(dev); + 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(dev, blknr, blkcnt, buffer, WRITE_CMD); - if (fsl_sata_get_wcache(dev) && fsl_sata_get_flush(dev)) - fsl_sata_flush_cache(dev); + rc = ata_low_level_rw_lba28(sata, blknr, blkcnt, buffer, + WRITE_CMD); + if (sata->wcache && sata->flush) + fsl_sata_flush_cache(sata); } return rc; } +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); +} + +#if !CONFIG_IS_ENABLED(BLK) int scan_sata(int dev) { fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; +#else +static int scan_sata(struct udevice *dev) +{ + struct blk_desc *desc = dev_get_uclass_platdata(dev); + struct fsl_ata_priv *priv = dev_get_platdata(dev); + fsl_sata_t *sata = priv->fsl_sata; +#endif + unsigned char serial[ATA_ID_SERNO_LEN + 1]; unsigned char firmware[ATA_ID_FW_REV_LEN + 1]; unsigned char product[ATA_ID_PROD_LEN + 1]; @@ -806,23 +828,19 @@ int scan_sata(int dev) } /* Identify device to get information */ - fsl_sata_identify(dev, id); + fsl_sata_identify(sata, id); /* Serial number */ ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial)); - memcpy(sata_dev_desc[dev].product, serial, sizeof(serial)); /* Firmware version */ ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware)); - memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware)); /* Product model */ ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product)); - memcpy(sata_dev_desc[dev].vendor, product, sizeof(product)); /* Totoal sectors */ n_sectors = ata_id_n_sectors(id); - sata_dev_desc[dev].lba = (u32)n_sectors; #ifdef CONFIG_LBA48 /* Check if support LBA48 */ @@ -833,21 +851,136 @@ int scan_sata(int dev) debug("Device supports LBA28\n\r"); #endif +#if !CONFIG_IS_ENABLED(BLK) + memcpy(sata_dev_desc[dev].product, serial, sizeof(serial)); + memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware)); + memcpy(sata_dev_desc[dev].vendor, product, sizeof(product)); + sata_dev_desc[dev].lba = (u32)n_sectors; +#ifdef CONFIG_LBA48 + sata_dev_desc[dev].lba48 = sata->lba48; +#endif +#else + memcpy(desc->product, serial, sizeof(serial)); + memcpy(desc->revision, firmware, sizeof(firmware)); + memcpy(desc->vendor, product, sizeof(product)); + desc->lba = n_sectors; +#ifdef CONFIG_LBA48 + desc->lba48 = sata->lba48; +#endif +#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(dev, id); + fsl_sata_xfer_mode(sata, id); /* Get the write cache status from device */ - fsl_sata_init_wcache(dev, id); + fsl_sata_init_wcache(sata, id); /* Set the xfer mode to highest speed */ - fsl_sata_set_features(dev); + fsl_sata_set_features(sata); + #ifdef DEBUG - fsl_sata_identify(dev, id); ata_dump_id(id); #endif free((void *)id); return 0; } + +#if CONFIG_IS_ENABLED(BLK) +static const struct blk_ops sata_fsl_blk_ops = { + .read = sata_read, + .write = sata_write, +}; + +U_BOOT_DRIVER(sata_fsl_driver) = { + .name = "sata_fsl_blk", + .id = UCLASS_BLK, + .ops = &sata_fsl_blk_ops, + .platdata_auto_alloc_size = sizeof(struct fsl_ata_priv), +}; + +static int fsl_ata_ofdata_to_platdata(struct udevice *dev) +{ + struct fsl_ata_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->offset = dev_read_u32_default(dev, "sata-offset", -1); + + priv->base = dev_read_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + return 0; +} + +static int fsl_ata_probe(struct udevice *dev) +{ + struct fsl_ata_priv *blk_priv, *priv; + struct udevice *blk; + char sata_name[10]; + int nr_ports; + int ret; + int i; + + priv = dev_get_priv(dev); + nr_ports = priv->number; + nr_ports = min(nr_ports, CONFIG_SYS_SATA_MAX_DEVICE); + + for (i = 0; i < nr_ports; i++) { + snprintf(sata_name, sizeof(sata_name), "fsl_sata%d", i); + ret = blk_create_devicef(dev, "sata_fsl_blk", sata_name, + IF_TYPE_SATA, -1, 512, 0, &blk); + if (ret) { + debug("Can't create device\n"); + return ret; + } + + /* Init SATA port */ + ret = init_sata(priv, i); + if (ret) { + debug("%s: Failed to init sata\n", __func__); + return ret; + } + + blk_priv = dev_get_platdata(blk); + blk_priv->fsl_sata = priv->fsl_sata; + /* Scan SATA port */ + ret = scan_sata(blk); + if (ret) { + debug("%s: Failed to scan bus\n", __func__); + return ret; + } + } + + return 0; +} + +static int sata_fsl_scan(struct udevice *dev) +{ + /* Nothing to do here */ + + return 0; +} + +struct ahci_ops sata_fsl_ahci_ops = { + .scan = sata_fsl_scan, +}; + +static const struct udevice_id fsl_ata_ids[] = { + { .compatible = "fsl,pq-sata-v2" }, + { } +}; + +U_BOOT_DRIVER(fsl_ahci) = { + .name = "fsl_ahci", + .id = UCLASS_AHCI, + .of_match = fsl_ata_ids, + .ops = &sata_fsl_ahci_ops, + .ofdata_to_platdata = fsl_ata_ofdata_to_platdata, + .probe = fsl_ata_probe, + .priv_auto_alloc_size = sizeof(struct fsl_ata_priv), +}; +#endif diff --git a/drivers/ata/fsl_sata.h b/drivers/ata/fsl_sata.h index a4ee83d187..5b9daa79e0 100644 --- a/drivers/ata/fsl_sata.h +++ b/drivers/ata/fsl_sata.h @@ -1,7 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2007-2008 Freescale Semiconductor, Inc. - * Dave Liu <daveliu@freescale.com> + * Copyright 2019 NXP + * Author: Dave Liu <daveliu@freescale.com> */ #ifndef __FSL_SATA_H__ @@ -318,4 +319,14 @@ typedef struct fsl_sata { #define READ_CMD 0 #define WRITE_CMD 1 +#if CONFIG_IS_ENABLED(BLK) +struct fsl_ata_priv { + u32 base; + u32 flag; + u32 number; + u32 offset; + fsl_sata_t *fsl_sata; +}; +#endif + #endif /* __FSL_SATA_H__ */ diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index a8598d9f21..d06d7a079d 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Copyright 2019 NXP * Author: Tang Yuantian <b29983@freescale.com> */ @@ -14,18 +15,29 @@ #include <sata.h> #include <libata.h> #include <sata.h> + +#if CONFIG_IS_ENABLED(BLK) +#include <dm.h> +#include <blk.h> +#endif + #include "sata_sil.h" -/* Convert sectorsize to wordsize */ -#define ATA_SECTOR_WORDS (ATA_SECT_SIZE/2) #define virt_to_bus(devno, v) pci_virt_to_mem(devno, (void *) (v)) +/* just compatible ahci_ops */ +struct sil_ops { + int *rev0; + int *rev1; + int (*scan)(struct udevice *dev); +}; + static struct sata_info sata_info; static struct pci_device_id supported[] = { - {PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3131}, - {PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3132}, - {PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3124}, + { PCI_DEVICE(PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3131) }, + { PCI_DEVICE(PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3132) }, + { PCI_DEVICE(PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3124) }, {} }; @@ -113,9 +125,9 @@ static int sil_init_port(void *port) return 0; } -static void sil_read_fis(int dev, int tag, struct sata_fis_d2h *fis) +static void sil_read_fis(struct sil_sata *sata, int tag, + struct sata_fis_d2h *fis) { - struct sil_sata *sata = sata_dev_desc[dev].priv; void *port = sata->port; struct sil_prb *prb; int i; @@ -128,9 +140,9 @@ static void sil_read_fis(int dev, int tag, struct sata_fis_d2h *fis) *dst++ = readl(src++); } -static int sil_exec_cmd(int dev, struct sil_cmd_block *pcmd, int tag) +static int sil_exec_cmd(struct sil_sata *sata, struct sil_cmd_block *pcmd, + int tag) { - struct sil_sata *sata = sata_dev_desc[dev].priv; void *port = sata->port; u64 paddr = virt_to_bus(sata->devno, pcmd); u32 irq_mask, irq_stat; @@ -164,9 +176,8 @@ static int sil_exec_cmd(int dev, struct sil_cmd_block *pcmd, int tag) return rc; } -static int sil_cmd_set_feature(int dev) +static int sil_cmd_set_feature(struct sil_sata *sata) { - struct sil_sata *sata = sata_dev_desc[dev].priv; struct sil_cmd_block cmdb, *pcmd = &cmdb; struct sata_fis_d2h fis; u8 udma_cap; @@ -191,9 +202,9 @@ static int sil_cmd_set_feature(int dev) if (udma_cap == ATA_UDMA3) pcmd->prb.fis.sector_count = XFER_UDMA_3; - ret = sil_exec_cmd(dev, pcmd, 0); + ret = sil_exec_cmd(sata, pcmd, 0); if (ret) { - sil_read_fis(dev, 0, &fis); + sil_read_fis(sata, 0, &fis); printf("Err: exe cmd(0x%x).\n", readl(sata->port + PORT_SERROR)); sil_sata_dump_fis(&fis); @@ -203,9 +214,34 @@ static int sil_cmd_set_feature(int dev) return 0; } -static int sil_cmd_identify_device(int dev, u16 *id) +static void sil_sata_init_wcache(struct sil_sata *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 sil_sata_set_feature_by_id(struct sil_sata *sata, u16 *id) +{ +#ifdef CONFIG_LBA48 + /* Check if support LBA48 */ + if (ata_id_has_lba48(id)) { + sata->lba48 = 1; + debug("Device supports LBA48\n"); + } else { + debug("Device supports LBA28\n"); + } +#endif + + sil_sata_init_wcache(sata, id); + sil_cmd_set_feature(sata); +} + +static int sil_cmd_identify_device(struct sil_sata *sata, u16 *id) { - struct sil_sata *sata = sata_dev_desc[dev].priv; struct sil_cmd_block cmdb, *pcmd = &cmdb; struct sata_fis_d2h fis; int ret; @@ -220,9 +256,9 @@ static int sil_cmd_identify_device(int dev, u16 *id) pcmd->sge.cnt = cpu_to_le32(sizeof(id[0]) * ATA_ID_WORDS); pcmd->sge.flags = cpu_to_le32(SGE_TRM); - ret = sil_exec_cmd(dev, pcmd, 0); + ret = sil_exec_cmd(sata, pcmd, 0); if (ret) { - sil_read_fis(dev, 0, &fis); + sil_read_fis(sata, 0, &fis); printf("Err: id cmd(0x%x).\n", readl(sata->port + PORT_SERROR)); sil_sata_dump_fis(&fis); return 1; @@ -232,17 +268,16 @@ static int sil_cmd_identify_device(int dev, u16 *id) return 0; } -static int sil_cmd_soft_reset(int dev) +static int sil_cmd_soft_reset(struct sil_sata *sata) { struct sil_cmd_block cmdb, *pcmd = &cmdb; - struct sil_sata *sata = sata_dev_desc[dev].priv; struct sata_fis_d2h fis; void *port = sata->port; int ret; /* put the port into known state */ if (sil_init_port(port)) { - printf("SRST: port %d not ready\n", dev); + printf("SRST: port %d not ready\n", sata->id); return 1; } @@ -252,9 +287,9 @@ static int sil_cmd_soft_reset(int dev) pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D; pcmd->prb.fis.pm_port_c = 0xf; - ret = sil_exec_cmd(dev, &cmdb, 0); + ret = sil_exec_cmd(sata, &cmdb, 0); if (ret) { - sil_read_fis(dev, 0, &fis); + sil_read_fis(sata, 0, &fis); printf("SRST cmd error.\n"); sil_sata_dump_fis(&fis); return 1; @@ -263,10 +298,9 @@ static int sil_cmd_soft_reset(int dev) return 0; } -static ulong sil_sata_rw_cmd(int dev, ulong start, ulong blkcnt, - u8 *buffer, int is_write) +static ulong sil_sata_rw_cmd(struct sil_sata *sata, ulong start, ulong blkcnt, + u8 *buffer, int is_write) { - struct sil_sata *sata = sata_dev_desc[dev].priv; struct sil_cmd_block cmdb, *pcmd = &cmdb; struct sata_fis_d2h fis; u64 block; @@ -296,9 +330,9 @@ static ulong sil_sata_rw_cmd(int dev, ulong start, ulong blkcnt, pcmd->sge.cnt = cpu_to_le32(blkcnt * ATA_SECT_SIZE); pcmd->sge.flags = cpu_to_le32(SGE_TRM); - ret = sil_exec_cmd(dev, pcmd, 0); + ret = sil_exec_cmd(sata, pcmd, 0); if (ret) { - sil_read_fis(dev, 0, &fis); + sil_read_fis(sata, 0, &fis); printf("Err: rw cmd(0x%08x).\n", readl(sata->port + PORT_SERROR)); sil_sata_dump_fis(&fis); @@ -308,10 +342,9 @@ static ulong sil_sata_rw_cmd(int dev, ulong start, ulong blkcnt, return blkcnt; } -static ulong sil_sata_rw_cmd_ext(int dev, ulong start, ulong blkcnt, - u8 *buffer, int is_write) +static ulong sil_sata_rw_cmd_ext(struct sil_sata *sata, ulong start, + ulong blkcnt, u8 *buffer, int is_write) { - struct sil_sata *sata = sata_dev_desc[dev].priv; struct sil_cmd_block cmdb, *pcmd = &cmdb; struct sata_fis_d2h fis; u64 block; @@ -344,9 +377,9 @@ static ulong sil_sata_rw_cmd_ext(int dev, ulong start, ulong blkcnt, pcmd->sge.cnt = cpu_to_le32(blkcnt * ATA_SECT_SIZE); pcmd->sge.flags = cpu_to_le32(SGE_TRM); - ret = sil_exec_cmd(dev, pcmd, 0); + ret = sil_exec_cmd(sata, pcmd, 0); if (ret) { - sil_read_fis(dev, 0, &fis); + sil_read_fis(sata, 0, &fis); printf("Err: rw ext cmd(0x%08x).\n", readl(sata->port + PORT_SERROR)); sil_sata_dump_fis(&fis); @@ -356,8 +389,9 @@ static ulong sil_sata_rw_cmd_ext(int dev, ulong start, ulong blkcnt, return blkcnt; } -static ulong sil_sata_rw_lba28(int dev, ulong blknr, lbaint_t blkcnt, - const void *buffer, int is_write) +static ulong sil_sata_rw_lba28(struct sil_sata *sata, ulong blknr, + lbaint_t blkcnt, const void *buffer, + int is_write) { ulong start, blks, max_blks; u8 *addr; @@ -369,12 +403,12 @@ static ulong sil_sata_rw_lba28(int dev, ulong blknr, lbaint_t blkcnt, max_blks = ATA_MAX_SECTORS; do { if (blks > max_blks) { - sil_sata_rw_cmd(dev, start, max_blks, addr, is_write); + sil_sata_rw_cmd(sata, start, max_blks, addr, is_write); start += max_blks; blks -= max_blks; addr += ATA_SECT_SIZE * max_blks; } else { - sil_sata_rw_cmd(dev, start, blks, addr, is_write); + sil_sata_rw_cmd(sata, start, blks, addr, is_write); start += blks; blks = 0; addr += ATA_SECT_SIZE * blks; @@ -384,8 +418,9 @@ static ulong sil_sata_rw_lba28(int dev, ulong blknr, lbaint_t blkcnt, return blkcnt; } -static ulong sil_sata_rw_lba48(int dev, ulong blknr, lbaint_t blkcnt, - const void *buffer, int is_write) +static ulong sil_sata_rw_lba48(struct sil_sata *sata, ulong blknr, + lbaint_t blkcnt, const void *buffer, + int is_write) { ulong start, blks, max_blks; u8 *addr; @@ -397,14 +432,14 @@ static ulong sil_sata_rw_lba48(int dev, ulong blknr, lbaint_t blkcnt, max_blks = ATA_MAX_SECTORS_LBA48; do { if (blks > max_blks) { - sil_sata_rw_cmd_ext(dev, start, max_blks, - addr, is_write); + sil_sata_rw_cmd_ext(sata, start, max_blks, + addr, is_write); start += max_blks; blks -= max_blks; addr += ATA_SECT_SIZE * max_blks; } else { - sil_sata_rw_cmd_ext(dev, start, blks, - addr, is_write); + sil_sata_rw_cmd_ext(sata, start, blks, + addr, is_write); start += blks; blks = 0; addr += ATA_SECT_SIZE * blks; @@ -414,7 +449,7 @@ static ulong sil_sata_rw_lba48(int dev, ulong blknr, lbaint_t blkcnt, return blkcnt; } -static void sil_sata_cmd_flush_cache(int dev) +static void sil_sata_cmd_flush_cache(struct sil_sata *sata) { struct sil_cmd_block cmdb, *pcmd = &cmdb; @@ -423,10 +458,10 @@ static void sil_sata_cmd_flush_cache(int dev) pcmd->prb.fis.pm_port_c = (1 << 7); pcmd->prb.fis.command = ATA_CMD_FLUSH; - sil_exec_cmd(dev, pcmd, 0); + sil_exec_cmd(sata, pcmd, 0); } -static void sil_sata_cmd_flush_cache_ext(int dev) +static void sil_sata_cmd_flush_cache_ext(struct sil_sata *sata) { struct sil_cmd_block cmdb, *pcmd = &cmdb; @@ -435,54 +470,30 @@ static void sil_sata_cmd_flush_cache_ext(int dev) pcmd->prb.fis.pm_port_c = (1 << 7); pcmd->prb.fis.command = ATA_CMD_FLUSH_EXT; - sil_exec_cmd(dev, pcmd, 0); -} - -static void sil_sata_init_wcache(int dev, u16 *id) -{ - struct sil_sata *sata = sata_dev_desc[dev].priv; - - 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 int sil_sata_get_wcache(int dev) -{ - struct sil_sata *sata = sata_dev_desc[dev].priv; - - return sata->wcache; -} - -static int sil_sata_get_flush(int dev) -{ - struct sil_sata *sata = sata_dev_desc[dev].priv; - - return sata->flush; -} - -static int sil_sata_get_flush_ext(int dev) -{ - struct sil_sata *sata = sata_dev_desc[dev].priv; - - return sata->flush_ext; + sil_exec_cmd(sata, pcmd, 0); } /* * SATA interface between low level driver and command layer */ +#if !CONFIG_IS_ENABLED(BLK) ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer) { - struct sil_sata *sata = sata_dev_desc[dev].priv; + struct sil_sata *sata = (struct sil_sata *)sata_dev_desc[dev].priv; +#else +static ulong sata_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, + void *buffer) +{ + struct sil_sata_priv *priv = dev_get_platdata(dev); + int port_number = priv->port_num; + struct sil_sata *sata = priv->sil_sata_desc[port_number]; +#endif ulong rc; if (sata->lba48) - rc = sil_sata_rw_lba48(dev, blknr, blkcnt, buffer, READ_CMD); + rc = sil_sata_rw_lba48(sata, blknr, blkcnt, buffer, READ_CMD); else - rc = sil_sata_rw_lba28(dev, blknr, blkcnt, buffer, READ_CMD); + rc = sil_sata_rw_lba28(sata, blknr, blkcnt, buffer, READ_CMD); return rc; } @@ -490,111 +501,48 @@ ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer) /* * SATA interface between low level driver and command layer */ +#if !CONFIG_IS_ENABLED(BLK) ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, const void *buffer) { - struct sil_sata *sata = sata_dev_desc[dev].priv; + struct sil_sata *sata = (struct sil_sata *)sata_dev_desc[dev].priv; +#else +ulong sata_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, + const void *buffer) +{ + struct sil_sata_priv *priv = dev_get_platdata(dev); + int port_number = priv->port_num; + struct sil_sata *sata = priv->sil_sata_desc[port_number]; +#endif ulong rc; if (sata->lba48) { - rc = sil_sata_rw_lba48(dev, blknr, blkcnt, buffer, WRITE_CMD); - if (sil_sata_get_wcache(dev) && sil_sata_get_flush_ext(dev)) - sil_sata_cmd_flush_cache_ext(dev); + rc = sil_sata_rw_lba48(sata, blknr, blkcnt, buffer, WRITE_CMD); + if (sata->wcache && sata->flush_ext) + sil_sata_cmd_flush_cache_ext(sata); } else { - rc = sil_sata_rw_lba28(dev, blknr, blkcnt, buffer, WRITE_CMD); - if (sil_sata_get_wcache(dev) && sil_sata_get_flush(dev)) - sil_sata_cmd_flush_cache(dev); + rc = sil_sata_rw_lba28(sata, blknr, blkcnt, buffer, WRITE_CMD); + if (sata->wcache && sata->flush) + sil_sata_cmd_flush_cache(sata); } return rc; } -/* - * SATA interface between low level driver and command layer - */ -int init_sata(int dev) -{ - static int init_done, idx; - pci_dev_t devno; - u16 word; - - if (init_done == 1 && dev < sata_info.maxport) - return 0; - - init_done = 1; - - /* Find PCI device(s) */ - devno = pci_find_devices(supported, idx++); - if (devno == -1) - return 1; - - pci_read_config_word(devno, PCI_DEVICE_ID, &word); - - /* get the port count */ - word &= 0xf; - - sata_info.portbase = sata_info.maxport; - sata_info.maxport = sata_info.portbase + word; - sata_info.devno = devno; - - /* Read out all BARs */ - sata_info.iobase[0] = (ulong)pci_map_bar(devno, - PCI_BASE_ADDRESS_0, PCI_REGION_MEM); - sata_info.iobase[1] = (ulong)pci_map_bar(devno, - PCI_BASE_ADDRESS_2, PCI_REGION_MEM); - sata_info.iobase[2] = (ulong)pci_map_bar(devno, - PCI_BASE_ADDRESS_4, PCI_REGION_MEM); - - /* mask out the unused bits */ - sata_info.iobase[0] &= 0xffffff80; - sata_info.iobase[1] &= 0xfffffc00; - sata_info.iobase[2] &= 0xffffff80; - - /* Enable Bus Mastering and memory region */ - pci_write_config_word(devno, PCI_COMMAND, - PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - - /* Check if mem accesses and Bus Mastering are enabled. */ - pci_read_config_word(devno, PCI_COMMAND, &word); - if (!(word & PCI_COMMAND_MEMORY) || - (!(word & PCI_COMMAND_MASTER))) { - printf("Error: Can not enable MEM access or Bus Mastering.\n"); - debug("PCI command: %04x\n", word); - return 1; - } - - /* GPIO off */ - writel(0, (void *)(sata_info.iobase[0] + HOST_FLASH_CMD)); - /* clear global reset & mask interrupts during initialization */ - writel(0, (void *)(sata_info.iobase[0] + HOST_CTRL)); - - return 0; -} - -int reset_sata(int dev) +#if !CONFIG_IS_ENABLED(BLK) +static int sil_init_sata(int dev) { - return 0; -} - -/* - * SATA interface between low level driver and command layer - */ -int scan_sata(int dev) +#else +static int sil_init_sata(struct udevice *uc_dev, int dev) { - unsigned char serial[ATA_ID_SERNO_LEN + 1]; - unsigned char firmware[ATA_ID_FW_REV_LEN + 1]; - unsigned char product[ATA_ID_PROD_LEN + 1]; + struct sil_sata_priv *priv = dev_get_platdata(uc_dev); +#endif struct sil_sata *sata; void *port; - int cnt; - u16 *id; u32 tmp; + int cnt; - if (dev >= sata_info.maxport) { - printf("SATA#%d is not present\n", dev); - return 1; - } + printf("SATA#%d:\n", dev); - printf("SATA#%d\n", dev); port = (void *)sata_info.iobase[1] + PORT_REGS_SIZE * (dev - sata_info.portbase); @@ -653,62 +601,263 @@ int scan_sata(int dev) } memset((void *)sata, 0, sizeof(struct sil_sata)); - /* turn on port interrupt */ - tmp = readl((void *)(sata_info.iobase[0] + HOST_CTRL)); - tmp |= (1 << (dev - sata_info.portbase)); - writel(tmp, (void *)(sata_info.iobase[0] + HOST_CTRL)); - /* Save the private struct to block device struct */ +#if !CONFIG_IS_ENABLED(BLK) sata_dev_desc[dev].priv = (void *)sata; +#else + priv->sil_sata_desc[dev] = sata; + priv->port_num = dev; +#endif + sata->id = dev; sata->port = port; sata->devno = sata_info.devno; sprintf(sata->name, "SATA#%d", dev); - sil_cmd_soft_reset(dev); + sil_cmd_soft_reset(sata); tmp = readl(port + PORT_SSTATUS); tmp = (tmp >> 4) & 0xf; printf(" (%s)\n", sata_spd_string(tmp)); + return 0; +} + +#if !CONFIG_IS_ENABLED(BLK) +/* + * SATA interface between low level driver and command layer + */ +int init_sata(int dev) +{ + static int init_done, idx; + pci_dev_t devno; + u16 word; + + if (init_done == 1 && dev < sata_info.maxport) + goto init_start; + + init_done = 1; + + /* Find PCI device(s) */ + devno = pci_find_devices(supported, idx++); + if (devno == -1) + return 1; + + pci_read_config_word(devno, PCI_DEVICE_ID, &word); + + /* get the port count */ + word &= 0xf; + + sata_info.portbase = 0; + sata_info.maxport = sata_info.portbase + word; + sata_info.devno = devno; + + /* Read out all BARs */ + sata_info.iobase[0] = (ulong)pci_map_bar(devno, + PCI_BASE_ADDRESS_0, PCI_REGION_MEM); + sata_info.iobase[1] = (ulong)pci_map_bar(devno, + PCI_BASE_ADDRESS_2, PCI_REGION_MEM); + + /* mask out the unused bits */ + sata_info.iobase[0] &= 0xffffff80; + sata_info.iobase[1] &= 0xfffffc00; + + /* Enable Bus Mastering and memory region */ + pci_write_config_word(devno, PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + /* Check if mem accesses and Bus Mastering are enabled. */ + pci_read_config_word(devno, PCI_COMMAND, &word); + if (!(word & PCI_COMMAND_MEMORY) || + (!(word & PCI_COMMAND_MASTER))) { + printf("Error: Can not enable MEM access or Bus Mastering.\n"); + debug("PCI command: %04x\n", word); + return 1; + } + + /* GPIO off */ + writel(0, (void *)(sata_info.iobase[0] + HOST_FLASH_CMD)); + /* clear global reset & mask interrupts during initialization */ + writel(0, (void *)(sata_info.iobase[0] + HOST_CTRL)); + +init_start: + return sil_init_sata(dev); +} + +int reset_sata(int dev) +{ + return 0; +} + +/* + * SATA interface between low level driver and command layer + */ +int scan_sata(int dev) +{ + struct sil_sata *sata = (struct sil_sata *)sata_dev_desc[dev].priv; +#else +static int scan_sata(struct udevice *blk_dev, int dev) +{ + struct blk_desc *desc = dev_get_uclass_platdata(blk_dev); + struct sil_sata_priv *priv = dev_get_platdata(blk_dev); + struct sil_sata *sata = priv->sil_sata_desc[dev]; +#endif + unsigned char serial[ATA_ID_SERNO_LEN + 1]; + unsigned char firmware[ATA_ID_FW_REV_LEN + 1]; + unsigned char product[ATA_ID_PROD_LEN + 1]; + u16 *id; + id = (u16 *)malloc(ATA_ID_WORDS * 2); if (!id) { printf("Id malloc failed\n"); - free((void *)sata); return 1; } - sil_cmd_identify_device(dev, id); + sil_cmd_identify_device(sata, id); -#ifdef CONFIG_LBA48 - /* Check if support LBA48 */ - if (ata_id_has_lba48(id)) { - sata_dev_desc[dev].lba48 = 1; - sata->lba48 = 1; - debug("Device supports LBA48\n"); - } else - debug("Device supports LBA28\n"); -#endif + sil_sata_set_feature_by_id(sata, id); /* Serial number */ ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial)); - memcpy(sata_dev_desc[dev].product, serial, sizeof(serial)); /* Firmware version */ ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware)); - memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware)); /* Product model */ ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product)); - memcpy(sata_dev_desc[dev].vendor, product, sizeof(product)); +#if !CONFIG_IS_ENABLED(BLK) + memcpy(sata_dev_desc[dev].product, serial, sizeof(serial)); + memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware)); + memcpy(sata_dev_desc[dev].vendor, product, sizeof(product)); /* Totoal sectors */ sata_dev_desc[dev].lba = ata_id_n_sectors(id); - - sil_sata_init_wcache(dev, id); - sil_cmd_set_feature(dev); +#ifdef CONFIG_LBA48 + sata_dev_desc[dev].lba48 = sata->lba48; +#endif +#else + memcpy(desc->product, serial, sizeof(serial)); + memcpy(desc->revision, firmware, sizeof(firmware)); + memcpy(desc->vendor, product, sizeof(product)); + desc->lba = ata_id_n_sectors(id); +#ifdef CONFIG_LBA48 + desc->lba48 = sata->lba48; +#endif +#endif #ifdef DEBUG - sil_cmd_identify_device(dev, id); ata_dump_id(id); #endif free((void *)id); return 0; } + +#if CONFIG_IS_ENABLED(BLK) +static const struct blk_ops sata_sil_blk_ops = { + .read = sata_read, + .write = sata_write, +}; + +U_BOOT_DRIVER(sata_sil_driver) = { + .name = "sata_sil_blk", + .id = UCLASS_BLK, + .ops = &sata_sil_blk_ops, + .platdata_auto_alloc_size = sizeof(struct sil_sata_priv), +}; + +static int sil_pci_probe(struct udevice *dev) +{ + struct udevice *blk; + char sata_name[10]; + pci_dev_t devno; + u16 word; + int ret; + int i; + + /* Get PCI device number */ + devno = dm_pci_get_bdf(dev); + if (devno == -1) + return 1; + + dm_pci_read_config16(dev, PCI_DEVICE_ID, &word); + + /* get the port count */ + word &= 0xf; + + sata_info.portbase = 0; + sata_info.maxport = sata_info.portbase + word; + sata_info.devno = devno; + + /* Read out all BARs */ + sata_info.iobase[0] = (ulong)dm_pci_map_bar(dev, + PCI_BASE_ADDRESS_0, PCI_REGION_MEM); + sata_info.iobase[1] = (ulong)dm_pci_map_bar(dev, + PCI_BASE_ADDRESS_2, PCI_REGION_MEM); + + /* mask out the unused bits */ + sata_info.iobase[0] &= 0xffffff80; + sata_info.iobase[1] &= 0xfffffc00; + + /* Enable Bus Mastering and memory region */ + dm_pci_write_config16(dev, PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + /* Check if mem accesses and Bus Mastering are enabled. */ + dm_pci_read_config16(dev, PCI_COMMAND, &word); + if (!(word & PCI_COMMAND_MEMORY) || + (!(word & PCI_COMMAND_MASTER))) { + printf("Error: Can not enable MEM access or Bus Mastering.\n"); + debug("PCI command: %04x\n", word); + return 1; + } + + /* GPIO off */ + writel(0, (void *)(sata_info.iobase[0] + HOST_FLASH_CMD)); + /* clear global reset & mask interrupts during initialization */ + writel(0, (void *)(sata_info.iobase[0] + HOST_CTRL)); + + for (i = sata_info.portbase; i < sata_info.maxport; i++) { + snprintf(sata_name, sizeof(sata_name), "sil_sata%d", i); + ret = blk_create_devicef(dev, "sata_sil_blk", sata_name, + IF_TYPE_SATA, -1, 512, 0, &blk); + if (ret) { + debug("Can't create device\n"); + return ret; + } + + ret = sil_init_sata(blk, i); + if (ret) + return -ENODEV; + + ret = scan_sata(blk, i); + if (ret) + return -ENODEV; + } + + return 0; +} + +static int sata_sil_scan(struct udevice *dev) +{ + /* Nothing to do here */ + + return 0; +} + +struct sil_ops sata_sil_ops = { + .scan = sata_sil_scan, +}; + +static const struct udevice_id sil_pci_ids[] = { + { .compatible = "sil-pci-sample" }, + { } +}; + +U_BOOT_DRIVER(sil_ahci_pci) = { + .name = "sil_ahci_pci", + .id = UCLASS_AHCI, + .of_match = sil_pci_ids, + .ops = &sata_sil_ops, + .probe = sil_pci_probe, + .priv_auto_alloc_size = sizeof(struct sil_sata_priv), +}; + +U_BOOT_PCI_DEVICE(sil_ahci_pci, supported); +#endif diff --git a/drivers/ata/sata_sil.h b/drivers/ata/sata_sil.h index 8b7cbdf2d2..ef41e8259a 100644 --- a/drivers/ata/sata_sil.h +++ b/drivers/ata/sata_sil.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Copyright 2019 NXP * Author: Tang Yuantian <b29983@freescale.com> */ @@ -24,6 +25,7 @@ struct sil_sata { int wcache; int flush; int flush_ext; + int id; }; /* sata info for each controller */ @@ -210,4 +212,12 @@ enum { CMD_ERR = 0x21, }; +#if CONFIG_IS_ENABLED(BLK) +#define ATA_MAX_PORTS 32 +struct sil_sata_priv { + int port_num; + struct sil_sata *sil_sata_desc[ATA_MAX_PORTS]; +}; +#endif + #endif diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index 41cfb7ad3f..4cfcf83309 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -3,11 +3,14 @@ # Copyright (c) 2017 Rockchip Electronics Co., Ltd # +obj-y += clk_pll.o +obj-$(CONFIG_ROCKCHIP_PX30) += clk_px30.o obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o obj-$(CONFIG_ROCKCHIP_RK3128) += clk_rk3128.o obj-$(CONFIG_ROCKCHIP_RK3188) += clk_rk3188.o obj-$(CONFIG_ROCKCHIP_RK322X) += clk_rk322x.o obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o +obj-$(CONFIG_ROCKCHIP_RK3308) += clk_rk3308.o obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o obj-$(CONFIG_ROCKCHIP_RK3368) += clk_rk3368.o obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o diff --git a/drivers/clk/rockchip/clk_pll.c b/drivers/clk/rockchip/clk_pll.c new file mode 100644 index 0000000000..c4b45314ec --- /dev/null +++ b/drivers/clk/rockchip/clk_pll.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2018-2019 Rockchip Electronics Co., Ltd + */ + #include <common.h> +#include <bitfield.h> +#include <clk-uclass.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/hardware.h> +#include <div64.h> + +static struct rockchip_pll_rate_table rockchip_auto_table; + +#define PLL_MODE_MASK 0x3 +#define PLL_RK3328_MODE_MASK 0x1 + +#define RK3036_PLLCON0_FBDIV_MASK 0xfff +#define RK3036_PLLCON0_FBDIV_SHIFT 0 +#define RK3036_PLLCON0_POSTDIV1_MASK 0x7 << 12 +#define RK3036_PLLCON0_POSTDIV1_SHIFT 12 +#define RK3036_PLLCON1_REFDIV_MASK 0x3f +#define RK3036_PLLCON1_REFDIV_SHIFT 0 +#define RK3036_PLLCON1_POSTDIV2_MASK 0x7 << 6 +#define RK3036_PLLCON1_POSTDIV2_SHIFT 6 +#define RK3036_PLLCON1_DSMPD_MASK 0x1 << 12 +#define RK3036_PLLCON1_DSMPD_SHIFT 12 +#define RK3036_PLLCON2_FRAC_MASK 0xffffff +#define RK3036_PLLCON2_FRAC_SHIFT 0 +#define RK3036_PLLCON1_PWRDOWN_SHIT 13 + +#define MHZ 1000000 +#define KHZ 1000 +enum { + OSC_HZ = 24 * 1000000, + VCO_MAX_HZ = 3200U * 1000000, + VCO_MIN_HZ = 800 * 1000000, + OUTPUT_MAX_HZ = 3200U * 1000000, + OUTPUT_MIN_HZ = 24 * 1000000, +}; + +#define MIN_FOUTVCO_FREQ (800 * MHZ) +#define MAX_FOUTVCO_FREQ (2000 * MHZ) + +int gcd(int m, int n) +{ + int t; + + while (m > 0) { + if (n > m) { + t = m; + m = n; + n = t; + } /* swap */ + m -= n; + } + return n; +} + +/* + * How to calculate the PLL(from TRM V0.3 Part 1 Page 63): + * Formulas also embedded within the Fractional PLL Verilog model: + * If DSMPD = 1 (DSM is disabled, "integer mode") + * FOUTVCO = FREF / REFDIV * FBDIV + * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2 + * Where: + * FOUTVCO = Fractional PLL non-divided output frequency + * FOUTPOSTDIV = Fractional PLL divided output frequency + * (output of second post divider) + * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input) + * REFDIV = Fractional PLL input reference clock divider + * FBDIV = Integer value programmed into feedback divide + * + */ + +static int rockchip_pll_clk_set_postdiv(ulong fout_hz, + u32 *postdiv1, + u32 *postdiv2, + u32 *foutvco) +{ + ulong freq; + + if (fout_hz < MIN_FOUTVCO_FREQ) { + for (*postdiv1 = 1; *postdiv1 <= 7; (*postdiv1)++) { + for (*postdiv2 = 1; *postdiv2 <= 7; (*postdiv2)++) { + freq = fout_hz * (*postdiv1) * (*postdiv2); + if (freq >= MIN_FOUTVCO_FREQ && + freq <= MAX_FOUTVCO_FREQ) { + *foutvco = freq; + return 0; + } + } + } + printf("Can't FIND postdiv1/2 to make fout=%lu in 800~2000M.\n", + fout_hz); + } else { + *postdiv1 = 1; + *postdiv2 = 1; + } + return 0; +} + +static struct rockchip_pll_rate_table * +rockchip_pll_clk_set_by_auto(ulong fin_hz, + ulong fout_hz) +{ + struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table; + /* FIXME set postdiv1/2 always 1*/ + u32 foutvco = fout_hz; + ulong fin_64, frac_64; + u32 f_frac, postdiv1, postdiv2; + ulong clk_gcd = 0; + + if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz) + return NULL; + + rockchip_pll_clk_set_postdiv(fout_hz, &postdiv1, &postdiv2, &foutvco); + rate_table->postdiv1 = postdiv1; + rate_table->postdiv2 = postdiv2; + rate_table->dsmpd = 1; + + if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) { + fin_hz /= MHZ; + foutvco /= MHZ; + clk_gcd = gcd(fin_hz, foutvco); + rate_table->refdiv = fin_hz / clk_gcd; + rate_table->fbdiv = foutvco / clk_gcd; + + rate_table->frac = 0; + + debug("fin = %ld, fout = %ld, clk_gcd = %ld,\n", + fin_hz, fout_hz, clk_gcd); + debug("refdiv= %d,fbdiv= %d,postdiv1= %d,postdiv2= %d\n", + rate_table->refdiv, + rate_table->fbdiv, rate_table->postdiv1, + rate_table->postdiv2); + } else { + debug("frac div,fin_hz = %ld,fout_hz = %ld\n", + fin_hz, fout_hz); + debug("frac get postdiv1 = %d, postdiv2 = %d, foutvco = %d\n", + rate_table->postdiv1, rate_table->postdiv2, foutvco); + clk_gcd = gcd(fin_hz / MHZ, foutvco / MHZ); + rate_table->refdiv = fin_hz / MHZ / clk_gcd; + rate_table->fbdiv = foutvco / MHZ / clk_gcd; + debug("frac get refdiv = %d, fbdiv = %d\n", + rate_table->refdiv, rate_table->fbdiv); + + rate_table->frac = 0; + + f_frac = (foutvco % MHZ); + fin_64 = fin_hz; + fin_64 = fin_64 / rate_table->refdiv; + frac_64 = f_frac << 24; + frac_64 = frac_64 / fin_64; + rate_table->frac = frac_64; + if (rate_table->frac > 0) + rate_table->dsmpd = 0; + debug("frac = %x\n", rate_table->frac); + } + return rate_table; +} + +static const struct rockchip_pll_rate_table * +rockchip_get_pll_settings(struct rockchip_pll_clock *pll, ulong rate) +{ + struct rockchip_pll_rate_table *rate_table = pll->rate_table; + + while (rate_table->rate) { + if (rate_table->rate == rate) + break; + rate_table++; + } + if (rate_table->rate != rate) + return rockchip_pll_clk_set_by_auto(24 * MHZ, rate); + else + return rate_table; +} + +static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll, + void __iomem *base, ulong pll_id, + ulong drate) +{ + const struct rockchip_pll_rate_table *rate; + + rate = rockchip_get_pll_settings(pll, drate); + if (!rate) { + printf("%s unsupport rate\n", __func__); + return -EINVAL; + } + + debug("%s: rate settings for %lu fbdiv: %d, postdiv1: %d, refdiv: %d\n", + __func__, rate->rate, rate->fbdiv, rate->postdiv1, rate->refdiv); + debug("%s: rate settings for %lu postdiv2: %d, dsmpd: %d, frac: %d\n", + __func__, rate->rate, rate->postdiv2, rate->dsmpd, rate->frac); + + /* + * When power on or changing PLL setting, + * we must force PLL into slow mode to ensure output stable clock. + */ + rk_clrsetreg(base + pll->mode_offset, + pll->mode_mask << pll->mode_shift, + RKCLK_PLL_MODE_SLOW << pll->mode_shift); + + /* Power down */ + rk_setreg(base + pll->con_offset + 0x4, + 1 << RK3036_PLLCON1_PWRDOWN_SHIT); + + rk_clrsetreg(base + pll->con_offset, + (RK3036_PLLCON0_POSTDIV1_MASK | + RK3036_PLLCON0_FBDIV_MASK), + (rate->postdiv1 << RK3036_PLLCON0_POSTDIV1_SHIFT) | + rate->fbdiv); + rk_clrsetreg(base + pll->con_offset + 0x4, + (RK3036_PLLCON1_POSTDIV2_MASK | + RK3036_PLLCON1_REFDIV_MASK), + (rate->postdiv2 << RK3036_PLLCON1_POSTDIV2_SHIFT | + rate->refdiv << RK3036_PLLCON1_REFDIV_SHIFT)); + if (!rate->dsmpd) { + rk_clrsetreg(base + pll->con_offset + 0x4, + RK3036_PLLCON1_DSMPD_MASK, + rate->dsmpd << RK3036_PLLCON1_DSMPD_SHIFT); + writel((readl(base + pll->con_offset + 0x8) & + (~RK3036_PLLCON2_FRAC_MASK)) | + (rate->frac << RK3036_PLLCON2_FRAC_SHIFT), + base + pll->con_offset + 0x8); + } + + /* Power Up */ + rk_clrreg(base + pll->con_offset + 0x4, + 1 << RK3036_PLLCON1_PWRDOWN_SHIT); + + /* waiting for pll lock */ + while (!(readl(base + pll->con_offset + 0x4) & (1 << pll->lock_shift))) + udelay(1); + + rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift, + RKCLK_PLL_MODE_NORMAL << pll->mode_shift); + debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n", + pll, readl(base + pll->con_offset), + readl(base + pll->con_offset + 0x4), + readl(base + pll->con_offset + 0x8), + readl(base + pll->mode_offset)); + + return 0; +} + +static ulong rk3036_pll_get_rate(struct rockchip_pll_clock *pll, + void __iomem *base, ulong pll_id) +{ + u32 refdiv, fbdiv, postdiv1, postdiv2, dsmpd, frac; + u32 con = 0, shift, mask; + ulong rate; + + con = readl(base + pll->mode_offset); + shift = pll->mode_shift; + mask = pll->mode_mask << shift; + + switch ((con & mask) >> shift) { + case RKCLK_PLL_MODE_SLOW: + return OSC_HZ; + case RKCLK_PLL_MODE_NORMAL: + /* normal mode */ + con = readl(base + pll->con_offset); + postdiv1 = (con & RK3036_PLLCON0_POSTDIV1_MASK) >> + RK3036_PLLCON0_POSTDIV1_SHIFT; + fbdiv = (con & RK3036_PLLCON0_FBDIV_MASK) >> + RK3036_PLLCON0_FBDIV_SHIFT; + con = readl(base + pll->con_offset + 0x4); + postdiv2 = (con & RK3036_PLLCON1_POSTDIV2_MASK) >> + RK3036_PLLCON1_POSTDIV2_SHIFT; + refdiv = (con & RK3036_PLLCON1_REFDIV_MASK) >> + RK3036_PLLCON1_REFDIV_SHIFT; + dsmpd = (con & RK3036_PLLCON1_DSMPD_MASK) >> + RK3036_PLLCON1_DSMPD_SHIFT; + con = readl(base + pll->con_offset + 0x8); + frac = (con & RK3036_PLLCON2_FRAC_MASK) >> + RK3036_PLLCON2_FRAC_SHIFT; + rate = (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000; + if (dsmpd == 0) { + u64 frac_rate = OSC_HZ * (u64)frac; + + do_div(frac_rate, refdiv); + frac_rate >>= 24; + do_div(frac_rate, postdiv1); + do_div(frac_rate, postdiv1); + rate += frac_rate; + } + return rate; + case RKCLK_PLL_MODE_DEEP: + default: + return 32768; + } +} + +ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll, + void __iomem *base, + ulong pll_id) +{ + ulong rate = 0; + + switch (pll->type) { + case pll_rk3036: + pll->mode_mask = PLL_MODE_MASK; + rate = rk3036_pll_get_rate(pll, base, pll_id); + break; + case pll_rk3328: + pll->mode_mask = PLL_RK3328_MODE_MASK; + rate = rk3036_pll_get_rate(pll, base, pll_id); + break; + default: + printf("%s: Unknown pll type for pll clk %ld\n", + __func__, pll_id); + } + return rate; +} + +int rockchip_pll_set_rate(struct rockchip_pll_clock *pll, + void __iomem *base, ulong pll_id, + ulong drate) +{ + int ret = 0; + + if (rockchip_pll_get_rate(pll, base, pll_id) == drate) + return 0; + + switch (pll->type) { + case pll_rk3036: + pll->mode_mask = PLL_MODE_MASK; + ret = rk3036_pll_set_rate(pll, base, pll_id, drate); + break; + case pll_rk3328: + pll->mode_mask = PLL_RK3328_MODE_MASK; + ret = rk3036_pll_set_rate(pll, base, pll_id, drate); + break; + default: + printf("%s: Unknown pll type for pll clk %ld\n", + __func__, pll_id); + } + return ret; +} + +const struct rockchip_cpu_rate_table * +rockchip_get_cpu_settings(struct rockchip_cpu_rate_table *cpu_table, + ulong rate) +{ + struct rockchip_cpu_rate_table *ps = cpu_table; + + while (ps->rate) { + if (ps->rate == rate) + break; + ps++; + } + if (ps->rate != rate) + return NULL; + else + return ps; +} + diff --git a/drivers/clk/rockchip/clk_px30.c b/drivers/clk/rockchip/clk_px30.c new file mode 100644 index 0000000000..36764c128b --- /dev/null +++ b/drivers/clk/rockchip/clk_px30.c @@ -0,0 +1,1630 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + */ + +#include <common.h> +#include <bitfield.h> +#include <clk-uclass.h> +#include <dm.h> +#include <errno.h> +#include <syscon.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_px30.h> +#include <asm/arch-rockchip/hardware.h> +#include <asm/io.h> +#include <dm/lists.h> +#include <dt-bindings/clock/px30-cru.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum { + VCO_MAX_HZ = 3200U * 1000000, + VCO_MIN_HZ = 800 * 1000000, + OUTPUT_MAX_HZ = 3200U * 1000000, + OUTPUT_MIN_HZ = 24 * 1000000, +}; + +#define PX30_VOP_PLL_LIMIT 600000000 + +#define PX30_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, \ + _postdiv2, _dsmpd, _frac) \ +{ \ + .rate = _rate##U, \ + .fbdiv = _fbdiv, \ + .postdiv1 = _postdiv1, \ + .refdiv = _refdiv, \ + .postdiv2 = _postdiv2, \ + .dsmpd = _dsmpd, \ + .frac = _frac, \ +} + +#define PX30_CPUCLK_RATE(_rate, _aclk_div, _pclk_div) \ +{ \ + .rate = _rate##U, \ + .aclk_div = _aclk_div, \ + .pclk_div = _pclk_div, \ +} + +#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) + +#define PX30_CLK_DUMP(_id, _name, _iscru) \ +{ \ + .id = _id, \ + .name = _name, \ + .is_cru = _iscru, \ +} + +static struct pll_rate_table px30_pll_rates[] = { + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ + PX30_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0), + PX30_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0), + PX30_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0), + PX30_PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0), + PX30_PLL_RATE(1000000000, 6, 500, 2, 1, 1, 0), + PX30_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0), + PX30_PLL_RATE(600000000, 1, 75, 3, 1, 1, 0), +}; + +static struct cpu_rate_table px30_cpu_rates[] = { + PX30_CPUCLK_RATE(1200000000, 1, 5), + PX30_CPUCLK_RATE(1008000000, 1, 5), + PX30_CPUCLK_RATE(816000000, 1, 3), + PX30_CPUCLK_RATE(600000000, 1, 3), + PX30_CPUCLK_RATE(408000000, 1, 1), +}; + +static u8 pll_mode_shift[PLL_COUNT] = { + APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT, + NPLL_MODE_SHIFT, GPLL_MODE_SHIFT +}; + +static u32 pll_mode_mask[PLL_COUNT] = { + APLL_MODE_MASK, DPLL_MODE_MASK, CPLL_MODE_MASK, + NPLL_MODE_MASK, GPLL_MODE_MASK +}; + +static struct pll_rate_table auto_table; + +static ulong px30_clk_get_pll_rate(struct px30_clk_priv *priv, + enum px30_pll_id pll_id); + +static struct pll_rate_table *pll_clk_set_by_auto(u32 drate) +{ + struct pll_rate_table *rate = &auto_table; + u32 ref_khz = OSC_HZ / KHz, refdiv, fbdiv = 0; + u32 postdiv1, postdiv2 = 1; + u32 fref_khz; + u32 diff_khz, best_diff_khz; + const u32 max_refdiv = 63, max_fbdiv = 3200, min_fbdiv = 16; + const u32 max_postdiv1 = 7, max_postdiv2 = 7; + u32 vco_khz; + u32 rate_khz = drate / KHz; + + if (!drate) { + printf("%s: the frequency can't be 0 Hz\n", __func__); + return NULL; + } + + postdiv1 = DIV_ROUND_UP(VCO_MIN_HZ / 1000, rate_khz); + if (postdiv1 > max_postdiv1) { + postdiv2 = DIV_ROUND_UP(postdiv1, max_postdiv1); + postdiv1 = DIV_ROUND_UP(postdiv1, postdiv2); + } + + vco_khz = rate_khz * postdiv1 * postdiv2; + + if (vco_khz < (VCO_MIN_HZ / KHz) || vco_khz > (VCO_MAX_HZ / KHz) || + postdiv2 > max_postdiv2) { + printf("%s: Cannot find out a supported VCO for Freq (%uHz)\n", + __func__, rate_khz); + return NULL; + } + + rate->postdiv1 = postdiv1; + rate->postdiv2 = postdiv2; + + best_diff_khz = vco_khz; + for (refdiv = 1; refdiv < max_refdiv && best_diff_khz; refdiv++) { + fref_khz = ref_khz / refdiv; + + fbdiv = vco_khz / fref_khz; + if (fbdiv >= max_fbdiv || fbdiv <= min_fbdiv) + continue; + + diff_khz = vco_khz - fbdiv * fref_khz; + if (fbdiv + 1 < max_fbdiv && diff_khz > fref_khz / 2) { + fbdiv++; + diff_khz = fref_khz - diff_khz; + } + + if (diff_khz >= best_diff_khz) + continue; + + best_diff_khz = diff_khz; + rate->refdiv = refdiv; + rate->fbdiv = fbdiv; + } + + if (best_diff_khz > 4 * (MHz / KHz)) { + printf("%s: Failed to match output frequency %u bestis %u Hz\n", + __func__, rate_khz, + best_diff_khz * KHz); + return NULL; + } + + return rate; +} + +static const struct pll_rate_table *get_pll_settings(unsigned long rate) +{ + unsigned int rate_count = ARRAY_SIZE(px30_pll_rates); + int i; + + for (i = 0; i < rate_count; i++) { + if (rate == px30_pll_rates[i].rate) + return &px30_pll_rates[i]; + } + + return pll_clk_set_by_auto(rate); +} + +static const struct cpu_rate_table *get_cpu_settings(unsigned long rate) +{ + unsigned int rate_count = ARRAY_SIZE(px30_cpu_rates); + int i; + + for (i = 0; i < rate_count; i++) { + if (rate == px30_cpu_rates[i].rate) + return &px30_cpu_rates[i]; + } + + return NULL; +} + +/* + * How to calculate the PLL(from TRM V0.3 Part 1 Page 63): + * Formulas also embedded within the Fractional PLL Verilog model: + * If DSMPD = 1 (DSM is disabled, "integer mode") + * FOUTVCO = FREF / REFDIV * FBDIV + * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2 + * Where: + * FOUTVCO = Fractional PLL non-divided output frequency + * FOUTPOSTDIV = Fractional PLL divided output frequency + * (output of second post divider) + * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input) + * REFDIV = Fractional PLL input reference clock divider + * FBDIV = Integer value programmed into feedback divide + * + */ +static int rkclk_set_pll(struct px30_pll *pll, unsigned int *mode, + enum px30_pll_id pll_id, + unsigned long drate) +{ + const struct pll_rate_table *rate; + uint vco_hz, output_hz; + + rate = get_pll_settings(drate); + if (!rate) { + printf("%s unsupport rate\n", __func__); + return -EINVAL; + } + + /* All PLLs have same VCO and output frequency range restrictions. */ + vco_hz = OSC_HZ / 1000 * rate->fbdiv / rate->refdiv * 1000; + output_hz = vco_hz / rate->postdiv1 / rate->postdiv2; + + debug("PLL at %p: fb=%d, ref=%d, pst1=%d, pst2=%d, vco=%u Hz, output=%u Hz\n", + pll, rate->fbdiv, rate->refdiv, rate->postdiv1, + rate->postdiv2, vco_hz, output_hz); + assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ && + output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ); + + /* + * When power on or changing PLL setting, + * we must force PLL into slow mode to ensure output stable clock. + */ + rk_clrsetreg(mode, pll_mode_mask[pll_id], + PLLMUX_FROM_XIN24M << pll_mode_shift[pll_id]); + + /* use integer mode */ + rk_setreg(&pll->con1, 1 << PLL_DSMPD_SHIFT); + /* Power down */ + rk_setreg(&pll->con1, 1 << PLL_PD_SHIFT); + + rk_clrsetreg(&pll->con0, + PLL_POSTDIV1_MASK | PLL_FBDIV_MASK, + (rate->postdiv1 << PLL_POSTDIV1_SHIFT) | rate->fbdiv); + rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK | PLL_REFDIV_MASK, + (rate->postdiv2 << PLL_POSTDIV2_SHIFT | + rate->refdiv << PLL_REFDIV_SHIFT)); + + /* Power Up */ + rk_clrreg(&pll->con1, 1 << PLL_PD_SHIFT); + + /* waiting for pll lock */ + while (!(readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT))) + udelay(1); + + rk_clrsetreg(mode, pll_mode_mask[pll_id], + PLLMUX_FROM_PLL << pll_mode_shift[pll_id]); + + return 0; +} + +static uint32_t rkclk_pll_get_rate(struct px30_pll *pll, unsigned int *mode, + enum px30_pll_id pll_id) +{ + u32 refdiv, fbdiv, postdiv1, postdiv2; + u32 con, shift, mask; + + con = readl(mode); + shift = pll_mode_shift[pll_id]; + mask = pll_mode_mask[pll_id]; + + switch ((con & mask) >> shift) { + case PLLMUX_FROM_XIN24M: + return OSC_HZ; + case PLLMUX_FROM_PLL: + /* normal mode */ + con = readl(&pll->con0); + postdiv1 = (con & PLL_POSTDIV1_MASK) >> PLL_POSTDIV1_SHIFT; + fbdiv = (con & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT; + con = readl(&pll->con1); + postdiv2 = (con & PLL_POSTDIV2_MASK) >> PLL_POSTDIV2_SHIFT; + refdiv = (con & PLL_REFDIV_MASK) >> PLL_REFDIV_SHIFT; + return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000; + case PLLMUX_FROM_RTC32K: + default: + return 32768; + } +} + +static ulong px30_i2c_get_clk(struct px30_clk_priv *priv, ulong clk_id) +{ + struct px30_cru *cru = priv->cru; + u32 div, con; + + switch (clk_id) { + case SCLK_I2C0: + con = readl(&cru->clksel_con[49]); + div = con >> CLK_I2C0_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; + break; + case SCLK_I2C1: + con = readl(&cru->clksel_con[49]); + div = con >> CLK_I2C1_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; + break; + case SCLK_I2C2: + con = readl(&cru->clksel_con[50]); + div = con >> CLK_I2C2_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; + break; + case SCLK_I2C3: + con = readl(&cru->clksel_con[50]); + div = con >> CLK_I2C3_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; + break; + default: + printf("do not support this i2c bus\n"); + return -EINVAL; + } + + return DIV_TO_RATE(priv->gpll_hz, div); +} + +static ulong px30_i2c_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz) +{ + struct px30_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); + assert(src_clk_div - 1 <= 127); + + switch (clk_id) { + case SCLK_I2C0: + rk_clrsetreg(&cru->clksel_con[49], + CLK_I2C_DIV_CON_MASK << CLK_I2C0_DIV_CON_SHIFT | + CLK_I2C_PLL_SEL_MASK << CLK_I2C0_PLL_SEL_SHIFT, + (src_clk_div - 1) << CLK_I2C0_DIV_CON_SHIFT | + CLK_I2C_PLL_SEL_GPLL << CLK_I2C0_PLL_SEL_SHIFT); + break; + case SCLK_I2C1: + rk_clrsetreg(&cru->clksel_con[49], + CLK_I2C_DIV_CON_MASK << CLK_I2C1_DIV_CON_SHIFT | + CLK_I2C_PLL_SEL_MASK << CLK_I2C1_PLL_SEL_SHIFT, + (src_clk_div - 1) << CLK_I2C1_DIV_CON_SHIFT | + CLK_I2C_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT); + break; + case SCLK_I2C2: + rk_clrsetreg(&cru->clksel_con[50], + CLK_I2C_DIV_CON_MASK << CLK_I2C2_DIV_CON_SHIFT | + CLK_I2C_PLL_SEL_MASK << CLK_I2C2_PLL_SEL_SHIFT, + (src_clk_div - 1) << CLK_I2C2_DIV_CON_SHIFT | + CLK_I2C_PLL_SEL_GPLL << CLK_I2C2_PLL_SEL_SHIFT); + break; + case SCLK_I2C3: + rk_clrsetreg(&cru->clksel_con[50], + CLK_I2C_DIV_CON_MASK << CLK_I2C3_DIV_CON_SHIFT | + CLK_I2C_PLL_SEL_MASK << CLK_I2C3_PLL_SEL_SHIFT, + (src_clk_div - 1) << CLK_I2C3_DIV_CON_SHIFT | + CLK_I2C_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT); + break; + default: + printf("do not support this i2c bus\n"); + return -EINVAL; + } + + return px30_i2c_get_clk(priv, clk_id); +} + +/* + * calculate best rational approximation for a given fraction + * taking into account restricted register size, e.g. to find + * appropriate values for a pll with 5 bit denominator and + * 8 bit numerator register fields, trying to set up with a + * frequency ratio of 3.1415, one would say: + * + * rational_best_approximation(31415, 10000, + * (1 << 8) - 1, (1 << 5) - 1, &n, &d); + * + * you may look at given_numerator as a fixed point number, + * with the fractional part size described in given_denominator. + * + * for theoretical background, see: + * http://en.wikipedia.org/wiki/Continued_fraction + */ +static void rational_best_approximation(unsigned long given_numerator, + unsigned long given_denominator, + unsigned long max_numerator, + unsigned long max_denominator, + unsigned long *best_numerator, + unsigned long *best_denominator) +{ + unsigned long n, d, n0, d0, n1, d1; + + n = given_numerator; + d = given_denominator; + n0 = 0; + d1 = 0; + n1 = 1; + d0 = 1; + for (;;) { + unsigned long t, a; + + if (n1 > max_numerator || d1 > max_denominator) { + n1 = n0; + d1 = d0; + break; + } + if (d == 0) + break; + t = d; + a = n / d; + d = n % d; + n = t; + t = n0 + a * n1; + n0 = n1; + n1 = t; + t = d0 + a * d1; + d0 = d1; + d1 = t; + } + *best_numerator = n1; + *best_denominator = d1; +} + +static ulong px30_i2s_get_clk(struct px30_clk_priv *priv, ulong clk_id) +{ + u32 con, fracdiv, gate; + u32 clk_src = priv->gpll_hz / 2; + unsigned long m, n; + struct px30_cru *cru = priv->cru; + + switch (clk_id) { + case SCLK_I2S1: + con = readl(&cru->clksel_con[30]); + fracdiv = readl(&cru->clksel_con[31]); + gate = readl(&cru->clkgate_con[10]); + m = fracdiv & CLK_I2S1_FRAC_NUMERATOR_MASK; + m >>= CLK_I2S1_FRAC_NUMERATOR_SHIFT; + n = fracdiv & CLK_I2S1_FRAC_DENOMINATOR_MASK; + n >>= CLK_I2S1_FRAC_DENOMINATOR_SHIFT; + debug("con30: 0x%x, gate: 0x%x, frac: 0x%x\n", + con, gate, fracdiv); + break; + default: + printf("do not support this i2s bus\n"); + return -EINVAL; + } + + return clk_src * n / m; +} + +static ulong px30_i2s_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz) +{ + u32 clk_src; + unsigned long m, n, val; + struct px30_cru *cru = priv->cru; + + clk_src = priv->gpll_hz / 2; + rational_best_approximation(hz, clk_src, + GENMASK(16 - 1, 0), + GENMASK(16 - 1, 0), + &m, &n); + switch (clk_id) { + case SCLK_I2S1: + rk_clrsetreg(&cru->clksel_con[30], + CLK_I2S1_PLL_SEL_MASK, CLK_I2S1_PLL_SEL_GPLL); + rk_clrsetreg(&cru->clksel_con[30], + CLK_I2S1_DIV_CON_MASK, 0x1); + rk_clrsetreg(&cru->clksel_con[30], + CLK_I2S1_SEL_MASK, CLK_I2S1_SEL_FRAC); + val = m << CLK_I2S1_FRAC_NUMERATOR_SHIFT | n; + writel(val, &cru->clksel_con[31]); + rk_clrsetreg(&cru->clkgate_con[10], + CLK_I2S1_OUT_MCLK_PAD_MASK, + CLK_I2S1_OUT_MCLK_PAD_ENABLE); + break; + default: + printf("do not support this i2s bus\n"); + return -EINVAL; + } + + return px30_i2s_get_clk(priv, clk_id); +} + +static ulong px30_nandc_get_clk(struct px30_clk_priv *priv) +{ + struct px30_cru *cru = priv->cru; + u32 div, con; + + con = readl(&cru->clksel_con[15]); + div = (con & NANDC_DIV_MASK) >> NANDC_DIV_SHIFT; + + return DIV_TO_RATE(priv->gpll_hz, div); +} + +static ulong px30_nandc_set_clk(struct px30_clk_priv *priv, + ulong set_rate) +{ + struct px30_cru *cru = priv->cru; + int src_clk_div; + + /* Select nandc source from GPLL by default */ + /* nandc clock defaulg div 2 internal, need provide double in cru */ + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, set_rate); + assert(src_clk_div - 1 <= 31); + + rk_clrsetreg(&cru->clksel_con[15], + NANDC_CLK_SEL_MASK | NANDC_PLL_MASK | + NANDC_DIV_MASK, + NANDC_CLK_SEL_NANDC << NANDC_CLK_SEL_SHIFT | + NANDC_SEL_GPLL << NANDC_PLL_SHIFT | + (src_clk_div - 1) << NANDC_DIV_SHIFT); + + return px30_nandc_get_clk(priv); +} + +static ulong px30_mmc_get_clk(struct px30_clk_priv *priv, uint clk_id) +{ + struct px30_cru *cru = priv->cru; + u32 div, con, con_id; + + switch (clk_id) { + case HCLK_SDMMC: + case SCLK_SDMMC: + con_id = 16; + break; + case HCLK_EMMC: + case SCLK_EMMC: + case SCLK_EMMC_SAMPLE: + con_id = 20; + break; + default: + return -EINVAL; + } + + con = readl(&cru->clksel_con[con_id]); + div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT; + + if ((con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT + == EMMC_SEL_24M) + return DIV_TO_RATE(OSC_HZ, div) / 2; + else + return DIV_TO_RATE(priv->gpll_hz, div) / 2; +} + +static ulong px30_mmc_set_clk(struct px30_clk_priv *priv, + ulong clk_id, ulong set_rate) +{ + struct px30_cru *cru = priv->cru; + int src_clk_div; + u32 con_id; + + switch (clk_id) { + case HCLK_SDMMC: + case SCLK_SDMMC: + con_id = 16; + break; + case HCLK_EMMC: + case SCLK_EMMC: + con_id = 20; + break; + default: + return -EINVAL; + } + + /* Select clk_sdmmc/emmc source from GPLL by default */ + /* mmc clock defaulg div 2 internal, need provide double in cru */ + src_clk_div = DIV_ROUND_UP(priv->gpll_hz / 2, set_rate); + + if (src_clk_div > 127) { + /* use 24MHz source for 400KHz clock */ + src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, set_rate); + rk_clrsetreg(&cru->clksel_con[con_id], + EMMC_PLL_MASK | EMMC_DIV_MASK, + EMMC_SEL_24M << EMMC_PLL_SHIFT | + (src_clk_div - 1) << EMMC_DIV_SHIFT); + } else { + rk_clrsetreg(&cru->clksel_con[con_id], + EMMC_PLL_MASK | EMMC_DIV_MASK, + EMMC_SEL_GPLL << EMMC_PLL_SHIFT | + (src_clk_div - 1) << EMMC_DIV_SHIFT); + } + rk_clrsetreg(&cru->clksel_con[con_id + 1], EMMC_CLK_SEL_MASK, + EMMC_CLK_SEL_EMMC); + + return px30_mmc_get_clk(priv, clk_id); +} + +static ulong px30_pwm_get_clk(struct px30_clk_priv *priv, ulong clk_id) +{ + struct px30_cru *cru = priv->cru; + u32 div, con; + + switch (clk_id) { + case SCLK_PWM0: + con = readl(&cru->clksel_con[52]); + div = con >> CLK_PWM0_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK; + break; + case SCLK_PWM1: + con = readl(&cru->clksel_con[52]); + div = con >> CLK_PWM1_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK; + break; + default: + printf("do not support this pwm bus\n"); + return -EINVAL; + } + + return DIV_TO_RATE(priv->gpll_hz, div); +} + +static ulong px30_pwm_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz) +{ + struct px30_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); + assert(src_clk_div - 1 <= 127); + + switch (clk_id) { + case SCLK_PWM0: + rk_clrsetreg(&cru->clksel_con[52], + CLK_PWM_DIV_CON_MASK << CLK_PWM0_DIV_CON_SHIFT | + CLK_PWM_PLL_SEL_MASK << CLK_PWM0_PLL_SEL_SHIFT, + (src_clk_div - 1) << CLK_PWM0_DIV_CON_SHIFT | + CLK_PWM_PLL_SEL_GPLL << CLK_PWM0_PLL_SEL_SHIFT); + break; + case SCLK_PWM1: + rk_clrsetreg(&cru->clksel_con[52], + CLK_PWM_DIV_CON_MASK << CLK_PWM1_DIV_CON_SHIFT | + CLK_PWM_PLL_SEL_MASK << CLK_PWM1_PLL_SEL_SHIFT, + (src_clk_div - 1) << CLK_PWM1_DIV_CON_SHIFT | + CLK_PWM_PLL_SEL_GPLL << CLK_PWM1_PLL_SEL_SHIFT); + break; + default: + printf("do not support this pwm bus\n"); + return -EINVAL; + } + + return px30_pwm_get_clk(priv, clk_id); +} + +static ulong px30_saradc_get_clk(struct px30_clk_priv *priv) +{ + struct px30_cru *cru = priv->cru; + u32 div, con; + + con = readl(&cru->clksel_con[55]); + div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK; + + return DIV_TO_RATE(OSC_HZ, div); +} + +static ulong px30_saradc_set_clk(struct px30_clk_priv *priv, uint hz) +{ + struct px30_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(OSC_HZ, hz); + assert(src_clk_div - 1 <= 2047); + + rk_clrsetreg(&cru->clksel_con[55], + CLK_SARADC_DIV_CON_MASK, + (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT); + + return px30_saradc_get_clk(priv); +} + +static ulong px30_tsadc_get_clk(struct px30_clk_priv *priv) +{ + struct px30_cru *cru = priv->cru; + u32 div, con; + + con = readl(&cru->clksel_con[54]); + div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK; + + return DIV_TO_RATE(OSC_HZ, div); +} + +static ulong px30_tsadc_set_clk(struct px30_clk_priv *priv, uint hz) +{ + struct px30_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(OSC_HZ, hz); + assert(src_clk_div - 1 <= 2047); + + rk_clrsetreg(&cru->clksel_con[54], + CLK_SARADC_DIV_CON_MASK, + (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT); + + return px30_tsadc_get_clk(priv); +} + +static ulong px30_spi_get_clk(struct px30_clk_priv *priv, ulong clk_id) +{ + struct px30_cru *cru = priv->cru; + u32 div, con; + + switch (clk_id) { + case SCLK_SPI0: + con = readl(&cru->clksel_con[53]); + div = con >> CLK_SPI0_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK; + break; + case SCLK_SPI1: + con = readl(&cru->clksel_con[53]); + div = con >> CLK_SPI1_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK; + break; + default: + printf("do not support this pwm bus\n"); + return -EINVAL; + } + + return DIV_TO_RATE(priv->gpll_hz, div); +} + +static ulong px30_spi_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz) +{ + struct px30_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); + assert(src_clk_div - 1 <= 127); + + switch (clk_id) { + case SCLK_SPI0: + rk_clrsetreg(&cru->clksel_con[53], + CLK_SPI_DIV_CON_MASK << CLK_SPI0_DIV_CON_SHIFT | + CLK_SPI_PLL_SEL_MASK << CLK_SPI0_PLL_SEL_SHIFT, + (src_clk_div - 1) << CLK_SPI0_DIV_CON_SHIFT | + CLK_SPI_PLL_SEL_GPLL << CLK_SPI0_PLL_SEL_SHIFT); + break; + case SCLK_SPI1: + rk_clrsetreg(&cru->clksel_con[53], + CLK_SPI_DIV_CON_MASK << CLK_SPI1_DIV_CON_SHIFT | + CLK_SPI_PLL_SEL_MASK << CLK_SPI1_PLL_SEL_SHIFT, + (src_clk_div - 1) << CLK_SPI1_DIV_CON_SHIFT | + CLK_SPI_PLL_SEL_GPLL << CLK_SPI1_PLL_SEL_SHIFT); + break; + default: + printf("do not support this pwm bus\n"); + return -EINVAL; + } + + return px30_spi_get_clk(priv, clk_id); +} + +static ulong px30_vop_get_clk(struct px30_clk_priv *priv, ulong clk_id) +{ + struct px30_cru *cru = priv->cru; + u32 div, con, parent; + + switch (clk_id) { + case ACLK_VOPB: + case ACLK_VOPL: + con = readl(&cru->clksel_con[3]); + div = con & ACLK_VO_DIV_MASK; + parent = priv->gpll_hz; + break; + case DCLK_VOPB: + con = readl(&cru->clksel_con[5]); + div = con & DCLK_VOPB_DIV_MASK; + parent = rkclk_pll_get_rate(&cru->pll[CPLL], &cru->mode, CPLL); + break; + case DCLK_VOPL: + con = readl(&cru->clksel_con[8]); + div = con & DCLK_VOPL_DIV_MASK; + parent = rkclk_pll_get_rate(&cru->pll[NPLL], &cru->mode, NPLL); + break; + default: + return -ENOENT; + } + + return DIV_TO_RATE(parent, div); +} + +static ulong px30_vop_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz) +{ + struct px30_cru *cru = priv->cru; + ulong npll_hz; + int src_clk_div; + + switch (clk_id) { + case ACLK_VOPB: + case ACLK_VOPL: + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); + assert(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[3], + ACLK_VO_PLL_MASK | ACLK_VO_DIV_MASK, + ACLK_VO_SEL_GPLL << ACLK_VO_PLL_SHIFT | + (src_clk_div - 1) << ACLK_VO_DIV_SHIFT); + break; + case DCLK_VOPB: + if (hz < PX30_VOP_PLL_LIMIT) { + src_clk_div = DIV_ROUND_UP(PX30_VOP_PLL_LIMIT, hz); + if (src_clk_div % 2) + src_clk_div = src_clk_div - 1; + } else { + src_clk_div = 1; + } + assert(src_clk_div - 1 <= 255); + rkclk_set_pll(&cru->pll[CPLL], &cru->mode, + CPLL, hz * src_clk_div); + rk_clrsetreg(&cru->clksel_con[5], + DCLK_VOPB_SEL_MASK | DCLK_VOPB_PLL_SEL_MASK | + DCLK_VOPB_DIV_MASK, + DCLK_VOPB_SEL_DIVOUT << DCLK_VOPB_SEL_SHIFT | + DCLK_VOPB_PLL_SEL_CPLL << DCLK_VOPB_PLL_SEL_SHIFT | + (src_clk_div - 1) << DCLK_VOPB_DIV_SHIFT); + break; + case DCLK_VOPL: + npll_hz = px30_clk_get_pll_rate(priv, NPLL); + if (npll_hz >= PX30_VOP_PLL_LIMIT && npll_hz >= hz && + npll_hz % hz == 0) { + src_clk_div = npll_hz / hz; + assert(src_clk_div - 1 <= 255); + } else { + if (hz < PX30_VOP_PLL_LIMIT) { + src_clk_div = DIV_ROUND_UP(PX30_VOP_PLL_LIMIT, + hz); + if (src_clk_div % 2) + src_clk_div = src_clk_div - 1; + } else { + src_clk_div = 1; + } + assert(src_clk_div - 1 <= 255); + rkclk_set_pll(&cru->pll[NPLL], &cru->mode, NPLL, + hz * src_clk_div); + } + rk_clrsetreg(&cru->clksel_con[8], + DCLK_VOPL_SEL_MASK | DCLK_VOPL_PLL_SEL_MASK | + DCLK_VOPL_DIV_MASK, + DCLK_VOPL_SEL_DIVOUT << DCLK_VOPL_SEL_SHIFT | + DCLK_VOPL_PLL_SEL_NPLL << DCLK_VOPL_PLL_SEL_SHIFT | + (src_clk_div - 1) << DCLK_VOPL_DIV_SHIFT); + break; + default: + printf("do not support this vop freq\n"); + return -EINVAL; + } + + return px30_vop_get_clk(priv, clk_id); +} + +static ulong px30_bus_get_clk(struct px30_clk_priv *priv, ulong clk_id) +{ + struct px30_cru *cru = priv->cru; + u32 div, con, parent; + + switch (clk_id) { + case ACLK_BUS_PRE: + con = readl(&cru->clksel_con[23]); + div = (con & BUS_ACLK_DIV_MASK) >> BUS_ACLK_DIV_SHIFT; + parent = priv->gpll_hz; + break; + case HCLK_BUS_PRE: + con = readl(&cru->clksel_con[24]); + div = (con & BUS_HCLK_DIV_MASK) >> BUS_HCLK_DIV_SHIFT; + parent = priv->gpll_hz; + break; + case PCLK_BUS_PRE: + case PCLK_WDT_NS: + parent = px30_bus_get_clk(priv, ACLK_BUS_PRE); + con = readl(&cru->clksel_con[24]); + div = (con & BUS_PCLK_DIV_MASK) >> BUS_PCLK_DIV_SHIFT; + break; + default: + return -ENOENT; + } + + return DIV_TO_RATE(parent, div); +} + +static ulong px30_bus_set_clk(struct px30_clk_priv *priv, ulong clk_id, + ulong hz) +{ + struct px30_cru *cru = priv->cru; + int src_clk_div; + + /* + * select gpll as pd_bus bus clock source and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + switch (clk_id) { + case ACLK_BUS_PRE: + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); + assert(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[23], + BUS_PLL_SEL_MASK | BUS_ACLK_DIV_MASK, + BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT | + (src_clk_div - 1) << BUS_ACLK_DIV_SHIFT); + break; + case HCLK_BUS_PRE: + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); + assert(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[24], + BUS_PLL_SEL_MASK | BUS_HCLK_DIV_MASK, + BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT | + (src_clk_div - 1) << BUS_HCLK_DIV_SHIFT); + break; + case PCLK_BUS_PRE: + src_clk_div = + DIV_ROUND_UP(px30_bus_get_clk(priv, ACLK_BUS_PRE), hz); + assert(src_clk_div - 1 <= 3); + rk_clrsetreg(&cru->clksel_con[24], + BUS_PCLK_DIV_MASK, + (src_clk_div - 1) << BUS_PCLK_DIV_SHIFT); + break; + default: + printf("do not support this bus freq\n"); + return -EINVAL; + } + + return px30_bus_get_clk(priv, clk_id); +} + +static ulong px30_peri_get_clk(struct px30_clk_priv *priv, ulong clk_id) +{ + struct px30_cru *cru = priv->cru; + u32 div, con, parent; + + switch (clk_id) { + case ACLK_PERI_PRE: + con = readl(&cru->clksel_con[14]); + div = (con & PERI_ACLK_DIV_MASK) >> PERI_ACLK_DIV_SHIFT; + parent = priv->gpll_hz; + break; + case HCLK_PERI_PRE: + con = readl(&cru->clksel_con[14]); + div = (con & PERI_HCLK_DIV_MASK) >> PERI_HCLK_DIV_SHIFT; + parent = priv->gpll_hz; + break; + default: + return -ENOENT; + } + + return DIV_TO_RATE(parent, div); +} + +static ulong px30_peri_set_clk(struct px30_clk_priv *priv, ulong clk_id, + ulong hz) +{ + struct px30_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); + assert(src_clk_div - 1 <= 31); + + /* + * select gpll as pd_peri bus clock source and + * set up dependent divisors for HCLK and ACLK clocks. + */ + switch (clk_id) { + case ACLK_PERI_PRE: + rk_clrsetreg(&cru->clksel_con[14], + PERI_PLL_SEL_MASK | PERI_ACLK_DIV_MASK, + PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT | + (src_clk_div - 1) << PERI_ACLK_DIV_SHIFT); + break; + case HCLK_PERI_PRE: + rk_clrsetreg(&cru->clksel_con[14], + PERI_PLL_SEL_MASK | PERI_HCLK_DIV_MASK, + PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT | + (src_clk_div - 1) << PERI_HCLK_DIV_SHIFT); + break; + default: + printf("do not support this peri freq\n"); + return -EINVAL; + } + + return px30_peri_get_clk(priv, clk_id); +} + +#ifndef CONFIG_SPL_BUILD +static ulong px30_crypto_get_clk(struct px30_clk_priv *priv, ulong clk_id) +{ + struct px30_cru *cru = priv->cru; + u32 div, con, parent; + + switch (clk_id) { + case SCLK_CRYPTO: + con = readl(&cru->clksel_con[25]); + div = (con & CRYPTO_DIV_MASK) >> CRYPTO_DIV_SHIFT; + parent = priv->gpll_hz; + break; + case SCLK_CRYPTO_APK: + con = readl(&cru->clksel_con[25]); + div = (con & CRYPTO_APK_DIV_MASK) >> CRYPTO_APK_DIV_SHIFT; + parent = priv->gpll_hz; + break; + default: + return -ENOENT; + } + + return DIV_TO_RATE(parent, div); +} + +static ulong px30_crypto_set_clk(struct px30_clk_priv *priv, ulong clk_id, + ulong hz) +{ + struct px30_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); + assert(src_clk_div - 1 <= 31); + + /* + * select gpll as crypto clock source and + * set up dependent divisors for crypto clocks. + */ + switch (clk_id) { + case SCLK_CRYPTO: + rk_clrsetreg(&cru->clksel_con[25], + CRYPTO_PLL_SEL_MASK | CRYPTO_DIV_MASK, + CRYPTO_PLL_SEL_GPLL << CRYPTO_PLL_SEL_SHIFT | + (src_clk_div - 1) << CRYPTO_DIV_SHIFT); + break; + case SCLK_CRYPTO_APK: + rk_clrsetreg(&cru->clksel_con[25], + CRYPTO_APK_PLL_SEL_MASK | CRYPTO_APK_DIV_MASK, + CRYPTO_PLL_SEL_GPLL << CRYPTO_APK_SEL_SHIFT | + (src_clk_div - 1) << CRYPTO_APK_DIV_SHIFT); + break; + default: + printf("do not support this peri freq\n"); + return -EINVAL; + } + + return px30_crypto_get_clk(priv, clk_id); +} + +static ulong px30_i2s1_mclk_get_clk(struct px30_clk_priv *priv, ulong clk_id) +{ + struct px30_cru *cru = priv->cru; + u32 con; + + con = readl(&cru->clksel_con[30]); + + if (!(con & CLK_I2S1_OUT_SEL_MASK)) + return -ENOENT; + + return 12000000; +} + +static ulong px30_i2s1_mclk_set_clk(struct px30_clk_priv *priv, ulong clk_id, + ulong hz) +{ + struct px30_cru *cru = priv->cru; + + if (hz != 12000000) { + printf("do not support this i2s1_mclk freq\n"); + return -EINVAL; + } + + rk_clrsetreg(&cru->clksel_con[30], CLK_I2S1_OUT_SEL_MASK, + CLK_I2S1_OUT_SEL_OSC); + rk_clrsetreg(&cru->clkgate_con[10], CLK_I2S1_OUT_MCLK_PAD_MASK, + CLK_I2S1_OUT_MCLK_PAD_ENABLE); + + return px30_i2s1_mclk_get_clk(priv, clk_id); +} + +static ulong px30_mac_set_clk(struct px30_clk_priv *priv, uint hz) +{ + struct px30_cru *cru = priv->cru; + u32 con = readl(&cru->clksel_con[22]); + ulong pll_rate; + u8 div; + + if ((con >> GMAC_PLL_SEL_SHIFT) & GMAC_PLL_SEL_CPLL) + pll_rate = px30_clk_get_pll_rate(priv, CPLL); + else if ((con >> GMAC_PLL_SEL_SHIFT) & GMAC_PLL_SEL_NPLL) + pll_rate = px30_clk_get_pll_rate(priv, NPLL); + else + pll_rate = priv->gpll_hz; + + /*default set 50MHZ for gmac*/ + if (!hz) + hz = 50000000; + + div = DIV_ROUND_UP(pll_rate, hz) - 1; + assert(div < 32); + rk_clrsetreg(&cru->clksel_con[22], CLK_GMAC_DIV_MASK, + div << CLK_GMAC_DIV_SHIFT); + + return DIV_TO_RATE(pll_rate, div); +} + +static int px30_mac_set_speed_clk(struct px30_clk_priv *priv, uint hz) +{ + struct px30_cru *cru = priv->cru; + + if (hz != 2500000 && hz != 25000000) { + debug("Unsupported mac speed:%d\n", hz); + return -EINVAL; + } + + rk_clrsetreg(&cru->clksel_con[23], RMII_CLK_SEL_MASK, + ((hz == 2500000) ? 0 : 1) << RMII_CLK_SEL_SHIFT); + + return 0; +} + +#endif + +static ulong px30_clk_get_pll_rate(struct px30_clk_priv *priv, + enum px30_pll_id pll_id) +{ + struct px30_cru *cru = priv->cru; + + return rkclk_pll_get_rate(&cru->pll[pll_id], &cru->mode, pll_id); +} + +static ulong px30_clk_set_pll_rate(struct px30_clk_priv *priv, + enum px30_pll_id pll_id, ulong hz) +{ + struct px30_cru *cru = priv->cru; + + if (rkclk_set_pll(&cru->pll[pll_id], &cru->mode, pll_id, hz)) + return -EINVAL; + return rkclk_pll_get_rate(&cru->pll[pll_id], &cru->mode, pll_id); +} + +static ulong px30_armclk_set_clk(struct px30_clk_priv *priv, ulong hz) +{ + struct px30_cru *cru = priv->cru; + const struct cpu_rate_table *rate; + ulong old_rate; + + rate = get_cpu_settings(hz); + if (!rate) { + printf("%s unsupport rate\n", __func__); + return -EINVAL; + } + + /* + * select apll as cpu/core clock pll source and + * set up dependent divisors for PERI and ACLK clocks. + * core hz : apll = 1:1 + */ + old_rate = px30_clk_get_pll_rate(priv, APLL); + if (old_rate > hz) { + if (rkclk_set_pll(&cru->pll[APLL], &cru->mode, APLL, hz)) + return -EINVAL; + rk_clrsetreg(&cru->clksel_con[0], + CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK | + CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK, + rate->aclk_div << CORE_ACLK_DIV_SHIFT | + rate->pclk_div << CORE_DBG_DIV_SHIFT | + CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | + 0 << CORE_DIV_CON_SHIFT); + } else if (old_rate < hz) { + rk_clrsetreg(&cru->clksel_con[0], + CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK | + CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK, + rate->aclk_div << CORE_ACLK_DIV_SHIFT | + rate->pclk_div << CORE_DBG_DIV_SHIFT | + CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | + 0 << CORE_DIV_CON_SHIFT); + if (rkclk_set_pll(&cru->pll[APLL], &cru->mode, APLL, hz)) + return -EINVAL; + } + + return px30_clk_get_pll_rate(priv, APLL); +} + +static ulong px30_clk_get_rate(struct clk *clk) +{ + struct px30_clk_priv *priv = dev_get_priv(clk->dev); + ulong rate = 0; + + if (!priv->gpll_hz && clk->id > ARMCLK) { + printf("%s gpll=%lu\n", __func__, priv->gpll_hz); + return -ENOENT; + } + + debug("%s %ld\n", __func__, clk->id); + switch (clk->id) { + case PLL_APLL: + rate = px30_clk_get_pll_rate(priv, APLL); + break; + case PLL_DPLL: + rate = px30_clk_get_pll_rate(priv, DPLL); + break; + case PLL_CPLL: + rate = px30_clk_get_pll_rate(priv, CPLL); + break; + case PLL_NPLL: + rate = px30_clk_get_pll_rate(priv, NPLL); + break; + case ARMCLK: + rate = px30_clk_get_pll_rate(priv, APLL); + break; + case HCLK_SDMMC: + case HCLK_EMMC: + case SCLK_SDMMC: + case SCLK_EMMC: + case SCLK_EMMC_SAMPLE: + rate = px30_mmc_get_clk(priv, clk->id); + break; + case SCLK_I2C0: + case SCLK_I2C1: + case SCLK_I2C2: + case SCLK_I2C3: + rate = px30_i2c_get_clk(priv, clk->id); + break; + case SCLK_I2S1: + rate = px30_i2s_get_clk(priv, clk->id); + break; + case SCLK_NANDC: + rate = px30_nandc_get_clk(priv); + break; + case SCLK_PWM0: + case SCLK_PWM1: + rate = px30_pwm_get_clk(priv, clk->id); + break; + case SCLK_SARADC: + rate = px30_saradc_get_clk(priv); + break; + case SCLK_TSADC: + rate = px30_tsadc_get_clk(priv); + break; + case SCLK_SPI0: + case SCLK_SPI1: + rate = px30_spi_get_clk(priv, clk->id); + break; + case ACLK_VOPB: + case ACLK_VOPL: + case DCLK_VOPB: + case DCLK_VOPL: + rate = px30_vop_get_clk(priv, clk->id); + break; + case ACLK_BUS_PRE: + case HCLK_BUS_PRE: + case PCLK_BUS_PRE: + case PCLK_WDT_NS: + rate = px30_bus_get_clk(priv, clk->id); + break; + case ACLK_PERI_PRE: + case HCLK_PERI_PRE: + rate = px30_peri_get_clk(priv, clk->id); + break; +#ifndef CONFIG_SPL_BUILD + case SCLK_CRYPTO: + case SCLK_CRYPTO_APK: + rate = px30_crypto_get_clk(priv, clk->id); + break; +#endif + default: + return -ENOENT; + } + + return rate; +} + +static ulong px30_clk_set_rate(struct clk *clk, ulong rate) +{ + struct px30_clk_priv *priv = dev_get_priv(clk->dev); + ulong ret = 0; + + if (!priv->gpll_hz && clk->id > ARMCLK) { + printf("%s gpll=%lu\n", __func__, priv->gpll_hz); + return -ENOENT; + } + + debug("%s %ld %ld\n", __func__, clk->id, rate); + switch (clk->id) { + case PLL_NPLL: + ret = px30_clk_set_pll_rate(priv, NPLL, rate); + break; + case ARMCLK: + ret = px30_armclk_set_clk(priv, rate); + break; + case HCLK_SDMMC: + case HCLK_EMMC: + case SCLK_SDMMC: + case SCLK_EMMC: + ret = px30_mmc_set_clk(priv, clk->id, rate); + break; + case SCLK_I2C0: + case SCLK_I2C1: + case SCLK_I2C2: + case SCLK_I2C3: + ret = px30_i2c_set_clk(priv, clk->id, rate); + break; + case SCLK_I2S1: + ret = px30_i2s_set_clk(priv, clk->id, rate); + break; + case SCLK_NANDC: + ret = px30_nandc_set_clk(priv, rate); + break; + case SCLK_PWM0: + case SCLK_PWM1: + ret = px30_pwm_set_clk(priv, clk->id, rate); + break; + case SCLK_SARADC: + ret = px30_saradc_set_clk(priv, rate); + break; + case SCLK_TSADC: + ret = px30_tsadc_set_clk(priv, rate); + break; + case SCLK_SPI0: + case SCLK_SPI1: + ret = px30_spi_set_clk(priv, clk->id, rate); + break; + case ACLK_VOPB: + case ACLK_VOPL: + case DCLK_VOPB: + case DCLK_VOPL: + ret = px30_vop_set_clk(priv, clk->id, rate); + break; + case ACLK_BUS_PRE: + case HCLK_BUS_PRE: + case PCLK_BUS_PRE: + ret = px30_bus_set_clk(priv, clk->id, rate); + break; + case ACLK_PERI_PRE: + case HCLK_PERI_PRE: + ret = px30_peri_set_clk(priv, clk->id, rate); + break; +#ifndef CONFIG_SPL_BUILD + case SCLK_CRYPTO: + case SCLK_CRYPTO_APK: + ret = px30_crypto_set_clk(priv, clk->id, rate); + break; + case SCLK_I2S1_OUT: + ret = px30_i2s1_mclk_set_clk(priv, clk->id, rate); + break; + case SCLK_GMAC: + case SCLK_GMAC_SRC: + ret = px30_mac_set_clk(priv, rate); + break; + case SCLK_GMAC_RMII: + ret = px30_mac_set_speed_clk(priv, rate); + break; +#endif + default: + return -ENOENT; + } + + return ret; +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) +static int px30_gmac_set_parent(struct clk *clk, struct clk *parent) +{ + struct px30_clk_priv *priv = dev_get_priv(clk->dev); + struct px30_cru *cru = priv->cru; + + if (parent->id == SCLK_GMAC_SRC) { + debug("%s: switching GAMC to SCLK_GMAC_SRC\n", __func__); + rk_clrsetreg(&cru->clksel_con[23], RMII_EXTCLK_SEL_MASK, + RMII_EXTCLK_SEL_INT << RMII_EXTCLK_SEL_SHIFT); + } else { + debug("%s: switching GMAC to external clock\n", __func__); + rk_clrsetreg(&cru->clksel_con[23], RMII_EXTCLK_SEL_MASK, + RMII_EXTCLK_SEL_EXT << RMII_EXTCLK_SEL_SHIFT); + } + return 0; +} + +static int px30_clk_set_parent(struct clk *clk, struct clk *parent) +{ + switch (clk->id) { + case SCLK_GMAC: + return px30_gmac_set_parent(clk, parent); + default: + return -ENOENT; + } +} +#endif + +static int px30_clk_enable(struct clk *clk) +{ + switch (clk->id) { + case HCLK_HOST: + case SCLK_GMAC: + case SCLK_GMAC_RX_TX: + case SCLK_MAC_REF: + case SCLK_MAC_REFOUT: + case ACLK_GMAC: + case PCLK_GMAC: + case SCLK_GMAC_RMII: + /* Required to successfully probe the Designware GMAC driver */ + return 0; + } + + debug("%s: unsupported clk %ld\n", __func__, clk->id); + return -ENOENT; +} + +static struct clk_ops px30_clk_ops = { + .get_rate = px30_clk_get_rate, + .set_rate = px30_clk_set_rate, +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + .set_parent = px30_clk_set_parent, +#endif + .enable = px30_clk_enable, +}; + +static void px30_clk_init(struct px30_clk_priv *priv) +{ + ulong npll_hz; + int ret; + + npll_hz = px30_clk_get_pll_rate(priv, NPLL); + if (npll_hz != NPLL_HZ) { + ret = px30_clk_set_pll_rate(priv, NPLL, NPLL_HZ); + if (ret < 0) + printf("%s failed to set npll rate\n", __func__); + } + + px30_bus_set_clk(priv, ACLK_BUS_PRE, ACLK_BUS_HZ); + px30_bus_set_clk(priv, HCLK_BUS_PRE, HCLK_BUS_HZ); + px30_bus_set_clk(priv, PCLK_BUS_PRE, PCLK_BUS_HZ); + px30_peri_set_clk(priv, ACLK_PERI_PRE, ACLK_PERI_HZ); + px30_peri_set_clk(priv, HCLK_PERI_PRE, HCLK_PERI_HZ); +} + +static int px30_clk_probe(struct udevice *dev) +{ + struct px30_clk_priv *priv = dev_get_priv(dev); + struct clk clk_gpll; + int ret; + + if (px30_clk_get_pll_rate(priv, APLL) != APLL_HZ) + px30_armclk_set_clk(priv, APLL_HZ); + + /* get the GPLL rate from the pmucru */ + ret = clk_get_by_name(dev, "gpll", &clk_gpll); + if (ret) { + printf("%s: failed to get gpll clk from pmucru\n", __func__); + return ret; + } + + priv->gpll_hz = clk_get_rate(&clk_gpll); + + px30_clk_init(priv); + + return 0; +} + +static int px30_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct px30_clk_priv *priv = dev_get_priv(dev); + + priv->cru = dev_read_addr_ptr(dev); + + return 0; +} + +static int px30_clk_bind(struct udevice *dev) +{ + int ret; + struct udevice *sys_child; + struct sysreset_reg *priv; + + /* The reset driver does not have a device node, so bind it here */ + ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", + &sys_child); + if (ret) { + debug("Warning: No sysreset driver: ret=%d\n", ret); + } else { + priv = malloc(sizeof(struct sysreset_reg)); + priv->glb_srst_fst_value = offsetof(struct px30_cru, + glb_srst_fst); + priv->glb_srst_snd_value = offsetof(struct px30_cru, + glb_srst_snd); + sys_child->priv = priv; + } + +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) + ret = offsetof(struct px30_cru, softrst_con[0]); + ret = rockchip_reset_bind(dev, ret, 12); + if (ret) + debug("Warning: software reset driver bind faile\n"); +#endif + + return 0; +} + +static const struct udevice_id px30_clk_ids[] = { + { .compatible = "rockchip,px30-cru" }, + { } +}; + +U_BOOT_DRIVER(rockchip_px30_cru) = { + .name = "rockchip_px30_cru", + .id = UCLASS_CLK, + .of_match = px30_clk_ids, + .priv_auto_alloc_size = sizeof(struct px30_clk_priv), + .ofdata_to_platdata = px30_clk_ofdata_to_platdata, + .ops = &px30_clk_ops, + .bind = px30_clk_bind, + .probe = px30_clk_probe, +}; + +static ulong px30_pclk_pmu_get_pmuclk(struct px30_pmuclk_priv *priv) +{ + struct px30_pmucru *pmucru = priv->pmucru; + u32 div, con; + + con = readl(&pmucru->pmu_clksel_con[0]); + div = (con & CLK_PMU_PCLK_DIV_MASK) >> CLK_PMU_PCLK_DIV_SHIFT; + + return DIV_TO_RATE(priv->gpll_hz, div); +} + +static ulong px30_pclk_pmu_set_pmuclk(struct px30_pmuclk_priv *priv, ulong hz) +{ + struct px30_pmucru *pmucru = priv->pmucru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); + assert(src_clk_div - 1 <= 31); + + rk_clrsetreg(&pmucru->pmu_clksel_con[0], + CLK_PMU_PCLK_DIV_MASK, + (src_clk_div - 1) << CLK_PMU_PCLK_DIV_SHIFT); + + return px30_pclk_pmu_get_pmuclk(priv); +} + +static ulong px30_pmuclk_get_gpll_rate(struct px30_pmuclk_priv *priv) +{ + struct px30_pmucru *pmucru = priv->pmucru; + + return rkclk_pll_get_rate(&pmucru->pll, &pmucru->pmu_mode, GPLL); +} + +static ulong px30_pmuclk_set_gpll_rate(struct px30_pmuclk_priv *priv, ulong hz) +{ + struct px30_pmucru *pmucru = priv->pmucru; + ulong pclk_pmu_rate; + u32 div; + + if (priv->gpll_hz == hz) + return priv->gpll_hz; + + div = DIV_ROUND_UP(hz, priv->gpll_hz); + + /* save clock rate */ + pclk_pmu_rate = px30_pclk_pmu_get_pmuclk(priv); + + /* avoid rate too large, reduce rate first */ + px30_pclk_pmu_set_pmuclk(priv, pclk_pmu_rate / div); + + /* change gpll rate */ + rkclk_set_pll(&pmucru->pll, &pmucru->pmu_mode, GPLL, hz); + priv->gpll_hz = px30_pmuclk_get_gpll_rate(priv); + + /* restore clock rate */ + px30_pclk_pmu_set_pmuclk(priv, pclk_pmu_rate); + + return priv->gpll_hz; +} + +static ulong px30_pmuclk_get_rate(struct clk *clk) +{ + struct px30_pmuclk_priv *priv = dev_get_priv(clk->dev); + ulong rate = 0; + + debug("%s %ld\n", __func__, clk->id); + switch (clk->id) { + case PLL_GPLL: + rate = px30_pmuclk_get_gpll_rate(priv); + break; + case PCLK_PMU_PRE: + rate = px30_pclk_pmu_get_pmuclk(priv); + break; + default: + return -ENOENT; + } + + return rate; +} + +static ulong px30_pmuclk_set_rate(struct clk *clk, ulong rate) +{ + struct px30_pmuclk_priv *priv = dev_get_priv(clk->dev); + ulong ret = 0; + + debug("%s %ld %ld\n", __func__, clk->id, rate); + switch (clk->id) { + case PLL_GPLL: + ret = px30_pmuclk_set_gpll_rate(priv, rate); + break; + case PCLK_PMU_PRE: + ret = px30_pclk_pmu_set_pmuclk(priv, rate); + break; + default: + return -ENOENT; + } + + return ret; +} + +static struct clk_ops px30_pmuclk_ops = { + .get_rate = px30_pmuclk_get_rate, + .set_rate = px30_pmuclk_set_rate, +}; + +static void px30_pmuclk_init(struct px30_pmuclk_priv *priv) +{ + priv->gpll_hz = px30_pmuclk_get_gpll_rate(priv); + px30_pmuclk_set_gpll_rate(priv, GPLL_HZ); + + px30_pclk_pmu_set_pmuclk(priv, PCLK_PMU_HZ); +} + +static int px30_pmuclk_probe(struct udevice *dev) +{ + struct px30_pmuclk_priv *priv = dev_get_priv(dev); + + px30_pmuclk_init(priv); + + return 0; +} + +static int px30_pmuclk_ofdata_to_platdata(struct udevice *dev) +{ + struct px30_pmuclk_priv *priv = dev_get_priv(dev); + + priv->pmucru = dev_read_addr_ptr(dev); + + return 0; +} + +static const struct udevice_id px30_pmuclk_ids[] = { + { .compatible = "rockchip,px30-pmucru" }, + { } +}; + +U_BOOT_DRIVER(rockchip_px30_pmucru) = { + .name = "rockchip_px30_pmucru", + .id = UCLASS_CLK, + .of_match = px30_pmuclk_ids, + .priv_auto_alloc_size = sizeof(struct px30_pmuclk_priv), + .ofdata_to_platdata = px30_pmuclk_ofdata_to_platdata, + .ops = &px30_pmuclk_ops, + .probe = px30_pmuclk_probe, +}; diff --git a/drivers/clk/rockchip/clk_rk3036.c b/drivers/clk/rockchip/clk_rk3036.c index 9bf9cedaf8..6d5ae3d003 100644 --- a/drivers/clk/rockchip/clk_rk3036.c +++ b/drivers/clk/rockchip/clk_rk3036.c @@ -352,7 +352,7 @@ static int rk3036_clk_bind(struct udevice *dev) sys_child->priv = priv; } -#if CONFIG_IS_ENABLED(CONFIG_RESET_ROCKCHIP) +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) ret = offsetof(struct rk3036_cru, cru_softrst_con[0]); ret = rockchip_reset_bind(dev, ret, 9); if (ret) diff --git a/drivers/clk/rockchip/clk_rk3188.c b/drivers/clk/rockchip/clk_rk3188.c index dda686cfb3..3ea9a81b32 100644 --- a/drivers/clk/rockchip/clk_rk3188.c +++ b/drivers/clk/rockchip/clk_rk3188.c @@ -590,7 +590,7 @@ static int rk3188_clk_bind(struct udevice *dev) sys_child->priv = priv; } -#if CONFIG_IS_ENABLED(CONFIG_RESET_ROCKCHIP) +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) ret = offsetof(struct rk3188_cru, cru_softrst_con[0]); ret = rockchip_reset_bind(dev, ret, 9); if (ret) diff --git a/drivers/clk/rockchip/clk_rk322x.c b/drivers/clk/rockchip/clk_rk322x.c index f09730c91b..6e8a164d62 100644 --- a/drivers/clk/rockchip/clk_rk322x.c +++ b/drivers/clk/rockchip/clk_rk322x.c @@ -508,7 +508,7 @@ static int rk322x_clk_bind(struct udevice *dev) sys_child->priv = priv; } -#if CONFIG_IS_ENABLED(CONFIG_RESET_ROCKCHIP) +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) ret = offsetof(struct rk322x_cru, cru_softrst_con[0]); ret = rockchip_reset_bind(dev, ret, 9); if (ret) diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c index 0122381633..85d1b67e43 100644 --- a/drivers/clk/rockchip/clk_rk3288.c +++ b/drivers/clk/rockchip/clk_rk3288.c @@ -1015,7 +1015,7 @@ static int rk3288_clk_bind(struct udevice *dev) sys_child->priv = priv; } -#if CONFIG_IS_ENABLED(CONFIG_RESET_ROCKCHIP) +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) ret = offsetof(struct rk3288_cru, cru_softrst_con[0]); ret = rockchip_reset_bind(dev, ret, 12); if (ret) diff --git a/drivers/clk/rockchip/clk_rk3308.c b/drivers/clk/rockchip/clk_rk3308.c new file mode 100644 index 0000000000..f212c5ffc2 --- /dev/null +++ b/drivers/clk/rockchip/clk_rk3308.c @@ -0,0 +1,1072 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2017-2019 Rockchip Electronics Co., Ltd + */ +#include <common.h> +#include <bitfield.h> +#include <clk-uclass.h> +#include <dm.h> +#include <div64.h> +#include <errno.h> +#include <syscon.h> +#include <asm/io.h> +#include <asm/arch/cru_rk3308.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/hardware.h> +#include <dm/lists.h> +#include <dt-bindings/clock/rk3308-cru.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum { + VCO_MAX_HZ = 3200U * 1000000, + VCO_MIN_HZ = 800 * 1000000, + OUTPUT_MAX_HZ = 3200U * 1000000, + OUTPUT_MIN_HZ = 24 * 1000000, +}; + +#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) + +#define RK3308_CPUCLK_RATE(_rate, _aclk_div, _pclk_div) \ +{ \ + .rate = _rate##U, \ + .aclk_div = _aclk_div, \ + .pclk_div = _pclk_div, \ +} + +static struct rockchip_pll_rate_table rk3308_pll_rates[] = { + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ + RK3036_PLL_RATE(1300000000, 6, 325, 1, 1, 1, 0), + RK3036_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0), + RK3036_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0), + RK3036_PLL_RATE(748000000, 2, 187, 3, 1, 1, 0), +}; + +static struct rockchip_cpu_rate_table rk3308_cpu_rates[] = { + RK3308_CPUCLK_RATE(1200000000, 1, 5), + RK3308_CPUCLK_RATE(1008000000, 1, 5), + RK3308_CPUCLK_RATE(816000000, 1, 3), + RK3308_CPUCLK_RATE(600000000, 1, 3), + RK3308_CPUCLK_RATE(408000000, 1, 1), +}; + +static struct rockchip_pll_clock rk3308_pll_clks[] = { + [APLL] = PLL(pll_rk3328, PLL_APLL, RK3308_PLL_CON(0), + RK3308_MODE_CON, 0, 10, 0, rk3308_pll_rates), + [DPLL] = PLL(pll_rk3328, PLL_DPLL, RK3308_PLL_CON(8), + RK3308_MODE_CON, 2, 10, 0, NULL), + [VPLL0] = PLL(pll_rk3328, PLL_VPLL0, RK3308_PLL_CON(16), + RK3308_MODE_CON, 4, 10, 0, NULL), + [VPLL1] = PLL(pll_rk3328, PLL_VPLL1, RK3308_PLL_CON(24), + RK3308_MODE_CON, 6, 10, 0, NULL), +}; + +static ulong rk3308_armclk_set_clk(struct rk3308_clk_priv *priv, ulong hz) +{ + struct rk3308_cru *cru = priv->cru; + const struct rockchip_cpu_rate_table *rate; + ulong old_rate; + + rate = rockchip_get_cpu_settings(rk3308_cpu_rates, hz); + if (!rate) { + printf("%s unsupport rate\n", __func__); + return -EINVAL; + } + + /* + * select apll as cpu/core clock pll source and + * set up dependent divisors for PERI and ACLK clocks. + * core hz : apll = 1:1 + */ + old_rate = rockchip_pll_get_rate(&rk3308_pll_clks[APLL], + priv->cru, APLL); + if (old_rate > hz) { + if (rockchip_pll_set_rate(&rk3308_pll_clks[APLL], + priv->cru, APLL, hz)) + return -EINVAL; + rk_clrsetreg(&cru->clksel_con[0], + CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK | + CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK, + rate->aclk_div << CORE_ACLK_DIV_SHIFT | + rate->pclk_div << CORE_DBG_DIV_SHIFT | + CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | + 0 << CORE_DIV_CON_SHIFT); + } else if (old_rate < hz) { + rk_clrsetreg(&cru->clksel_con[0], + CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK | + CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK, + rate->aclk_div << CORE_ACLK_DIV_SHIFT | + rate->pclk_div << CORE_DBG_DIV_SHIFT | + CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | + 0 << CORE_DIV_CON_SHIFT); + if (rockchip_pll_set_rate(&rk3308_pll_clks[APLL], + priv->cru, APLL, hz)) + return -EINVAL; + } + + return rockchip_pll_get_rate(&rk3308_pll_clks[APLL], priv->cru, APLL); +} + +static void rk3308_clk_get_pll_rate(struct rk3308_clk_priv *priv) +{ + if (!priv->dpll_hz) + priv->dpll_hz = rockchip_pll_get_rate(&rk3308_pll_clks[DPLL], + priv->cru, DPLL); + if (!priv->vpll0_hz) + priv->vpll0_hz = rockchip_pll_get_rate(&rk3308_pll_clks[VPLL0], + priv->cru, VPLL0); + if (!priv->vpll1_hz) + priv->vpll1_hz = rockchip_pll_get_rate(&rk3308_pll_clks[VPLL1], + priv->cru, VPLL1); +} + +static ulong rk3308_i2c_get_clk(struct clk *clk) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + u32 div, con, con_id; + + switch (clk->id) { + case SCLK_I2C0: + con_id = 25; + break; + case SCLK_I2C1: + con_id = 26; + break; + case SCLK_I2C2: + con_id = 27; + break; + case SCLK_I2C3: + con_id = 28; + break; + default: + printf("do not support this i2c bus\n"); + return -EINVAL; + } + + con = readl(&cru->clksel_con[con_id]); + div = con >> CLK_I2C_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; + + return DIV_TO_RATE(priv->dpll_hz, div); +} + +static ulong rk3308_i2c_set_clk(struct clk *clk, uint hz) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + u32 src_clk_div, con_id; + + src_clk_div = DIV_ROUND_UP(priv->dpll_hz, hz); + assert(src_clk_div - 1 <= 127); + + switch (clk->id) { + case SCLK_I2C0: + con_id = 25; + break; + case SCLK_I2C1: + con_id = 26; + break; + case SCLK_I2C2: + con_id = 27; + break; + case SCLK_I2C3: + con_id = 28; + break; + default: + printf("do not support this i2c bus\n"); + return -EINVAL; + } + rk_clrsetreg(&cru->clksel_con[con_id], + CLK_I2C_PLL_SEL_MASK | CLK_I2C_DIV_CON_MASK, + CLK_I2C_PLL_SEL_DPLL << CLK_I2C_PLL_SEL_SHIFT | + (src_clk_div - 1) << CLK_I2C_DIV_CON_SHIFT); + + return rk3308_i2c_get_clk(clk); +} + +static ulong rk3308_mac_set_clk(struct clk *clk, uint hz) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + u32 con = readl(&cru->clksel_con[43]); + ulong pll_rate; + u8 div; + + if ((con >> MAC_PLL_SHIFT) & MAC_SEL_VPLL0) + pll_rate = rockchip_pll_get_rate(&rk3308_pll_clks[VPLL0], + priv->cru, VPLL0); + else if ((con >> MAC_PLL_SHIFT) & MAC_SEL_VPLL1) + pll_rate = rockchip_pll_get_rate(&rk3308_pll_clks[VPLL1], + priv->cru, VPLL1); + else + pll_rate = rockchip_pll_get_rate(&rk3308_pll_clks[DPLL], + priv->cru, DPLL); + + /*default set 50MHZ for gmac*/ + if (!hz) + hz = 50000000; + + div = DIV_ROUND_UP(pll_rate, hz) - 1; + assert(div < 32); + rk_clrsetreg(&cru->clksel_con[43], MAC_DIV_MASK, + div << MAC_DIV_SHIFT); + + return DIV_TO_RATE(pll_rate, div); +} + +static int rk3308_mac_set_speed_clk(struct clk *clk, uint hz) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + + if (hz != 2500000 && hz != 25000000) { + debug("Unsupported mac speed:%d\n", hz); + return -EINVAL; + } + + rk_clrsetreg(&cru->clksel_con[43], MAC_CLK_SPEED_SEL_MASK, + ((hz == 2500000) ? 0 : 1) << MAC_CLK_SPEED_SEL_SHIFT); + + return 0; +} + +static ulong rk3308_mmc_get_clk(struct clk *clk) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + u32 div, con, con_id; + + switch (clk->id) { + case HCLK_SDMMC: + case SCLK_SDMMC: + con_id = 39; + break; + case HCLK_EMMC: + case SCLK_EMMC: + case SCLK_EMMC_SAMPLE: + con_id = 41; + break; + default: + return -EINVAL; + } + + con = readl(&cru->clksel_con[con_id]); + div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT; + + if ((con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT + == EMMC_SEL_24M) + return DIV_TO_RATE(OSC_HZ, div) / 2; + else + return DIV_TO_RATE(priv->vpll0_hz, div) / 2; +} + +static ulong rk3308_mmc_set_clk(struct clk *clk, ulong set_rate) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + int src_clk_div; + u32 con_id; + + switch (clk->id) { + case HCLK_SDMMC: + case SCLK_SDMMC: + con_id = 39; + break; + case HCLK_EMMC: + case SCLK_EMMC: + con_id = 41; + break; + default: + return -EINVAL; + } + /* Select clk_sdmmc/emmc source from VPLL0 by default */ + /* mmc clock defaulg div 2 internal, need provide double in cru */ + src_clk_div = DIV_ROUND_UP(priv->vpll0_hz / 2, set_rate); + + if (src_clk_div > 127) { + /* use 24MHz source for 400KHz clock */ + src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, set_rate); + rk_clrsetreg(&cru->clksel_con[con_id], + EMMC_PLL_MASK | EMMC_DIV_MASK | EMMC_CLK_SEL_MASK, + EMMC_CLK_SEL_EMMC << EMMC_CLK_SEL_SHIFT | + EMMC_SEL_24M << EMMC_PLL_SHIFT | + (src_clk_div - 1) << EMMC_DIV_SHIFT); + } else { + rk_clrsetreg(&cru->clksel_con[con_id], + EMMC_PLL_MASK | EMMC_DIV_MASK | EMMC_CLK_SEL_MASK, + EMMC_CLK_SEL_EMMC << EMMC_CLK_SEL_SHIFT | + EMMC_SEL_VPLL0 << EMMC_PLL_SHIFT | + (src_clk_div - 1) << EMMC_DIV_SHIFT); + } + + return rk3308_mmc_get_clk(clk); +} + +static ulong rk3308_saradc_get_clk(struct clk *clk) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + u32 div, con; + + con = readl(&cru->clksel_con[34]); + div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK; + + return DIV_TO_RATE(OSC_HZ, div); +} + +static ulong rk3308_saradc_set_clk(struct clk *clk, uint hz) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(OSC_HZ, hz); + assert(src_clk_div - 1 <= 2047); + + rk_clrsetreg(&cru->clksel_con[34], + CLK_SARADC_DIV_CON_MASK, + (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT); + + return rk3308_saradc_get_clk(clk); +} + +static ulong rk3308_tsadc_get_clk(struct clk *clk) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + u32 div, con; + + con = readl(&cru->clksel_con[33]); + div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK; + + return DIV_TO_RATE(OSC_HZ, div); +} + +static ulong rk3308_tsadc_set_clk(struct clk *clk, uint hz) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(OSC_HZ, hz); + assert(src_clk_div - 1 <= 2047); + + rk_clrsetreg(&cru->clksel_con[33], + CLK_SARADC_DIV_CON_MASK, + (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT); + + return rk3308_tsadc_get_clk(clk); +} + +static ulong rk3308_spi_get_clk(struct clk *clk) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + u32 div, con, con_id; + + switch (clk->id) { + case SCLK_SPI0: + con_id = 30; + break; + case SCLK_SPI1: + con_id = 31; + break; + case SCLK_SPI2: + con_id = 32; + break; + default: + printf("do not support this spi bus\n"); + return -EINVAL; + } + + con = readl(&cru->clksel_con[con_id]); + div = con >> CLK_SPI_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK; + + return DIV_TO_RATE(priv->dpll_hz, div); +} + +static ulong rk3308_spi_set_clk(struct clk *clk, uint hz) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + u32 src_clk_div, con_id; + + src_clk_div = DIV_ROUND_UP(priv->dpll_hz, hz); + assert(src_clk_div - 1 <= 127); + + switch (clk->id) { + case SCLK_SPI0: + con_id = 30; + break; + case SCLK_SPI1: + con_id = 31; + break; + case SCLK_SPI2: + con_id = 32; + break; + default: + printf("do not support this spi bus\n"); + return -EINVAL; + } + + rk_clrsetreg(&cru->clksel_con[con_id], + CLK_SPI_PLL_SEL_MASK | CLK_SPI_DIV_CON_MASK, + CLK_SPI_PLL_SEL_DPLL << CLK_SPI_PLL_SEL_SHIFT | + (src_clk_div - 1) << CLK_SPI_DIV_CON_SHIFT); + + return rk3308_spi_get_clk(clk); +} + +static ulong rk3308_pwm_get_clk(struct clk *clk) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + u32 div, con; + + con = readl(&cru->clksel_con[29]); + div = con >> CLK_PWM_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK; + + return DIV_TO_RATE(priv->dpll_hz, div); +} + +static ulong rk3308_pwm_set_clk(struct clk *clk, uint hz) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->dpll_hz, hz); + assert(src_clk_div - 1 <= 127); + + rk_clrsetreg(&cru->clksel_con[29], + CLK_PWM_PLL_SEL_MASK | CLK_PWM_DIV_CON_MASK, + CLK_PWM_PLL_SEL_DPLL << CLK_PWM_PLL_SEL_SHIFT | + (src_clk_div - 1) << CLK_PWM_DIV_CON_SHIFT); + + return rk3308_pwm_get_clk(clk); +} + +static ulong rk3308_vop_get_clk(struct clk *clk) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + u32 div, pll_sel, vol_sel, con, parent; + + con = readl(&cru->clksel_con[8]); + vol_sel = (con & DCLK_VOP_SEL_MASK) >> DCLK_VOP_SEL_SHIFT; + pll_sel = (con & DCLK_VOP_PLL_SEL_MASK) >> DCLK_VOP_PLL_SEL_SHIFT; + div = con & DCLK_VOP_DIV_MASK; + + if (vol_sel == DCLK_VOP_SEL_24M) { + parent = OSC_HZ; + } else if (vol_sel == DCLK_VOP_SEL_DIVOUT) { + switch (pll_sel) { + case DCLK_VOP_PLL_SEL_DPLL: + parent = priv->dpll_hz; + break; + case DCLK_VOP_PLL_SEL_VPLL0: + parent = priv->vpll0_hz; + break; + case DCLK_VOP_PLL_SEL_VPLL1: + parent = priv->vpll0_hz; + break; + default: + printf("do not support this vop pll sel\n"); + return -EINVAL; + } + } else { + printf("do not support this vop sel\n"); + return -EINVAL; + } + + return DIV_TO_RATE(parent, div); +} + +static ulong rk3308_vop_set_clk(struct clk *clk, ulong hz) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + ulong pll_rate, now, best_rate = 0; + u32 i, div, best_div = 0, best_sel = 0; + + for (i = 0; i <= DCLK_VOP_PLL_SEL_VPLL1; i++) { + switch (i) { + case DCLK_VOP_PLL_SEL_DPLL: + pll_rate = priv->dpll_hz; + break; + case DCLK_VOP_PLL_SEL_VPLL0: + pll_rate = priv->vpll0_hz; + break; + case DCLK_VOP_PLL_SEL_VPLL1: + pll_rate = priv->vpll1_hz; + break; + default: + printf("do not support this vop pll sel\n"); + return -EINVAL; + } + + div = DIV_ROUND_UP(pll_rate, hz); + if (div > 255) + continue; + now = pll_rate / div; + if (abs(hz - now) < abs(hz - best_rate)) { + best_rate = now; + best_div = div; + best_sel = i; + } + debug("pll_rate=%lu, best_rate=%lu, best_div=%u, best_sel=%u\n", + pll_rate, best_rate, best_div, best_sel); + } + + if (best_rate != hz && hz == OSC_HZ) { + rk_clrsetreg(&cru->clksel_con[8], + DCLK_VOP_SEL_MASK, + DCLK_VOP_SEL_24M << DCLK_VOP_SEL_SHIFT); + } else if (best_rate) { + rk_clrsetreg(&cru->clksel_con[8], + DCLK_VOP_SEL_MASK | DCLK_VOP_PLL_SEL_MASK | + DCLK_VOP_DIV_MASK, + DCLK_VOP_SEL_DIVOUT << DCLK_VOP_SEL_SHIFT | + best_sel << DCLK_VOP_PLL_SEL_SHIFT | + (best_div - 1) << DCLK_VOP_DIV_SHIFT); + } else { + printf("do not support this vop freq\n"); + return -EINVAL; + } + + return rk3308_vop_get_clk(clk); +} + +static ulong rk3308_bus_get_clk(struct rk3308_clk_priv *priv, ulong clk_id) +{ + struct rk3308_cru *cru = priv->cru; + u32 div, con, parent = priv->dpll_hz; + + switch (clk_id) { + case ACLK_BUS: + con = readl(&cru->clksel_con[5]); + div = (con & BUS_ACLK_DIV_MASK) >> BUS_ACLK_DIV_SHIFT; + break; + case HCLK_BUS: + con = readl(&cru->clksel_con[6]); + div = (con & BUS_HCLK_DIV_MASK) >> BUS_HCLK_DIV_SHIFT; + break; + case PCLK_BUS: + case PCLK_WDT: + con = readl(&cru->clksel_con[6]); + div = (con & BUS_PCLK_DIV_MASK) >> BUS_PCLK_DIV_SHIFT; + break; + default: + return -ENOENT; + } + + return DIV_TO_RATE(parent, div); +} + +static ulong rk3308_bus_set_clk(struct rk3308_clk_priv *priv, ulong clk_id, + ulong hz) +{ + struct rk3308_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->dpll_hz, hz); + assert(src_clk_div - 1 <= 31); + + /* + * select dpll as pd_bus bus clock source and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + switch (clk_id) { + case ACLK_BUS: + rk_clrsetreg(&cru->clksel_con[5], + BUS_PLL_SEL_MASK | BUS_ACLK_DIV_MASK, + BUS_PLL_SEL_DPLL << BUS_PLL_SEL_SHIFT | + (src_clk_div - 1) << BUS_ACLK_DIV_SHIFT); + break; + case HCLK_BUS: + rk_clrsetreg(&cru->clksel_con[6], + BUS_HCLK_DIV_MASK, + (src_clk_div - 1) << BUS_HCLK_DIV_SHIFT); + break; + case PCLK_BUS: + rk_clrsetreg(&cru->clksel_con[6], + BUS_PCLK_DIV_MASK, + (src_clk_div - 1) << BUS_PCLK_DIV_SHIFT); + break; + default: + printf("do not support this bus freq\n"); + return -EINVAL; + } + + return rk3308_bus_get_clk(priv, clk_id); +} + +static ulong rk3308_peri_get_clk(struct rk3308_clk_priv *priv, ulong clk_id) +{ + struct rk3308_cru *cru = priv->cru; + u32 div, con, parent = priv->dpll_hz; + + switch (clk_id) { + case ACLK_PERI: + con = readl(&cru->clksel_con[36]); + div = (con & PERI_ACLK_DIV_MASK) >> PERI_ACLK_DIV_SHIFT; + break; + case HCLK_PERI: + con = readl(&cru->clksel_con[37]); + div = (con & PERI_HCLK_DIV_MASK) >> PERI_HCLK_DIV_SHIFT; + break; + case PCLK_PERI: + con = readl(&cru->clksel_con[37]); + div = (con & PERI_PCLK_DIV_MASK) >> PERI_PCLK_DIV_SHIFT; + break; + default: + return -ENOENT; + } + + return DIV_TO_RATE(parent, div); +} + +static ulong rk3308_peri_set_clk(struct rk3308_clk_priv *priv, ulong clk_id, + ulong hz) +{ + struct rk3308_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->dpll_hz, hz); + assert(src_clk_div - 1 <= 31); + + /* + * select dpll as pd_peri bus clock source and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + switch (clk_id) { + case ACLK_PERI: + rk_clrsetreg(&cru->clksel_con[36], + PERI_PLL_SEL_MASK | PERI_ACLK_DIV_MASK, + PERI_PLL_DPLL << PERI_PLL_SEL_SHIFT | + (src_clk_div - 1) << PERI_ACLK_DIV_SHIFT); + break; + case HCLK_PERI: + rk_clrsetreg(&cru->clksel_con[37], + PERI_HCLK_DIV_MASK, + (src_clk_div - 1) << PERI_HCLK_DIV_SHIFT); + break; + case PCLK_PERI: + rk_clrsetreg(&cru->clksel_con[37], + PERI_PCLK_DIV_MASK, + (src_clk_div - 1) << PERI_PCLK_DIV_SHIFT); + break; + default: + printf("do not support this peri freq\n"); + return -EINVAL; + } + + return rk3308_peri_get_clk(priv, clk_id); +} + +static ulong rk3308_audio_get_clk(struct rk3308_clk_priv *priv, ulong clk_id) +{ + struct rk3308_cru *cru = priv->cru; + u32 div, con, parent = priv->vpll0_hz; + + switch (clk_id) { + case HCLK_AUDIO: + con = readl(&cru->clksel_con[45]); + div = (con & AUDIO_HCLK_DIV_MASK) >> AUDIO_HCLK_DIV_SHIFT; + break; + case PCLK_AUDIO: + con = readl(&cru->clksel_con[45]); + div = (con & AUDIO_PCLK_DIV_MASK) >> AUDIO_PCLK_DIV_SHIFT; + break; + default: + return -ENOENT; + } + + return DIV_TO_RATE(parent, div); +} + +static ulong rk3308_audio_set_clk(struct rk3308_clk_priv *priv, ulong clk_id, + ulong hz) +{ + struct rk3308_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->vpll0_hz, hz); + assert(src_clk_div - 1 <= 31); + + /* + * select vpll0 as audio bus clock source and + * set up dependent divisors for HCLK and PCLK clocks. + */ + switch (clk_id) { + case HCLK_AUDIO: + rk_clrsetreg(&cru->clksel_con[45], + AUDIO_PLL_SEL_MASK | AUDIO_HCLK_DIV_MASK, + AUDIO_PLL_VPLL0 << AUDIO_PLL_SEL_SHIFT | + (src_clk_div - 1) << AUDIO_HCLK_DIV_SHIFT); + break; + case PCLK_AUDIO: + rk_clrsetreg(&cru->clksel_con[45], + AUDIO_PLL_SEL_MASK | AUDIO_PCLK_DIV_MASK, + AUDIO_PLL_VPLL0 << AUDIO_PLL_SEL_SHIFT | + (src_clk_div - 1) << AUDIO_PCLK_DIV_SHIFT); + break; + default: + printf("do not support this audio freq\n"); + return -EINVAL; + } + + return rk3308_peri_get_clk(priv, clk_id); +} + +static ulong rk3308_crypto_get_clk(struct rk3308_clk_priv *priv, ulong clk_id) +{ + struct rk3308_cru *cru = priv->cru; + u32 div, con, parent; + + switch (clk_id) { + case SCLK_CRYPTO: + con = readl(&cru->clksel_con[7]); + div = (con & CRYPTO_DIV_MASK) >> CRYPTO_DIV_SHIFT; + parent = priv->vpll0_hz; + break; + case SCLK_CRYPTO_APK: + con = readl(&cru->clksel_con[7]); + div = (con & CRYPTO_APK_DIV_MASK) >> CRYPTO_APK_DIV_SHIFT; + parent = priv->vpll0_hz; + break; + default: + return -ENOENT; + } + + return DIV_TO_RATE(parent, div); +} + +static ulong rk3308_crypto_set_clk(struct rk3308_clk_priv *priv, ulong clk_id, + ulong hz) +{ + struct rk3308_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->vpll0_hz, hz); + assert(src_clk_div - 1 <= 31); + + /* + * select gpll as crypto clock source and + * set up dependent divisors for crypto clocks. + */ + switch (clk_id) { + case SCLK_CRYPTO: + rk_clrsetreg(&cru->clksel_con[7], + CRYPTO_PLL_SEL_MASK | CRYPTO_DIV_MASK, + CRYPTO_PLL_SEL_VPLL0 << CRYPTO_PLL_SEL_SHIFT | + (src_clk_div - 1) << CRYPTO_DIV_SHIFT); + break; + case SCLK_CRYPTO_APK: + rk_clrsetreg(&cru->clksel_con[7], + CRYPTO_APK_PLL_SEL_MASK | CRYPTO_APK_DIV_MASK, + CRYPTO_PLL_SEL_VPLL0 << CRYPTO_APK_SEL_SHIFT | + (src_clk_div - 1) << CRYPTO_APK_DIV_SHIFT); + break; + default: + printf("do not support this peri freq\n"); + return -EINVAL; + } + + return rk3308_crypto_get_clk(priv, clk_id); +} + +static ulong rk3308_clk_get_rate(struct clk *clk) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + ulong rate = 0; + + debug("%s id:%ld\n", __func__, clk->id); + + switch (clk->id) { + case PLL_APLL: + case ARMCLK: + rate = rockchip_pll_get_rate(&rk3308_pll_clks[APLL], + priv->cru, APLL); + break; + case PLL_DPLL: + rate = rockchip_pll_get_rate(&rk3308_pll_clks[DPLL], + priv->cru, DPLL); + break; + case PLL_VPLL0: + rate = rockchip_pll_get_rate(&rk3308_pll_clks[VPLL0], + priv->cru, VPLL0); + break; + case PLL_VPLL1: + rate = rockchip_pll_get_rate(&rk3308_pll_clks[VPLL1], + priv->cru, VPLL1); + break; + case HCLK_SDMMC: + case HCLK_EMMC: + case SCLK_SDMMC: + case SCLK_EMMC: + case SCLK_EMMC_SAMPLE: + rate = rk3308_mmc_get_clk(clk); + break; + case SCLK_I2C0: + case SCLK_I2C1: + case SCLK_I2C2: + case SCLK_I2C3: + rate = rk3308_i2c_get_clk(clk); + break; + case SCLK_SARADC: + rate = rk3308_saradc_get_clk(clk); + break; + case SCLK_TSADC: + rate = rk3308_tsadc_get_clk(clk); + break; + case SCLK_SPI0: + case SCLK_SPI1: + rate = rk3308_spi_get_clk(clk); + break; + case SCLK_PWM0: + rate = rk3308_pwm_get_clk(clk); + break; + case DCLK_VOP: + rate = rk3308_vop_get_clk(clk); + break; + case ACLK_BUS: + case HCLK_BUS: + case PCLK_BUS: + case PCLK_WDT: + rate = rk3308_bus_get_clk(priv, clk->id); + break; + case ACLK_PERI: + case HCLK_PERI: + case PCLK_PERI: + rate = rk3308_peri_get_clk(priv, clk->id); + break; + case HCLK_AUDIO: + case PCLK_AUDIO: + rate = rk3308_audio_get_clk(priv, clk->id); + break; + case SCLK_CRYPTO: + case SCLK_CRYPTO_APK: + rate = rk3308_crypto_get_clk(priv, clk->id); + break; + default: + return -ENOENT; + } + + return rate; +} + +static ulong rk3308_clk_set_rate(struct clk *clk, ulong rate) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + ulong ret = 0; + + debug("%s %ld %ld\n", __func__, clk->id, rate); + + switch (clk->id) { + case PLL_DPLL: + ret = rockchip_pll_set_rate(&rk3308_pll_clks[DPLL], priv->cru, + DPLL, rate); + priv->dpll_hz = rockchip_pll_get_rate(&rk3308_pll_clks[DPLL], + priv->cru, DPLL); + break; + case ARMCLK: + if (priv->armclk_hz) + rk3308_armclk_set_clk(priv, rate); + priv->armclk_hz = rate; + break; + case HCLK_SDMMC: + case HCLK_EMMC: + case SCLK_SDMMC: + case SCLK_EMMC: + ret = rk3308_mmc_set_clk(clk, rate); + break; + case SCLK_I2C0: + case SCLK_I2C1: + case SCLK_I2C2: + case SCLK_I2C3: + ret = rk3308_i2c_set_clk(clk, rate); + break; + case SCLK_MAC: + ret = rk3308_mac_set_clk(clk, rate); + break; + case SCLK_MAC_RMII: + ret = rk3308_mac_set_speed_clk(clk, rate); + break; + case SCLK_SARADC: + ret = rk3308_saradc_set_clk(clk, rate); + break; + case SCLK_TSADC: + ret = rk3308_tsadc_set_clk(clk, rate); + break; + case SCLK_SPI0: + case SCLK_SPI1: + ret = rk3308_spi_set_clk(clk, rate); + break; + case SCLK_PWM0: + ret = rk3308_pwm_set_clk(clk, rate); + break; + case DCLK_VOP: + ret = rk3308_vop_set_clk(clk, rate); + break; + case ACLK_BUS: + case HCLK_BUS: + case PCLK_BUS: + rate = rk3308_bus_set_clk(priv, clk->id, rate); + break; + case ACLK_PERI: + case HCLK_PERI: + case PCLK_PERI: + rate = rk3308_peri_set_clk(priv, clk->id, rate); + break; + case HCLK_AUDIO: + case PCLK_AUDIO: + rate = rk3308_audio_set_clk(priv, clk->id, rate); + break; + case SCLK_CRYPTO: + case SCLK_CRYPTO_APK: + ret = rk3308_crypto_set_clk(priv, clk->id, rate); + break; + default: + return -ENOENT; + } + + return ret; +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) +static int __maybe_unused rk3308_mac_set_parent(struct clk *clk, struct clk *parent) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + + /* + * If the requested parent is in the same clock-controller and + * the id is SCLK_MAC_SRC, switch to the internal clock. + */ + if (parent->id == SCLK_MAC_SRC) { + debug("%s: switching RMII to SCLK_MAC\n", __func__); + rk_clrreg(&priv->cru->clksel_con[43], BIT(14)); + } else { + debug("%s: switching RMII to CLKIN\n", __func__); + rk_setreg(&priv->cru->clksel_con[43], BIT(14)); + } + + return 0; +} + +static int __maybe_unused rk3308_clk_set_parent(struct clk *clk, struct clk *parent) +{ + switch (clk->id) { + case SCLK_MAC: + return rk3308_mac_set_parent(clk, parent); + default: + break; + } + + debug("%s: unsupported clk %ld\n", __func__, clk->id); + return -ENOENT; +} +#endif + +static struct clk_ops rk3308_clk_ops = { + .get_rate = rk3308_clk_get_rate, + .set_rate = rk3308_clk_set_rate, +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + .set_parent = rk3308_clk_set_parent, +#endif +}; + +static void rk3308_clk_init(struct udevice *dev) +{ + struct rk3308_clk_priv *priv = dev_get_priv(dev); + int ret; + + if (rockchip_pll_get_rate(&rk3308_pll_clks[APLL], + priv->cru, APLL) != APLL_HZ) { + ret = rk3308_armclk_set_clk(priv, APLL_HZ); + if (ret < 0) + printf("%s failed to set armclk rate\n", __func__); + } + + rk3308_clk_get_pll_rate(priv); + + rk3308_bus_set_clk(priv, ACLK_BUS, BUS_ACLK_HZ); + rk3308_bus_set_clk(priv, HCLK_BUS, BUS_HCLK_HZ); + rk3308_bus_set_clk(priv, PCLK_BUS, BUS_PCLK_HZ); + + rk3308_peri_set_clk(priv, ACLK_PERI, PERI_ACLK_HZ); + rk3308_peri_set_clk(priv, HCLK_PERI, PERI_HCLK_HZ); + rk3308_peri_set_clk(priv, PCLK_PERI, PERI_PCLK_HZ); + + rk3308_audio_set_clk(priv, HCLK_AUDIO, AUDIO_HCLK_HZ); + rk3308_audio_set_clk(priv, PCLK_AUDIO, AUDIO_PCLK_HZ); +} + +static int rk3308_clk_probe(struct udevice *dev) +{ + int ret; + + rk3308_clk_init(dev); + + /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */ + ret = clk_set_defaults(dev, 1); + if (ret) + debug("%s clk_set_defaults failed %d\n", __func__, ret); + + return ret; +} + +static int rk3308_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct rk3308_clk_priv *priv = dev_get_priv(dev); + + priv->cru = dev_read_addr_ptr(dev); + + return 0; +} + +static int rk3308_clk_bind(struct udevice *dev) +{ + int ret; + struct udevice *sys_child; + struct sysreset_reg *priv; + + /* The reset driver does not have a device node, so bind it here */ + ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", + &sys_child); + if (ret) { + debug("Warning: No sysreset driver: ret=%d\n", ret); + } else { + priv = malloc(sizeof(struct sysreset_reg)); + priv->glb_srst_fst_value = offsetof(struct rk3308_cru, + glb_srst_fst); + priv->glb_srst_snd_value = offsetof(struct rk3308_cru, + glb_srst_snd); + sys_child->priv = priv; + } + +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) + ret = offsetof(struct rk3308_cru, softrst_con[0]); + ret = rockchip_reset_bind(dev, ret, 12); + if (ret) + debug("Warning: software reset driver bind faile\n"); +#endif + + return 0; +} + +static const struct udevice_id rk3308_clk_ids[] = { + { .compatible = "rockchip,rk3308-cru" }, + { } +}; + +U_BOOT_DRIVER(rockchip_rk3308_cru) = { + .name = "rockchip_rk3308_cru", + .id = UCLASS_CLK, + .of_match = rk3308_clk_ids, + .priv_auto_alloc_size = sizeof(struct rk3308_clk_priv), + .ofdata_to_platdata = rk3308_clk_ofdata_to_platdata, + .ops = &rk3308_clk_ops, + .bind = rk3308_clk_bind, + .probe = rk3308_clk_probe, +}; diff --git a/drivers/clk/rockchip/clk_rk3328.c b/drivers/clk/rockchip/clk_rk3328.c index 4331048a87..e700a1bc25 100644 --- a/drivers/clk/rockchip/clk_rk3328.c +++ b/drivers/clk/rockchip/clk_rk3328.c @@ -791,7 +791,7 @@ static int rk3328_clk_bind(struct udevice *dev) sys_child->priv = priv; } -#if CONFIG_IS_ENABLED(CONFIG_RESET_ROCKCHIP) +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) ret = offsetof(struct rk3328_cru, softrst_con[0]); ret = rockchip_reset_bind(dev, ret, 12); if (ret) diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index c1a867b2ed..b51d529ade 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -620,7 +620,7 @@ static int rk3368_clk_bind(struct udevice *dev) sys_child->priv = priv; } -#if CONFIG_IS_ENABLED(CONFIG_RESET_ROCKCHIP) +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) ret = offsetof(struct rk3368_cru, softrst_con[0]); ret = rockchip_reset_bind(dev, ret, 15); if (ret) diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c index a273bd1beb..9020a9f202 100644 --- a/drivers/clk/rockchip/clk_rk3399.c +++ b/drivers/clk/rockchip/clk_rk3399.c @@ -1195,7 +1195,7 @@ static int rk3399_clk_bind(struct udevice *dev) sys_child->priv = priv; } -#if CONFIG_IS_ENABLED(CONFIG_RESET_ROCKCHIP) +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) ret = offsetof(struct rk3399_cru, softrst_con[0]); ret = rockchip_reset_bind(dev, ret, 21); if (ret) diff --git a/drivers/clk/rockchip/clk_rv1108.c b/drivers/clk/rockchip/clk_rv1108.c index 3ebb007fab..97fdd099ef 100644 --- a/drivers/clk/rockchip/clk_rv1108.c +++ b/drivers/clk/rockchip/clk_rv1108.c @@ -679,9 +679,8 @@ static int rv1108_clk_probe(struct udevice *dev) static int rv1108_clk_bind(struct udevice *dev) { int ret; - struct udevice *sys_child, *sf_child; + struct udevice *sys_child; struct sysreset_reg *priv; - struct softreset_reg *sf_priv; /* The reset driver does not have a device node, so bind it here */ ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", @@ -697,23 +696,12 @@ static int rv1108_clk_bind(struct udevice *dev) sys_child->priv = priv; } -#if CONFIG_IS_ENABLED(CONFIG_RESET_ROCKCHIP) - ret = offsetof(struct rk3368_cru, softrst_con[0]); +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) + ret = offsetof(struct rv1108_cru, softrst_con[0]); ret = rockchip_reset_bind(dev, ret, 13); if (ret) debug("Warning: software reset driver bind faile\n"); #endif - ret = device_bind_driver_to_node(dev, "rockchip_reset", "reset", - dev_ofnode(dev), &sf_child); - if (ret) { - debug("Warning: No rockchip reset driver: ret=%d\n", ret); - } else { - sf_priv = malloc(sizeof(struct softreset_reg)); - sf_priv->sf_reset_offset = offsetof(struct rv1108_cru, - softrst_con[0]); - sf_priv->sf_reset_num = 13; - sf_child->priv = sf_priv; - } return 0; } diff --git a/drivers/input/Makefile b/drivers/input/Makefile index fd56d7b349..e440c921e4 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -3,15 +3,15 @@ # (C) Copyright 2000-2007 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. -obj-y += input.o +obj-$(CONFIG_KEYBOARD) += input.o obj-$(CONFIG_$(SPL_TPL_)CROS_EC_KEYB) += cros_ec_keyb.o obj-$(CONFIG_$(SPL_TPL_)OF_CONTROL) += key_matrix.o -obj-$(CONFIG_$(SPL_TPL_)DM_KEYBOARD) += keyboard-uclass.o +obj-$(CONFIG_$(SPL_TPL_)DM_KEYBOARD) += input.o keyboard-uclass.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_I8042_KEYB) += i8042.o -obj-$(CONFIG_TEGRA_KEYBOARD) += tegra-kbc.o +obj-$(CONFIG_TEGRA_KEYBOARD) += input.o tegra-kbc.o obj-$(CONFIG_TWL4030_INPUT) += twl4030.o obj-$(CONFIG_TWL6030_INPUT) += twl6030.o endif diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7a8ba587da..82bb093c56 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -59,6 +59,15 @@ config ROCKCHIP_EFUSE extended (by porting the read function from the Linux kernel sources) to support other recent Rockchip devices. +config ROCKCHIP_OTP + bool "Rockchip OTP Support" + depends on MISC + help + Enable (read-only) access for the one-time-programmable memory block + found in Rockchip SoCs: accesses can either be made using byte + addressing and a length or through child-nodes that are generated + based on the e-fuse map retrieved from the DTS. + config VEXPRESS_CONFIG bool "Enable support for Arm Versatile Express config bus" depends on MISC diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 870655e802..55976d6be5 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_PCA9551_LED) += pca9551_led.o obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o obj-$(CONFIG_QFW) += qfw.o obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o +obj-$(CONFIG_ROCKCHIP_OTP) += rockchip-otp.o obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o obj-$(CONFIG_SMSC_SIO1007) += smsc_sio1007.o diff --git a/drivers/misc/rockchip-otp.c b/drivers/misc/rockchip-otp.c new file mode 100644 index 0000000000..bdd443b3db --- /dev/null +++ b/drivers/misc/rockchip-otp.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd + */ + +#include <common.h> +#include <asm/io.h> +#include <command.h> +#include <dm.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <misc.h> + +/* OTP Register Offsets */ +#define OTPC_SBPI_CTRL 0x0020 +#define OTPC_SBPI_CMD_VALID_PRE 0x0024 +#define OTPC_SBPI_CS_VALID_PRE 0x0028 +#define OTPC_SBPI_STATUS 0x002C +#define OTPC_USER_CTRL 0x0100 +#define OTPC_USER_ADDR 0x0104 +#define OTPC_USER_ENABLE 0x0108 +#define OTPC_USER_QP 0x0120 +#define OTPC_USER_Q 0x0124 +#define OTPC_INT_STATUS 0x0304 +#define OTPC_SBPI_CMD0_OFFSET 0x1000 +#define OTPC_SBPI_CMD1_OFFSET 0x1004 + +/* OTP Register bits and masks */ +#define OTPC_USER_ADDR_MASK GENMASK(31, 16) +#define OTPC_USE_USER BIT(0) +#define OTPC_USE_USER_MASK GENMASK(16, 16) +#define OTPC_USER_FSM_ENABLE BIT(0) +#define OTPC_USER_FSM_ENABLE_MASK GENMASK(16, 16) +#define OTPC_SBPI_DONE BIT(1) +#define OTPC_USER_DONE BIT(2) + +#define SBPI_DAP_ADDR 0x02 +#define SBPI_DAP_ADDR_SHIFT 8 +#define SBPI_DAP_ADDR_MASK GENMASK(31, 24) +#define SBPI_CMD_VALID_MASK GENMASK(31, 16) +#define SBPI_DAP_CMD_WRF 0xC0 +#define SBPI_DAP_REG_ECC 0x3A +#define SBPI_ECC_ENABLE 0x00 +#define SBPI_ECC_DISABLE 0x09 +#define SBPI_ENABLE BIT(0) +#define SBPI_ENABLE_MASK GENMASK(16, 16) + +#define OTPC_TIMEOUT 10000 + +struct rockchip_otp_platdata { + void __iomem *base; + unsigned long secure_conf_base; + unsigned long otp_mask_base; +}; + +static int rockchip_otp_wait_status(struct rockchip_otp_platdata *otp, + u32 flag) +{ + int delay = OTPC_TIMEOUT; + + while (!(readl(otp->base + OTPC_INT_STATUS) & flag)) { + udelay(1); + delay--; + if (delay <= 0) { + printf("%s: wait init status timeout\n", __func__); + return -ETIMEDOUT; + } + } + + /* clean int status */ + writel(flag, otp->base + OTPC_INT_STATUS); + + return 0; +} + +static int rockchip_otp_ecc_enable(struct rockchip_otp_platdata *otp, + bool enable) +{ + int ret = 0; + + writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT), + otp->base + OTPC_SBPI_CTRL); + + writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE); + writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC, + otp->base + OTPC_SBPI_CMD0_OFFSET); + + if (enable) + writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); + else + writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); + + writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL); + + ret = rockchip_otp_wait_status(otp, OTPC_SBPI_DONE); + if (ret < 0) + printf("%s timeout during ecc_enable\n", __func__); + + return ret; +} + +static int rockchip_px30_otp_read(struct udevice *dev, int offset, + void *buf, int size) +{ + struct rockchip_otp_platdata *otp = dev_get_platdata(dev); + u8 *buffer = buf; + int ret = 0; + + ret = rockchip_otp_ecc_enable(otp, false); + if (ret < 0) { + printf("%s rockchip_otp_ecc_enable err\n", __func__); + return ret; + } + + writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); + udelay(5); + while (size--) { + writel(offset++ | OTPC_USER_ADDR_MASK, + otp->base + OTPC_USER_ADDR); + writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, + otp->base + OTPC_USER_ENABLE); + + ret = rockchip_otp_wait_status(otp, OTPC_USER_DONE); + if (ret < 0) { + printf("%s timeout during read setup\n", __func__); + goto read_end; + } + + *buffer++ = readb(otp->base + OTPC_USER_Q); + } + +read_end: + writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); + + return ret; +} + +static int rockchip_otp_read(struct udevice *dev, int offset, + void *buf, int size) +{ + return rockchip_px30_otp_read(dev, offset, buf, size); +} + +static const struct misc_ops rockchip_otp_ops = { + .read = rockchip_otp_read, +}; + +static int rockchip_otp_ofdata_to_platdata(struct udevice *dev) +{ + struct rockchip_otp_platdata *otp = dev_get_platdata(dev); + + otp->base = dev_read_addr_ptr(dev); + + return 0; +} + +static const struct udevice_id rockchip_otp_ids[] = { + { + .compatible = "rockchip,px30-otp", + .data = (ulong)&rockchip_px30_otp_read, + }, + { + .compatible = "rockchip,rk3308-otp", + .data = (ulong)&rockchip_px30_otp_read, + }, + {} +}; + +U_BOOT_DRIVER(rockchip_otp) = { + .name = "rockchip_otp", + .id = UCLASS_MISC, + .of_match = rockchip_otp_ids, + .ops = &rockchip_otp_ops, + .ofdata_to_platdata = rockchip_otp_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct rockchip_otp_platdata), +}; diff --git a/drivers/net/gmac_rockchip.c b/drivers/net/gmac_rockchip.c index 26a6121175..d2c52b4c46 100644 --- a/drivers/net/gmac_rockchip.c +++ b/drivers/net/gmac_rockchip.c @@ -14,6 +14,7 @@ #include <asm/arch-rockchip/periph.h> #include <asm/arch-rockchip/clock.h> #include <asm/arch-rockchip/hardware.h> +#include <asm/arch-rockchip/grf_px30.h> #include <asm/arch-rockchip/grf_rk322x.h> #include <asm/arch-rockchip/grf_rk3288.h> #include <asm/arch-rockchip/grf_rk3328.h> @@ -72,6 +73,47 @@ static int gmac_rockchip_ofdata_to_platdata(struct udevice *dev) return designware_eth_ofdata_to_platdata(dev); } +static int px30_gmac_fix_mac_speed(struct dw_eth_dev *priv) +{ + struct px30_grf *grf; + struct clk clk_speed; + int speed, ret; + enum { + PX30_GMAC_SPEED_SHIFT = 0x2, + PX30_GMAC_SPEED_MASK = BIT(2), + PX30_GMAC_SPEED_10M = 0, + PX30_GMAC_SPEED_100M = BIT(2), + }; + + ret = clk_get_by_name(priv->phydev->dev, "clk_mac_speed", + &clk_speed); + if (ret) + return ret; + + switch (priv->phydev->speed) { + case 10: + speed = PX30_GMAC_SPEED_10M; + ret = clk_set_rate(&clk_speed, 2500000); + if (ret) + return ret; + break; + case 100: + speed = PX30_GMAC_SPEED_100M; + ret = clk_set_rate(&clk_speed, 25000000); + if (ret) + return ret; + break; + default: + debug("Unknown phy speed: %d\n", priv->phydev->speed); + return -EINVAL; + } + + grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + rk_clrsetreg(&grf->mac_con1, PX30_GMAC_SPEED_MASK, speed); + + return 0; +} + static int rk3228_gmac_fix_mac_speed(struct dw_eth_dev *priv) { struct rk322x_grf *grf; @@ -257,6 +299,22 @@ static int rv1108_set_rmii_speed(struct dw_eth_dev *priv) return 0; } +static void px30_gmac_set_to_rmii(struct gmac_rockchip_platdata *pdata) +{ + struct px30_grf *grf; + enum { + PX30_GMAC_PHY_INTF_SEL_SHIFT = 4, + PX30_GMAC_PHY_INTF_SEL_MASK = GENMASK(4, 6), + PX30_GMAC_PHY_INTF_SEL_RMII = BIT(6), + }; + + grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + + rk_clrsetreg(&grf->mac_con1, + PX30_GMAC_PHY_INTF_SEL_MASK, + PX30_GMAC_PHY_INTF_SEL_RMII); +} + static void rk3228_gmac_set_to_rgmii(struct gmac_rockchip_platdata *pdata) { struct rk322x_grf *grf; @@ -445,6 +503,10 @@ static int gmac_rockchip_probe(struct udevice *dev) ulong rate; int ret; + ret = clk_set_defaults(dev, 0); + if (ret) + debug("%s clk_set_defaults failed %d\n", __func__, ret); + ret = clk_get_by_index(dev, 0, &clk); if (ret) return ret; @@ -569,6 +631,11 @@ const struct eth_ops gmac_rockchip_eth_ops = { .write_hwaddr = designware_eth_write_hwaddr, }; +const struct rk_gmac_ops px30_gmac_ops = { + .fix_mac_speed = px30_gmac_fix_mac_speed, + .set_to_rmii = px30_gmac_set_to_rmii, +}; + const struct rk_gmac_ops rk3228_gmac_ops = { .fix_mac_speed = rk3228_gmac_fix_mac_speed, .set_to_rgmii = rk3228_gmac_set_to_rgmii, @@ -600,6 +667,8 @@ const struct rk_gmac_ops rv1108_gmac_ops = { }; static const struct udevice_id rockchip_gmac_ids[] = { + { .compatible = "rockchip,px30-gmac", + .data = (ulong)&px30_gmac_ops }, { .compatible = "rockchip,rk3228-gmac", .data = (ulong)&rk3228_gmac_ops }, { .compatible = "rockchip,rk3288-gmac", diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c index 761ee29f41..3425ed11b1 100644 --- a/drivers/pinctrl/pinctrl-uclass.c +++ b/drivers/pinctrl/pinctrl-uclass.c @@ -15,18 +15,6 @@ DECLARE_GLOBAL_DATA_PTR; -int pinctrl_decode_pin_config(const void *blob, int node) -{ - int flags = 0; - - if (fdtdec_get_bool(blob, node, "bias-pull-up")) - flags |= 1 << PIN_CONFIG_BIAS_PULL_UP; - else if (fdtdec_get_bool(blob, node, "bias-pull-down")) - flags |= 1 << PIN_CONFIG_BIAS_PULL_DOWN; - - return flags; -} - #if CONFIG_IS_ENABLED(PINCTRL_FULL) /** * pinctrl_config_one() - apply pinctrl settings for a single node diff --git a/drivers/pinctrl/rockchip/Makefile b/drivers/pinctrl/rockchip/Makefile index a616d8587f..83913f668f 100644 --- a/drivers/pinctrl/rockchip/Makefile +++ b/drivers/pinctrl/rockchip/Makefile @@ -3,6 +3,7 @@ # Copyright (c) 2017 Rockchip Electronics Co., Ltd obj-y += pinctrl-rockchip-core.o +obj-$(CONFIG_ROCKCHIP_PX30) += pinctrl-px30.o obj-$(CONFIG_ROCKCHIP_RK3036) += pinctrl-rk3036.o obj-$(CONFIG_ROCKCHIP_RK3128) += pinctrl-rk3128.o obj-$(CONFIG_ROCKCHIP_RK3188) += pinctrl-rk3188.o diff --git a/drivers/pinctrl/rockchip/pinctrl-px30.c b/drivers/pinctrl/rockchip/pinctrl-px30.c new file mode 100644 index 0000000000..bb56ae9fb3 --- /dev/null +++ b/drivers/pinctrl/rockchip/pinctrl-px30.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 Rockchip Electronics Co., Ltd + */ + +#include <common.h> +#include <dm.h> +#include <dm/pinctrl.h> +#include <regmap.h> +#include <syscon.h> + +#include "pinctrl-rockchip.h" + +static struct rockchip_mux_route_data px30_mux_route_data[] = { + { + /* cif-d2m0 */ + .bank_num = 2, + .pin = 0, + .func = 1, + .route_offset = 0x184, + .route_val = BIT(16 + 7), + }, { + /* cif-d2m1 */ + .bank_num = 3, + .pin = 3, + .func = 3, + .route_offset = 0x184, + .route_val = BIT(16 + 7) | BIT(7), + }, { + /* pdm-m0 */ + .bank_num = 3, + .pin = 22, + .func = 2, + .route_offset = 0x184, + .route_val = BIT(16 + 8), + }, { + /* pdm-m1 */ + .bank_num = 2, + .pin = 22, + .func = 1, + .route_offset = 0x184, + .route_val = BIT(16 + 8) | BIT(8), + }, { + /* uart2-rxm0 */ + .bank_num = 1, + .pin = 27, + .func = 2, + .route_offset = 0x184, + .route_val = BIT(16 + 10), + }, { + /* uart2-rxm1 */ + .bank_num = 2, + .pin = 14, + .func = 2, + .route_offset = 0x184, + .route_val = BIT(16 + 10) | BIT(10), + }, { + /* uart3-rxm0 */ + .bank_num = 0, + .pin = 17, + .func = 2, + .route_offset = 0x184, + .route_val = BIT(16 + 9), + }, { + /* uart3-rxm1 */ + .bank_num = 1, + .pin = 15, + .func = 2, + .route_offset = 0x184, + .route_val = BIT(16 + 9) | BIT(9), + }, +}; + +static int px30_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 PX30_PULL_PMU_OFFSET 0x10 +#define PX30_PULL_GRF_OFFSET 0x60 +#define PX30_PULL_BITS_PER_PIN 2 +#define PX30_PULL_PINS_PER_REG 8 +#define PX30_PULL_BANK_STRIDE 16 + +static void px30_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + + /* The first 32 pins of the first bank are located in PMU */ + if (bank->bank_num == 0) { + *regmap = priv->regmap_pmu; + *reg = PX30_PULL_PMU_OFFSET; + } else { + *regmap = priv->regmap_base; + *reg = PX30_PULL_GRF_OFFSET; + + /* correct the offset, as we're starting with the 2nd bank */ + *reg -= 0x10; + *reg += bank->bank_num * PX30_PULL_BANK_STRIDE; + } + + *reg += ((pin_num / PX30_PULL_PINS_PER_REG) * 4); + *bit = (pin_num % PX30_PULL_PINS_PER_REG); + *bit *= PX30_PULL_BITS_PER_PIN; +} + +static int px30_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; + + px30_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 PX30_DRV_PMU_OFFSET 0x20 +#define PX30_DRV_GRF_OFFSET 0xf0 +#define PX30_DRV_BITS_PER_PIN 2 +#define PX30_DRV_PINS_PER_REG 8 +#define PX30_DRV_BANK_STRIDE 16 + +static void px30_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + + /* The first 32 pins of the first bank are located in PMU */ + if (bank->bank_num == 0) { + *regmap = priv->regmap_pmu; + *reg = PX30_DRV_PMU_OFFSET; + } else { + *regmap = priv->regmap_base; + *reg = PX30_DRV_GRF_OFFSET; + + /* correct the offset, as we're starting with the 2nd bank */ + *reg -= 0x10; + *reg += bank->bank_num * PX30_DRV_BANK_STRIDE; + } + + *reg += ((pin_num / PX30_DRV_PINS_PER_REG) * 4); + *bit = (pin_num % PX30_DRV_PINS_PER_REG); + *bit *= PX30_DRV_BITS_PER_PIN; +} + +static int px30_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; + + px30_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; +} + +#define PX30_SCHMITT_PMU_OFFSET 0x38 +#define PX30_SCHMITT_GRF_OFFSET 0xc0 +#define PX30_SCHMITT_PINS_PER_PMU_REG 16 +#define PX30_SCHMITT_BANK_STRIDE 16 +#define PX30_SCHMITT_PINS_PER_GRF_REG 8 + +static int px30_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, + struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + int pins_per_reg; + + if (bank->bank_num == 0) { + *regmap = priv->regmap_pmu; + *reg = PX30_SCHMITT_PMU_OFFSET; + pins_per_reg = PX30_SCHMITT_PINS_PER_PMU_REG; + } else { + *regmap = priv->regmap_base; + *reg = PX30_SCHMITT_GRF_OFFSET; + pins_per_reg = PX30_SCHMITT_PINS_PER_GRF_REG; + *reg += (bank->bank_num - 1) * PX30_SCHMITT_BANK_STRIDE; + } + *reg += ((pin_num / pins_per_reg) * 4); + *bit = pin_num % pins_per_reg; + + return 0; +} + +static int px30_set_schmitt(struct rockchip_pin_bank *bank, + int pin_num, int enable) +{ + struct regmap *regmap; + int reg; + u8 bit; + u32 data; + + px30_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 px30_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU + ), + PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT + ), + PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT + ), + PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT + ), +}; + +static struct rockchip_pin_ctrl px30_pin_ctrl = { + .pin_banks = px30_pin_banks, + .nr_banks = ARRAY_SIZE(px30_pin_banks), + .grf_mux_offset = 0x0, + .pmu_mux_offset = 0x0, + .grf_drv_offset = 0xf0, + .pmu_drv_offset = 0x20, + .iomux_routes = px30_mux_route_data, + .niomux_routes = ARRAY_SIZE(px30_mux_route_data), + .set_mux = px30_set_mux, + .set_pull = px30_set_pull, + .set_drive = px30_set_drive, + .set_schmitt = px30_set_schmitt, +}; + +static const struct udevice_id px30_pinctrl_ids[] = { + { + .compatible = "rockchip,px30-pinctrl", + .data = (ulong)&px30_pin_ctrl + }, + { } +}; + +U_BOOT_DRIVER(pinctrl_px30) = { + .name = "rockchip_px30_pinctrl", + .id = UCLASS_PINCTRL, + .of_match = px30_pinctrl_ids, + .priv_auto_alloc_size = sizeof(struct rockchip_pinctrl_priv), + .ops = &rockchip_pinctrl_ops, +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + .bind = dm_scan_fdt_dev, +#endif + .probe = rockchip_pinctrl_probe, +}; diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig index bb431ccfbf..b454ceb599 100644 --- a/drivers/ram/Kconfig +++ b/drivers/ram/Kconfig @@ -19,7 +19,7 @@ config SPL_RAM config TPL_RAM bool "Enable RAM support in TPL" - depends on RAM && TPL_DM + depends on RAM help The RAM subsystem adds a small amount of overhead to the image. If this is acceptable and you have a need to use RAM drivers in diff --git a/drivers/ram/rockchip/Kconfig b/drivers/ram/rockchip/Kconfig index 4f274e01b3..b75d581f57 100644 --- a/drivers/ram/rockchip/Kconfig +++ b/drivers/ram/rockchip/Kconfig @@ -5,10 +5,15 @@ config RAM_ROCKCHIP help This enables support for ram drivers Rockchip SoCs. -if RAM_ROCKCHIP +config ROCKCHIP_SDRAM_COMMON + bool "Enable rockchip sdram common driver" + depends on TPL_RAM || SPL_RAM + help + This enable sdram common driver config RAM_ROCKCHIP_DEBUG bool "Rockchip ram drivers debugging" + default y help This enables debugging ram driver API's for the platforms based on Rockchip SoCs. @@ -16,18 +21,10 @@ config RAM_ROCKCHIP_DEBUG This is an option for developers to understand the ram drivers initialization, configurations and etc. -config RAM_RK3399 - bool "Ram driver for Rockchip RK3399" - default ROCKCHIP_RK3399 - help - This enables ram drivers support for the platforms based on - Rockchip RK3399 SoC. - config RAM_RK3399_LPDDR4 bool "LPDDR4 support for Rockchip RK3399" - depends on RAM_RK3399 + depends on RAM_ROCKCHIP && ROCKCHIP_RK3399 help This enables LPDDR4 sdram code support for the platforms based on Rockchip RK3399 SoC. -endif # RAM_ROCKCHIP diff --git a/drivers/ram/rockchip/Makefile b/drivers/ram/rockchip/Makefile index feb1f82d00..c3ec89ada4 100644 --- a/drivers/ram/rockchip/Makefile +++ b/drivers/ram/rockchip/Makefile @@ -3,11 +3,13 @@ # Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH # -obj-$(CONFIG_RAM_ROCKCHIP_DEBUG) += sdram_debug.o +obj-$(CONFIG_ROCKCHIP_PX30) += sdram_px30.o sdram_pctl_px30.o sdram_phy_px30.o obj-$(CONFIG_ROCKCHIP_RK3368) = dmc-rk3368.o obj-$(CONFIG_ROCKCHIP_RK3128) = sdram_rk3128.o obj-$(CONFIG_ROCKCHIP_RK3188) = sdram_rk3188.o obj-$(CONFIG_ROCKCHIP_RK322X) = sdram_rk322x.o obj-$(CONFIG_ROCKCHIP_RK3288) = sdram_rk3288.o -obj-$(CONFIG_ROCKCHIP_RK3328) = sdram_rk3328.o -obj-$(CONFIG_RAM_RK3399) += sdram_rk3399.o +obj-$(CONFIG_ROCKCHIP_RK3308) = sdram_rk3308.o +obj-$(CONFIG_ROCKCHIP_RK3328) = sdram_rk3328.o sdram_pctl_px30.o sdram_phy_px30.o +obj-$(CONFIG_ROCKCHIP_RK3399) += sdram_rk3399.o +obj-$(CONFIG_ROCKCHIP_SDRAM_COMMON) += sdram_common.o diff --git a/drivers/ram/rockchip/dmc-rk3368.c b/drivers/ram/rockchip/dmc-rk3368.c index e52fc3baad..9df8f8f4af 100644 --- a/drivers/ram/rockchip/dmc-rk3368.c +++ b/drivers/ram/rockchip/dmc-rk3368.c @@ -17,7 +17,7 @@ #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> +#include <asm/arch-rockchip/sdram_rk3288.h> struct dram_info { struct ram_info info; diff --git a/drivers/ram/rockchip/sdram-px30-ddr3-detect-333.inc b/drivers/ram/rockchip/sdram-px30-ddr3-detect-333.inc new file mode 100644 index 0000000000..76cd8dc1a5 --- /dev/null +++ b/drivers/ram/rockchip/sdram-px30-ddr3-detect-333.inc @@ -0,0 +1,72 @@ +{ + { + { + .rank = 0x1, + .col = 0xC, + .bk = 0x3, + .bw = 0x1, + .dbw = 0x0, + .row_3_4 = 0x0, + .cs0_row = 0x10, + .cs1_row = 0x10, + .cs0_high16bit_row = 0x10, + .cs1_high16bit_row = 0x10, + .ddrconfig = 0, + }, + { + {0x290b0609}, + {0x08020401}, + {0x00000002}, + {0x00001111}, + {0x0000000c}, + {0x00000222}, + 0x000000ff + } + }, + { + .ddr_freq = 333, + .dramtype = DDR3, + .num_channels = 1, + .stride = 0, + .odt = 0, + }, + { + { + {0x00000000, 0x43041001}, /* MSTR */ + {0x00000064, 0x0028003b}, /* RFSHTMG */ + {0x000000d0, 0x00020053}, /* INIT0 */ + {0x000000d4, 0x00020000}, /* INIT1 */ + {0x000000d8, 0x00000100}, /* INIT2 */ + {0x000000dc, 0x03200000}, /* INIT3 */ + {0x000000e0, 0x00000000}, /* INIT4 */ + {0x000000e4, 0x00090000}, /* INIT5 */ + {0x000000f4, 0x000f012f}, /* RANKCTL */ + {0x00000100, 0x07090b06}, /* DRAMTMG0 */ + {0x00000104, 0x00050209}, /* DRAMTMG1 */ + {0x00000108, 0x03030407}, /* DRAMTMG2 */ + {0x0000010c, 0x00202006}, /* DRAMTMG3 */ + {0x00000110, 0x03020204}, /* DRAMTMG4 */ + {0x00000114, 0x03030202}, /* DRAMTMG5 */ + {0x00000120, 0x00000903}, /* DRAMTMG8 */ + {0x00000180, 0x00800020}, /* ZQCTL0 */ + {0x00000184, 0x00000000}, /* ZQCTL1 */ + {0x00000190, 0x07010001}, /* DFITMG0 */ + {0x00000198, 0x07000101}, /* DFILPCFG0 */ + {0x000001a0, 0xc0400003}, /* DFIUPD0 */ + {0x00000240, 0x06000604}, /* ODTCFG */ + {0x00000244, 0x00000201}, /* ODTMAP */ + {0x00000250, 0x00001f00}, /* SCHED */ + {0x00000490, 0x00000001}, /* PCTRL_0 */ + {0xffffffff, 0xffffffff} + } + }, + { + { + {0x00000004, 0x0000000a}, /* PHYREG01 */ + {0x00000028, 0x00000006}, /* PHYREG0A */ + {0x0000002c, 0x00000000}, /* PHYREG0B */ + {0x00000030, 0x00000005}, /* PHYREG0C */ + {0xffffffff, 0xffffffff} + } + } +}, diff --git a/drivers/ram/rockchip/sdram-px30-ddr4-detect-333.inc b/drivers/ram/rockchip/sdram-px30-ddr4-detect-333.inc new file mode 100644 index 0000000000..f804d28393 --- /dev/null +++ b/drivers/ram/rockchip/sdram-px30-ddr4-detect-333.inc @@ -0,0 +1,75 @@ +{ + { + { + .rank = 0x1, + .col = 0xA, + .bk = 0x2, + .bw = 0x1, + .dbw = 0x0, + .row_3_4 = 0x0, + .cs0_row = 0x11, + .cs1_row = 0x0, + .cs0_high16bit_row = 0x11, + .cs1_high16bit_row = 0x0, + .ddrconfig = 0, + }, + { + {0x4d110a08}, + {0x06020501}, + {0x00000002}, + {0x00001111}, + {0x0000000c}, + {0x0000022a}, + 0x000000ff + } + }, + { + .ddr_freq = 333, + .dramtype = DDR4, + .num_channels = 1, + .stride = 0, + .odt = 0, + }, + { + { + {0x00000000, 0x43049010}, /* MSTR */ + {0x00000064, 0x0028003b}, /* RFSHTMG */ + {0x000000d0, 0x00020053}, /* INIT0 */ + {0x000000d4, 0x00220000}, /* INIT1 */ + {0x000000d8, 0x00000100}, /* INIT2 */ + {0x000000dc, 0x00040000}, /* INIT3 */ + {0x000000e0, 0x00000000}, /* INIT4 */ + {0x000000e4, 0x00110000}, /* INIT5 */ + {0x000000e8, 0x00000420}, /* INIT6 */ + {0x000000ec, 0x00000400}, /* INIT7 */ + {0x000000f4, 0x000f012f}, /* RANKCTL */ + {0x00000100, 0x09060b06}, /* DRAMTMG0 */ + {0x00000104, 0x00020209}, /* DRAMTMG1 */ + {0x00000108, 0x0505040a}, /* DRAMTMG2 */ + {0x0000010c, 0x0040400c}, /* DRAMTMG3 */ + {0x00000110, 0x05030206}, /* DRAMTMG4 */ + {0x00000114, 0x03030202}, /* DRAMTMG5 */ + {0x00000120, 0x03030b03}, /* DRAMTMG8 */ + {0x00000124, 0x00020208}, /* DRAMTMG9 */ + {0x00000180, 0x01000040}, /* ZQCTL0 */ + {0x00000184, 0x00000000}, /* ZQCTL1 */ + {0x00000190, 0x07030003}, /* DFITMG0 */ + {0x00000198, 0x07000101}, /* DFILPCFG0 */ + {0x000001a0, 0xc0400003}, /* DFIUPD0 */ + {0x00000240, 0x06000604}, /* ODTCFG */ + {0x00000244, 0x00000201}, /* ODTMAP */ + {0x00000250, 0x00001f00}, /* SCHED */ + {0x00000490, 0x00000001}, /* PCTRL_0 */ + {0xffffffff, 0xffffffff} + } + }, + { + { + {0x00000004, 0x0000000c}, /* PHYREG01 */ + {0x00000028, 0x0000000a}, /* PHYREG0A */ + {0x0000002c, 0x00000000}, /* PHYREG0B */ + {0x00000030, 0x00000009}, /* PHYREG0C */ + {0xffffffff, 0xffffffff} + } + } +},
\ No newline at end of file diff --git a/drivers/ram/rockchip/sdram-px30-ddr_skew.inc b/drivers/ram/rockchip/sdram-px30-ddr_skew.inc new file mode 100644 index 0000000000..f24343dda1 --- /dev/null +++ b/drivers/ram/rockchip/sdram-px30-ddr_skew.inc @@ -0,0 +1,121 @@ + { + 0x77, + 0x88, + 0x79, + 0x79, + 0x87, + 0x97, + 0x87, + 0x78, + 0x77, + 0x78, + 0x87, + 0x88, + 0x87, + 0x87, + 0x77 + }, + { + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x69, + 0x9, + }, + { + 0x77, + 0x78, + 0x77, + 0x78, + 0x77, + 0x78, + 0x77, + 0x78, + 0x77, + 0x79, + 0x9, + }, + { + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x69, + 0x9, + }, + { + 0x77, + 0x78, + 0x77, + 0x77, + 0x77, + 0x77, + 0x77, + 0x77, + 0x77, + 0x79, + 0x9, + }, + { + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x69, + 0x9, + }, + { + 0x77, + 0x78, + 0x77, + 0x78, + 0x77, + 0x78, + 0x77, + 0x78, + 0x77, + 0x79, + 0x9, + }, + { + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x78, + 0x69, + 0x9, + }, + { + 0x77, + 0x78, + 0x77, + 0x77, + 0x77, + 0x77, + 0x77, + 0x77, + 0x77, + 0x79, + 0x9, + } diff --git a/drivers/ram/rockchip/sdram-px30-lpddr2-detect-333.inc b/drivers/ram/rockchip/sdram-px30-lpddr2-detect-333.inc new file mode 100644 index 0000000000..948ade483b --- /dev/null +++ b/drivers/ram/rockchip/sdram-px30-lpddr2-detect-333.inc @@ -0,0 +1,73 @@ +{ + { + { + .rank = 0x1, + .col = 0xC, + .bk = 0x3, + .bw = 0x1, + .dbw = 0x0, + .row_3_4 = 0x0, + .cs0_row = 0xF, + .cs1_row = 0xF, + .cs0_high16bit_row = 0xF, + .cs1_high16bit_row = 0xF, + .ddrconfig = 0, + }, + { + {0x2b0c070a}, + {0x08020303}, + {0x00000002}, + {0x00001111}, + {0x0000000c}, + {0x00000219}, + 0x000000ff + } + }, + { + .ddr_freq = 333, + .dramtype = LPDDR2, + .num_channels = 1, + .stride = 0, + .odt = 0, + }, + { + { + {0x00000000, 0x41041004}, /* MSTR */ + {0x00000064, 0x00140023}, /* RFSHTMG */ + {0x000000d0, 0x00220002}, /* INIT0 */ + {0x000000d4, 0x00010000}, /* INIT1 */ + {0x000000d8, 0x00000703}, /* INIT2 */ + {0x000000dc, 0x00630005}, /* INIT3 */ + {0x000000e0, 0x00010000}, /* INIT4 */ + {0x000000e4, 0x00070003}, /* INIT5 */ + {0x000000f4, 0x000f012f}, /* RANKCTL */ + {0x00000100, 0x07090b07}, /* DRAMTMG0 */ + {0x00000104, 0x0002010b}, /* DRAMTMG1 */ + {0x00000108, 0x02040506}, /* DRAMTMG2 */ + {0x0000010c, 0x00303000}, /* DRAMTMG3 */ + {0x00000110, 0x04010204}, /* DRAMTMG4 */ + {0x00000114, 0x01010303}, /* DRAMTMG5 */ + {0x00000118, 0x02020003}, /* DRAMTMG6 */ + {0x00000120, 0x00000303}, /* DRAMTMG8 */ + {0x00000138, 0x00000025}, /* DRAMTMG14 */ + {0x00000180, 0x003c000f}, /* ZQCTL0 */ + {0x00000184, 0x00900000}, /* ZQCTL1 */ + {0x00000190, 0x07020001}, /* DFITMG0 */ + {0x00000198, 0x07000101}, /* DFILPCFG0 */ + {0x000001a0, 0xc0400003}, /* DFIUPD0 */ + {0x00000240, 0x07030718}, /* ODTCFG */ + {0x00000250, 0x00001f00}, /* SCHED */ + {0x00000490, 0x00000001}, /* PCTRL_0 */ + {0xffffffff, 0xffffffff} + } + }, + { + { + {0x00000004, 0x00000009}, /* PHYREG01 */ + {0x00000028, 0x00000007}, /* PHYREG0A */ + {0x0000002c, 0x00000000}, /* PHYREG0B */ + {0x00000030, 0x00000004}, /* PHYREG0C */ + {0xffffffff, 0xffffffff} + } + } +}, diff --git a/drivers/ram/rockchip/sdram-px30-lpddr3-detect-333.inc b/drivers/ram/rockchip/sdram-px30-lpddr3-detect-333.inc new file mode 100644 index 0000000000..f694a0e5b0 --- /dev/null +++ b/drivers/ram/rockchip/sdram-px30-lpddr3-detect-333.inc @@ -0,0 +1,74 @@ +{ + { + { + .rank = 0x1, + .col = 0xC, + .bk = 0x3, + .bw = 0x1, + .dbw = 0x0, + .row_3_4 = 0x0, + .cs0_row = 0x10, + .cs1_row = 0x10, + .cs0_high16bit_row = 0x10, + .cs1_high16bit_row = 0x10, + .ddrconfig = 0, + }, + { + {0x290a060a}, + {0x08020303}, + {0x00000002}, + {0x00001111}, + {0x0000000c}, + {0x0000021a}, + 0x000000ff + } + }, + { + .ddr_freq = 333, + .dramtype = LPDDR3, + .num_channels = 1, + .stride = 0, + .odt = 0, + }, + { + { + {0x00000000, 0x43041008}, /* MSTR */ + {0x00000064, 0x00140023}, /* RFSHTMG */ + {0x000000d0, 0x00220002}, /* INIT0 */ + {0x000000d4, 0x00010000}, /* INIT1 */ + {0x000000d8, 0x00000703}, /* INIT2 */ + {0x000000dc, 0x00830004}, /* INIT3 */ + {0x000000e0, 0x00010000}, /* INIT4 */ + {0x000000e4, 0x00070003}, /* INIT5 */ + {0x000000f4, 0x000f012f}, /* RANKCTL */ + {0x00000100, 0x06090b07}, /* DRAMTMG0 */ + {0x00000104, 0x0002020b}, /* DRAMTMG1 */ + {0x00000108, 0x02030506}, /* DRAMTMG2 */ + {0x0000010c, 0x00505000}, /* DRAMTMG3 */ + {0x00000110, 0x03020204}, /* DRAMTMG4 */ + {0x00000114, 0x01010303}, /* DRAMTMG5 */ + {0x00000118, 0x02020003}, /* DRAMTMG6 */ + {0x00000120, 0x00000303}, /* DRAMTMG8 */ + {0x00000138, 0x00000025}, /* DRAMTMG14 */ + {0x00000180, 0x003c000f}, /* ZQCTL0 */ + {0x00000184, 0x00900000}, /* ZQCTL1 */ + {0x00000190, 0x07020000}, /* DFITMG0 */ + {0x00000198, 0x07000101}, /* DFILPCFG0 */ + {0x000001a0, 0xc0400003}, /* DFIUPD0 */ + {0x00000240, 0x0900090c}, /* ODTCFG */ + {0x00000244, 0x00000101}, /* ODTMAP */ + {0x00000250, 0x00001f00}, /* SCHED */ + {0x00000490, 0x00000001}, /* PCTRL_0 */ + {0xffffffff, 0xffffffff} + } + }, + { + { + {0x00000004, 0x0000000b}, /* PHYREG01 */ + {0x00000028, 0x00000006}, /* PHYREG0A */ + {0x0000002c, 0x00000000}, /* PHYREG0B */ + {0x00000030, 0x00000003}, /* PHYREG0C */ + {0xffffffff, 0xffffffff} + } + } +}, diff --git a/drivers/ram/rockchip/sdram-rk3399-lpddr4-400.inc b/drivers/ram/rockchip/sdram-rk3399-lpddr4-400.inc index c50a03d9dd..209ef57228 100644 --- a/drivers/ram/rockchip/sdram-rk3399-lpddr4-400.inc +++ b/drivers/ram/rockchip/sdram-rk3399-lpddr4-400.inc @@ -16,15 +16,23 @@ .row_3_4 = 0x0, .cs0_row = 0xF, .cs1_row = 0xF, + .cs0_high16bit_row = 0xF, + .cs1_high16bit_row = 0xF, .ddrconfig = 1, }, { - .ddrtiminga0 = 0x80241d22, - .ddrtimingb0 = 0x15050f08, + .ddrtiminga0 = { + 0x8010100d, + }, + .ddrtimingb0 = { + 0x08020b04, + }, .ddrtimingc0 = { 0x00000602, }, - .devtodev0 = 0x00002122, + .devtodev0 = { + 0x00002562, + }, .ddrmode = { 0x0000004c, }, @@ -41,15 +49,23 @@ .row_3_4 = 0x0, .cs0_row = 0xF, .cs1_row = 0xF, + .cs0_high16bit_row = 0xF, + .cs1_high16bit_row = 0xF, .ddrconfig = 1, }, { - .ddrtiminga0 = 0x80241d22, - .ddrtimingb0 = 0x15050f08, + .ddrtiminga0 = { + 0x8010100d, + }, + .ddrtimingb0 = { + 0x08020b04, + }, .ddrtimingc0 = { 0x00000602, }, - .devtodev0 = 0x00002122, + .devtodev0 = { + 0x00002562, + }, .ddrmode = { 0x0000004c, }, diff --git a/drivers/ram/rockchip/sdram-rk3399-lpddr4-800.inc b/drivers/ram/rockchip/sdram-rk3399-lpddr4-800.inc index d8ae3359a3..7d11b4c563 100644 --- a/drivers/ram/rockchip/sdram-rk3399-lpddr4-800.inc +++ b/drivers/ram/rockchip/sdram-rk3399-lpddr4-800.inc @@ -16,15 +16,23 @@ .row_3_4 = 0x0, .cs0_row = 0xF, .cs1_row = 0xF, + .cs0_high16bit_row = 0xF, + .cs1_high16bit_row = 0xF, .ddrconfig = 1, }, { - .ddrtiminga0 = 0x80241d22, - .ddrtimingb0 = 0x15050f08, + .ddrtiminga0 = { + 0x801c1819, + }, + .ddrtimingb0 = { + 0x10040c05, + }, .ddrtimingc0 = { 0x00000602, }, - .devtodev0 = 0x00002122, + .devtodev0 = { + 0x00002672, + }, .ddrmode = { 0x0000004c, }, @@ -41,15 +49,23 @@ .row_3_4 = 0x0, .cs0_row = 0xF, .cs1_row = 0xF, + .cs0_high16bit_row = 0xF, + .cs1_high16bit_row = 0xF, .ddrconfig = 1, }, { - .ddrtiminga0 = 0x80241d22, - .ddrtimingb0 = 0x15050f08, + .ddrtiminga0 = { + 0x80241d22, + }, + .ddrtimingb0 = { + 0x15050f08, + }, .ddrtimingc0 = { 0x00000602, }, - .devtodev0 = 0x00002122, + .devtodev0 = { + 0x00002122, + }, .ddrmode = { 0x0000004c, }, diff --git a/drivers/ram/rockchip/sdram_common.c b/drivers/ram/rockchip/sdram_common.c new file mode 100644 index 0000000000..6bc51572b2 --- /dev/null +++ b/drivers/ram/rockchip/sdram_common.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2018 Rockchip Electronics Co., Ltd. + */ + +#include <common.h> +#include <debug_uart.h> +#include <ram.h> +#include <asm/io.h> +#include <asm/arch-rockchip/sdram.h> +#include <asm/arch-rockchip/sdram_common.h> + +#ifdef CONFIG_RAM_ROCKCHIP_DEBUG +void sdram_print_dram_type(unsigned char dramtype) +{ + switch (dramtype) { + case DDR3: + printascii("DDR3"); + break; + case DDR4: + printascii("DDR4"); + break; + case LPDDR2: + printascii("LPDDR2"); + break; + case LPDDR3: + printascii("LPDDR3"); + break; + case LPDDR4: + printascii("LPDDR4"); + break; + default: + printascii("Unknown Device"); + break; + } +} + +void sdram_print_ddr_info(struct sdram_cap_info *cap_info, + struct sdram_base_params *base) +{ + u64 cap; + u32 bg; + + bg = (cap_info->dbw == 0) ? 2 : 1; + + sdram_print_dram_type(base->dramtype); + + printascii(", "); + printdec(base->ddr_freq); + printascii("MHz\n"); + + printascii("BW="); + printdec(8 << cap_info->bw); + printascii(" Col="); + printdec(cap_info->col); + printascii(" Bk="); + printdec(0x1 << cap_info->bk); + if (base->dramtype == DDR4) { + printascii(" BG="); + printdec(1 << bg); + } + printascii(" CS0 Row="); + printdec(cap_info->cs0_row); + if (cap_info->cs0_high16bit_row != + cap_info->cs0_row) { + printascii("/"); + printdec(cap_info->cs0_high16bit_row); + } + if (cap_info->rank > 1) { + printascii(" CS1 Row="); + printdec(cap_info->cs1_row); + if (cap_info->cs1_high16bit_row != + cap_info->cs1_row) { + printascii("/"); + printdec(cap_info->cs1_high16bit_row); + } + } + printascii(" CS="); + printdec(cap_info->rank); + printascii(" Die BW="); + printdec(8 << cap_info->dbw); + + cap = sdram_get_cs_cap(cap_info, 3, base->dramtype); + if (cap_info->row_3_4) + cap = cap * 3 / 4; + + printascii(" Size="); + printdec(cap >> 20); + printascii("MB\n"); +} + +void sdram_print_stride(unsigned int stride) +{ + switch (stride) { + case 0xc: + printf("128B stride\n"); + break; + case 5: + case 9: + case 0xd: + case 0x11: + case 0x19: + printf("256B stride\n"); + break; + case 0xa: + case 0xe: + case 0x12: + printf("512B stride\n"); + break; + case 0xf: + printf("4K stride\n"); + break; + case 0x1f: + printf("32MB + 256B stride\n"); + break; + default: + printf("no stride\n"); + } +} +#endif + +/* + * cs: 0:cs0 + * 1:cs1 + * else cs0+cs1 + * note: it didn't consider about row_3_4 + */ +u64 sdram_get_cs_cap(struct sdram_cap_info *cap_info, u32 cs, u32 dram_type) +{ + u32 bg; + u64 cap[2]; + + if (dram_type == DDR4) + /* DDR4 8bit dram BG = 2(4bank groups), + * 16bit dram BG = 1 (2 bank groups) + */ + bg = (cap_info->dbw == 0) ? 2 : 1; + else + bg = 0; + cap[0] = 1llu << (cap_info->bw + cap_info->col + + bg + cap_info->bk + cap_info->cs0_row); + + if (cap_info->rank == 2) + cap[1] = 1llu << (cap_info->bw + cap_info->col + + bg + cap_info->bk + cap_info->cs1_row); + else + cap[1] = 0; + + if (cs == 0) + return cap[0]; + else if (cs == 1) + return cap[1]; + else + return (cap[0] + cap[1]); +} + +/* n: Unit bytes */ +void sdram_copy_to_reg(u32 *dest, const u32 *src, u32 n) +{ + int i; + + for (i = 0; i < n / sizeof(u32); i++) { + writel(*src, dest); + src++; + dest++; + } +} + +void sdram_org_config(struct sdram_cap_info *cap_info, + struct sdram_base_params *base, + u32 *p_os_reg2, u32 *p_os_reg3, u32 channel) +{ + *p_os_reg2 |= SYS_REG_ENC_DDRTYPE(base->dramtype); + *p_os_reg2 |= SYS_REG_ENC_NUM_CH(base->num_channels); + + *p_os_reg2 |= SYS_REG_ENC_ROW_3_4(cap_info->row_3_4, channel); + *p_os_reg2 |= SYS_REG_ENC_CHINFO(channel); + *p_os_reg2 |= SYS_REG_ENC_RANK(cap_info->rank, channel); + *p_os_reg2 |= SYS_REG_ENC_COL(cap_info->col, channel); + *p_os_reg2 |= SYS_REG_ENC_BK(cap_info->bk, channel); + *p_os_reg2 |= SYS_REG_ENC_BW(cap_info->bw, channel); + *p_os_reg2 |= SYS_REG_ENC_DBW(cap_info->dbw, channel); + + SYS_REG_ENC_CS0_ROW(cap_info->cs0_row, *p_os_reg2, *p_os_reg3, channel); + if (cap_info->cs1_row) + SYS_REG_ENC_CS1_ROW(cap_info->cs1_row, *p_os_reg2, + *p_os_reg3, channel); + *p_os_reg3 |= SYS_REG_ENC_CS1_COL(cap_info->col, channel); + *p_os_reg3 |= SYS_REG_ENC_VERSION(DDR_SYS_REG_VERSION); +} + +int sdram_detect_bw(struct sdram_cap_info *cap_info) +{ + return 0; +} + +int sdram_detect_cs(struct sdram_cap_info *cap_info) +{ + return 0; +} + +int sdram_detect_col(struct sdram_cap_info *cap_info, + u32 coltmp) +{ + void __iomem *test_addr; + u32 col; + u32 bw = cap_info->bw; + + for (col = coltmp; col >= 9; col -= 1) { + writel(0, CONFIG_SYS_SDRAM_BASE); + test_addr = (void __iomem *)(CONFIG_SYS_SDRAM_BASE + + (1ul << (col + bw - 1ul))); + writel(PATTERN, test_addr); + if ((readl(test_addr) == PATTERN) && + (readl(CONFIG_SYS_SDRAM_BASE) == 0)) + break; + } + if (col == 8) { + printascii("col error\n"); + return -1; + } + + cap_info->col = col; + + return 0; +} + +int sdram_detect_bank(struct sdram_cap_info *cap_info, + u32 coltmp, u32 bktmp) +{ + void __iomem *test_addr; + u32 bk; + u32 bw = cap_info->bw; + + test_addr = (void __iomem *)(CONFIG_SYS_SDRAM_BASE + + (1ul << (coltmp + bktmp + bw - 1ul))); + writel(0, CONFIG_SYS_SDRAM_BASE); + writel(PATTERN, test_addr); + if ((readl(test_addr) == PATTERN) && + (readl(CONFIG_SYS_SDRAM_BASE) == 0)) + bk = 3; + else + bk = 2; + + cap_info->bk = bk; + + return 0; +} + +/* detect bg for ddr4 */ +int sdram_detect_bg(struct sdram_cap_info *cap_info, + u32 coltmp) +{ + void __iomem *test_addr; + u32 dbw; + u32 bw = cap_info->bw; + + test_addr = (void __iomem *)(CONFIG_SYS_SDRAM_BASE + + (1ul << (coltmp + bw + 1ul))); + writel(0, CONFIG_SYS_SDRAM_BASE); + writel(PATTERN, test_addr); + if ((readl(test_addr) == PATTERN) && + (readl(CONFIG_SYS_SDRAM_BASE) == 0)) + dbw = 0; + else + dbw = 1; + + cap_info->dbw = dbw; + + return 0; +} + +/* detect dbw for ddr3,lpddr2,lpddr3,lpddr4 */ +int sdram_detect_dbw(struct sdram_cap_info *cap_info, u32 dram_type) +{ + u32 row, col, bk, bw, cs_cap, cs; + u32 die_bw_0 = 0, die_bw_1 = 0; + + if (dram_type == DDR3 || dram_type == LPDDR4) { + cap_info->dbw = 1; + } else if (dram_type == LPDDR3 || dram_type == LPDDR2) { + row = cap_info->cs0_row; + col = cap_info->col; + bk = cap_info->bk; + cs = cap_info->rank; + bw = cap_info->bw; + cs_cap = (1 << (row + col + bk + bw - 20)); + if (bw == 2) { + if (cs_cap <= 0x2000000) /* 256Mb */ + die_bw_0 = (col < 9) ? 2 : 1; + else if (cs_cap <= 0x10000000) /* 2Gb */ + die_bw_0 = (col < 10) ? 2 : 1; + else if (cs_cap <= 0x40000000) /* 8Gb */ + die_bw_0 = (col < 11) ? 2 : 1; + else + die_bw_0 = (col < 12) ? 2 : 1; + if (cs > 1) { + row = cap_info->cs1_row; + cs_cap = (1 << (row + col + bk + bw - 20)); + if (cs_cap <= 0x2000000) /* 256Mb */ + die_bw_0 = (col < 9) ? 2 : 1; + else if (cs_cap <= 0x10000000) /* 2Gb */ + die_bw_0 = (col < 10) ? 2 : 1; + else if (cs_cap <= 0x40000000) /* 8Gb */ + die_bw_0 = (col < 11) ? 2 : 1; + else + die_bw_0 = (col < 12) ? 2 : 1; + } + } else { + die_bw_1 = 1; + die_bw_0 = 1; + } + cap_info->dbw = (die_bw_0 > die_bw_1) ? die_bw_0 : die_bw_1; + } + + return 0; +} + +int sdram_detect_row(struct sdram_cap_info *cap_info, + u32 coltmp, u32 bktmp, u32 rowtmp) +{ + u32 row; + u32 bw = cap_info->bw; + void __iomem *test_addr; + + for (row = rowtmp; row > 12; row--) { + writel(0, CONFIG_SYS_SDRAM_BASE); + test_addr = (void __iomem *)(CONFIG_SYS_SDRAM_BASE + + (1ul << (row + bktmp + coltmp + bw - 1ul))); + writel(PATTERN, test_addr); + if ((readl(test_addr) == PATTERN) && + (readl(CONFIG_SYS_SDRAM_BASE) == 0)) + break; + } + if (row == 12) { + printascii("row error"); + return -1; + } + + cap_info->cs0_row = row; + + return 0; +} + +int sdram_detect_row_3_4(struct sdram_cap_info *cap_info, + u32 coltmp, u32 bktmp) +{ + u32 row_3_4; + u32 bw = cap_info->bw; + u32 row = cap_info->cs0_row; + void __iomem *test_addr, *test_addr1; + + test_addr = CONFIG_SYS_SDRAM_BASE; + test_addr1 = (void __iomem *)(CONFIG_SYS_SDRAM_BASE + + (0x3ul << (row + bktmp + coltmp + bw - 1ul - 1ul))); + + writel(0, test_addr); + writel(PATTERN, test_addr1); + if ((readl(test_addr) == 0) && (readl(test_addr1) == PATTERN)) + row_3_4 = 0; + else + row_3_4 = 1; + + cap_info->row_3_4 = row_3_4; + + return 0; +} + +int sdram_detect_high_row(struct sdram_cap_info *cap_info) +{ + cap_info->cs0_high16bit_row = cap_info->cs0_row; + cap_info->cs1_high16bit_row = cap_info->cs1_row; + + return 0; +} + +int sdram_detect_cs1_row(struct sdram_cap_info *cap_info, u32 dram_type) +{ + void __iomem *test_addr; + u32 row = 0, bktmp, coltmp, bw; + ulong cs0_cap; + u32 byte_mask; + + if (cap_info->rank == 2) { + cs0_cap = sdram_get_cs_cap(cap_info, 0, dram_type); + + if (dram_type == DDR4) { + if (cap_info->dbw == 0) + bktmp = cap_info->bk + 2; + else + bktmp = cap_info->bk + 1; + } else { + bktmp = cap_info->bk; + } + bw = cap_info->bw; + coltmp = cap_info->col; + + /* + * because px30 support axi split,min bandwidth + * is 8bit. if cs0 is 32bit, cs1 may 32bit or 16bit + * so we check low 16bit data when detect cs1 row. + * if cs0 is 16bit/8bit, we check low 8bit data. + */ + if (bw == 2) + byte_mask = 0xFFFF; + else + byte_mask = 0xFF; + + /* detect cs1 row */ + for (row = cap_info->cs0_row; row > 12; row--) { + test_addr = (void __iomem *)(CONFIG_SYS_SDRAM_BASE + + cs0_cap + + (1ul << (row + bktmp + coltmp + bw - 1ul))); + writel(0, CONFIG_SYS_SDRAM_BASE + cs0_cap); + writel(PATTERN, test_addr); + + if (((readl(test_addr) & byte_mask) == + (PATTERN & byte_mask)) && + ((readl(CONFIG_SYS_SDRAM_BASE + cs0_cap) & + byte_mask) == 0)) { + break; + } + } + } + + cap_info->cs1_row = row; + + return 0; +} diff --git a/drivers/ram/rockchip/sdram_debug.c b/drivers/ram/rockchip/sdram_debug.c deleted file mode 100644 index 9cf662675b..0000000000 --- a/drivers/ram/rockchip/sdram_debug.c +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR MIT) -/* - * (C) Copyright 2019 Rockchip Electronics Co., Ltd - * (C) Copyright 2019 Amarula Solutions. - * Author: Jagan Teki <jagan@amarulasolutions.com> - */ - -#include <common.h> -#include <debug_uart.h> -#include <asm/arch-rockchip/sdram_common.h> - -void sdram_print_dram_type(unsigned char dramtype) -{ - switch (dramtype) { - case DDR3: - printascii("DDR3"); - break; - case DDR4: - printascii("DDR4"); - break; - case LPDDR2: - printascii("LPDDR2"); - break; - case LPDDR3: - printascii("LPDDR3"); - break; - case LPDDR4: - printascii("LPDDR4"); - break; - default: - printascii("Unknown Device"); - break; - } -} - -/** - * cs = 0, cs0 - * cs = 1, cs1 - * cs => 2, cs0+cs1 - * note: it didn't consider about row_3_4 - */ -u64 sdram_get_cs_cap(struct sdram_cap_info *cap_info, u32 cs, u32 dram_type) -{ - u32 bg; - u64 cap[2]; - - if (dram_type == DDR4) - /* DDR4 8bit dram BG = 2(4bank groups), - * 16bit dram BG = 1 (2 bank groups) - */ - bg = (cap_info->dbw == 0) ? 2 : 1; - else - bg = 0; - - cap[0] = 1llu << (cap_info->bw + cap_info->col + - bg + cap_info->bk + cap_info->cs0_row); - - if (cap_info->rank == 2) - cap[1] = 1llu << (cap_info->bw + cap_info->col + - bg + cap_info->bk + cap_info->cs1_row); - else - cap[1] = 0; - - if (cs == 0) - return cap[0]; - else if (cs == 1) - return cap[1]; - else - return (cap[0] + cap[1]); -} - -void sdram_print_ddr_info(struct sdram_cap_info *cap_info, - struct sdram_base_params *base) -{ - u32 bg, cap; - - bg = (cap_info->dbw == 0) ? 2 : 1; - - sdram_print_dram_type(base->dramtype); - - printascii(", "); - printdec(base->ddr_freq); - printascii("MHz\n"); - - printascii("BW="); - printdec(8 << cap_info->bw); - - printascii(" Col="); - printdec(cap_info->col); - - printascii(" Bk="); - printdec(0x1 << cap_info->bk); - if (base->dramtype == DDR4) { - printascii(" BG="); - printdec(1 << bg); - } - - printascii(" CS0 Row="); - printdec(cap_info->cs0_row); - if (cap_info->rank > 1) { - printascii(" CS1 Row="); - printdec(cap_info->cs1_row); - } - - printascii(" CS="); - printdec(cap_info->rank); - - printascii(" Die BW="); - printdec(8 << cap_info->dbw); - - cap = sdram_get_cs_cap(cap_info, 3, base->dramtype); - if (cap_info->row_3_4) - cap = cap * 3 / 4; - - printascii(" Size="); - printdec(cap >> 20); - printascii("MB\n"); -} - -void sdram_print_stride(unsigned int stride) -{ - switch (stride) { - case 0xc: - printf("128B stride\n"); - break; - case 5: - case 9: - case 0xd: - case 0x11: - case 0x19: - printf("256B stride\n"); - break; - case 0xa: - case 0xe: - case 0x12: - printf("512B stride\n"); - break; - case 0xf: - printf("4K stride\n"); - break; - case 0x1f: - printf("32MB + 256B stride\n"); - break; - default: - printf("no stride\n"); - } -} diff --git a/drivers/ram/rockchip/sdram_pctl_px30.c b/drivers/ram/rockchip/sdram_pctl_px30.c new file mode 100644 index 0000000000..1839cebb67 --- /dev/null +++ b/drivers/ram/rockchip/sdram_pctl_px30.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2018 Rockchip Electronics Co., Ltd. + */ + +#include <common.h> +#include <ram.h> +#include <asm/io.h> +#include <asm/arch-rockchip/sdram.h> +#include <asm/arch-rockchip/sdram_pctl_px30.h> + +/* + * rank = 1: cs0 + * rank = 2: cs1 + */ +void pctl_read_mr(void __iomem *pctl_base, u32 rank, u32 mr_num) +{ + writel((rank << 4) | (1 << 0), pctl_base + DDR_PCTL2_MRCTRL0); + writel((mr_num << 8), pctl_base + DDR_PCTL2_MRCTRL1); + setbits_le32(pctl_base + DDR_PCTL2_MRCTRL0, 1u << 31); + while (readl(pctl_base + DDR_PCTL2_MRCTRL0) & (1u << 31)) + continue; + while (readl(pctl_base + DDR_PCTL2_MRSTAT) & MR_WR_BUSY) + continue; +} + +/* rank = 1: cs0 + * rank = 2: cs1 + * rank = 3: cs0 & cs1 + * note: be careful of keep mr original val + */ +int pctl_write_mr(void __iomem *pctl_base, u32 rank, u32 mr_num, u32 arg, + u32 dramtype) +{ + while (readl(pctl_base + DDR_PCTL2_MRSTAT) & MR_WR_BUSY) + continue; + if (dramtype == DDR3 || dramtype == DDR4) { + writel((mr_num << 12) | (rank << 4) | (0 << 0), + pctl_base + DDR_PCTL2_MRCTRL0); + writel(arg, pctl_base + DDR_PCTL2_MRCTRL1); + } else { + writel((rank << 4) | (0 << 0), + pctl_base + DDR_PCTL2_MRCTRL0); + writel((mr_num << 8) | (arg & 0xff), + pctl_base + DDR_PCTL2_MRCTRL1); + } + + setbits_le32(pctl_base + DDR_PCTL2_MRCTRL0, 1u << 31); + while (readl(pctl_base + DDR_PCTL2_MRCTRL0) & (1u << 31)) + continue; + while (readl(pctl_base + DDR_PCTL2_MRSTAT) & MR_WR_BUSY) + continue; + + return 0; +} + +/* + * rank : 1:cs0, 2:cs1, 3:cs0&cs1 + * vrefrate: 4500: 45%, + */ +int pctl_write_vrefdq(void __iomem *pctl_base, u32 rank, u32 vrefrate, + u32 dramtype) +{ + u32 tccd_l, value; + u32 dis_auto_zq = 0; + + if (dramtype != DDR4 || vrefrate < 4500 || + vrefrate > 9200) + return (-1); + + tccd_l = (readl(pctl_base + DDR_PCTL2_DRAMTMG4) >> 16) & 0xf; + tccd_l = (tccd_l - 4) << 10; + + if (vrefrate > 7500) { + /* range 1 */ + value = ((vrefrate - 6000) / 65) | tccd_l; + } else { + /* range 2 */ + value = ((vrefrate - 4500) / 65) | tccd_l | (1 << 6); + } + + dis_auto_zq = pctl_dis_zqcs_aref(pctl_base); + + /* enable vrefdq calibratin */ + pctl_write_mr(pctl_base, rank, 6, value | (1 << 7), dramtype); + udelay(1);/* tvrefdqe */ + /* write vrefdq value */ + pctl_write_mr(pctl_base, rank, 6, value | (1 << 7), dramtype); + udelay(1);/* tvref_time */ + pctl_write_mr(pctl_base, rank, 6, value | (0 << 7), dramtype); + udelay(1);/* tvrefdqx */ + + pctl_rest_zqcs_aref(pctl_base, dis_auto_zq); + + return 0; +} + +static int upctl2_update_ref_reg(void __iomem *pctl_base) +{ + u32 ret; + + ret = readl(pctl_base + DDR_PCTL2_RFSHCTL3) ^ (1 << 1); + writel(ret, pctl_base + DDR_PCTL2_RFSHCTL3); + + return 0; +} + +u32 pctl_dis_zqcs_aref(void __iomem *pctl_base) +{ + u32 dis_auto_zq = 0; + + /* disable zqcs */ + if (!(readl(pctl_base + DDR_PCTL2_ZQCTL0) & + (1ul << 31))) { + dis_auto_zq = 1; + setbits_le32(pctl_base + DDR_PCTL2_ZQCTL0, 1 << 31); + } + + /* disable auto refresh */ + setbits_le32(pctl_base + DDR_PCTL2_RFSHCTL3, 1); + + upctl2_update_ref_reg(pctl_base); + + return dis_auto_zq; +} + +void pctl_rest_zqcs_aref(void __iomem *pctl_base, u32 dis_auto_zq) +{ + /* restore zqcs */ + if (dis_auto_zq) + clrbits_le32(pctl_base + DDR_PCTL2_ZQCTL0, 1 << 31); + + /* restore auto refresh */ + clrbits_le32(pctl_base + DDR_PCTL2_RFSHCTL3, 1); + + upctl2_update_ref_reg(pctl_base); +} + +u32 pctl_remodify_sdram_params(struct ddr_pctl_regs *pctl_regs, + struct sdram_cap_info *cap_info, + u32 dram_type) +{ + u32 tmp = 0, tmp_adr = 0, i; + + for (i = 0; pctl_regs->pctl[i][0] != 0xFFFFFFFF; i++) { + if (pctl_regs->pctl[i][0] == 0) { + tmp = pctl_regs->pctl[i][1];/* MSTR */ + tmp_adr = i; + } + } + + tmp &= ~((3ul << 30) | (3ul << 24) | (3ul << 12)); + + switch (cap_info->dbw) { + case 2: + tmp |= (3ul << 30); + break; + case 1: + tmp |= (2ul << 30); + break; + case 0: + default: + tmp |= (1ul << 30); + break; + } + + /* + * If DDR3 or DDR4 MSTR.active_ranks=1, + * it will gate memory clock when enter power down. + * Force set active_ranks to 3 to workaround it. + */ + if (cap_info->rank == 2 || dram_type == DDR3 || + dram_type == DDR4) + tmp |= 3 << 24; + else + tmp |= 1 << 24; + + tmp |= (2 - cap_info->bw) << 12; + + pctl_regs->pctl[tmp_adr][1] = tmp; + + return 0; +} + +int pctl_cfg(void __iomem *pctl_base, struct ddr_pctl_regs *pctl_regs, + u32 sr_idle, u32 pd_idle) +{ + u32 i; + + for (i = 0; pctl_regs->pctl[i][0] != 0xFFFFFFFF; i++) { + writel(pctl_regs->pctl[i][1], + pctl_base + pctl_regs->pctl[i][0]); + } + clrsetbits_le32(pctl_base + DDR_PCTL2_PWRTMG, + (0xff << 16) | 0x1f, + ((sr_idle & 0xff) << 16) | (pd_idle & 0x1f)); + + clrsetbits_le32(pctl_base + DDR_PCTL2_HWLPCTL, + 0xfff << 16, + 5 << 16); + /* disable zqcs */ + setbits_le32(pctl_base + DDR_PCTL2_ZQCTL0, 1u << 31); + + return 0; +} diff --git a/drivers/ram/rockchip/sdram_phy_px30.c b/drivers/ram/rockchip/sdram_phy_px30.c new file mode 100644 index 0000000000..5de73770a8 --- /dev/null +++ b/drivers/ram/rockchip/sdram_phy_px30.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2018 Rockchip Electronics Co., Ltd. + */ + +#include <common.h> +#include <ram.h> +#include <asm/io.h> +#include <asm/arch-rockchip/sdram.h> +#include <asm/arch-rockchip/sdram_common.h> +#include <asm/arch-rockchip/sdram_phy_px30.h> + +static void sdram_phy_dll_bypass_set(void __iomem *phy_base, u32 freq) +{ + u32 tmp; + u32 i, j; + u32 dqs_dll_freq; + + setbits_le32(PHY_REG(phy_base, 0x13), 1 << 4); + clrbits_le32(PHY_REG(phy_base, 0x14), 1 << 3); + for (i = 0; i < 4; i++) { + j = 0x26 + i * 0x10; + setbits_le32(PHY_REG(phy_base, j), 1 << 4); + clrbits_le32(PHY_REG(phy_base, j + 0x1), 1 << 3); + } + + if (freq <= 400) + /* DLL bypass */ + setbits_le32(PHY_REG(phy_base, 0xa4), 0x1f); + else + clrbits_le32(PHY_REG(phy_base, 0xa4), 0x1f); + + #ifdef CONFIG_ROCKCHIP_RK3328 + dqs_dll_freq = 680; + #else + dqs_dll_freq = 801; + #endif + + if (freq <= dqs_dll_freq) + tmp = 2; + else + tmp = 1; + + for (i = 0; i < 4; i++) { + j = 0x28 + i * 0x10; + writel(tmp, PHY_REG(phy_base, j)); + } +} + +static void sdram_phy_set_ds_odt(void __iomem *phy_base, + u32 dram_type) +{ + u32 cmd_drv, clk_drv, dqs_drv, dqs_odt; + u32 i, j; + + if (dram_type == DDR3) { + cmd_drv = PHY_DDR3_RON_RTT_34ohm; + clk_drv = PHY_DDR3_RON_RTT_45ohm; + dqs_drv = PHY_DDR3_RON_RTT_34ohm; + dqs_odt = PHY_DDR3_RON_RTT_225ohm; + } else { + cmd_drv = PHY_DDR4_LPDDR3_RON_RTT_34ohm; + clk_drv = PHY_DDR4_LPDDR3_RON_RTT_43ohm; + dqs_drv = PHY_DDR4_LPDDR3_RON_RTT_34ohm; + if (dram_type == LPDDR2) + dqs_odt = PHY_DDR4_LPDDR3_RON_RTT_DISABLE; + else + dqs_odt = PHY_DDR4_LPDDR3_RON_RTT_240ohm; + } + /* DS */ + writel(cmd_drv, PHY_REG(phy_base, 0x11)); + clrsetbits_le32(PHY_REG(phy_base, 0x12), 0x1f << 3, cmd_drv << 3); + writel(clk_drv, PHY_REG(phy_base, 0x16)); + writel(clk_drv, PHY_REG(phy_base, 0x18)); + + for (i = 0; i < 4; i++) { + j = 0x20 + i * 0x10; + writel(dqs_drv, PHY_REG(phy_base, j)); + writel(dqs_drv, PHY_REG(phy_base, j + 0xf)); + /* ODT */ + writel(dqs_odt, PHY_REG(phy_base, j + 0x1)); + writel(dqs_odt, PHY_REG(phy_base, j + 0xe)); + } +} + +void phy_soft_reset(void __iomem *phy_base) +{ + clrbits_le32(PHY_REG(phy_base, 0), 0x3 << 2); + udelay(1); + setbits_le32(PHY_REG(phy_base, 0), ANALOG_DERESET); + udelay(5); + setbits_le32(PHY_REG(phy_base, 0), DIGITAL_DERESET); + udelay(1); +} + +void phy_dram_set_bw(void __iomem *phy_base, u32 bw) +{ + if (bw == 2) { + clrsetbits_le32(PHY_REG(phy_base, 0), 0xf << 4, 0xf << 4); + setbits_le32(PHY_REG(phy_base, 0x46), 1 << 3); + setbits_le32(PHY_REG(phy_base, 0x56), 1 << 3); + } else if (bw == 1) { + clrsetbits_le32(PHY_REG(phy_base, 0), 0xf << 4, 3 << 4); + clrbits_le32(PHY_REG(phy_base, 0x46), 1 << 3); + clrbits_le32(PHY_REG(phy_base, 0x56), 1 << 3); + } else if (bw == 0) { + clrsetbits_le32(PHY_REG(phy_base, 0), 0xf << 4, 1 << 4); + clrbits_le32(PHY_REG(phy_base, 0x36), 1 << 3); + clrbits_le32(PHY_REG(phy_base, 0x46), 1 << 3); + clrbits_le32(PHY_REG(phy_base, 0x56), 1 << 3); + } + + phy_soft_reset(phy_base); +} + +int phy_data_training(void __iomem *phy_base, u32 cs, u32 dramtype) +{ + u32 ret; + u32 odt_val; + u32 i, j; + + odt_val = readl(PHY_REG(phy_base, 0x2e)); + + for (i = 0; i < 4; i++) { + j = 0x20 + i * 0x10; + writel(PHY_DDR3_RON_RTT_225ohm, PHY_REG(phy_base, j + 0x1)); + writel(0, PHY_REG(phy_base, j + 0xe)); + } + + if (dramtype == DDR4) { + clrsetbits_le32(PHY_REG(phy_base, 0x29), 0x3, 0); + clrsetbits_le32(PHY_REG(phy_base, 0x39), 0x3, 0); + clrsetbits_le32(PHY_REG(phy_base, 0x49), 0x3, 0); + clrsetbits_le32(PHY_REG(phy_base, 0x59), 0x3, 0); + } + /* choose training cs */ + clrsetbits_le32(PHY_REG(phy_base, 2), 0x33, (0x20 >> cs)); + /* enable gate training */ + clrsetbits_le32(PHY_REG(phy_base, 2), 0x33, (0x20 >> cs) | 1); + udelay(50); + ret = readl(PHY_REG(phy_base, 0xff)); + /* disable gate training */ + clrsetbits_le32(PHY_REG(phy_base, 2), 0x33, (0x20 >> cs) | 0); + #ifndef CONFIG_ROCKCHIP_RK3328 + clrbits_le32(PHY_REG(phy_base, 2), 0x30); + #endif + + if (dramtype == DDR4) { + clrsetbits_le32(PHY_REG(phy_base, 0x29), 0x3, 0x2); + clrsetbits_le32(PHY_REG(phy_base, 0x39), 0x3, 0x2); + clrsetbits_le32(PHY_REG(phy_base, 0x49), 0x3, 0x2); + clrsetbits_le32(PHY_REG(phy_base, 0x59), 0x3, 0x2); + } + + if (ret & 0x10) { + ret = -1; + } else { + ret = (ret & 0xf) ^ (readl(PHY_REG(phy_base, 0)) >> 4); + ret = (ret == 0) ? 0 : -1; + } + + for (i = 0; i < 4; i++) { + j = 0x20 + i * 0x10; + writel(odt_val, PHY_REG(phy_base, j + 0x1)); + writel(odt_val, PHY_REG(phy_base, j + 0xe)); + } + return ret; +} + +void phy_cfg(void __iomem *phy_base, + struct ddr_phy_regs *phy_regs, struct ddr_phy_skew *skew, + struct sdram_base_params *base, u32 bw) +{ + u32 i; + + sdram_phy_dll_bypass_set(phy_base, base->ddr_freq); + for (i = 0; phy_regs->phy[i][0] != 0xFFFFFFFF; i++) { + writel(phy_regs->phy[i][1], + phy_base + phy_regs->phy[i][0]); + } + if (bw == 2) { + clrsetbits_le32(PHY_REG(phy_base, 0), 0xf << 4, 0xf << 4); + } else if (bw == 1) { + clrsetbits_le32(PHY_REG(phy_base, 0), 0xf << 4, 3 << 4); + /* disable DQS2,DQS3 tx dll for saving power */ + clrbits_le32(PHY_REG(phy_base, 0x46), 1 << 3); + clrbits_le32(PHY_REG(phy_base, 0x56), 1 << 3); + } else { + clrsetbits_le32(PHY_REG(phy_base, 0), 0xf << 4, 1 << 4); + /* disable DQS2,DQS3 tx dll for saving power */ + clrbits_le32(PHY_REG(phy_base, 0x36), 1 << 3); + clrbits_le32(PHY_REG(phy_base, 0x46), 1 << 3); + clrbits_le32(PHY_REG(phy_base, 0x56), 1 << 3); + } + sdram_phy_set_ds_odt(phy_base, base->dramtype); + + /* deskew */ + setbits_le32(PHY_REG(phy_base, 2), 8); + sdram_copy_to_reg(PHY_REG(phy_base, 0xb0), + &skew->a0_a1_skew[0], 15 * 4); + sdram_copy_to_reg(PHY_REG(phy_base, 0x70), + &skew->cs0_dm0_skew[0], 44 * 4); + sdram_copy_to_reg(PHY_REG(phy_base, 0xc0), + &skew->cs1_dm0_skew[0], 44 * 4); +} diff --git a/drivers/ram/rockchip/sdram_px30.c b/drivers/ram/rockchip/sdram_px30.c new file mode 100644 index 0000000000..729255493a --- /dev/null +++ b/drivers/ram/rockchip/sdram_px30.c @@ -0,0 +1,751 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2018 Rockchip Electronics Co., Ltd. + */ + +#include <common.h> +#include <debug_uart.h> +#include <dm.h> +#include <ram.h> +#include <syscon.h> +#include <asm/io.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/cru_px30.h> +#include <asm/arch-rockchip/grf_px30.h> +#include <asm/arch-rockchip/hardware.h> +#include <asm/arch-rockchip/sdram.h> +#include <asm/arch-rockchip/sdram_px30.h> + +struct dram_info { +#ifdef CONFIG_TPL_BUILD + struct ddr_pctl_regs *pctl; + struct ddr_phy_regs *phy; + struct px30_cru *cru; + struct msch_regs *msch; + struct px30_ddr_grf_regs *ddr_grf; + struct px30_grf *grf; +#endif + struct ram_info info; + struct px30_pmugrf *pmugrf; +}; + +#ifdef CONFIG_TPL_BUILD + +u8 ddr_cfg_2_rbc[] = { + /* + * [6:4] max row: 13+n + * [3] bank(0:4bank,1:8bank) + * [2:0] col(10+n) + */ + ((5 << 4) | (1 << 3) | 0), /* 0 */ + ((5 << 4) | (1 << 3) | 1), /* 1 */ + ((4 << 4) | (1 << 3) | 2), /* 2 */ + ((3 << 4) | (1 << 3) | 3), /* 3 */ + ((2 << 4) | (1 << 3) | 4), /* 4 */ + ((5 << 4) | (0 << 3) | 2), /* 5 */ + ((4 << 4) | (1 << 3) | 2), /* 6 */ + /*((0<<3)|3),*/ /* 12 for ddr4 */ + /*((1<<3)|1),*/ /* 13 B,C exchange for rkvdec */ +}; + +/* + * for ddr4 if ddrconfig=7, upctl should set 7 and noc should + * set to 1 for more efficient. + * noc ddrconf, upctl addrmap + * 1 7 + * 2 8 + * 3 9 + * 12 10 + * 5 11 + */ +u8 d4_rbc_2_d3_rbc[] = { + 1, /* 7 */ + 2, /* 8 */ + 3, /* 9 */ + 12, /* 10 */ + 5, /* 11 */ +}; + +/* + * row higher than cs should be disabled by set to 0xf + * rank addrmap calculate by real cap. + */ +u32 addrmap[][8] = { + /* map0 map1, map2, map3, map4, map5 + * map6, map7, map8 + * ------------------------------------------------------- + * bk2-0 col 5-2 col 9-6 col 11-10 row 11-0 + * row 15-12 row 17-16 bg1,0 + * ------------------------------------------------------- + * 4,3,2 5-2 9-6 6 + * 3,2 + */ + {0x00060606, 0x00000000, 0x1f1f0000, 0x00001f1f, 0x05050505, + 0x05050505, 0x00000505, 0x3f3f}, /* 0 */ + {0x00070707, 0x00000000, 0x1f000000, 0x00001f1f, 0x06060606, + 0x06060606, 0x06060606, 0x3f3f}, /* 1 */ + {0x00080808, 0x00000000, 0x00000000, 0x00001f1f, 0x07070707, + 0x07070707, 0x00000f07, 0x3f3f}, /* 2 */ + {0x00090909, 0x00000000, 0x00000000, 0x00001f00, 0x08080808, + 0x08080808, 0x00000f0f, 0x3f3f}, /* 3 */ + {0x000a0a0a, 0x00000000, 0x00000000, 0x00000000, 0x09090909, + 0x0f090909, 0x00000f0f, 0x3f3f}, /* 4 */ + {0x00080808, 0x00000000, 0x00000000, 0x00001f1f, 0x06060606, + 0x06060606, 0x00000606, 0x3f3f}, /* 5 */ + {0x00080808, 0x00000000, 0x00000000, 0x00001f1f, 0x07070707, + 0x07070707, 0x00000f0f, 0x3f3f}, /* 6 */ + {0x003f0808, 0x00000006, 0x1f1f0000, 0x00001f1f, 0x06060606, + 0x06060606, 0x00000606, 0x0600}, /* 7 */ + {0x003f0909, 0x00000007, 0x1f000000, 0x00001f1f, 0x07070707, + 0x07070707, 0x00000f07, 0x0700}, /* 8 */ + {0x003f0a0a, 0x01010100, 0x01010101, 0x00001f1f, 0x08080808, + 0x08080808, 0x00000f0f, 0x0801}, /* 9 */ + {0x003f0909, 0x01010100, 0x01010101, 0x00001f1f, 0x07070707, + 0x07070707, 0x00000f07, 0x3f01}, /* 10 */ + {0x003f0808, 0x00000007, 0x1f000000, 0x00001f1f, 0x06060606, + 0x06060606, 0x00000606, 0x3f00}, /* 11 */ + /* when ddr4 12 map to 10, when ddr3 12 unused */ + {0x003f0909, 0x01010100, 0x01010101, 0x00001f1f, 0x07070707, + 0x07070707, 0x00000f07, 0x3f01}, /* 10 */ + {0x00070706, 0x00000000, 0x1f010000, 0x00001f1f, 0x06060606, + 0x06060606, 0x00000606, 0x3f3f}, /* 13 */ +}; + +#define PMUGRF_BASE_ADDR 0xFF010000 +#define CRU_BASE_ADDR 0xFF2B0000 +#define GRF_BASE_ADDR 0xFF140000 +#define DDRC_BASE_ADDR 0xFF600000 +#define DDR_PHY_BASE_ADDR 0xFF2A0000 +#define SERVER_MSCH0_BASE_ADDR 0xFF530000 +#define DDR_GRF_BASE_ADDR 0xff630000 + +struct dram_info dram_info; + +struct px30_sdram_params sdram_configs[] = { +#include "sdram-px30-ddr3-detect-333.inc" +}; + +struct ddr_phy_skew skew = { +#include "sdram-px30-ddr_skew.inc" +}; + +static void rkclk_ddr_reset(struct dram_info *dram, + u32 ctl_srstn, u32 ctl_psrstn, + u32 phy_srstn, u32 phy_psrstn) +{ + writel(upctl2_srstn_req(ctl_srstn) | upctl2_psrstn_req(ctl_psrstn) | + upctl2_asrstn_req(ctl_srstn), + &dram->cru->softrst_con[1]); + writel(ddrphy_srstn_req(phy_srstn) | ddrphy_psrstn_req(phy_psrstn), + &dram->cru->softrst_con[2]); +} + +static void rkclk_set_dpll(struct dram_info *dram, unsigned int hz) +{ + unsigned int refdiv, postdiv1, postdiv2, fbdiv; + int delay = 1000; + u32 mhz = hz / MHz; + + refdiv = 1; + if (mhz <= 300) { + postdiv1 = 4; + postdiv2 = 2; + } else if (mhz <= 400) { + postdiv1 = 6; + postdiv2 = 1; + } else if (mhz <= 600) { + postdiv1 = 4; + postdiv2 = 1; + } else if (mhz <= 800) { + postdiv1 = 3; + postdiv2 = 1; + } else if (mhz <= 1600) { + postdiv1 = 2; + postdiv2 = 1; + } else { + postdiv1 = 1; + postdiv2 = 1; + } + fbdiv = (mhz * refdiv * postdiv1 * postdiv2) / 24; + + writel(DPLL_MODE(CLOCK_FROM_XIN_OSC), &dram->cru->mode); + + writel(POSTDIV1(postdiv1) | FBDIV(fbdiv), &dram->cru->pll[1].con0); + writel(DSMPD(1) | POSTDIV2(postdiv2) | REFDIV(refdiv), + &dram->cru->pll[1].con1); + + while (delay > 0) { + udelay(1); + if (LOCK(readl(&dram->cru->pll[1].con1))) + break; + delay--; + } + + writel(DPLL_MODE(CLOCK_FROM_PLL), &dram->cru->mode); +} + +static void rkclk_configure_ddr(struct dram_info *dram, + struct px30_sdram_params *sdram_params) +{ + /* for inno ddr phy need 2*freq */ + rkclk_set_dpll(dram, sdram_params->base.ddr_freq * MHz * 2); +} + +/* return ddrconfig value + * (-1), find ddrconfig fail + * other, the ddrconfig value + * only support cs0_row >= cs1_row + */ +static unsigned int calculate_ddrconfig(struct px30_sdram_params *sdram_params) +{ + struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; + u32 bw, die_bw, col, bank; + u32 i, tmp; + u32 ddrconf = -1; + + bw = cap_info->bw; + die_bw = cap_info->dbw; + col = cap_info->col; + bank = cap_info->bk; + + if (sdram_params->base.dramtype == DDR4) { + if (die_bw == 0) + ddrconf = 7 + bw; + else + ddrconf = 12 - bw; + ddrconf = d4_rbc_2_d3_rbc[ddrconf - 7]; + } else { + tmp = ((bank - 2) << 3) | (col + bw - 10); + for (i = 0; i < 7; i++) + if ((ddr_cfg_2_rbc[i] & 0xf) == tmp) { + ddrconf = i; + break; + } + if (i > 6) + printascii("calculate ddrconfig error\n"); + } + + return ddrconf; +} + +/* + * calculate controller dram address map, and setting to register. + * argument sdram_params->ch.ddrconf must be right value before + * call this function. + */ +static void set_ctl_address_map(struct dram_info *dram, + struct px30_sdram_params *sdram_params) +{ + struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; + void __iomem *pctl_base = dram->pctl; + u32 cs_pst, bg, max_row, ddrconf; + u32 i; + + if (sdram_params->base.dramtype == DDR4) + /* + * DDR4 8bit dram BG = 2(4bank groups), + * 16bit dram BG = 1 (2 bank groups) + */ + bg = (cap_info->dbw == 0) ? 2 : 1; + else + bg = 0; + + cs_pst = cap_info->bw + cap_info->col + + bg + cap_info->bk + cap_info->cs0_row; + if (cs_pst >= 32 || cap_info->rank == 1) + writel(0x1f, pctl_base + DDR_PCTL2_ADDRMAP0); + else + writel(cs_pst - 8, pctl_base + DDR_PCTL2_ADDRMAP0); + + ddrconf = cap_info->ddrconfig; + if (sdram_params->base.dramtype == DDR4) { + for (i = 0; i < ARRAY_SIZE(d4_rbc_2_d3_rbc); i++) { + if (d4_rbc_2_d3_rbc[i] == ddrconf) { + ddrconf = 7 + i; + break; + } + } + } + + sdram_copy_to_reg((u32 *)(pctl_base + DDR_PCTL2_ADDRMAP1), + &addrmap[ddrconf][0], 8 * 4); + max_row = cs_pst - 1 - 8 - (addrmap[ddrconf][5] & 0xf); + + if (max_row < 12) + printascii("set addrmap fail\n"); + /* need to disable row ahead of rank by set to 0xf */ + for (i = 17; i > max_row; i--) + clrsetbits_le32(pctl_base + DDR_PCTL2_ADDRMAP6 + + ((i - 12) * 8 / 32) * 4, + 0xf << ((i - 12) * 8 % 32), + 0xf << ((i - 12) * 8 % 32)); + + if ((sdram_params->base.dramtype == LPDDR3 || + sdram_params->base.dramtype == LPDDR2) && + cap_info->row_3_4) + setbits_le32(pctl_base + DDR_PCTL2_ADDRMAP6, 1 << 31); + if (sdram_params->base.dramtype == DDR4 && cap_info->bw != 0x2) + setbits_le32(pctl_base + DDR_PCTL2_PCCFG, 1 << 8); +} + +/* + * rank = 1: cs0 + * rank = 2: cs1 + */ +int read_mr(struct dram_info *dram, u32 rank, u32 mr_num) +{ + void __iomem *ddr_grf_base = dram->ddr_grf; + + pctl_read_mr(dram->pctl, rank, mr_num); + + return (readl(ddr_grf_base + DDR_GRF_STATUS(0)) & 0xff); +} + +#define MIN(a, b) (((a) > (b)) ? (b) : (a)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +static u32 check_rd_gate(struct dram_info *dram) +{ + void __iomem *phy_base = dram->phy; + + u32 max_val = 0; + u32 min_val = 0xff; + u32 gate[4]; + u32 i, bw; + + bw = (readl(PHY_REG(phy_base, 0x0)) >> 4) & 0xf; + switch (bw) { + case 0x1: + bw = 1; + break; + case 0x3: + bw = 2; + break; + case 0xf: + default: + bw = 4; + break; + } + + for (i = 0; i < bw; i++) { + gate[i] = readl(PHY_REG(phy_base, 0xfb + i)); + max_val = MAX(max_val, gate[i]); + min_val = MIN(min_val, gate[i]); + } + + if (max_val > 0x80 || min_val < 0x20) + return -1; + else + return 0; +} + +static int data_training(struct dram_info *dram, u32 cs, u32 dramtype) +{ + void __iomem *pctl_base = dram->pctl; + u32 dis_auto_zq = 0; + u32 pwrctl; + u32 ret; + + /* disable auto low-power */ + pwrctl = readl(pctl_base + DDR_PCTL2_PWRCTL); + writel(0, pctl_base + DDR_PCTL2_PWRCTL); + + dis_auto_zq = pctl_dis_zqcs_aref(dram->pctl); + + ret = phy_data_training(dram->phy, cs, dramtype); + + pctl_rest_zqcs_aref(dram->pctl, dis_auto_zq); + + /* restore auto low-power */ + writel(pwrctl, pctl_base + DDR_PCTL2_PWRCTL); + + return ret; +} + +static void dram_set_bw(struct dram_info *dram, u32 bw) +{ + phy_dram_set_bw(dram->phy, bw); +} + +static void set_ddrconfig(struct dram_info *dram, u32 ddrconfig) +{ + writel(ddrconfig | (ddrconfig << 8), &dram->msch->deviceconf); + rk_clrsetreg(&dram->grf->soc_noc_con[1], 0x3 << 14, 0 << 14); +} + +static void sdram_msch_config(struct msch_regs *msch, + struct sdram_msch_timings *noc_timings, + struct sdram_cap_info *cap_info, + struct sdram_base_params *base) +{ + u64 cs_cap[2]; + + cs_cap[0] = sdram_get_cs_cap(cap_info, 0, base->dramtype); + cs_cap[1] = sdram_get_cs_cap(cap_info, 1, base->dramtype); + writel(((((cs_cap[1] >> 20) / 64) & 0xff) << 8) | + (((cs_cap[0] >> 20) / 64) & 0xff), + &msch->devicesize); + + writel(noc_timings->ddrtiminga0.d32, + &msch->ddrtiminga0); + writel(noc_timings->ddrtimingb0.d32, + &msch->ddrtimingb0); + writel(noc_timings->ddrtimingc0.d32, + &msch->ddrtimingc0); + writel(noc_timings->devtodev0.d32, + &msch->devtodev0); + writel(noc_timings->ddrmode.d32, &msch->ddrmode); + writel(noc_timings->ddr4timing.d32, + &msch->ddr4timing); + writel(noc_timings->agingx0, &msch->agingx0); + writel(noc_timings->agingx0, &msch->aging0); + writel(noc_timings->agingx0, &msch->aging1); + writel(noc_timings->agingx0, &msch->aging2); + writel(noc_timings->agingx0, &msch->aging3); +} + +static void dram_all_config(struct dram_info *dram, + struct px30_sdram_params *sdram_params) +{ + struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; + u32 sys_reg2 = 0; + u32 sys_reg3 = 0; + + set_ddrconfig(dram, cap_info->ddrconfig); + sdram_org_config(cap_info, &sdram_params->base, &sys_reg2, + &sys_reg3, 0); + writel(sys_reg2, &dram->pmugrf->os_reg[2]); + writel(sys_reg3, &dram->pmugrf->os_reg[3]); + sdram_msch_config(dram->msch, &sdram_params->ch.noc_timings, cap_info, + &sdram_params->base); +} + +static void enable_low_power(struct dram_info *dram, + struct px30_sdram_params *sdram_params) +{ + void __iomem *pctl_base = dram->pctl; + void __iomem *phy_base = dram->phy; + void __iomem *ddr_grf_base = dram->ddr_grf; + u32 grf_lp_con; + + /* + * bit0: grf_upctl_axi_cg_en = 1 enable upctl2 axi clk auto gating + * bit1: grf_upctl_apb_cg_en = 1 ungated axi,core clk for apb access + * bit2: grf_upctl_core_cg_en = 1 enable upctl2 core clk auto gating + * bit3: grf_selfref_type2_en = 0 disable core clk gating when type2 sr + * bit4: grf_upctl_syscreq_cg_en = 1 + * ungating coreclk when c_sysreq assert + * bit8-11: grf_auto_sr_dly = 6 + */ + writel(0x1f1f0617, &dram->ddr_grf->ddr_grf_con[1]); + + if (sdram_params->base.dramtype == DDR4) + grf_lp_con = (0x7 << 16) | (1 << 1); + else if (sdram_params->base.dramtype == DDR3) + grf_lp_con = (0x7 << 16) | (1 << 0); + else + grf_lp_con = (0x7 << 16) | (1 << 2); + + /* en lpckdis_en */ + grf_lp_con = grf_lp_con | (0x1 << (9 + 16)) | (0x1 << 9); + writel(grf_lp_con, ddr_grf_base + DDR_GRF_LP_CON); + + /* off digit module clock when enter power down */ + setbits_le32(PHY_REG(phy_base, 7), 1 << 7); + + /* enable sr, pd */ + if (PD_IDLE == 0) + clrbits_le32(pctl_base + DDR_PCTL2_PWRCTL, (1 << 1)); + else + setbits_le32(pctl_base + DDR_PCTL2_PWRCTL, (1 << 1)); + if (SR_IDLE == 0) + clrbits_le32(pctl_base + DDR_PCTL2_PWRCTL, 1); + else + setbits_le32(pctl_base + DDR_PCTL2_PWRCTL, 1); + setbits_le32(pctl_base + DDR_PCTL2_PWRCTL, (1 << 3)); +} + +/* + * pre_init: 0: pre init for dram cap detect + * 1: detect correct cap(except cs1 row)info, than reinit + * 2: after reinit, we detect cs1_row, if cs1_row not equal + * to cs0_row and cs is in middle on ddrconf map, we need + * to reinit dram, than set the correct ddrconf. + */ +static int sdram_init_(struct dram_info *dram, + struct px30_sdram_params *sdram_params, u32 pre_init) +{ + struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; + void __iomem *pctl_base = dram->pctl; + + rkclk_ddr_reset(dram, 1, 1, 1, 1); + udelay(10); + /* + * dereset ddr phy psrstn to config pll, + * if using phy pll psrstn must be dereset + * before config pll + */ + rkclk_ddr_reset(dram, 1, 1, 1, 0); + rkclk_configure_ddr(dram, sdram_params); + + /* release phy srst to provide clk to ctrl */ + rkclk_ddr_reset(dram, 1, 1, 0, 0); + udelay(10); + phy_soft_reset(dram->phy); + /* release ctrl presetn, and config ctl registers */ + rkclk_ddr_reset(dram, 1, 0, 0, 0); + pctl_cfg(dram->pctl, &sdram_params->pctl_regs, SR_IDLE, PD_IDLE); + cap_info->ddrconfig = calculate_ddrconfig(sdram_params); + set_ctl_address_map(dram, sdram_params); + phy_cfg(dram->phy, &sdram_params->phy_regs, sdram_params->skew, + &sdram_params->base, cap_info->bw); + + /* enable dfi_init_start to init phy after ctl srstn deassert */ + setbits_le32(pctl_base + DDR_PCTL2_DFIMISC, (1 << 5) | (1 << 4)); + + rkclk_ddr_reset(dram, 0, 0, 0, 0); + /* wait for dfi_init_done and dram init complete */ + while ((readl(pctl_base + DDR_PCTL2_STAT) & 0x7) == 0) + continue; + + if (sdram_params->base.dramtype == LPDDR3) + pctl_write_mr(dram->pctl, 3, 11, 3, LPDDR3); + + /* do ddr gate training */ +redo_cs0_training: + if (data_training(dram, 0, sdram_params->base.dramtype) != 0) { + if (pre_init != 0) + printascii("DTT cs0 error\n"); + return -1; + } + if (check_rd_gate(dram)) { + printascii("re training cs0"); + goto redo_cs0_training; + } + + if (sdram_params->base.dramtype == LPDDR3) { + if ((read_mr(dram, 1, 8) & 0x3) != 0x3) + return -1; + } else if (sdram_params->base.dramtype == LPDDR2) { + if ((read_mr(dram, 1, 8) & 0x3) != 0x0) + return -1; + } + /* for px30: when 2cs, both 2 cs should be training */ + if (pre_init != 0 && cap_info->rank == 2) { +redo_cs1_training: + if (data_training(dram, 1, sdram_params->base.dramtype) != 0) { + printascii("DTT cs1 error\n"); + return -1; + } + if (check_rd_gate(dram)) { + printascii("re training cs1"); + goto redo_cs1_training; + } + } + + if (sdram_params->base.dramtype == DDR4) + pctl_write_vrefdq(dram->pctl, 0x3, 5670, + sdram_params->base.dramtype); + + dram_all_config(dram, sdram_params); + enable_low_power(dram, sdram_params); + + return 0; +} + +static int dram_detect_cap(struct dram_info *dram, + struct px30_sdram_params *sdram_params, + unsigned char channel) +{ + struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; + + /* + * for ddr3: ddrconf = 3 + * for ddr4: ddrconf = 12 + * for lpddr3: ddrconf = 3 + * default bw = 1 + */ + u32 bk, bktmp; + u32 col, coltmp; + u32 rowtmp; + u32 cs; + u32 bw = 1; + u32 dram_type = sdram_params->base.dramtype; + + if (dram_type != DDR4) { + /* detect col and bk for ddr3/lpddr3 */ + coltmp = 12; + bktmp = 3; + if (dram_type == LPDDR2) + rowtmp = 15; + else + rowtmp = 16; + + if (sdram_detect_col(cap_info, coltmp) != 0) + goto cap_err; + sdram_detect_bank(cap_info, coltmp, bktmp); + sdram_detect_dbw(cap_info, dram_type); + } else { + /* detect bg for ddr4 */ + coltmp = 10; + bktmp = 4; + rowtmp = 17; + + col = 10; + bk = 2; + cap_info->col = col; + cap_info->bk = bk; + sdram_detect_bg(cap_info, coltmp); + } + + /* detect row */ + if (sdram_detect_row(cap_info, coltmp, bktmp, rowtmp) != 0) + goto cap_err; + + /* detect row_3_4 */ + sdram_detect_row_3_4(cap_info, coltmp, bktmp); + + /* bw and cs detect using data training */ + if (data_training(dram, 1, dram_type) == 0) + cs = 1; + else + cs = 0; + cap_info->rank = cs + 1; + + dram_set_bw(dram, 2); + if (data_training(dram, 0, dram_type) == 0) + bw = 2; + else + bw = 1; + cap_info->bw = bw; + + cap_info->cs0_high16bit_row = cap_info->cs0_row; + if (cs) { + cap_info->cs1_row = cap_info->cs0_row; + cap_info->cs1_high16bit_row = cap_info->cs0_row; + } else { + cap_info->cs1_row = 0; + cap_info->cs1_high16bit_row = 0; + } + + return 0; +cap_err: + return -1; +} + +/* return: 0 = success, other = fail */ +static int sdram_init_detect(struct dram_info *dram, + struct px30_sdram_params *sdram_params) +{ + struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; + u32 ret; + u32 sys_reg = 0; + u32 sys_reg3 = 0; + + if (sdram_init_(dram, sdram_params, 0) != 0) + return -1; + + if (dram_detect_cap(dram, sdram_params, 0) != 0) + return -1; + + /* modify bw, cs related timing */ + pctl_remodify_sdram_params(&sdram_params->pctl_regs, cap_info, + sdram_params->base.dramtype); + /* reinit sdram by real dram cap */ + ret = sdram_init_(dram, sdram_params, 1); + if (ret != 0) + goto out; + + /* redetect cs1 row */ + sdram_detect_cs1_row(cap_info, sdram_params->base.dramtype); + if (cap_info->cs1_row) { + sys_reg = readl(&dram->pmugrf->os_reg[2]); + sys_reg3 = readl(&dram->pmugrf->os_reg[3]); + SYS_REG_ENC_CS1_ROW(cap_info->cs1_row, + sys_reg, sys_reg3, 0); + writel(sys_reg, &dram->pmugrf->os_reg[2]); + writel(sys_reg3, &dram->pmugrf->os_reg[3]); + } + + ret = sdram_detect_high_row(cap_info); + +out: + return ret; +} + +struct px30_sdram_params + *get_default_sdram_config(void) +{ + sdram_configs[0].skew = &skew; + + return &sdram_configs[0]; +} + +/* return: 0 = success, other = fail */ +int sdram_init(void) +{ + struct px30_sdram_params *sdram_params; + int ret = 0; + + dram_info.phy = (void *)DDR_PHY_BASE_ADDR; + dram_info.pctl = (void *)DDRC_BASE_ADDR; + dram_info.grf = (void *)GRF_BASE_ADDR; + dram_info.cru = (void *)CRU_BASE_ADDR; + dram_info.msch = (void *)SERVER_MSCH0_BASE_ADDR; + dram_info.ddr_grf = (void *)DDR_GRF_BASE_ADDR; + dram_info.pmugrf = (void *)PMUGRF_BASE_ADDR; + + sdram_params = get_default_sdram_config(); + ret = sdram_init_detect(&dram_info, sdram_params); + + if (ret) + goto error; + + sdram_print_ddr_info(&sdram_params->ch.cap_info, &sdram_params->base); + + printascii("out\n"); + return ret; +error: + return (-1); +} +#else + +static int px30_dmc_probe(struct udevice *dev) +{ + struct dram_info *priv = dev_get_priv(dev); + + priv->pmugrf = syscon_get_first_range(ROCKCHIP_SYSCON_PMUGRF); + debug("%s: grf=%p\n", __func__, priv->pmugrf); + priv->info.base = CONFIG_SYS_SDRAM_BASE; + priv->info.size = + rockchip_sdram_size((phys_addr_t)&priv->pmugrf->os_reg[2]); + + return 0; +} + +static int px30_dmc_get_info(struct udevice *dev, struct ram_info *info) +{ + struct dram_info *priv = dev_get_priv(dev); + + *info = priv->info; + + return 0; +} + +static struct ram_ops px30_dmc_ops = { + .get_info = px30_dmc_get_info, +}; + +static const struct udevice_id px30_dmc_ids[] = { + { .compatible = "rockchip,px30-dmc" }, + { } +}; + +U_BOOT_DRIVER(dmc_px30) = { + .name = "rockchip_px30_dmc", + .id = UCLASS_RAM, + .of_match = px30_dmc_ids, + .ops = &px30_dmc_ops, + .probe = px30_dmc_probe, + .priv_auto_alloc_size = sizeof(struct dram_info), +}; +#endif /* CONFIG_TPL_BUILD */ diff --git a/drivers/ram/rockchip/sdram_rk3128.c b/drivers/ram/rockchip/sdram_rk3128.c index bfabc22a7d..8486653c6f 100644 --- a/drivers/ram/rockchip/sdram_rk3128.c +++ b/drivers/ram/rockchip/sdram_rk3128.c @@ -9,7 +9,7 @@ #include <syscon.h> #include <asm/arch-rockchip/clock.h> #include <asm/arch-rockchip/grf_rk3128.h> -#include <asm/arch-rockchip/sdram_common.h> +#include <asm/arch-rockchip/sdram.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 00e52ec949..d3e4316ef0 100644 --- a/drivers/ram/rockchip/sdram_rk3188.c +++ b/drivers/ram/rockchip/sdram_rk3188.c @@ -21,7 +21,7 @@ #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 <asm/arch-rockchip/sdram_rk3288.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 94893e17cf..223f048161 100644 --- a/drivers/ram/rockchip/sdram_rk322x.c +++ b/drivers/ram/rockchip/sdram_rk322x.c @@ -17,7 +17,7 @@ #include <asm/arch-rockchip/hardware.h> #include <asm/arch-rockchip/sdram_rk322x.h> #include <asm/arch-rockchip/uart.h> -#include <asm/arch-rockchip/sdram_common.h> +#include <asm/arch-rockchip/sdram.h> #include <asm/types.h> #include <linux/err.h> diff --git a/drivers/ram/rockchip/sdram_rk3288.c b/drivers/ram/rockchip/sdram_rk3288.c index 5775254007..690751d074 100644 --- a/drivers/ram/rockchip/sdram_rk3288.c +++ b/drivers/ram/rockchip/sdram_rk3288.c @@ -21,7 +21,7 @@ #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 <asm/arch-rockchip/sdram_rk3288.h> #include <linux/err.h> #include <power/regulator.h> #include <power/rk8xx_pmic.h> diff --git a/drivers/ram/rockchip/sdram_rk3308.c b/drivers/ram/rockchip/sdram_rk3308.c new file mode 100644 index 0000000000..310df79123 --- /dev/null +++ b/drivers/ram/rockchip/sdram_rk3308.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * (C) Copyright 2019 Rockchip Electronics Co., Ltd. + */ + +#include <common.h> +#include <dm.h> +#include <ram.h> +#include <syscon.h> +#include <asm/arch/grf_rk3308.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/sdram.h> + +struct dram_info { + struct ram_info info; + struct rk3308_grf *grf; +}; + +static int rk3308_dmc_probe(struct udevice *dev) +{ + struct dram_info *priv = dev_get_priv(dev); + + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + priv->info.base = CONFIG_SYS_SDRAM_BASE; + priv->info.size = rockchip_sdram_size((phys_addr_t)&priv->grf->os_reg2); + + return 0; +} + +static int rk3308_dmc_get_info(struct udevice *dev, struct ram_info *info) +{ + struct dram_info *priv = dev_get_priv(dev); + + *info = priv->info; + + return 0; +} + +static struct ram_ops rk3308_dmc_ops = { + .get_info = rk3308_dmc_get_info, +}; + +static const struct udevice_id rk3308_dmc_ids[] = { + { .compatible = "rockchip,rk3308-dmc" }, + { } +}; + +U_BOOT_DRIVER(dmc_rk3308) = { + .name = "rockchip_rk3308_dmc", + .id = UCLASS_RAM, + .of_match = rk3308_dmc_ids, + .ops = &rk3308_dmc_ops, + .probe = rk3308_dmc_probe, + .priv_auto_alloc_size = sizeof(struct dram_info), +}; diff --git a/drivers/ram/rockchip/sdram_rk3328.c b/drivers/ram/rockchip/sdram_rk3328.c index e84c9be6a2..69521cef69 100644 --- a/drivers/ram/rockchip/sdram_rk3328.c +++ b/drivers/ram/rockchip/sdram_rk3328.c @@ -14,17 +14,17 @@ #include <asm/arch-rockchip/clock.h> #include <asm/arch-rockchip/cru_rk3328.h> #include <asm/arch-rockchip/grf_rk3328.h> -#include <asm/arch-rockchip/sdram_common.h> +#include <asm/arch-rockchip/sdram.h> #include <asm/arch-rockchip/sdram_rk3328.h> #include <asm/arch-rockchip/uart.h> struct dram_info { #ifdef CONFIG_TPL_BUILD - struct rk3328_ddr_pctl_regs *pctl; - struct rk3328_ddr_phy_regs *phy; + struct ddr_pctl_regs *pctl; + struct ddr_phy_regs *phy; struct clk ddr_clk; struct rk3328_cru *cru; - struct rk3328_msch_regs *msch; + struct msch_regs *msch; struct rk3328_ddr_grf_regs *ddr_grf; #endif struct ram_info info; @@ -71,10 +71,11 @@ static void rkclk_ddr_reset(struct dram_info *dram, writel(ddrctrl_asrstn_req(ctl_srstn), &dram->cru->softrst_con[9]); } -static void rkclk_set_dpll(struct dram_info *dram, unsigned int mhz) +static void rkclk_set_dpll(struct dram_info *dram, unsigned int hz) { unsigned int refdiv, postdiv1, postdiv2, fbdiv; int delay = 1000; + u32 mhz = hz / MHZ; refdiv = 1; if (mhz <= 300) { @@ -122,52 +123,7 @@ static void rkclk_configure_ddr(struct dram_info *dram, clrbits_le32(PHY_REG(phy_base, 0xef), 1 << 7); /* for inno ddr phy need 2*freq */ - rkclk_set_dpll(dram, sdram_params->ddr_freq * 2); -} - -static void phy_soft_reset(struct dram_info *dram) -{ - void __iomem *phy_base = dram->phy; - - clrbits_le32(PHY_REG(phy_base, 0), 0x3 << 2); - udelay(1); - setbits_le32(PHY_REG(phy_base, 0), ANALOG_DERESET); - udelay(5); - setbits_le32(PHY_REG(phy_base, 0), DIGITAL_DERESET); - udelay(1); -} - -static int pctl_cfg(struct dram_info *dram, - struct rk3328_sdram_params *sdram_params) -{ - u32 i; - void __iomem *pctl_base = dram->pctl; - - for (i = 0; sdram_params->pctl_regs.pctl[i][0] != 0xFFFFFFFF; i++) { - writel(sdram_params->pctl_regs.pctl[i][1], - pctl_base + sdram_params->pctl_regs.pctl[i][0]); - } - clrsetbits_le32(pctl_base + DDR_PCTL2_PWRTMG, - (0xff << 16) | 0x1f, - ((SR_IDLE & 0xff) << 16) | (PD_IDLE & 0x1f)); - /* - * dfi_lp_en_pd=1,dfi_lp_wakeup_pd=2 - * hw_lp_idle_x32=1 - */ - if (sdram_params->dramtype == LPDDR3) { - setbits_le32(pctl_base + DDR_PCTL2_DFILPCFG0, 1); - clrsetbits_le32(pctl_base + DDR_PCTL2_DFILPCFG0, - 0xf << 4, - 2 << 4); - } - clrsetbits_le32(pctl_base + DDR_PCTL2_HWLPCTL, - 0xfff << 16, - 1 << 16); - /* disable zqcs */ - setbits_le32(pctl_base + DDR_PCTL2_ZQCTL0, 1u << 31); - setbits_le32(pctl_base + 0x2000 + DDR_PCTL2_ZQCTL0, 1u << 31); - - return 0; + rkclk_set_dpll(dram, sdram_params->base.ddr_freq * MHZ * 2); } /* return ddrconfig value @@ -175,62 +131,39 @@ static int pctl_cfg(struct dram_info *dram, * other, the ddrconfig value * only support cs0_row >= cs1_row */ -static unsigned int calculate_ddrconfig(struct rk3328_sdram_params *sdram_params) +static u32 calculate_ddrconfig(struct rk3328_sdram_params *sdram_params) { - static const u16 ddr_cfg_2_rbc[] = { - /*************************** - * [5:4] row(13+n) - * [3] cs(0:0 cs, 1:2 cs) - * [2] bank(0:0bank,1:8bank) - * [1:0] col(11+n) - ****************************/ - /* row, cs, bank, col */ - ((3 << 4) | (0 << 3) | (1 << 2) | 0), - ((3 << 4) | (0 << 3) | (1 << 2) | 1), - ((2 << 4) | (0 << 3) | (1 << 2) | 2), - ((3 << 4) | (0 << 3) | (1 << 2) | 2), - ((2 << 4) | (0 << 3) | (1 << 2) | 3), - ((3 << 4) | (1 << 3) | (1 << 2) | 0), - ((3 << 4) | (1 << 3) | (1 << 2) | 1), - ((2 << 4) | (1 << 3) | (1 << 2) | 2), - ((3 << 4) | (0 << 3) | (0 << 2) | 1), - ((2 << 4) | (0 << 3) | (1 << 2) | 1), - }; - - static const u16 ddr4_cfg_2_rbc[] = { - /*************************** - * [6] cs 0:0cs 1:2 cs - * [5:3] row(13+n) - * [2] cs(0:0 cs, 1:2 cs) - * [1] bw 0: 16bit 1:32bit - * [0] diebw 0:8bit 1:16bit - ***************************/ - /* cs, row, cs, bw, diebw */ - ((0 << 6) | (3 << 3) | (0 << 2) | (1 << 1) | 0), - ((1 << 6) | (2 << 3) | (0 << 2) | (1 << 1) | 0), - ((0 << 6) | (4 << 3) | (0 << 2) | (0 << 1) | 0), - ((1 << 6) | (3 << 3) | (0 << 2) | (0 << 1) | 0), - ((0 << 6) | (4 << 3) | (0 << 2) | (1 << 1) | 1), - ((1 << 6) | (3 << 3) | (0 << 2) | (1 << 1) | 1), - ((1 << 6) | (4 << 3) | (0 << 2) | (0 << 1) | 1), - ((0 << 6) | (2 << 3) | (1 << 2) | (1 << 1) | 0), - ((0 << 6) | (3 << 3) | (1 << 2) | (0 << 1) | 0), - ((0 << 6) | (3 << 3) | (1 << 2) | (1 << 1) | 1), - ((0 << 6) | (4 << 3) | (1 << 2) | (0 << 1) | 1), - }; - + struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; u32 cs, bw, die_bw, col, row, bank; + u32 cs1_row; u32 i, tmp; u32 ddrconf = -1; - cs = sdram_ch.rank; - bw = sdram_ch.bw; - die_bw = sdram_ch.dbw; - col = sdram_ch.col; - row = sdram_ch.cs0_row; - bank = sdram_ch.bk; + cs = cap_info->rank; + bw = cap_info->bw; + die_bw = cap_info->dbw; + col = cap_info->col; + row = cap_info->cs0_row; + cs1_row = cap_info->cs1_row; + bank = cap_info->bk; + + if (sdram_params->base.dramtype == DDR4) { + /* when DDR_TEST, CS always at MSB position for easy test */ + if (cs == 2 && row == cs1_row) { + /* include 2cs cap both 2^n or both (2^n - 2^(n-2)) */ + tmp = ((row - 13) << 3) | (1 << 2) | (bw & 0x2) | + die_bw; + for (i = 17; i < 21; i++) { + if (((tmp & 0x7) == + (ddr4_cfg_2_rbc[i - 10] & 0x7)) && + ((tmp & 0x3c) <= + (ddr4_cfg_2_rbc[i - 10] & 0x3c))) { + ddrconf = i; + goto out; + } + } + } - if (sdram_params->dramtype == DDR4) { tmp = ((cs - 1) << 6) | ((row - 13) << 3) | (bw & 0x2) | die_bw; for (i = 10; i < 17; i++) { if (((tmp & 0x7) == (ddr4_cfg_2_rbc[i - 10] & 0x7)) && @@ -246,6 +179,18 @@ static unsigned int calculate_ddrconfig(struct rk3328_sdram_params *sdram_params goto out; } + /* when DDR_TEST, CS always at MSB position for easy test */ + if (cs == 2 && row == cs1_row) { + /* include 2cs cap both 2^n or both (2^n - 2^(n-2)) */ + for (i = 5; i < 8; i++) { + if ((bw + col - 11) == (ddr_cfg_2_rbc[i] & + 0x3)) { + ddrconf = i; + goto out; + } + } + } + tmp = ((row - 13) << 4) | (1 << 2) | ((bw + col - 11) << 0); for (i = 0; i < 5; i++) if (((tmp & 0xf) == (ddr_cfg_2_rbc[i] & 0xf)) && @@ -257,23 +202,11 @@ static unsigned int calculate_ddrconfig(struct rk3328_sdram_params *sdram_params out: if (ddrconf > 20) - printf("calculate_ddrconfig error\n"); + printf("calculate ddrconfig error\n"); return ddrconf; } -/* n: Unit bytes */ -static void copy_to_reg(u32 *dest, u32 *src, u32 n) -{ - int i; - - for (i = 0; i < n / sizeof(u32); i++) { - writel(*src, dest); - src++; - dest++; - } -} - /******* * calculate controller dram address map, and setting to register. * argument sdram_ch.ddrconf must be right value before @@ -282,273 +215,42 @@ static void copy_to_reg(u32 *dest, u32 *src, u32 n) static void set_ctl_address_map(struct dram_info *dram, struct rk3328_sdram_params *sdram_params) { + struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; void __iomem *pctl_base = dram->pctl; - copy_to_reg((u32 *)(pctl_base + DDR_PCTL2_ADDRMAP0), - &addrmap[sdram_ch.ddrconfig][0], 9 * 4); - if (sdram_params->dramtype == LPDDR3 && sdram_ch.row_3_4) + sdram_copy_to_reg((u32 *)(pctl_base + DDR_PCTL2_ADDRMAP0), + &addrmap[cap_info->ddrconfig][0], 9 * 4); + if (sdram_params->base.dramtype == LPDDR3 && cap_info->row_3_4) setbits_le32(pctl_base + DDR_PCTL2_ADDRMAP6, 1 << 31); - if (sdram_params->dramtype == DDR4 && sdram_ch.bw == 0x1) + if (sdram_params->base.dramtype == DDR4 && cap_info->bw == 0x1) setbits_le32(pctl_base + DDR_PCTL2_PCCFG, 1 << 8); - if (sdram_ch.rank == 1) + if (cap_info->rank == 1) clrsetbits_le32(pctl_base + DDR_PCTL2_ADDRMAP0, 0x1f, 0x1f); } -static void phy_dll_bypass_set(struct dram_info *dram, u32 freq) -{ - u32 tmp; - void __iomem *phy_base = dram->phy; - - setbits_le32(PHY_REG(phy_base, 0x13), 1 << 4); - clrbits_le32(PHY_REG(phy_base, 0x14), 1 << 3); - setbits_le32(PHY_REG(phy_base, 0x26), 1 << 4); - clrbits_le32(PHY_REG(phy_base, 0x27), 1 << 3); - setbits_le32(PHY_REG(phy_base, 0x36), 1 << 4); - clrbits_le32(PHY_REG(phy_base, 0x37), 1 << 3); - setbits_le32(PHY_REG(phy_base, 0x46), 1 << 4); - clrbits_le32(PHY_REG(phy_base, 0x47), 1 << 3); - setbits_le32(PHY_REG(phy_base, 0x56), 1 << 4); - clrbits_le32(PHY_REG(phy_base, 0x57), 1 << 3); - - if (freq <= 400) - /* DLL bypass */ - setbits_le32(PHY_REG(phy_base, 0xa4), 0x1f); - else - clrbits_le32(PHY_REG(phy_base, 0xa4), 0x1f); - if (freq <= 680) - tmp = 2; - else - tmp = 1; - writel(tmp, PHY_REG(phy_base, 0x28)); - writel(tmp, PHY_REG(phy_base, 0x38)); - writel(tmp, PHY_REG(phy_base, 0x48)); - writel(tmp, PHY_REG(phy_base, 0x58)); -} - -static void set_ds_odt(struct dram_info *dram, - struct rk3328_sdram_params *sdram_params) -{ - u32 cmd_drv, clk_drv, dqs_drv, dqs_odt; - void __iomem *phy_base = dram->phy; - - if (sdram_params->dramtype == DDR3) { - cmd_drv = PHY_DDR3_RON_RTT_34ohm; - clk_drv = PHY_DDR3_RON_RTT_45ohm; - dqs_drv = PHY_DDR3_RON_RTT_34ohm; - dqs_odt = PHY_DDR3_RON_RTT_225ohm; - } else { - cmd_drv = PHY_DDR4_LPDDR3_RON_RTT_34ohm; - clk_drv = PHY_DDR4_LPDDR3_RON_RTT_43ohm; - dqs_drv = PHY_DDR4_LPDDR3_RON_RTT_34ohm; - dqs_odt = PHY_DDR4_LPDDR3_RON_RTT_240ohm; - } - /* DS */ - writel(cmd_drv, PHY_REG(phy_base, 0x11)); - clrsetbits_le32(PHY_REG(phy_base, 0x12), 0x1f << 3, cmd_drv << 3); - writel(clk_drv, PHY_REG(phy_base, 0x16)); - writel(clk_drv, PHY_REG(phy_base, 0x18)); - writel(dqs_drv, PHY_REG(phy_base, 0x20)); - writel(dqs_drv, PHY_REG(phy_base, 0x2f)); - writel(dqs_drv, PHY_REG(phy_base, 0x30)); - writel(dqs_drv, PHY_REG(phy_base, 0x3f)); - writel(dqs_drv, PHY_REG(phy_base, 0x40)); - writel(dqs_drv, PHY_REG(phy_base, 0x4f)); - writel(dqs_drv, PHY_REG(phy_base, 0x50)); - writel(dqs_drv, PHY_REG(phy_base, 0x5f)); - /* ODT */ - writel(dqs_odt, PHY_REG(phy_base, 0x21)); - writel(dqs_odt, PHY_REG(phy_base, 0x2e)); - writel(dqs_odt, PHY_REG(phy_base, 0x31)); - writel(dqs_odt, PHY_REG(phy_base, 0x3e)); - writel(dqs_odt, PHY_REG(phy_base, 0x41)); - writel(dqs_odt, PHY_REG(phy_base, 0x4e)); - writel(dqs_odt, PHY_REG(phy_base, 0x51)); - writel(dqs_odt, PHY_REG(phy_base, 0x5e)); -} - -static void phy_cfg(struct dram_info *dram, - struct rk3328_sdram_params *sdram_params) -{ - u32 i; - void __iomem *phy_base = dram->phy; - - phy_dll_bypass_set(dram, sdram_params->ddr_freq); - for (i = 0; sdram_params->phy_regs.phy[i][0] != 0xFFFFFFFF; i++) { - writel(sdram_params->phy_regs.phy[i][1], - phy_base + sdram_params->phy_regs.phy[i][0]); - } - if (sdram_ch.bw == 2) { - clrsetbits_le32(PHY_REG(phy_base, 0), 0xf << 4, 0xf << 4); - } else { - clrsetbits_le32(PHY_REG(phy_base, 0), 0xf << 4, 3 << 4); - /* disable DQS2,DQS3 tx dll for saving power */ - clrbits_le32(PHY_REG(phy_base, 0x46), 1 << 3); - clrbits_le32(PHY_REG(phy_base, 0x56), 1 << 3); - } - set_ds_odt(dram, sdram_params); - /* deskew */ - setbits_le32(PHY_REG(phy_base, 2), 8); - copy_to_reg(PHY_REG(phy_base, 0xb0), - &sdram_params->skew.a0_a1_skew[0], 15 * 4); - copy_to_reg(PHY_REG(phy_base, 0x70), - &sdram_params->skew.cs0_dm0_skew[0], 44 * 4); - copy_to_reg(PHY_REG(phy_base, 0xc0), - &sdram_params->skew.cs1_dm0_skew[0], 44 * 4); -} - -static int update_refresh_reg(struct dram_info *dram) -{ - void __iomem *pctl_base = dram->pctl; - u32 ret; - - ret = readl(pctl_base + DDR_PCTL2_RFSHCTL3) ^ (1 << 1); - writel(ret, pctl_base + DDR_PCTL2_RFSHCTL3); - - return 0; -} - static int data_training(struct dram_info *dram, u32 cs, u32 dramtype) { - u32 ret; - u32 dis_auto_zq = 0; - void __iomem *pctl_base = dram->pctl; - void __iomem *phy_base = dram->phy; - - /* disable zqcs */ - if (!(readl(pctl_base + DDR_PCTL2_ZQCTL0) & - (1ul << 31))) { - dis_auto_zq = 1; - setbits_le32(pctl_base + DDR_PCTL2_ZQCTL0, 1 << 31); - } - /* disable auto refresh */ - setbits_le32(pctl_base + DDR_PCTL2_RFSHCTL3, 1); - update_refresh_reg(dram); - - if (dramtype == DDR4) { - clrsetbits_le32(PHY_REG(phy_base, 0x29), 0x3, 0); - clrsetbits_le32(PHY_REG(phy_base, 0x39), 0x3, 0); - clrsetbits_le32(PHY_REG(phy_base, 0x49), 0x3, 0); - clrsetbits_le32(PHY_REG(phy_base, 0x59), 0x3, 0); - } - /* choose training cs */ - clrsetbits_le32(PHY_REG(phy_base, 2), 0x33, (0x20 >> cs)); - /* enable gate training */ - clrsetbits_le32(PHY_REG(phy_base, 2), 0x33, (0x20 >> cs) | 1); - udelay(50); - ret = readl(PHY_REG(phy_base, 0xff)); - /* disable gate training */ - clrsetbits_le32(PHY_REG(phy_base, 2), 0x33, (0x20 >> cs) | 0); - /* restore zqcs */ - if (dis_auto_zq) - clrbits_le32(pctl_base + DDR_PCTL2_ZQCTL0, 1 << 31); - /* restore auto refresh */ - clrbits_le32(pctl_base + DDR_PCTL2_RFSHCTL3, 1); - update_refresh_reg(dram); - - if (dramtype == DDR4) { - clrsetbits_le32(PHY_REG(phy_base, 0x29), 0x3, 0x2); - clrsetbits_le32(PHY_REG(phy_base, 0x39), 0x3, 0x2); - clrsetbits_le32(PHY_REG(phy_base, 0x49), 0x3, 0x2); - clrsetbits_le32(PHY_REG(phy_base, 0x59), 0x3, 0x2); - } - - if (ret & 0x10) { - ret = -1; - } else { - ret = (ret & 0xf) ^ (readl(PHY_REG(phy_base, 0)) >> 4); - ret = (ret == 0) ? 0 : -1; - } - return ret; -} - -/* rank = 1: cs0 - * rank = 2: cs1 - * rank = 3: cs0 & cs1 - * note: be careful of keep mr original val - */ -static int write_mr(struct dram_info *dram, u32 rank, u32 mr_num, u32 arg, - u32 dramtype) -{ void __iomem *pctl_base = dram->pctl; - - while (readl(pctl_base + DDR_PCTL2_MRSTAT) & MR_WR_BUSY) - continue; - if (dramtype == DDR3 || dramtype == DDR4) { - writel((mr_num << 12) | (rank << 4) | (0 << 0), - pctl_base + DDR_PCTL2_MRCTRL0); - writel(arg, pctl_base + DDR_PCTL2_MRCTRL1); - } else { - writel((rank << 4) | (0 << 0), - pctl_base + DDR_PCTL2_MRCTRL0); - writel((mr_num << 8) | (arg & 0xff), - pctl_base + DDR_PCTL2_MRCTRL1); - } - - setbits_le32(pctl_base + DDR_PCTL2_MRCTRL0, 1u << 31); - while (readl(pctl_base + DDR_PCTL2_MRCTRL0) & (1u << 31)) - continue; - while (readl(pctl_base + DDR_PCTL2_MRSTAT) & MR_WR_BUSY) - continue; - - return 0; -} - -/* - * rank : 1:cs0, 2:cs1, 3:cs0&cs1 - * vrefrate: 4500: 45%, - */ -static int write_vrefdq(struct dram_info *dram, u32 rank, u32 vrefrate, - u32 dramtype) -{ - u32 tccd_l, value; u32 dis_auto_zq = 0; - void __iomem *pctl_base = dram->pctl; + u32 pwrctl; + u32 ret; - if (dramtype != DDR4 || vrefrate < 4500 || vrefrate > 9200) - return -1; + /* disable auto low-power */ + pwrctl = readl(pctl_base + DDR_PCTL2_PWRCTL); + writel(0, pctl_base + DDR_PCTL2_PWRCTL); - tccd_l = (readl(pctl_base + DDR_PCTL2_DRAMTMG4) >> 16) & 0xf; - tccd_l = (tccd_l - 4) << 10; + dis_auto_zq = pctl_dis_zqcs_aref(dram->pctl); - if (vrefrate > 7500) { - /* range 1 */ - value = ((vrefrate - 6000) / 65) | tccd_l; - } else { - /* range 2 */ - value = ((vrefrate - 4500) / 65) | tccd_l | (1 << 6); - } + ret = phy_data_training(dram->phy, cs, dramtype); - /* disable zqcs */ - if (!(readl(pctl_base + DDR_PCTL2_ZQCTL0) & - (1ul << 31))) { - dis_auto_zq = 1; - setbits_le32(pctl_base + DDR_PCTL2_ZQCTL0, 1 << 31); - } - /* disable auto refresh */ - setbits_le32(pctl_base + DDR_PCTL2_RFSHCTL3, 1); - update_refresh_reg(dram); - - /* enable vrefdq calibratin */ - write_mr(dram, rank, 6, value | (1 << 7), dramtype); - udelay(1);/* tvrefdqe */ - /* write vrefdq value */ - write_mr(dram, rank, 6, value | (1 << 7), dramtype); - udelay(1);/* tvref_time */ - write_mr(dram, rank, 6, value | (0 << 7), dramtype); - udelay(1);/* tvrefdqx */ - - /* restore zqcs */ - if (dis_auto_zq) - clrbits_le32(pctl_base + DDR_PCTL2_ZQCTL0, 1 << 31); - /* restore auto refresh */ - clrbits_le32(pctl_base + DDR_PCTL2_RFSHCTL3, 1); - update_refresh_reg(dram); + pctl_rest_zqcs_aref(dram->pctl, dis_auto_zq); - return 0; -} + /* restore auto low-power */ + writel(pwrctl, pctl_base + DDR_PCTL2_PWRCTL); -#define _MAX_(x, y) ((x) > (y) ? (x) : (y)) + return ret; +} static void rx_deskew_switch_adjust(struct dram_info *dram) { @@ -557,7 +259,7 @@ static void rx_deskew_switch_adjust(struct dram_info *dram) void __iomem *phy_base = dram->phy; for (i = 0; i < 4; i++) - gate_val = _MAX_(readl(PHY_REG(phy_base, 0xfb + i)), gate_val); + gate_val = MAX(readl(PHY_REG(phy_base, 0xfb + i)), gate_val); deskew_val = (gate_val >> 3) + 1; deskew_val = (deskew_val > 0x1f) ? 0x1f : deskew_val; @@ -566,8 +268,6 @@ static void rx_deskew_switch_adjust(struct dram_info *dram) (deskew_val & 0x1c) << 2); } -#undef _MAX_ - static void tx_deskew_switch_adjust(struct dram_info *dram) { void __iomem *phy_base = dram->phy; @@ -580,40 +280,39 @@ static void set_ddrconfig(struct dram_info *dram, u32 ddrconfig) writel(ddrconfig, &dram->msch->ddrconf); } +static void sdram_msch_config(struct msch_regs *msch, + struct sdram_msch_timings *noc_timings) +{ + writel(noc_timings->ddrtiming.d32, &msch->ddrtiming); + + writel(noc_timings->ddrmode.d32, &msch->ddrmode); + writel(noc_timings->readlatency, &msch->readlatency); + + writel(noc_timings->activate.d32, &msch->activate); + writel(noc_timings->devtodev.d32, &msch->devtodev); + writel(noc_timings->ddr4timing.d32, &msch->ddr4_timing); + writel(noc_timings->agingx0, &msch->aging0); + writel(noc_timings->agingx0, &msch->aging1); + writel(noc_timings->agingx0, &msch->aging2); + writel(noc_timings->agingx0, &msch->aging3); + writel(noc_timings->agingx0, &msch->aging4); + writel(noc_timings->agingx0, &msch->aging5); +} + static void dram_all_config(struct dram_info *dram, struct rk3328_sdram_params *sdram_params) { - u32 sys_reg = 0, tmp = 0; - - set_ddrconfig(dram, sdram_ch.ddrconfig); - - sys_reg |= SYS_REG_ENC_DDRTYPE(sdram_params->dramtype); - sys_reg |= SYS_REG_ENC_ROW_3_4(sdram_ch.row_3_4, 0); - sys_reg |= SYS_REG_ENC_RANK(sdram_ch.rank, 0); - sys_reg |= SYS_REG_ENC_COL(sdram_ch.col, 0); - sys_reg |= SYS_REG_ENC_BK(sdram_ch.bk, 0); - SYS_REG_ENC_CS0_ROW(sdram_ch.cs0_row, sys_reg, tmp, 0); - if (sdram_ch.cs1_row) - SYS_REG_ENC_CS1_ROW(sdram_ch.cs1_row, sys_reg, tmp, 0); - sys_reg |= SYS_REG_ENC_BW(sdram_ch.bw, 0); - sys_reg |= SYS_REG_ENC_DBW(sdram_ch.dbw, 0); - - writel(sys_reg, &dram->grf->os_reg[2]); - - writel(sdram_ch.noc_timings.ddrtiming.d32, &dram->msch->ddrtiming); - - writel(sdram_ch.noc_timings.ddrmode.d32, &dram->msch->ddrmode); - writel(sdram_ch.noc_timings.readlatency, &dram->msch->readlatency); - - writel(sdram_ch.noc_timings.activate.d32, &dram->msch->activate); - writel(sdram_ch.noc_timings.devtodev.d32, &dram->msch->devtodev); - writel(sdram_ch.noc_timings.ddr4timing.d32, &dram->msch->ddr4_timing); - writel(sdram_ch.noc_timings.agingx0, &dram->msch->aging0); - writel(sdram_ch.noc_timings.agingx0, &dram->msch->aging1); - writel(sdram_ch.noc_timings.agingx0, &dram->msch->aging2); - writel(sdram_ch.noc_timings.agingx0, &dram->msch->aging3); - writel(sdram_ch.noc_timings.agingx0, &dram->msch->aging4); - writel(sdram_ch.noc_timings.agingx0, &dram->msch->aging5); + struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; + u32 sys_reg2 = 0; + u32 sys_reg3 = 0; + + set_ddrconfig(dram, cap_info->ddrconfig); + sdram_org_config(cap_info, &sdram_params->base, &sys_reg2, + &sys_reg3, 0); + writel(sys_reg2, &dram->grf->os_reg[2]); + writel(sys_reg3, &dram->grf->os_reg[3]); + + sdram_msch_config(dram->msch, &sdram_ch.noc_timings); } static void enable_low_power(struct dram_info *dram, @@ -641,6 +340,7 @@ static void enable_low_power(struct dram_info *dram, static int sdram_init(struct dram_info *dram, struct rk3328_sdram_params *sdram_params, u32 pre_init) { + struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; void __iomem *pctl_base = dram->pctl; rkclk_ddr_reset(dram, 1, 1, 1, 1); @@ -652,30 +352,18 @@ static int sdram_init(struct dram_info *dram, */ rkclk_ddr_reset(dram, 1, 1, 1, 0); rkclk_configure_ddr(dram, sdram_params); - if (pre_init == 0) { - switch (sdram_params->dramtype) { - case DDR3: - printf("DDR3\n"); - break; - case DDR4: - printf("DDR4\n"); - break; - case LPDDR3: - default: - printf("LPDDR3\n"); - break; - } - } + /* release phy srst to provide clk to ctrl */ rkclk_ddr_reset(dram, 1, 1, 0, 0); udelay(10); - phy_soft_reset(dram); + phy_soft_reset(dram->phy); /* release ctrl presetn, and config ctl registers */ rkclk_ddr_reset(dram, 1, 0, 0, 0); - pctl_cfg(dram, sdram_params); - sdram_ch.ddrconfig = calculate_ddrconfig(sdram_params); + pctl_cfg(dram->pctl, &sdram_params->pctl_regs, SR_IDLE, PD_IDLE); + cap_info->ddrconfig = calculate_ddrconfig(sdram_params); set_ctl_address_map(dram, sdram_params); - phy_cfg(dram, sdram_params); + phy_cfg(dram->phy, &sdram_params->phy_regs, &sdram_params->skew, + &sdram_params->base, cap_info->bw); /* enable dfi_init_start to init phy after ctl srstn deassert */ setbits_le32(pctl_base + DDR_PCTL2_DFIMISC, (1 << 5) | (1 << 4)); @@ -685,13 +373,18 @@ static int sdram_init(struct dram_info *dram, continue; /* do ddr gate training */ - if (data_training(dram, 0, sdram_params->dramtype) != 0) { + if (data_training(dram, 0, sdram_params->base.dramtype) != 0) { + printf("data training error\n"); + return -1; + } + if (data_training(dram, 1, sdram_params->base.dramtype) != 0) { printf("data training error\n"); return -1; } - if (sdram_params->dramtype == DDR4) - write_vrefdq(dram, 0x3, 5670, sdram_params->dramtype); + if (sdram_params->base.dramtype == DDR4) + pctl_write_vrefdq(dram->pctl, 0x3, 5670, + sdram_params->base.dramtype); if (pre_init == 0) { rx_deskew_switch_adjust(dram); @@ -708,7 +401,7 @@ static u64 dram_detect_cap(struct dram_info *dram, struct rk3328_sdram_params *sdram_params, unsigned char channel) { - void __iomem *pctl_base = dram->pctl; + struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; /* * for ddr3: ddrconf = 3 @@ -718,14 +411,10 @@ static u64 dram_detect_cap(struct dram_info *dram, */ u32 bk, bktmp; u32 col, coltmp; - u32 row, rowtmp, row_3_4; - void __iomem *test_addr, *test_addr1; - u32 dbw; + u32 rowtmp; u32 cs; u32 bw = 1; - u64 cap = 0; - u32 dram_type = sdram_params->dramtype; - u32 pwrctl; + u32 dram_type = sdram_params->base.dramtype; if (dram_type != DDR4) { /* detect col and bk for ddr3/lpddr3 */ @@ -733,33 +422,10 @@ static u64 dram_detect_cap(struct dram_info *dram, bktmp = 3; rowtmp = 16; - for (col = coltmp; col >= 9; col -= 1) { - writel(0, SDRAM_ADDR); - test_addr = (void __iomem *)(SDRAM_ADDR + - (1ul << (col + bw - 1ul))); - writel(PATTERN, test_addr); - if ((readl(test_addr) == PATTERN) && - (readl(SDRAM_ADDR) == 0)) - break; - } - if (col == 8) { - printf("col error\n"); + if (sdram_detect_col(cap_info, coltmp) != 0) goto cap_err; - } - - test_addr = (void __iomem *)(SDRAM_ADDR + - (1ul << (coltmp + bktmp + bw - 1ul))); - writel(0, SDRAM_ADDR); - writel(PATTERN, test_addr); - if ((readl(test_addr) == PATTERN) && - (readl(SDRAM_ADDR) == 0)) - bk = 3; - else - bk = 2; - if (dram_type == LPDDR3) - dbw = 2; - else - dbw = 1; + sdram_detect_bank(cap_info, coltmp, bktmp); + sdram_detect_dbw(cap_info, dram_type); } else { /* detect bg for ddr4 */ coltmp = 10; @@ -768,178 +434,49 @@ static u64 dram_detect_cap(struct dram_info *dram, col = 10; bk = 2; - test_addr = (void __iomem *)(SDRAM_ADDR + - (1ul << (coltmp + bw + 1ul))); - writel(0, SDRAM_ADDR); - writel(PATTERN, test_addr); - if ((readl(test_addr) == PATTERN) && - (readl(SDRAM_ADDR) == 0)) - dbw = 0; - else - dbw = 1; + cap_info->col = col; + cap_info->bk = bk; + sdram_detect_bg(cap_info, coltmp); } + /* detect row */ - for (row = rowtmp; row > 12; row--) { - writel(0, SDRAM_ADDR); - test_addr = (void __iomem *)(SDRAM_ADDR + - (1ul << (row + bktmp + coltmp + bw - 1ul))); - writel(PATTERN, test_addr); - if ((readl(test_addr) == PATTERN) && - (readl(SDRAM_ADDR) == 0)) - break; - } - if (row == 12) { - printf("row error"); + if (sdram_detect_row(cap_info, coltmp, bktmp, rowtmp) != 0) goto cap_err; - } - /* detect row_3_4 */ - test_addr = SDRAM_ADDR; - test_addr1 = (void __iomem *)(SDRAM_ADDR + - (0x3ul << (row + bktmp + coltmp + bw - 1ul - 1ul))); - - writel(0, test_addr); - writel(PATTERN, test_addr1); - if ((readl(test_addr) == 0) && - (readl(test_addr1) == PATTERN)) - row_3_4 = 0; - else - row_3_4 = 1; - /* disable auto low-power */ - pwrctl = readl(pctl_base + DDR_PCTL2_PWRCTL); - writel(0, pctl_base + DDR_PCTL2_PWRCTL); + /* detect row_3_4 */ + sdram_detect_row_3_4(cap_info, coltmp, bktmp); - /* bw and cs detect using phy read gate training */ + /* bw and cs detect using data training */ if (data_training(dram, 1, dram_type) == 0) cs = 1; else cs = 0; + cap_info->rank = cs + 1; bw = 2; + cap_info->bw = bw; - /* restore auto low-power */ - writel(pwrctl, pctl_base + DDR_PCTL2_PWRCTL); - - sdram_ch.rank = cs + 1; - sdram_ch.col = col; - sdram_ch.bk = bk; - sdram_ch.dbw = dbw; - sdram_ch.bw = bw; - sdram_ch.cs0_row = row; - if (cs) - sdram_ch.cs1_row = row; - else - sdram_ch.cs1_row = 0; - sdram_ch.row_3_4 = row_3_4; - - if (dram_type == DDR4) - cap = 1llu << (cs + row + bk + col + ((dbw == 0) ? 2 : 1) + bw); - else - cap = 1llu << (cs + row + bk + col + bw); - - return cap; - -cap_err: - return 0; -} - -static u32 remodify_sdram_params(struct rk3328_sdram_params *sdram_params) -{ - u32 tmp = 0, tmp_adr = 0, i; - - for (i = 0; sdram_params->pctl_regs.pctl[i][0] != 0xFFFFFFFF; i++) { - if (sdram_params->pctl_regs.pctl[i][0] == 0) { - tmp = sdram_params->pctl_regs.pctl[i][1];/* MSTR */ - tmp_adr = i; - } - } - - tmp &= ~((3ul << 30) | (3ul << 24) | (3ul << 12)); - - switch (sdram_ch.dbw) { - case 2: - tmp |= (3ul << 30); - break; - case 1: - tmp |= (2ul << 30); - break; - case 0: - default: - tmp |= (1ul << 30); - break; + cap_info->cs0_high16bit_row = cap_info->cs0_row; + if (cs) { + cap_info->cs1_row = cap_info->cs0_row; + cap_info->cs1_high16bit_row = cap_info->cs0_row; + } else { + cap_info->cs1_row = 0; + cap_info->cs1_high16bit_row = 0; } - if (sdram_ch.rank == 2) - tmp |= 3 << 24; - else - tmp |= 1 << 24; - - tmp |= (2 - sdram_ch.bw) << 12; - - sdram_params->pctl_regs.pctl[tmp_adr][1] = tmp; - - if (sdram_ch.bw == 2) - sdram_ch.noc_timings.ddrtiming.b.bwratio = 0; - else - sdram_ch.noc_timings.ddrtiming.b.bwratio = 1; - return 0; -} - -static int dram_detect_cs1_row(struct rk3328_sdram_params *sdram_params, - unsigned char channel) -{ - u32 ret = 0; - u32 cs1_bit; - void __iomem *test_addr, *cs1_addr; - u32 row, bktmp, coltmp, bw; - u32 ddrconf = sdram_ch.ddrconfig; - - if (sdram_ch.rank == 2) { - cs1_bit = addrmap[ddrconf][0] + 8; - - if (cs1_bit > 31) - goto out; - - cs1_addr = (void __iomem *)(1ul << cs1_bit); - if (cs1_bit < 20) - cs1_bit = 1; - else - cs1_bit = 0; - - if (sdram_params->dramtype == DDR4) { - if (sdram_ch.dbw == 0) - bktmp = sdram_ch.bk + 2; - else - bktmp = sdram_ch.bk + 1; - } else { - bktmp = sdram_ch.bk; - } - bw = sdram_ch.bw; - coltmp = sdram_ch.col; - - /* detect cs1 row */ - for (row = sdram_ch.cs0_row; row > 12; row--) { - test_addr = (void __iomem *)(SDRAM_ADDR + cs1_addr + - (1ul << (row + cs1_bit + bktmp + - coltmp + bw - 1ul))); - writel(0, SDRAM_ADDR + cs1_addr); - writel(PATTERN, test_addr); - if ((readl(test_addr) == PATTERN) && - (readl(SDRAM_ADDR + cs1_addr) == 0)) { - ret = row; - break; - } - } - } - -out: - return ret; +cap_err: + return -1; } static int sdram_init_detect(struct dram_info *dram, struct rk3328_sdram_params *sdram_params) { + u32 sys_reg = 0; + u32 sys_reg3 = 0; + struct sdram_cap_info *cap_info = &sdram_params->ch.cap_info; + debug("Starting SDRAM initialization...\n"); memcpy(&sdram_ch, &sdram_params->ch, @@ -949,13 +486,29 @@ static int sdram_init_detect(struct dram_info *dram, dram_detect_cap(dram, sdram_params, 0); /* modify bw, cs related timing */ - remodify_sdram_params(sdram_params); + pctl_remodify_sdram_params(&sdram_params->pctl_regs, cap_info, + sdram_params->base.dramtype); + + if (cap_info->bw == 2) + sdram_ch.noc_timings.ddrtiming.b.bwratio = 0; + else + sdram_ch.noc_timings.ddrtiming.b.bwratio = 1; + /* reinit sdram by real dram cap */ sdram_init(dram, sdram_params, 0); /* redetect cs1 row */ - sdram_ch.cs1_row = - dram_detect_cs1_row(sdram_params, 0); + sdram_detect_cs1_row(cap_info, sdram_params->base.dramtype); + if (cap_info->cs1_row) { + sys_reg = readl(&dram->grf->os_reg[2]); + sys_reg3 = readl(&dram->grf->os_reg[3]); + SYS_REG_ENC_CS1_ROW(cap_info->cs1_row, + sys_reg, sys_reg3, 0); + writel(sys_reg, &dram->grf->os_reg[2]); + writel(sys_reg3, &dram->grf->os_reg[3]); + } + + sdram_print_ddr_info(&sdram_params->ch.cap_info, &sdram_params->base); return 0; } diff --git a/drivers/ram/rockchip/sdram_rk3399.c b/drivers/ram/rockchip/sdram_rk3399.c index ed70137ce7..7b2bba03fe 100644 --- a/drivers/ram/rockchip/sdram_rk3399.c +++ b/drivers/ram/rockchip/sdram_rk3399.c @@ -18,7 +18,7 @@ #include <asm/arch-rockchip/grf_rk3399.h> #include <asm/arch-rockchip/pmu_rk3399.h> #include <asm/arch-rockchip/hardware.h> -#include <asm/arch-rockchip/sdram_common.h> +#include <asm/arch-rockchip/sdram.h> #include <asm/arch-rockchip/sdram_rk3399.h> #include <linux/err.h> #include <time.h> @@ -44,6 +44,11 @@ #define CS0_MR22_VAL 0 #define CS1_MR22_VAL 3 +/* LPDDR3 DRAM DS */ +#define LPDDR3_DS_34 0x1 +#define LPDDR3_DS_40 0x2 +#define LPDDR3_DS_48 0x3 + #define CRU_SFTRST_DDR_CTRL(ch, n) ((0x1 << (8 + 16 + (ch) * 4)) | \ ((n) << (8 + (ch) * 4))) #define CRU_SFTRST_DDR_PHY(ch, n) ((0x1 << (9 + 16 + (ch) * 4)) | \ @@ -52,7 +57,7 @@ struct chan_info { struct rk3399_ddr_pctl_regs *pctl; struct rk3399_ddr_pi_regs *pi; struct rk3399_ddr_publ_regs *publ; - struct rk3399_msch_regs *msch; + struct msch_regs *msch; }; struct dram_info { @@ -74,10 +79,15 @@ struct dram_info { }; struct sdram_rk3399_ops { - int (*data_training)(struct dram_info *dram, u32 channel, u8 rank, - struct rk3399_sdram_params *sdram); - int (*set_rate)(struct dram_info *dram, - struct rk3399_sdram_params *params); + int (*data_training_first)(struct dram_info *dram, u32 channel, u8 rank, + struct rk3399_sdram_params *sdram); + int (*set_rate_index)(struct dram_info *dram, + struct rk3399_sdram_params *params); + void (*modify_param)(const struct chan_info *chan, + struct rk3399_sdram_params *params); + struct rk3399_sdram_params * + (*get_phy_index_params)(u32 phy_fn, + struct rk3399_sdram_params *params); }; #if defined(CONFIG_TPL_BUILD) || \ @@ -144,38 +154,21 @@ struct io_setting { 32, /* rd_vref; (unit %, range 3.3% - 48.7%) */ }, { - 800 * MHz, + 933 * MHz, 0, /* dram side */ 1, /* dq_odt; */ 0, /* ca_odt; */ - 1, /* pdds; */ + 3, /* pdds; */ 0x72, /* dq_vref; */ 0x72, /* ca_vref; */ /* phy side */ - PHY_DRV_ODT_40, /* rd_odt; */ - PHY_DRV_ODT_48, /* wr_dq_drv; */ + PHY_DRV_ODT_80, /* rd_odt; */ + PHY_DRV_ODT_40, /* wr_dq_drv; */ PHY_DRV_ODT_40, /* wr_ca_drv; */ PHY_DRV_ODT_40, /* wr_ckcs_drv; */ 1, /* rd_odt_en; */ - 17, /* rd_vref; (unit %, range 3.3% - 48.7%) */ - }, - { - 933 * MHz, - 0, - /* dram side */ - 3, /* dq_odt; */ - 0, /* ca_odt; */ - 6, /* pdds; */ - 0x59, /* dq_vref; 32% */ - 0x72, /* ca_vref; */ - /* phy side */ - PHY_DRV_ODT_HI_Z, /* rd_odt; */ - PHY_DRV_ODT_48, /* wr_dq_drv; */ - PHY_DRV_ODT_40, /* wr_ca_drv; */ - PHY_DRV_ODT_40, /* wr_ckcs_drv; */ - 0, /* rd_odt_en; */ - 32, /* rd_vref; (unit %, range 3.3% - 48.7%) */ + 20, /* rd_vref; (unit %, range 3.3% - 48.7%) */ }, { 1066 * MHz, @@ -183,24 +176,19 @@ struct io_setting { /* dram side */ 6, /* dq_odt; */ 0, /* ca_odt; */ - 1, /* pdds; */ + 3, /* pdds; */ 0x10, /* dq_vref; */ 0x72, /* ca_vref; */ /* phy side */ - PHY_DRV_ODT_40, /* rd_odt; */ + PHY_DRV_ODT_80, /* rd_odt; */ PHY_DRV_ODT_60, /* wr_dq_drv; */ PHY_DRV_ODT_40, /* wr_ca_drv; */ PHY_DRV_ODT_40, /* wr_ckcs_drv; */ 1, /* rd_odt_en; */ - 17, /* rd_vref; (unit %, range 3.3% - 48.7%) */ + 20, /* rd_vref; (unit %, range 3.3% - 48.7%) */ }, }; -/** - * phy = 0, PHY boot freq - * phy = 1, PHY index 0 - * phy = 2, PHY index 1 - */ static struct io_setting * lpddr4_get_io_settings(const struct rk3399_sdram_params *params, u32 mr5) { @@ -223,32 +211,21 @@ lpddr4_get_io_settings(const struct rk3399_sdram_params *params, u32 mr5) return io; } -static void *get_denali_phy(const struct chan_info *chan, - struct rk3399_sdram_params *params, bool reg) -{ - return reg ? &chan->publ->denali_phy : ¶ms->phy_regs.denali_phy; -} - static void *get_denali_ctl(const struct chan_info *chan, struct rk3399_sdram_params *params, bool reg) { return reg ? &chan->pctl->denali_ctl : ¶ms->pctl_regs.denali_ctl; } -static void *get_ddrc0_con(struct dram_info *dram, u8 channel) +static void *get_denali_phy(const struct chan_info *chan, + struct rk3399_sdram_params *params, bool reg) { - return (channel == 0) ? &dram->grf->ddrc0_con0 : &dram->grf->ddrc0_con1; + return reg ? &chan->publ->denali_phy : ¶ms->phy_regs.denali_phy; } -static void copy_to_reg(u32 *dest, const u32 *src, u32 n) +static void *get_ddrc0_con(struct dram_info *dram, u8 channel) { - int i; - - for (i = 0; i < n / sizeof(u32); i++) { - writel(*src, dest); - src++; - dest++; - } + return (channel == 0) ? &dram->grf->ddrc0_con0 : &dram->grf->ddrc1_con0; } static void rkclk_ddr_reset(struct rk3399_cru *cru, u32 channel, u32 ctl, @@ -319,7 +296,8 @@ static void set_memory_map(const struct chan_info *chan, u32 channel, if (sdram_ch->cap_info.ddrconfig < 2 || sdram_ch->cap_info.ddrconfig == 4) row = 16; - else if (sdram_ch->cap_info.ddrconfig == 3) + else if (sdram_ch->cap_info.ddrconfig == 3 || + sdram_ch->cap_info.ddrconfig == 5) row = 14; else row = 15; @@ -344,7 +322,7 @@ static void set_memory_map(const struct chan_info *chan, u32 channel, ((3 - sdram_ch->cap_info.bk) << 16) | ((16 - row) << 24)); - if (IS_ENABLED(CONFIG_RAM_RK3399_LPDDR4)) { + if (params->base.dramtype == LPDDR4) { if (cs_map == 1) cs_map = 0x5; else if (cs_map == 2) @@ -363,11 +341,12 @@ static int phy_io_config(const struct chan_info *chan, const struct rk3399_sdram_params *params, u32 mr5) { u32 *denali_phy = chan->publ->denali_phy; + u32 *denali_ctl = chan->pctl->denali_ctl; u32 vref_mode_dq, vref_value_dq, vref_mode_ac, vref_value_ac; u32 mode_sel; - u32 reg_value; - u32 drv_value, odt_value; u32 speed; + u32 reg_value; + u32 ds_value, odt_value; /* vref setting & mode setting */ if (params->base.dramtype == LPDDR4) { @@ -393,12 +372,12 @@ static int phy_io_config(const struct chan_info *chan, } else if (params->base.dramtype == LPDDR3) { if (params->base.odt == 1) { vref_mode_dq = 0x5; /* LPDDR3 ODT */ - drv_value = (readl(&denali_phy[6]) >> 12) & 0xf; + ds_value = readl(&denali_ctl[138]) & 0xf; odt_value = (readl(&denali_phy[6]) >> 4) & 0xf; - if (drv_value == PHY_DRV_ODT_48) { + if (ds_value == LPDDR3_DS_48) { switch (odt_value) { case PHY_DRV_ODT_240: - vref_value_dq = 0x16; + vref_value_dq = 0x1B; break; case PHY_DRV_ODT_120: vref_value_dq = 0x26; @@ -410,7 +389,7 @@ static int phy_io_config(const struct chan_info *chan, debug("Invalid ODT value.\n"); return -EINVAL; } - } else if (drv_value == PHY_DRV_ODT_40) { + } else if (ds_value == LPDDR3_DS_40) { switch (odt_value) { case PHY_DRV_ODT_240: vref_value_dq = 0x19; @@ -425,7 +404,7 @@ static int phy_io_config(const struct chan_info *chan, debug("Invalid ODT value.\n"); return -EINVAL; } - } else if (drv_value == PHY_DRV_ODT_34_3) { + } else if (ds_value == LPDDR3_DS_34) { switch (odt_value) { case PHY_DRV_ODT_240: vref_value_dq = 0x17; @@ -496,7 +475,7 @@ static int phy_io_config(const struct chan_info *chan, /* PHY_939 PHY_PAD_CS_DRIVE */ clrsetbits_le32(&denali_phy[939], 0x7 << 14, mode_sel << 14); - if (IS_ENABLED(CONFIG_RAM_RK3399_LPDDR4)) { + if (params->base.dramtype == LPDDR4) { /* BOOSTP_EN & BOOSTN_EN */ reg_value = ((PHY_BOOSTP_EN << 4) | PHY_BOOSTN_EN); /* PHY_925 PHY_PAD_FDBK_DRIVE2 */ @@ -537,14 +516,7 @@ static int phy_io_config(const struct chan_info *chan, } /* speed setting */ - if (params->base.ddr_freq < 400) - speed = 0x0; - else if (params->base.ddr_freq < 800) - speed = 0x1; - else if (params->base.ddr_freq < 1200) - speed = 0x2; - else - speed = 0x3; + speed = 0x2; /* PHY_924 PHY_PAD_FDBK_DRIVE */ clrsetbits_le32(&denali_phy[924], 0x3 << 21, speed << 21); @@ -563,7 +535,7 @@ static int phy_io_config(const struct chan_info *chan, /* PHY_939 PHY_PAD_CS_DRIVE */ clrsetbits_le32(&denali_phy[939], 0x3 << 17, speed << 17); - if (IS_ENABLED(CONFIG_RAM_RK3399_LPDDR4)) { + if (params->base.dramtype == LPDDR4) { /* RX_CM_INPUT */ reg_value = PHY_RX_CM_INPUT; /* PHY_924 PHY_PAD_FDBK_DRIVE */ @@ -610,16 +582,17 @@ static void set_ds_odt(const struct chan_info *chan, tsel_rd_select_n = io->rd_odt; tsel_idle_select_p = PHY_DRV_ODT_HI_Z; - tsel_idle_select_n = PHY_DRV_ODT_240; + tsel_idle_select_n = PHY_DRV_ODT_HI_Z; tsel_wr_select_dq_p = io->wr_dq_drv; - tsel_wr_select_dq_n = PHY_DRV_ODT_40; + tsel_wr_select_dq_n = PHY_DRV_ODT_34_3; tsel_wr_select_ca_p = io->wr_ca_drv; - tsel_wr_select_ca_n = PHY_DRV_ODT_40; + tsel_wr_select_ca_n = PHY_DRV_ODT_34_3; tsel_ckcs_select_p = io->wr_ckcs_drv; tsel_ckcs_select_n = PHY_DRV_ODT_34_3; + switch (tsel_rd_select_n) { case PHY_DRV_ODT_240: soc_odt = 1; @@ -659,8 +632,8 @@ static void set_ds_odt(const struct chan_info *chan, tsel_wr_select_dq_p = PHY_DRV_ODT_34_3; tsel_wr_select_dq_n = PHY_DRV_ODT_34_3; - tsel_wr_select_ca_p = PHY_DRV_ODT_48; - tsel_wr_select_ca_n = PHY_DRV_ODT_48; + tsel_wr_select_ca_p = PHY_DRV_ODT_34_3; + tsel_wr_select_ca_n = PHY_DRV_ODT_34_3; tsel_ckcs_select_p = PHY_DRV_ODT_34_3; tsel_ckcs_select_n = PHY_DRV_ODT_34_3; @@ -733,7 +706,7 @@ static void set_ds_odt(const struct chan_info *chan, /* phy_adr_tsel_select_ 8bits DENALI_PHY_544/672/800 offset_0 */ reg_value = tsel_wr_select_ca_n | (tsel_wr_select_ca_p << 0x4); - if (IS_ENABLED(CONFIG_RAM_RK3399_LPDDR4)) { + if (params->base.dramtype == LPDDR4) { /* LPDDR4 these register read always return 0, so * can not use clrsetbits_le32(), need to write32 */ @@ -766,9 +739,9 @@ static void set_ds_odt(const struct chan_info *chan, /* phy_pad_fdbk_drive 23bit DENALI_PHY_924/925 */ clrsetbits_le32(&denali_phy[924], 0xff, - tsel_wr_select_dq_n | (tsel_wr_select_dq_p << 4)); + tsel_wr_select_ca_n | (tsel_wr_select_ca_p << 4)); clrsetbits_le32(&denali_phy[925], 0xff, - tsel_rd_select_n | (tsel_rd_select_p << 4)); + tsel_wr_select_dq_n | (tsel_wr_select_dq_p << 4)); /* phy_dq_tsel_enable_X 3bits DENALI_PHY_5/133/261/389 offset_16 */ reg_value = (tsel_rd_en | (tsel_wr_en << 1) | (tsel_idle_en << 2)) @@ -810,46 +783,107 @@ static void set_ds_odt(const struct chan_info *chan, phy_io_config(chan, params, mr5); } -static void pctl_start(struct dram_info *dram, u8 channel) +static void pctl_start(struct dram_info *dram, + struct rk3399_sdram_params *params, + u32 channel_mask) { - const struct chan_info *chan = &dram->chan[channel]; - u32 *denali_ctl = chan->pctl->denali_ctl; - u32 *denali_phy = chan->publ->denali_phy; - u32 *ddrc0_con = get_ddrc0_con(dram, channel); + const struct chan_info *chan_0 = &dram->chan[0]; + const struct chan_info *chan_1 = &dram->chan[1]; + + u32 *denali_ctl_0 = chan_0->pctl->denali_ctl; + u32 *denali_phy_0 = chan_0->publ->denali_phy; + u32 *ddrc0_con_0 = get_ddrc0_con(dram, 0); + u32 *denali_ctl_1 = chan_1->pctl->denali_ctl; + u32 *denali_phy_1 = chan_1->publ->denali_phy; + u32 *ddrc1_con_0 = get_ddrc0_con(dram, 1); u32 count = 0; u32 byte, tmp; - writel(0x01000000, &ddrc0_con); + /* PHY_DLL_RST_EN */ + if (channel_mask & 1) { + writel(0x01000000, &ddrc0_con_0); + clrsetbits_le32(&denali_phy_0[957], 0x3 << 24, 0x2 << 24); + } - clrsetbits_le32(&denali_phy[957], 0x3 << 24, 0x2 << 24); + if (channel_mask & 1) { + count = 0; + while (!(readl(&denali_ctl_0[203]) & (1 << 3))) { + if (count > 1000) { + printf("%s: Failed to init pctl channel 0\n", + __func__); + while (1) + ; + } + udelay(1); + count++; + } - while (!(readl(&denali_ctl[203]) & (1 << 3))) { - if (count > 1000) { - printf("%s: Failed to init pctl for channel %d\n", - __func__, channel); - while (1) - ; + writel(0x01000100, &ddrc0_con_0); + for (byte = 0; byte < 4; byte++) { + tmp = 0x820; + writel((tmp << 16) | tmp, + &denali_phy_0[53 + (128 * byte)]); + writel((tmp << 16) | tmp, + &denali_phy_0[54 + (128 * byte)]); + writel((tmp << 16) | tmp, + &denali_phy_0[55 + (128 * byte)]); + writel((tmp << 16) | tmp, + &denali_phy_0[56 + (128 * byte)]); + writel((tmp << 16) | tmp, + &denali_phy_0[57 + (128 * byte)]); + clrsetbits_le32(&denali_phy_0[58 + (128 * byte)], + 0xffff, tmp); } + clrsetbits_le32(&denali_ctl_0[68], PWRUP_SREFRESH_EXIT, + dram->pwrup_srefresh_exit[0]); + } - udelay(1); - count++; + if (channel_mask & 2) { + writel(0x01000000, &ddrc1_con_0); + clrsetbits_le32(&denali_phy_1[957], 0x3 << 24, 0x2 << 24); } + if (channel_mask & 2) { + count = 0; + while (!(readl(&denali_ctl_1[203]) & (1 << 3))) { + if (count > 1000) { + printf("%s: Failed to init pctl channel 1\n", + __func__); + while (1) + ; + } + udelay(1); + count++; + } - writel(0x01000100, &ddrc0_con); + writel(0x01000100, &ddrc1_con_0); + for (byte = 0; byte < 4; byte++) { + tmp = 0x820; + writel((tmp << 16) | tmp, + &denali_phy_1[53 + (128 * byte)]); + writel((tmp << 16) | tmp, + &denali_phy_1[54 + (128 * byte)]); + writel((tmp << 16) | tmp, + &denali_phy_1[55 + (128 * byte)]); + writel((tmp << 16) | tmp, + &denali_phy_1[56 + (128 * byte)]); + writel((tmp << 16) | tmp, + &denali_phy_1[57 + (128 * byte)]); + clrsetbits_le32(&denali_phy_1[58 + (128 * byte)], + 0xffff, tmp); + } - for (byte = 0; byte < 4; byte++) { - tmp = 0x820; - writel((tmp << 16) | tmp, &denali_phy[53 + (128 * byte)]); - writel((tmp << 16) | tmp, &denali_phy[54 + (128 * byte)]); - writel((tmp << 16) | tmp, &denali_phy[55 + (128 * byte)]); - writel((tmp << 16) | tmp, &denali_phy[56 + (128 * byte)]); - writel((tmp << 16) | tmp, &denali_phy[57 + (128 * byte)]); + clrsetbits_le32(&denali_ctl_1[68], PWRUP_SREFRESH_EXIT, + dram->pwrup_srefresh_exit[1]); - clrsetbits_le32(&denali_phy[58 + (128 * byte)], 0xffff, tmp); + /* + * restore channel 1 RESET original setting + * to avoid 240ohm too weak to prevent ESD test + */ + if (params->base.dramtype == LPDDR4) + clrsetbits_le32(&denali_phy_1[937], 0xff, + params->phy_regs.denali_phy[937] & + 0xFF); } - - clrsetbits_le32(&denali_ctl[68], PWRUP_SREFRESH_EXIT, - dram->pwrup_srefresh_exit[channel]); } static int pctl_cfg(struct dram_info *dram, const struct chan_info *chan, @@ -861,13 +895,16 @@ static int pctl_cfg(struct dram_info *dram, const struct chan_info *chan, const u32 *params_ctl = params->pctl_regs.denali_ctl; const u32 *params_phy = params->phy_regs.denali_phy; u32 tmp, tmp1, tmp2; + struct rk3399_sdram_params *params_cfg; + u32 byte; + dram->ops->modify_param(chan, params); /* * work around controller bug: * Do not program DRAM_CLASS until NO_PHY_IND_TRAIN_INT is programmed */ - copy_to_reg(&denali_ctl[1], ¶ms_ctl[1], - sizeof(struct rk3399_ddr_pctl_regs) - 4); + sdram_copy_to_reg(&denali_ctl[1], ¶ms_ctl[1], + sizeof(struct rk3399_ddr_pctl_regs) - 4); writel(params_ctl[0], &denali_ctl[0]); /* @@ -884,8 +921,8 @@ static int pctl_cfg(struct dram_info *dram, const struct chan_info *chan, writel(tmp + tmp1, &denali_ctl[14]); } - copy_to_reg(denali_pi, ¶ms->pi_regs.denali_pi[0], - sizeof(struct rk3399_ddr_pi_regs)); + sdram_copy_to_reg(denali_pi, ¶ms->pi_regs.denali_pi[0], + sizeof(struct rk3399_ddr_pi_regs)); /* rank count need to set for init */ set_memory_map(chan, channel, params); @@ -894,7 +931,7 @@ static int pctl_cfg(struct dram_info *dram, const struct chan_info *chan, writel(params->phy_regs.denali_phy[911], &denali_phy[911]); writel(params->phy_regs.denali_phy[912], &denali_phy[912]); - if (IS_ENABLED(CONFIG_RAM_RK3399_LPDDR4)) { + if (params->base.dramtype == LPDDR4) { writel(params->phy_regs.denali_phy[898], &denali_phy[898]); writel(params->phy_regs.denali_phy[919], &denali_phy[919]); } @@ -927,41 +964,67 @@ static int pctl_cfg(struct dram_info *dram, const struct chan_info *chan, } } - copy_to_reg(&denali_phy[896], ¶ms_phy[896], (958 - 895) * 4); - copy_to_reg(&denali_phy[0], ¶ms_phy[0], (90 - 0 + 1) * 4); - copy_to_reg(&denali_phy[128], ¶ms_phy[128], (218 - 128 + 1) * 4); - copy_to_reg(&denali_phy[256], ¶ms_phy[256], (346 - 256 + 1) * 4); - copy_to_reg(&denali_phy[384], ¶ms_phy[384], (474 - 384 + 1) * 4); - copy_to_reg(&denali_phy[512], ¶ms_phy[512], (549 - 512 + 1) * 4); - copy_to_reg(&denali_phy[640], ¶ms_phy[640], (677 - 640 + 1) * 4); - copy_to_reg(&denali_phy[768], ¶ms_phy[768], (805 - 768 + 1) * 4); - set_ds_odt(chan, params, true, 0); + sdram_copy_to_reg(&denali_phy[896], ¶ms_phy[896], (958 - 895) * 4); + sdram_copy_to_reg(&denali_phy[0], ¶ms_phy[0], (90 - 0 + 1) * 4); + sdram_copy_to_reg(&denali_phy[128], ¶ms_phy[128], + (218 - 128 + 1) * 4); + sdram_copy_to_reg(&denali_phy[256], ¶ms_phy[256], + (346 - 256 + 1) * 4); + sdram_copy_to_reg(&denali_phy[384], ¶ms_phy[384], + (474 - 384 + 1) * 4); + sdram_copy_to_reg(&denali_phy[512], ¶ms_phy[512], + (549 - 512 + 1) * 4); + sdram_copy_to_reg(&denali_phy[640], ¶ms_phy[640], + (677 - 640 + 1) * 4); + sdram_copy_to_reg(&denali_phy[768], ¶ms_phy[768], + (805 - 768 + 1) * 4); - /* - * phy_dqs_tsel_wr_timing_X 8bits DENALI_PHY_84/212/340/468 offset_8 - * dqs_tsel_wr_end[7:4] add Half cycle - */ - tmp = (readl(&denali_phy[84]) >> 8) & 0xff; - clrsetbits_le32(&denali_phy[84], 0xff << 8, (tmp + 0x10) << 8); - tmp = (readl(&denali_phy[212]) >> 8) & 0xff; - clrsetbits_le32(&denali_phy[212], 0xff << 8, (tmp + 0x10) << 8); - tmp = (readl(&denali_phy[340]) >> 8) & 0xff; - clrsetbits_le32(&denali_phy[340], 0xff << 8, (tmp + 0x10) << 8); - tmp = (readl(&denali_phy[468]) >> 8) & 0xff; - clrsetbits_le32(&denali_phy[468], 0xff << 8, (tmp + 0x10) << 8); + if (params->base.dramtype == LPDDR4) + params_cfg = dram->ops->get_phy_index_params(1, params); + else + params_cfg = dram->ops->get_phy_index_params(0, params); + + clrsetbits_le32(¶ms_cfg->phy_regs.denali_phy[896], 0x3 << 8, + 0 << 8); + writel(params_cfg->phy_regs.denali_phy[896], &denali_phy[896]); + + writel(params->phy_regs.denali_phy[83] + (0x10 << 16), + &denali_phy[83]); + writel(params->phy_regs.denali_phy[84] + (0x10 << 8), + &denali_phy[84]); + writel(params->phy_regs.denali_phy[211] + (0x10 << 16), + &denali_phy[211]); + writel(params->phy_regs.denali_phy[212] + (0x10 << 8), + &denali_phy[212]); + writel(params->phy_regs.denali_phy[339] + (0x10 << 16), + &denali_phy[339]); + writel(params->phy_regs.denali_phy[340] + (0x10 << 8), + &denali_phy[340]); + writel(params->phy_regs.denali_phy[467] + (0x10 << 16), + &denali_phy[467]); + writel(params->phy_regs.denali_phy[468] + (0x10 << 8), + &denali_phy[468]); - /* - * phy_dqs_tsel_wr_timing_X 8bits DENALI_PHY_83/211/339/467 offset_8 - * dq_tsel_wr_end[7:4] add Half cycle - */ - tmp = (readl(&denali_phy[83]) >> 16) & 0xff; - clrsetbits_le32(&denali_phy[83], 0xff << 16, (tmp + 0x10) << 16); - tmp = (readl(&denali_phy[211]) >> 16) & 0xff; - clrsetbits_le32(&denali_phy[211], 0xff << 16, (tmp + 0x10) << 16); - tmp = (readl(&denali_phy[339]) >> 16) & 0xff; - clrsetbits_le32(&denali_phy[339], 0xff << 16, (tmp + 0x10) << 16); - tmp = (readl(&denali_phy[467]) >> 16) & 0xff; - clrsetbits_le32(&denali_phy[467], 0xff << 16, (tmp + 0x10) << 16); + if (params->base.dramtype == LPDDR4) { + /* + * to improve write dqs and dq phase from 1.5ns to 3.5ns + * at 50MHz. this's the measure result from oscilloscope + * of dqs and dq write signal. + */ + for (byte = 0; byte < 4; byte++) { + tmp = 0x680; + clrsetbits_le32(&denali_phy[1 + (128 * byte)], + 0xfff << 8, tmp << 8); + } + /* + * to workaround 366ball two channel's RESET connect to + * one RESET signal of die + */ + if (channel == 1) + clrsetbits_le32(&denali_phy[937], 0xff, + PHY_DRV_ODT_240 | + (PHY_DRV_ODT_240 << 0x4)); + } return 0; } @@ -1277,10 +1340,9 @@ static int data_training_wdql(const struct chan_info *chan, u32 channel, /* * disable PI_WDQLVL_VREF_EN before wdq leveling? - * PI_181 PI_WDQLVL_VREF_EN:RW:8:1 + * PI_117 PI_WDQLVL_VREF_EN:RW:8:1 */ - clrbits_le32(&denali_pi[181], 0x1 << 8); - + clrbits_le32(&denali_pi[117], 0x1 << 8); /* PI_124 PI_WDQLVL_EN:RW:16:2 */ clrsetbits_le32(&denali_pi[124], 0x3 << 16, 0x2 << 16); @@ -1392,7 +1454,7 @@ static void set_ddrconfig(const struct chan_info *chan, unsigned char channel, u32 ddrconfig) { /* only need to set ddrconfig */ - struct rk3399_msch_regs *ddr_msch_regs = chan->msch; + struct msch_regs *ddr_msch_regs = chan->msch; unsigned int cs0_cap = 0; unsigned int cs1_cap = 0; @@ -1413,52 +1475,43 @@ static void set_ddrconfig(const struct chan_info *chan, &ddr_msch_regs->ddrsize); } +static void sdram_msch_config(struct msch_regs *msch, + struct sdram_msch_timings *noc_timings) +{ + writel(noc_timings->ddrtiminga0.d32, + &msch->ddrtiminga0.d32); + writel(noc_timings->ddrtimingb0.d32, + &msch->ddrtimingb0.d32); + writel(noc_timings->ddrtimingc0.d32, + &msch->ddrtimingc0.d32); + writel(noc_timings->devtodev0.d32, + &msch->devtodev0.d32); + writel(noc_timings->ddrmode.d32, + &msch->ddrmode.d32); +} + static void dram_all_config(struct dram_info *dram, - const struct rk3399_sdram_params *params) + struct rk3399_sdram_params *params) { u32 sys_reg2 = 0; u32 sys_reg3 = 0; unsigned int channel, idx; - sys_reg2 |= SYS_REG_ENC_DDRTYPE(params->base.dramtype); - sys_reg2 |= SYS_REG_ENC_NUM_CH(params->base.num_channels); - for (channel = 0, idx = 0; (idx < params->base.num_channels) && (channel < 2); channel++) { - const struct rk3399_sdram_channel *info = ¶ms->ch[channel]; - struct rk3399_msch_regs *ddr_msch_regs; - const struct rk3399_msch_timings *noc_timing; + struct msch_regs *ddr_msch_regs; + struct sdram_msch_timings *noc_timing; if (params->ch[channel].cap_info.col == 0) continue; idx++; - sys_reg2 |= SYS_REG_ENC_ROW_3_4(info->cap_info.row_3_4, channel); - sys_reg2 |= SYS_REG_ENC_CHINFO(channel); - sys_reg2 |= SYS_REG_ENC_RANK(info->cap_info.rank, channel); - sys_reg2 |= SYS_REG_ENC_COL(info->cap_info.col, channel); - sys_reg2 |= SYS_REG_ENC_BK(info->cap_info.bk, channel); - sys_reg2 |= SYS_REG_ENC_BW(info->cap_info.bw, channel); - sys_reg2 |= SYS_REG_ENC_DBW(info->cap_info.dbw, channel); - SYS_REG_ENC_CS0_ROW(info->cap_info.cs0_row, sys_reg2, sys_reg3, channel); - if (info->cap_info.cs1_row) - SYS_REG_ENC_CS1_ROW(info->cap_info.cs1_row, sys_reg2, - sys_reg3, channel); - sys_reg3 |= SYS_REG_ENC_CS1_COL(info->cap_info.col, channel); - sys_reg3 |= SYS_REG_ENC_VERSION(DDR_SYS_REG_VERSION); - + sdram_org_config(¶ms->ch[channel].cap_info, + ¶ms->base, &sys_reg2, + &sys_reg3, channel); ddr_msch_regs = dram->chan[channel].msch; noc_timing = ¶ms->ch[channel].noc_timings; - writel(noc_timing->ddrtiminga0, - &ddr_msch_regs->ddrtiminga0); - writel(noc_timing->ddrtimingb0, - &ddr_msch_regs->ddrtimingb0); - writel(noc_timing->ddrtimingc0.d32, - &ddr_msch_regs->ddrtimingc0); - writel(noc_timing->devtodev0, - &ddr_msch_regs->devtodev0); - writel(noc_timing->ddrmode.d32, - &ddr_msch_regs->ddrmode); + sdram_msch_config(ddr_msch_regs, noc_timing); /** * rank 1 memory clock disable (dfi_dram_clk_disable = 1) @@ -1494,7 +1547,7 @@ static void set_cap_relate_config(const struct chan_info *chan, { u32 *denali_ctl = chan->pctl->denali_ctl; u32 tmp; - struct rk3399_msch_timings *noc_timing; + struct sdram_msch_timings *noc_timing; if (params->base.dramtype == LPDDR3) { tmp = (8 << params->ch[channel].cap_info.bw) / @@ -1566,9 +1619,14 @@ static u32 calculate_ddrconfig(struct rk3399_sdram_params *params, u32 channel) return i; } +static void set_ddr_stride(struct rk3399_pmusgrf_regs *pmusgrf, u32 stride) +{ + rk_clrsetreg(&pmusgrf->soc_con4, 0x1f << 10, stride << 10); +} + #if !defined(CONFIG_RAM_RK3399_LPDDR4) -static int default_data_training(struct dram_info *dram, u32 channel, u8 rank, - struct rk3399_sdram_params *params) +static int data_training_first(struct dram_info *dram, u32 channel, u8 rank, + struct rk3399_sdram_params *params) { u8 training_flag = PI_READ_GATE_TRAINING; @@ -1629,31 +1687,72 @@ static int switch_to_phy_index1(struct dram_info *dram, return 0; } +struct rk3399_sdram_params + *get_phy_index_params(u32 phy_fn, + struct rk3399_sdram_params *params) +{ + if (phy_fn == 0) + return params; + else + return NULL; +} + +void modify_param(const struct chan_info *chan, + struct rk3399_sdram_params *params) +{ + struct rk3399_sdram_params *params_cfg; + u32 *denali_pi_params; + + denali_pi_params = params->pi_regs.denali_pi; + + /* modify PHY F0/F1/F2 params */ + params_cfg = get_phy_index_params(0, params); + set_ds_odt(chan, params_cfg, false, 0); + + clrsetbits_le32(&denali_pi_params[45], 0x1 << 24, 0x1 << 24); + clrsetbits_le32(&denali_pi_params[61], 0x1 << 24, 0x1 << 24); + clrsetbits_le32(&denali_pi_params[76], 0x1 << 24, 0x1 << 24); + clrsetbits_le32(&denali_pi_params[77], 0x1, 0x1); +} #else -struct rk3399_sdram_params lpddr4_timings[] = { - #include "sdram-rk3399-lpddr4-400.inc" - #include "sdram-rk3399-lpddr4-800.inc" +struct rk3399_sdram_params dfs_cfgs_lpddr4[] = { +#include "sdram-rk3399-lpddr4-400.inc" +#include "sdram-rk3399-lpddr4-800.inc" }; +static struct rk3399_sdram_params + *lpddr4_get_phy_index_params(u32 phy_fn, + struct rk3399_sdram_params *params) +{ + if (phy_fn == 0) + return params; + else if (phy_fn == 1) + return &dfs_cfgs_lpddr4[1]; + else if (phy_fn == 2) + return &dfs_cfgs_lpddr4[0]; + else + return NULL; +} + static void *get_denali_pi(const struct chan_info *chan, struct rk3399_sdram_params *params, bool reg) { return reg ? &chan->pi->denali_pi : ¶ms->pi_regs.denali_pi; } -static u32 lpddr4_get_phy(struct rk3399_sdram_params *params, u32 ctl) +static u32 lpddr4_get_phy_fn(struct rk3399_sdram_params *params, u32 ctl_fn) { - u32 lpddr4_phy[] = {1, 0, 0xb}; + u32 lpddr4_phy_fn[] = {1, 0, 0xb}; - return lpddr4_phy[ctl]; + return lpddr4_phy_fn[ctl_fn]; } -static u32 lpddr4_get_ctl(struct rk3399_sdram_params *params, u32 phy) +static u32 lpddr4_get_ctl_fn(struct rk3399_sdram_params *params, u32 phy_fn) { - u32 lpddr4_ctl[] = {1, 0, 2}; + u32 lpddr4_ctl_fn[] = {1, 0, 2}; - return lpddr4_ctl[phy]; + return lpddr4_ctl_fn[phy_fn]; } static u32 get_ddr_stride(struct rk3399_pmusgrf_regs *pmusgrf) @@ -1661,12 +1760,7 @@ static u32 get_ddr_stride(struct rk3399_pmusgrf_regs *pmusgrf) return ((readl(&pmusgrf->soc_con4) >> 10) & 0x1F); } -static void set_ddr_stride(struct rk3399_pmusgrf_regs *pmusgrf, u32 stride) -{ - rk_clrsetreg(&pmusgrf->soc_con4, 0x1f << 10, stride << 10); -} - -/** +/* * read mr_num mode register * rank = 1: cs0 * rank = 2: cs1 @@ -1797,7 +1891,7 @@ end: } static void set_lpddr4_dq_odt(const struct chan_info *chan, - struct rk3399_sdram_params *params, u32 ctl, + struct rk3399_sdram_params *params, u32 ctl_fn, bool en, bool ctl_phy_reg, u32 mr5) { u32 *denali_ctl = get_denali_ctl(chan, params, ctl_phy_reg); @@ -1805,14 +1899,13 @@ static void set_lpddr4_dq_odt(const struct chan_info *chan, struct io_setting *io; u32 reg_value; - if (!en) - return; - io = lpddr4_get_io_settings(params, mr5); + if (en) + reg_value = io->dq_odt; + else + reg_value = 0; - reg_value = io->dq_odt; - - switch (ctl) { + switch (ctl_fn) { case 0: clrsetbits_le32(&denali_ctl[139], 0x7 << 24, reg_value << 24); clrsetbits_le32(&denali_ctl[153], 0x7 << 24, reg_value << 24); @@ -1845,7 +1938,7 @@ static void set_lpddr4_dq_odt(const struct chan_info *chan, } static void set_lpddr4_ca_odt(const struct chan_info *chan, - struct rk3399_sdram_params *params, u32 ctl, + struct rk3399_sdram_params *params, u32 ctl_fn, bool en, bool ctl_phy_reg, u32 mr5) { u32 *denali_ctl = get_denali_ctl(chan, params, ctl_phy_reg); @@ -1853,14 +1946,13 @@ static void set_lpddr4_ca_odt(const struct chan_info *chan, struct io_setting *io; u32 reg_value; - if (!en) - return; - io = lpddr4_get_io_settings(params, mr5); + if (en) + reg_value = io->ca_odt; + else + reg_value = 0; - reg_value = io->ca_odt; - - switch (ctl) { + switch (ctl_fn) { case 0: clrsetbits_le32(&denali_ctl[139], 0x7 << 28, reg_value << 28); clrsetbits_le32(&denali_ctl[153], 0x7 << 28, reg_value << 28); @@ -1893,7 +1985,7 @@ static void set_lpddr4_ca_odt(const struct chan_info *chan, } static void set_lpddr4_MR3(const struct chan_info *chan, - struct rk3399_sdram_params *params, u32 ctl, + struct rk3399_sdram_params *params, u32 ctl_fn, bool ctl_phy_reg, u32 mr5) { u32 *denali_ctl = get_denali_ctl(chan, params, ctl_phy_reg); @@ -1905,7 +1997,7 @@ static void set_lpddr4_MR3(const struct chan_info *chan, reg_value = ((io->pdds << 3) | 1); - switch (ctl) { + switch (ctl_fn) { case 0: clrsetbits_le32(&denali_ctl[138], 0xFFFF, reg_value); clrsetbits_le32(&denali_ctl[152], 0xFFFF, reg_value); @@ -1940,7 +2032,7 @@ static void set_lpddr4_MR3(const struct chan_info *chan, } static void set_lpddr4_MR12(const struct chan_info *chan, - struct rk3399_sdram_params *params, u32 ctl, + struct rk3399_sdram_params *params, u32 ctl_fn, bool ctl_phy_reg, u32 mr5) { u32 *denali_ctl = get_denali_ctl(chan, params, ctl_phy_reg); @@ -1952,7 +2044,7 @@ static void set_lpddr4_MR12(const struct chan_info *chan, reg_value = io->ca_vref; - switch (ctl) { + switch (ctl_fn) { case 0: clrsetbits_le32(&denali_ctl[140], 0xFFFF << 16, reg_value << 16); @@ -1989,7 +2081,7 @@ static void set_lpddr4_MR12(const struct chan_info *chan, } static void set_lpddr4_MR14(const struct chan_info *chan, - struct rk3399_sdram_params *params, u32 ctl, + struct rk3399_sdram_params *params, u32 ctl_fn, bool ctl_phy_reg, u32 mr5) { u32 *denali_ctl = get_denali_ctl(chan, params, ctl_phy_reg); @@ -2001,7 +2093,7 @@ static void set_lpddr4_MR14(const struct chan_info *chan, reg_value = io->dq_vref; - switch (ctl) { + switch (ctl_fn) { case 0: clrsetbits_le32(&denali_ctl[142], 0xFFFF << 16, reg_value << 16); @@ -2037,22 +2129,73 @@ static void set_lpddr4_MR14(const struct chan_info *chan, } } +void lpddr4_modify_param(const struct chan_info *chan, + struct rk3399_sdram_params *params) +{ + struct rk3399_sdram_params *params_cfg; + u32 *denali_ctl_params; + u32 *denali_pi_params; + u32 *denali_phy_params; + + denali_ctl_params = params->pctl_regs.denali_ctl; + denali_pi_params = params->pi_regs.denali_pi; + denali_phy_params = params->phy_regs.denali_phy; + + set_lpddr4_dq_odt(chan, params, 2, true, false, 0); + set_lpddr4_ca_odt(chan, params, 2, true, false, 0); + set_lpddr4_MR3(chan, params, 2, false, 0); + set_lpddr4_MR12(chan, params, 2, false, 0); + set_lpddr4_MR14(chan, params, 2, false, 0); + params_cfg = lpddr4_get_phy_index_params(0, params); + set_ds_odt(chan, params_cfg, false, 0); + /* read two cycle preamble */ + clrsetbits_le32(&denali_ctl_params[200], 0x3 << 24, 0x3 << 24); + clrsetbits_le32(&denali_phy_params[7], 0x3 << 24, 0x3 << 24); + clrsetbits_le32(&denali_phy_params[135], 0x3 << 24, 0x3 << 24); + clrsetbits_le32(&denali_phy_params[263], 0x3 << 24, 0x3 << 24); + clrsetbits_le32(&denali_phy_params[391], 0x3 << 24, 0x3 << 24); + + /* boot frequency two cycle preamble */ + clrsetbits_le32(&denali_phy_params[2], 0x3 << 16, 0x3 << 16); + clrsetbits_le32(&denali_phy_params[130], 0x3 << 16, 0x3 << 16); + clrsetbits_le32(&denali_phy_params[258], 0x3 << 16, 0x3 << 16); + clrsetbits_le32(&denali_phy_params[386], 0x3 << 16, 0x3 << 16); + + clrsetbits_le32(&denali_pi_params[45], 0x3 << 8, 0x3 << 8); + clrsetbits_le32(&denali_pi_params[58], 0x1, 0x1); + + /* + * bypass mode need PHY_SLICE_PWR_RDC_DISABLE_x = 1, + * boot frequency mode use bypass mode + */ + setbits_le32(&denali_phy_params[10], 1 << 16); + setbits_le32(&denali_phy_params[138], 1 << 16); + setbits_le32(&denali_phy_params[266], 1 << 16); + setbits_le32(&denali_phy_params[394], 1 << 16); + + clrsetbits_le32(&denali_pi_params[45], 0x1 << 24, 0x1 << 24); + clrsetbits_le32(&denali_pi_params[61], 0x1 << 24, 0x1 << 24); + clrsetbits_le32(&denali_pi_params[76], 0x1 << 24, 0x1 << 24); + clrsetbits_le32(&denali_pi_params[77], 0x1, 0x1); +} + static void lpddr4_copy_phy(struct dram_info *dram, - struct rk3399_sdram_params *params, u32 phy, - struct rk3399_sdram_params *timings, + struct rk3399_sdram_params *params, u32 phy_fn, + struct rk3399_sdram_params *params_cfg, u32 channel) { u32 *denali_ctl, *denali_phy; u32 *denali_phy_params; u32 speed = 0; - u32 ctl, mr5; + u32 ctl_fn, mr5; denali_ctl = dram->chan[channel].pctl->denali_ctl; denali_phy = dram->chan[channel].publ->denali_phy; - denali_phy_params = timings->phy_regs.denali_phy; + denali_phy_params = params_cfg->phy_regs.denali_phy; /* switch index */ - clrsetbits_le32(&denali_phy_params[896], 0x3 << 8, phy << 8); + clrsetbits_le32(&denali_phy_params[896], 0x3 << 8, + phy_fn << 8); writel(denali_phy_params[896], &denali_phy[896]); /* phy_pll_ctrl_ca, phy_pll_ctrl */ @@ -2112,14 +2255,14 @@ static void lpddr4_copy_phy(struct dram_info *dram, * phy_clk_wrdqz_slave_delay_x * phy_clk_wrdqs_slave_delay_x */ - copy_to_reg((u32 *)&denali_phy[59], (u32 *)&denali_phy_params[59], - (63 - 58) * 4); - copy_to_reg((u32 *)&denali_phy[187], (u32 *)&denali_phy_params[187], - (191 - 186) * 4); - copy_to_reg((u32 *)&denali_phy[315], (u32 *)&denali_phy_params[315], - (319 - 314) * 4); - copy_to_reg((u32 *)&denali_phy[443], (u32 *)&denali_phy_params[443], - (447 - 442) * 4); + sdram_copy_to_reg((u32 *)&denali_phy[59], + (u32 *)&denali_phy_params[59], (63 - 58) * 4); + sdram_copy_to_reg((u32 *)&denali_phy[187], + (u32 *)&denali_phy_params[187], (191 - 186) * 4); + sdram_copy_to_reg((u32 *)&denali_phy[315], + (u32 *)&denali_phy_params[315], (319 - 314) * 4); + sdram_copy_to_reg((u32 *)&denali_phy[443], + (u32 *)&denali_phy_params[443], (447 - 442) * 4); /* * phy_dqs_tsel_wr_timing_x 8bits denali_phy_84/212/340/468 offset_8 @@ -2218,31 +2361,30 @@ static void lpddr4_copy_phy(struct dram_info *dram, * phy_wrlvl_delay_period_threshold_x * phy_wrlvl_early_force_zero_x */ - copy_to_reg((u32 *)&denali_phy[64], (u32 *)&denali_phy_params[64], - (67 - 63) * 4); + sdram_copy_to_reg((u32 *)&denali_phy[64], + (u32 *)&denali_phy_params[64], (67 - 63) * 4); clrsetbits_le32(&denali_phy[68], 0xfffffc00, denali_phy_params[68] & 0xfffffc00); - copy_to_reg((u32 *)&denali_phy[69], (u32 *)&denali_phy_params[69], - (79 - 68) * 4); - copy_to_reg((u32 *)&denali_phy[192], (u32 *)&denali_phy_params[192], - (195 - 191) * 4); + sdram_copy_to_reg((u32 *)&denali_phy[69], + (u32 *)&denali_phy_params[69], (79 - 68) * 4); + sdram_copy_to_reg((u32 *)&denali_phy[192], + (u32 *)&denali_phy_params[192], (195 - 191) * 4); clrsetbits_le32(&denali_phy[196], 0xfffffc00, denali_phy_params[196] & 0xfffffc00); - copy_to_reg((u32 *)&denali_phy[197], (u32 *)&denali_phy_params[197], - (207 - 196) * 4); - copy_to_reg((u32 *)&denali_phy[320], (u32 *)&denali_phy_params[320], - (323 - 319) * 4); + sdram_copy_to_reg((u32 *)&denali_phy[197], + (u32 *)&denali_phy_params[197], (207 - 196) * 4); + sdram_copy_to_reg((u32 *)&denali_phy[320], + (u32 *)&denali_phy_params[320], (323 - 319) * 4); clrsetbits_le32(&denali_phy[324], 0xfffffc00, denali_phy_params[324] & 0xfffffc00); - copy_to_reg((u32 *)&denali_phy[325], (u32 *)&denali_phy_params[325], - (335 - 324) * 4); - - copy_to_reg((u32 *)&denali_phy[448], (u32 *)&denali_phy_params[448], - (451 - 447) * 4); + sdram_copy_to_reg((u32 *)&denali_phy[325], + (u32 *)&denali_phy_params[325], (335 - 324) * 4); + sdram_copy_to_reg((u32 *)&denali_phy[448], + (u32 *)&denali_phy_params[448], (451 - 447) * 4); clrsetbits_le32(&denali_phy[452], 0xfffffc00, denali_phy_params[452] & 0xfffffc00); - copy_to_reg((u32 *)&denali_phy[453], (u32 *)&denali_phy_params[453], - (463 - 452) * 4); + sdram_copy_to_reg((u32 *)&denali_phy[453], + (u32 *)&denali_phy_params[453], (463 - 452) * 4); /* phy_two_cyc_preamble_x */ clrsetbits_le32(&denali_phy[7], 0x3 << 24, @@ -2255,11 +2397,11 @@ static void lpddr4_copy_phy(struct dram_info *dram, denali_phy_params[391] & (0x3 << 24)); /* speed */ - if (timings->base.ddr_freq < 400 * MHz) + if (params_cfg->base.ddr_freq < 400) speed = 0x0; - else if (timings->base.ddr_freq < 800 * MHz) + else if (params_cfg->base.ddr_freq < 800) speed = 0x1; - else if (timings->base.ddr_freq < 1200 * MHz) + else if (params_cfg->base.ddr_freq < 1200) speed = 0x2; /* phy_924 phy_pad_fdbk_drive */ @@ -2279,52 +2421,63 @@ static void lpddr4_copy_phy(struct dram_info *dram, /* phy_939 phy_pad_cs_drive */ clrsetbits_le32(&denali_phy[939], 0x3 << 17, speed << 17); - read_mr(dram->chan[channel].pctl, 1, 5, &mr5); - set_ds_odt(&dram->chan[channel], timings, true, mr5); + if (params_cfg->base.dramtype == LPDDR4) { + read_mr(dram->chan[channel].pctl, 1, 5, &mr5); + set_ds_odt(&dram->chan[channel], params_cfg, true, mr5); + + ctl_fn = lpddr4_get_ctl_fn(params_cfg, phy_fn); + set_lpddr4_dq_odt(&dram->chan[channel], params_cfg, + ctl_fn, true, true, mr5); + set_lpddr4_ca_odt(&dram->chan[channel], params_cfg, + ctl_fn, true, true, mr5); + set_lpddr4_MR3(&dram->chan[channel], params_cfg, + ctl_fn, true, mr5); + set_lpddr4_MR12(&dram->chan[channel], params_cfg, + ctl_fn, true, mr5); + set_lpddr4_MR14(&dram->chan[channel], params_cfg, + ctl_fn, true, mr5); - ctl = lpddr4_get_ctl(timings, phy); - set_lpddr4_dq_odt(&dram->chan[channel], timings, ctl, true, true, mr5); - set_lpddr4_ca_odt(&dram->chan[channel], timings, ctl, true, true, mr5); - set_lpddr4_MR3(&dram->chan[channel], timings, ctl, true, mr5); - set_lpddr4_MR12(&dram->chan[channel], timings, ctl, true, mr5); - set_lpddr4_MR14(&dram->chan[channel], timings, ctl, true, mr5); - - /* - * if phy_sw_master_mode_x not bypass mode, - * clear phy_slice_pwr_rdc_disable. - * note: need use timings, not ddr_publ_regs - */ - if (!((denali_phy_params[86] >> 8) & (1 << 2))) { - clrbits_le32(&denali_phy[10], 1 << 16); - clrbits_le32(&denali_phy[138], 1 << 16); - clrbits_le32(&denali_phy[266], 1 << 16); - clrbits_le32(&denali_phy[394], 1 << 16); - } + /* + * if phy_sw_master_mode_x not bypass mode, + * clear phy_slice_pwr_rdc_disable. + * note: need use timings, not ddr_publ_regs + */ + if (!((denali_phy_params[86] >> 8) & (1 << 2))) { + clrbits_le32(&denali_phy[10], 1 << 16); + clrbits_le32(&denali_phy[138], 1 << 16); + clrbits_le32(&denali_phy[266], 1 << 16); + clrbits_le32(&denali_phy[394], 1 << 16); + } - /* - * when PHY_PER_CS_TRAINING_EN=1, W2W_DIFFCS_DLY_Fx can't - * smaller than 8 - * NOTE: need use timings, not ddr_publ_regs - */ - if ((denali_phy_params[84] >> 16) & 1) { - if (((readl(&denali_ctl[217 + ctl]) >> 16) & 0x1f) < 8) - clrsetbits_le32(&denali_ctl[217 + ctl], - 0x1f << 16, 8 << 16); + /* + * when PHY_PER_CS_TRAINING_EN=1, W2W_DIFFCS_DLY_Fx can't + * smaller than 8 + * NOTE: need use timings, not ddr_publ_regs + */ + if ((denali_phy_params[84] >> 16) & 1) { + if (((readl(&denali_ctl[217 + ctl_fn]) >> + 16) & 0x1f) < 8) + clrsetbits_le32(&denali_ctl[217 + ctl_fn], + 0x1f << 16, + 8 << 16); + } } } static void lpddr4_set_phy(struct dram_info *dram, - struct rk3399_sdram_params *params, u32 phy, - struct rk3399_sdram_params *timings) + struct rk3399_sdram_params *params, u32 phy_fn, + struct rk3399_sdram_params *params_cfg) { u32 channel; for (channel = 0; channel < 2; channel++) - lpddr4_copy_phy(dram, params, phy, timings, channel); + lpddr4_copy_phy(dram, params, phy_fn, params_cfg, + channel); } static int lpddr4_set_ctl(struct dram_info *dram, - struct rk3399_sdram_params *params, u32 ctl, u32 hz) + struct rk3399_sdram_params *params, + u32 fn, u32 hz) { u32 channel; int ret_clk, ret; @@ -2343,7 +2496,7 @@ static int lpddr4_set_ctl(struct dram_info *dram, /* change freq */ writel((((0x3 << 4) | (1 << 2) | 1) << 16) | - (ctl << 4) | (1 << 2) | 1, &dram->cic->cic_ctrl0); + (fn << 4) | (1 << 2) | 1, &dram->cic->cic_ctrl0); while (!(readl(&dram->cic->cic_status0) & (1 << 2))) ; @@ -2366,12 +2519,12 @@ static int lpddr4_set_ctl(struct dram_info *dram, clrbits_le32(&dram->pmu->pmu_noc_auto_ena, (0x3 << 7)); /* lpddr4 ctl2 can not do training, all training will fail */ - if (!(params->base.dramtype == LPDDR4 && ctl == 2)) { + if (!(params->base.dramtype == LPDDR4 && fn == 2)) { for (channel = 0; channel < 2; channel++) { if (!(params->ch[channel].cap_info.col)) continue; ret = data_training(dram, channel, params, - PI_FULL_TRAINING); + PI_FULL_TRAINING); if (ret) printf("%s: channel %d training failed!\n", __func__, channel); @@ -2387,35 +2540,237 @@ static int lpddr4_set_ctl(struct dram_info *dram, static int lpddr4_set_rate(struct dram_info *dram, struct rk3399_sdram_params *params) { - u32 ctl; - u32 phy; + u32 ctl_fn; + u32 phy_fn; - for (ctl = 0; ctl < 2; ctl++) { - phy = lpddr4_get_phy(params, ctl); + for (ctl_fn = 0; ctl_fn < 2; ctl_fn++) { + phy_fn = lpddr4_get_phy_fn(params, ctl_fn); - lpddr4_set_phy(dram, params, phy, &lpddr4_timings[ctl]); - lpddr4_set_ctl(dram, params, ctl, - lpddr4_timings[ctl].base.ddr_freq); + lpddr4_set_phy(dram, params, phy_fn, &dfs_cfgs_lpddr4[ctl_fn]); + lpddr4_set_ctl(dram, params, ctl_fn, + dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq); - debug("%s: change freq to %d mhz %d, %d\n", __func__, - lpddr4_timings[ctl].base.ddr_freq / MHz, ctl, phy); + printf("%s: change freq to %d mhz %d, %d\n", __func__, + dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq, ctl_fn, phy_fn); } return 0; } #endif /* CONFIG_RAM_RK3399_LPDDR4 */ +/* CS0,n=1 + * CS1,n=2 + * CS0 & CS1, n=3 + * cs0_cap: MB unit + */ +static void dram_set_cs(const struct chan_info *chan, u32 cs_map, u32 cs0_cap, + unsigned char dramtype) +{ + u32 *denali_ctl = chan->pctl->denali_ctl; + u32 *denali_pi = chan->pi->denali_pi; + struct msch_regs *ddr_msch_regs = chan->msch; + + clrsetbits_le32(&denali_ctl[196], 0x3, cs_map); + writel((cs0_cap / 32) | (((4096 - cs0_cap) / 32) << 8), + &ddr_msch_regs->ddrsize); + if (dramtype == LPDDR4) { + if (cs_map == 1) + cs_map = 0x5; + else if (cs_map == 2) + cs_map = 0xa; + else + cs_map = 0xF; + } + /*PI_41 PI_CS_MAP:RW:24:4*/ + clrsetbits_le32(&denali_pi[41], + 0xf << 24, cs_map << 24); + if (cs_map == 1 && dramtype == DDR3) + writel(0x2EC7FFFF, &denali_pi[34]); +} + +static void dram_set_bw(const struct chan_info *chan, u32 bw) +{ + u32 *denali_ctl = chan->pctl->denali_ctl; + + if (bw == 2) + clrbits_le32(&denali_ctl[196], 1 << 16); + else + setbits_le32(&denali_ctl[196], 1 << 16); +} + +static void dram_set_max_col(const struct chan_info *chan, u32 bw, u32 *pcol) +{ + u32 *denali_ctl = chan->pctl->denali_ctl; + struct msch_regs *ddr_msch_regs = chan->msch; + u32 *denali_pi = chan->pi->denali_pi; + u32 ddrconfig; + + clrbits_le32(&denali_ctl[191], 0xf); + clrsetbits_le32(&denali_ctl[190], + (7 << 24), + ((16 - ((bw == 2) ? 14 : 15)) << 24)); + /*PI_199 PI_COL_DIFF:RW:0:4*/ + clrbits_le32(&denali_pi[199], 0xf); + /*PI_155 PI_ROW_DIFF:RW:24:3*/ + clrsetbits_le32(&denali_pi[155], + (7 << 24), + ((16 - 12) << 24)); + ddrconfig = (bw == 2) ? 3 : 2; + writel(ddrconfig | (ddrconfig << 8), &ddr_msch_regs->ddrconf); + /* set max cs0 size */ + writel((4096 / 32) | ((0 / 32) << 8), + &ddr_msch_regs->ddrsize); + + *pcol = 12; +} + +static void dram_set_max_bank(const struct chan_info *chan, u32 bw, u32 *pbank, + u32 *pcol) +{ + u32 *denali_ctl = chan->pctl->denali_ctl; + u32 *denali_pi = chan->pi->denali_pi; + + clrbits_le32(&denali_ctl[191], 0xf); + clrbits_le32(&denali_ctl[190], (3 << 16)); + /*PI_199 PI_COL_DIFF:RW:0:4*/ + clrbits_le32(&denali_pi[199], 0xf); + /*PI_155 PI_BANK_DIFF:RW:16:2*/ + clrbits_le32(&denali_pi[155], (3 << 16)); + + *pbank = 3; + *pcol = 12; +} + +static void dram_set_max_row(const struct chan_info *chan, u32 bw, u32 *prow, + u32 *pbank, u32 *pcol) +{ + u32 *denali_ctl = chan->pctl->denali_ctl; + u32 *denali_pi = chan->pi->denali_pi; + struct msch_regs *ddr_msch_regs = chan->msch; + + clrsetbits_le32(&denali_ctl[191], 0xf, 12 - 10); + clrbits_le32(&denali_ctl[190], + (0x3 << 16) | (0x7 << 24)); + /*PI_199 PI_COL_DIFF:RW:0:4*/ + clrsetbits_le32(&denali_pi[199], 0xf, 12 - 10); + /*PI_155 PI_ROW_DIFF:RW:24:3 PI_BANK_DIFF:RW:16:2*/ + clrbits_le32(&denali_pi[155], + (0x3 << 16) | (0x7 << 24)); + writel(1 | (1 << 8), &ddr_msch_regs->ddrconf); + /* set max cs0 size */ + writel((4096 / 32) | ((0 / 32) << 8), + &ddr_msch_regs->ddrsize); + + *prow = 16; + *pbank = 3; + *pcol = (bw == 2) ? 10 : 11; +} + +static u64 dram_detect_cap(struct dram_info *dram, + struct rk3399_sdram_params *params, + unsigned char channel) +{ + const struct chan_info *chan = &dram->chan[channel]; + struct sdram_cap_info *cap_info = ¶ms->ch[channel].cap_info; + u32 bw; + u32 col_tmp; + u32 bk_tmp; + u32 row_tmp; + u32 cs0_cap; + u32 training_flag; + u32 ddrconfig; + + /* detect bw */ + bw = 2; + if (params->base.dramtype != LPDDR4) { + dram_set_bw(chan, bw); + cap_info->bw = bw; + if (data_training(dram, channel, params, + PI_READ_GATE_TRAINING)) { + bw = 1; + dram_set_bw(chan, 1); + cap_info->bw = bw; + if (data_training(dram, channel, params, + PI_READ_GATE_TRAINING)) { + printf("16bit error!!!\n"); + goto error; + } + } + } + /* + * LPDDR3 CA training msut be trigger before other training. + * DDR3 is not have CA training. + */ + if (params->base.dramtype == LPDDR3) + training_flag = PI_WRITE_LEVELING; + else + training_flag = PI_FULL_TRAINING; + + if (params->base.dramtype != LPDDR4) { + if (data_training(dram, channel, params, training_flag)) { + printf("full training error!!!\n"); + goto error; + } + } + + /* detect col */ + dram_set_max_col(chan, bw, &col_tmp); + if (sdram_detect_col(cap_info, col_tmp) != 0) + goto error; + + /* detect bank */ + dram_set_max_bank(chan, bw, &bk_tmp, &col_tmp); + sdram_detect_bank(cap_info, col_tmp, bk_tmp); + + /* detect row */ + dram_set_max_row(chan, bw, &row_tmp, &bk_tmp, &col_tmp); + if (sdram_detect_row(cap_info, col_tmp, bk_tmp, row_tmp) != 0) + goto error; + + /* detect row_3_4 */ + sdram_detect_row_3_4(cap_info, col_tmp, bk_tmp); + + /* set ddrconfig */ + cs0_cap = (1 << (cap_info->cs0_row + cap_info->col + cap_info->bk + + cap_info->bw - 20)); + if (cap_info->row_3_4) + cs0_cap = cs0_cap * 3 / 4; + + cap_info->cs1_row = cap_info->cs0_row; + set_memory_map(chan, channel, params); + ddrconfig = calculate_ddrconfig(params, channel); + if (-1 == ddrconfig) + goto error; + set_ddrconfig(chan, params, channel, + cap_info->ddrconfig); + + /* detect cs1 row */ + sdram_detect_cs1_row(cap_info, params->base.dramtype); + + /* detect die bw */ + sdram_detect_dbw(cap_info, params->base.dramtype); + + return 0; +error: + return (-1); +} + static unsigned char calculate_stride(struct rk3399_sdram_params *params) { - unsigned int stride = params->base.stride; - unsigned int channel, chinfo = 0; + unsigned int gstride_type; + unsigned int channel; + unsigned int chinfo = 0; + unsigned int cap = 0; + unsigned int stride = -1; unsigned int ch_cap[2] = {0, 0}; - u64 cap; + + gstride_type = STRIDE_256B; for (channel = 0; channel < 2; channel++) { unsigned int cs0_cap = 0; unsigned int cs1_cap = 0; - struct sdram_cap_info *cap_info = ¶ms->ch[channel].cap_info; + struct sdram_cap_info *cap_info = + ¶ms->ch[channel].cap_info; if (cap_info->col == 0) continue; @@ -2433,49 +2788,124 @@ static unsigned char calculate_stride(struct rk3399_sdram_params *params) chinfo |= 1 << channel; } - /* stride calculation for 1 channel */ - if (params->base.num_channels == 1 && chinfo & 1) - return 0x17; /* channel a */ - - /* stride calculation for 2 channels, default gstride type is 256B */ - if (ch_cap[0] == ch_cap[1]) { - cap = ch_cap[0] + ch_cap[1]; - switch (cap) { - /* 512MB */ - case 512: - stride = 0; - break; - /* 1GB */ - case 1024: - stride = 0x5; - break; + cap = ch_cap[0] + ch_cap[1]; + if (params->base.num_channels == 1) { + if (chinfo & 1) /* channel a only */ + stride = 0x17; + else /* channel b only */ + stride = 0x18; + } else {/* 2 channel */ + if (ch_cap[0] == ch_cap[1]) { + /* interleaved */ + if (gstride_type == PART_STRIDE) { + /* + * first 64MB no interleaved other 256B interleaved + * if 786M+768M.useful space from 0-1280MB and + * 1536MB-1792MB + * if 1.5G+1.5G(continuous).useful space from 0-2560MB + * and 3072MB-3584MB + */ + stride = 0x1F; + } else { + switch (cap) { + /* 512MB */ + case 512: + stride = 0; + break; + /* 1GB unstride or 256B stride*/ + case 1024: + stride = (gstride_type == UN_STRIDE) ? + 0x1 : 0x5; + break; + /* + * 768MB + 768MB same as total 2GB memory + * useful space: 0-768MB 1GB-1792MB + */ + case 1536: + /* 2GB unstride or 256B or 512B stride */ + case 2048: + stride = (gstride_type == UN_STRIDE) ? + 0x2 : + ((gstride_type == STRIDE_512B) ? + 0xA : 0x9); + break; + /* 1536MB + 1536MB */ + case 3072: + stride = (gstride_type == UN_STRIDE) ? + 0x3 : + ((gstride_type == STRIDE_512B) ? + 0x12 : 0x11); + break; + /* 4GB unstride or 128B,256B,512B,4KB stride */ + case 4096: + stride = (gstride_type == UN_STRIDE) ? + 0x3 : (0xC + gstride_type); + break; + } + } + } + if (ch_cap[0] == 2048 && ch_cap[1] == 1024) { + /* 2GB + 1GB */ + stride = (gstride_type == UN_STRIDE) ? 0x3 : 0x19; + } /* - * 768MB + 768MB same as total 2GB memory - * useful space: 0-768MB 1GB-1792MB + * remain two channel capability not equal OR capability + * power function of 2 */ - case 1536: - /* 2GB */ - case 2048: - stride = 0x9; - break; - /* 1536MB + 1536MB */ - case 3072: - stride = 0x11; - break; - /* 4GB */ - case 4096: - stride = 0xD; - break; - default: - printf("%s: Unable to calculate stride for ", __func__); - print_size((cap * (1 << 20)), " capacity\n"); - break; + if (stride == (-1)) { + switch ((ch_cap[0] > ch_cap[1]) ? + ch_cap[0] : ch_cap[1]) { + case 256: /* 256MB + 128MB */ + stride = 0; + break; + case 512: /* 512MB + 256MB */ + stride = 1; + break; + case 1024:/* 1GB + 128MB/256MB/384MB/512MB/768MB */ + stride = 2; + break; + case 2048: /* 2GB + 128MB/256MB/384MB/512MB/768MB/1GB */ + stride = 3; + break; + default: + break; + } } + if (stride == (-1)) + goto error; + } + switch (stride) { + case 0xc: + printf("128B stride\n"); + break; + case 5: + case 9: + case 0xd: + case 0x11: + case 0x19: + printf("256B stride\n"); + break; + case 0xa: + case 0xe: + case 0x12: + printf("512B stride\n"); + break; + case 0xf: + printf("4K stride\n"); + break; + case 0x1f: + printf("32MB + 256B stride\n"); + break; + default: + printf("no stride\n"); } sdram_print_stride(stride); return stride; +error: + printf("Cap not support!\n"); + return (-1); } static void clear_channel_params(struct rk3399_sdram_params *params, u8 channel) @@ -2491,39 +2921,13 @@ static void clear_channel_params(struct rk3399_sdram_params *params, u8 channel) params->ch[channel].cap_info.ddrconfig = 0; } -static int pctl_init(struct dram_info *dram, struct rk3399_sdram_params *params) -{ - int channel; - int ret; - - for (channel = 0; channel < 2; channel++) { - const struct chan_info *chan = &dram->chan[channel]; - struct rk3399_cru *cru = dram->cru; - struct rk3399_ddr_publ_regs *publ = chan->publ; - - phy_pctrl_reset(cru, channel); - phy_dll_bypass_set(publ, params->base.ddr_freq); - - ret = pctl_cfg(dram, chan, channel, params); - if (ret < 0) { - printf("%s: pctl config failed\n", __func__); - return ret; - } - - /* start to trigger initialization */ - pctl_start(dram, channel); - } - - return 0; -} - static int sdram_init(struct dram_info *dram, struct rk3399_sdram_params *params) { unsigned char dramtype = params->base.dramtype; unsigned int ddr_freq = params->base.ddr_freq; int channel, ch, rank; - int ret; + u32 tmp, ret; debug("Starting SDRAM initialization...\n"); @@ -2534,22 +2938,35 @@ static int sdram_init(struct dram_info *dram, return -E2BIG; } + /* detect rank */ for (ch = 0; ch < 2; ch++) { params->ch[ch].cap_info.rank = 2; for (rank = 2; rank != 0; rank--) { - ret = pctl_init(dram, params); - if (ret < 0) { - printf("%s: pctl init failed\n", __func__); - return ret; + for (channel = 0; channel < 2; channel++) { + const struct chan_info *chan = + &dram->chan[channel]; + struct rk3399_cru *cru = dram->cru; + struct rk3399_ddr_publ_regs *publ = chan->publ; + + phy_pctrl_reset(cru, channel); + phy_dll_bypass_set(publ, ddr_freq); + pctl_cfg(dram, chan, channel, params); } + /* start to trigger initialization */ + pctl_start(dram, params, 3); + /* LPDDR2/LPDDR3 need to wait DAI complete, max 10us */ if (dramtype == LPDDR3) udelay(10); + tmp = (rank == 2) ? 3 : 1; + dram_set_cs(&dram->chan[ch], tmp, 2048, + params->base.dramtype); params->ch[ch].cap_info.rank = rank; - ret = dram->ops->data_training(dram, ch, rank, params); + ret = dram->ops->data_training_first(dram, ch, + rank, params); if (!ret) { debug("%s: data trained for rank %d, ch %d\n", __func__, rank, ch); @@ -2563,38 +2980,37 @@ static int sdram_init(struct dram_info *dram, params->base.num_channels = 0; for (channel = 0; channel < 2; channel++) { const struct chan_info *chan = &dram->chan[channel]; - struct sdram_cap_info *cap_info = ¶ms->ch[channel].cap_info; - u8 training_flag = PI_FULL_TRAINING; + struct sdram_cap_info *cap_info = + ¶ms->ch[channel].cap_info; if (cap_info->rank == 0) { - clear_channel_params(params, channel); + clear_channel_params(params, 1); continue; } else { params->base.num_channels++; } - debug("Channel "); - debug(channel ? "1: " : "0: "); + printf("Channel "); + printf(channel ? "1: " : "0: "); - /* LPDDR3 should have write and read gate training */ - if (params->base.dramtype == LPDDR3) - training_flag = PI_WRITE_LEVELING | - PI_READ_GATE_TRAINING; + if (channel == 0) + set_ddr_stride(dram->pmusgrf, 0x17); + else + set_ddr_stride(dram->pmusgrf, 0x18); - if (params->base.dramtype != LPDDR4) { - ret = data_training(dram, channel, params, - training_flag); - if (!ret) { - debug("%s: data train failed for channel %d\n", - __func__, ret); - continue; - } + if (dram_detect_cap(dram, params, channel)) { + printf("Cap error!\n"); + continue; } sdram_print_ddr_info(cap_info, ¶ms->base); set_memory_map(chan, channel, params); - cap_info->ddrconfig = calculate_ddrconfig(params, channel); - + cap_info->ddrconfig = + calculate_ddrconfig(params, channel); + if (-1 == cap_info->ddrconfig) { + printf("no ddrconfig find, Cap not support!\n"); + continue; + } set_ddrconfig(chan, params, channel, cap_info->ddrconfig); set_cap_relate_config(chan, params, channel); } @@ -2608,7 +3024,8 @@ static int sdram_init(struct dram_info *dram, params->base.stride = calculate_stride(params); dram_all_config(dram, params); - dram->ops->set_rate(dram, params); + + dram->ops->set_rate_index(dram, params); debug("Finish SDRAM initialization...\n"); return 0; @@ -2655,11 +3072,15 @@ static int conv_of_platdata(struct udevice *dev) static const struct sdram_rk3399_ops rk3399_ops = { #if !defined(CONFIG_RAM_RK3399_LPDDR4) - .data_training = default_data_training, - .set_rate = switch_to_phy_index1, + .data_training_first = data_training_first, + .set_rate_index = switch_to_phy_index1, + .modify_param = modify_param, + .get_phy_index_params = get_phy_index_params, #else - .data_training = lpddr4_mr_detect, - .set_rate = lpddr4_set_rate, + .data_training_first = lpddr4_mr_detect, + .set_rate_index = lpddr4_set_rate, + .modify_param = lpddr4_modify_param, + .get_phy_index_params = lpddr4_get_phy_index_params, #endif }; diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index f1e4bb6278..8c8e02169e 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -18,7 +18,7 @@ #include <linux/kernel.h> #include <linux/io.h> #include <usb.h> -#include "../host/xhci.h" +#include <usb/xhci.h> #include "core.h" #include "host-export.h" diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index 79be63001d..425d9d053d 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -11,7 +11,7 @@ #include <dm.h> #include <linux/compat.h> #include <usb.h> -#include "../host/xhci.h" +#include <usb/xhci.h> #include "core.h" #include "drd.h" diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 618a7d5016..c98a444245 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -12,8 +12,16 @@ #define USB_BUFSIZ 4096 +/* Helper type for accessing packed u16 pointers */ +typedef struct { __le16 val; } __packed __le16_packed; + static struct usb_composite_driver *composite; +static inline void le16_add_cpu_packed(__le16_packed *var, u16 val) +{ + var->val = cpu_to_le16(le16_to_cpu(var->val) + val); +} + /** * usb_add_function() - add a function to a configuration * @config: the configuration @@ -480,20 +488,21 @@ done: * the host side. */ -static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) +static void collect_langs(struct usb_gadget_strings **sp, void *buf) { const struct usb_gadget_strings *s; u16 language; - __le16 *tmp; + __le16_packed *tmp; + __le16_packed *end = (buf + 252); while (*sp) { s = *sp; language = cpu_to_le16(s->language); - for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { - if (*tmp == language) + for (tmp = buf; tmp->val && tmp < end; tmp++) { + if (tmp->val == language) goto repeat; } - *tmp++ = language; + tmp->val = language; repeat: sp++; } @@ -705,7 +714,8 @@ static int bos_desc(struct usb_composite_dev *cdev) */ usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength); bos->bNumDeviceCaps++; - le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE); + le16_add_cpu_packed((__le16_packed *)&bos->wTotalLength, + USB_DT_USB_EXT_CAP_SIZE); usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE; usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY; usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT; @@ -721,7 +731,8 @@ static int bos_desc(struct usb_composite_dev *cdev) ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); bos->bNumDeviceCaps++; - le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE); + le16_add_cpu_packed((__le16_packed *)&bos->wTotalLength, + USB_DT_USB_SS_CAP_SIZE); ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE; ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE; diff --git a/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c b/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c index 7eb632d3b1..dba221dad0 100644 --- a/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c @@ -731,7 +731,7 @@ static int write_fifo_ep0(struct dwc2_ep *ep, struct dwc2_request *req) return 0; } -static int dwc2_fifo_read(struct dwc2_ep *ep, u32 *cp, int max) +static int dwc2_fifo_read(struct dwc2_ep *ep, void *cp, int max) { invalidate_dcache_range((unsigned long)cp, (unsigned long)cp + ROUND(max, CONFIG_SYS_CACHELINE_SIZE)); @@ -1285,7 +1285,7 @@ static void dwc2_ep0_setup(struct dwc2_udc *dev) nuke(ep, -EPROTO); /* read control req from fifo (8 bytes) */ - dwc2_fifo_read(ep, (u32 *)usb_ctrl, 8); + dwc2_fifo_read(ep, usb_ctrl, 8); debug_cond(DEBUG_SETUP != 0, "%s: bRequestType = 0x%x(%s), bRequest = 0x%x" diff --git a/drivers/usb/phy/rockchip_usb2_phy.c b/drivers/usb/phy/rockchip_usb2_phy.c index 16e899954a..69e408b6c1 100644 --- a/drivers/usb/phy/rockchip_usb2_phy.c +++ b/drivers/usb/phy/rockchip_usb2_phy.c @@ -5,7 +5,6 @@ #include <common.h> #include <asm/io.h> -#include <linux/libfdt.h> #include "../gadget/dwc2_udc_otg_priv.h" @@ -71,8 +70,8 @@ void otg_phy_init(struct dwc2_udc *dev) for (i = 0; i < ARRAY_SIZE(rockchip_usb2_phy_dt_ids); i++) { of_id = &rockchip_usb2_phy_dt_ids[i]; - if (fdt_node_check_compatible(gd->fdt_blob, pdata->phy_of_node, - of_id->compatible) == 0) { + if (ofnode_device_is_compatible(pdata->phy_of_node, + of_id->compatible)){ phy_cfg = (struct rockchip_usb2_phy_cfg *)of_id->data; break; } diff --git a/drivers/video/dw_mipi_dsi.c b/drivers/video/dw_mipi_dsi.c index 04b07e3a2c..83d7c7b2c0 100644 --- a/drivers/video/dw_mipi_dsi.c +++ b/drivers/video/dw_mipi_dsi.c @@ -816,15 +816,9 @@ static int dw_mipi_dsi_probe(struct udevice *dev) return 0; } -static const struct udevice_id dw_mipi_dsi_ids[] = { - { .compatible = "synopsys,dw-mipi-dsi" }, - { } -}; - U_BOOT_DRIVER(dw_mipi_dsi) = { .name = "dw_mipi_dsi", .id = UCLASS_DSI_HOST, - .of_match = dw_mipi_dsi_ids, .probe = dw_mipi_dsi_probe, .ops = &dw_mipi_dsi_ops, .priv_auto_alloc_size = sizeof(struct dw_mipi_dsi), diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index c52981053e..d73a8bac99 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -57,9 +57,6 @@ static void mxs_lcd_init(u32 fb_addr, struct ctfb_res_modes *mode, int bpp) uint32_t word_len = 0, bus_width = 0; uint8_t valid_data = 0; - /* Kick in the LCDIF clock */ - mxs_set_lcdclk(MXS_LCDIF_BASE, PS2KHZ(mode->pixclock)); - /* Restart the LCDIF block */ mxs_reset_block(®s->hw_lcdif_ctrl_reg); @@ -130,6 +127,9 @@ static void mxs_lcd_init(u32 fb_addr, struct ctfb_res_modes *mode, int bpp) /* FIFO cleared */ writel(LCDIF_CTRL1_FIFO_CLEAR, ®s->hw_lcdif_ctrl1_clr); + /* Kick in the LCDIF clock */ + mxs_set_lcdclk(MXS_LCDIF_BASE, PS2KHZ(mode->pixclock)); + /* RUN! */ writel(LCDIF_CTRL_RUN, ®s->hw_lcdif_ctrl_set); } diff --git a/drivers/video/stm32/stm32_dsi.c b/drivers/video/stm32/stm32_dsi.c index cb89576e1d..12895a8f5d 100644 --- a/drivers/video/stm32/stm32_dsi.c +++ b/drivers/video/stm32/stm32_dsi.c @@ -20,6 +20,7 @@ #include <asm/io.h> #include <asm/arch/gpio.h> #include <dm/device-internal.h> +#include <dm/lists.h> #include <linux/iopoll.h> #include <power/regulator.h> @@ -399,6 +400,18 @@ static int stm32_dsi_set_backlight(struct udevice *dev, int percent) return 0; } +static int stm32_dsi_bind(struct udevice *dev) +{ + int ret; + + ret = device_bind_driver_to_node(dev, "dw_mipi_dsi", "dsihost", + dev_ofnode(dev), NULL); + if (ret) + return ret; + + return dm_scan_fdt_dev(dev); +} + static int stm32_dsi_probe(struct udevice *dev) { struct stm32_dsi_priv *priv = dev_get_priv(dev); @@ -483,7 +496,7 @@ U_BOOT_DRIVER(stm32_dsi) = { .name = "stm32-display-dsi", .id = UCLASS_VIDEO_BRIDGE, .of_match = stm32_dsi_ids, - .bind = dm_scan_fdt_dev, + .bind = stm32_dsi_bind, .probe = stm32_dsi_probe, .ops = &stm32_dsi_ops, .priv_auto_alloc_size = sizeof(struct stm32_dsi_priv), |