diff options
author | Stefano Babic <sbabic@denx.de> | 2015-07-10 09:21:44 +0200 |
---|---|---|
committer | Stefano Babic <sbabic@denx.de> | 2015-07-10 09:21:44 +0200 |
commit | 1254ff97abb7606ccd0d7bdcd9f22581c50fe535 (patch) | |
tree | 61b31e61173154abd38aaa6584e84ea77314b861 /drivers | |
parent | 54e0f96f764f662be186baae7d6c2c97423bc29d (diff) | |
parent | f3edfd30541d6f245d7dfa6fa7354cc916cc53e1 (diff) | |
download | u-boot-imx-1254ff97abb7606ccd0d7bdcd9f22581c50fe535.zip u-boot-imx-1254ff97abb7606ccd0d7bdcd9f22581c50fe535.tar.gz u-boot-imx-1254ff97abb7606ccd0d7bdcd9f22581c50fe535.tar.bz2 |
Merge branch 'master' of git://git.denx.de/u-boot
Diffstat (limited to 'drivers')
38 files changed, 2035 insertions, 2294 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 1f40887..c7e526c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -57,7 +57,7 @@ source "drivers/thermal/Kconfig" endmenu config PHYS_TO_BUS - bool + bool "Custom physical to bus address mapping" help Some SoCs use a different address map for CPU physical addresses and peripheral DMA master accesses. If yours does, select this option in diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c index 6508648..4fb846a 100644 --- a/drivers/block/ahci.c +++ b/drivers/block/ahci.c @@ -39,7 +39,7 @@ u16 *ataid[AHCI_MAX_PORTS]; /* Maximum timeouts for each event */ #define WAIT_MS_SPINUP 20000 -#define WAIT_MS_DATAIO 5000 +#define WAIT_MS_DATAIO 10000 #define WAIT_MS_FLUSH 5000 #define WAIT_MS_LINKUP 200 @@ -726,18 +726,25 @@ static int ata_scsiop_inquiry(ccb *pccb) */ static int ata_scsiop_read_write(ccb *pccb, u8 is_write) { - u32 lba = 0; + lbaint_t lba = 0; u16 blocks = 0; u8 fis[20]; u8 *user_buffer = pccb->pdata; u32 user_buffer_size = pccb->datalen; /* Retrieve the base LBA number from the ccb structure. */ - memcpy(&lba, pccb->cmd + 2, sizeof(lba)); - lba = be32_to_cpu(lba); + if (pccb->cmd[0] == SCSI_READ16) { + memcpy(&lba, pccb->cmd + 2, 8); + lba = be64_to_cpu(lba); + } else { + u32 temp; + memcpy(&temp, pccb->cmd + 2, 4); + lba = be32_to_cpu(temp); + } /* - * And the number of blocks. + * Retrieve the base LBA number and the block count from + * the ccb structure. * * For 10-byte and 16-byte SCSI R/W commands, transfer * length 0 means transfer 0 block of data. @@ -746,10 +753,13 @@ static int ata_scsiop_read_write(ccb *pccb, u8 is_write) * * WARNING: one or two older ATA drives treat 0 as 0... */ - blocks = (((u16)pccb->cmd[7]) << 8) | ((u16) pccb->cmd[8]); + if (pccb->cmd[0] == SCSI_READ16) + blocks = (((u16)pccb->cmd[13]) << 8) | ((u16) pccb->cmd[14]); + else + blocks = (((u16)pccb->cmd[7]) << 8) | ((u16) pccb->cmd[8]); - debug("scsi_ahci: %s %d blocks starting from lba 0x%x\n", - is_write ? "write" : "read", (unsigned)lba, blocks); + debug("scsi_ahci: %s %u blocks starting from lba 0x" LBAFU "\n", + is_write ? "write" : "read", blocks, lba); /* Preset the FIS */ memset(fis, 0, sizeof(fis)); @@ -770,14 +780,23 @@ static int ata_scsiop_read_write(ccb *pccb, u8 is_write) return -EIO; } - /* LBA48 SATA command but only use 32bit address range within - * that. The next smaller command range (28bit) is too small. + /* + * LBA48 SATA command but only use 32bit address range within + * that (unless we've enabled 64bit LBA support). The next + * smaller command range (28bit) is too small. */ fis[4] = (lba >> 0) & 0xff; fis[5] = (lba >> 8) & 0xff; fis[6] = (lba >> 16) & 0xff; fis[7] = 1 << 6; /* device reg: set LBA mode */ fis[8] = ((lba >> 24) & 0xff); +#ifdef CONFIG_SYS_64BIT_LBA + if (pccb->cmd[0] == SCSI_READ16) { + fis[9] = ((lba >> 32) & 0xff); + fis[10] = ((lba >> 40) & 0xff); + } +#endif + fis[3] = 0xe0; /* features */ /* Block (sector) count */ @@ -883,6 +902,7 @@ int scsi_exec(ccb *pccb) int ret; switch (pccb->cmd[0]) { + case SCSI_READ16: case SCSI_READ10: ret = ata_scsiop_read_write(pccb, 0); break; diff --git a/drivers/gpio/lpc32xx_gpio.c b/drivers/gpio/lpc32xx_gpio.c index 96b3125..8a9826e 100644 --- a/drivers/gpio/lpc32xx_gpio.c +++ b/drivers/gpio/lpc32xx_gpio.c @@ -37,7 +37,7 @@ #define LPC32XX_GPIOS 128 -struct lpc32xx_gpio_platdata { +struct lpc32xx_gpio_priv { struct gpio_regs *regs; /* GPIO FUNCTION: SEE WARNING #2 */ signed char function[LPC32XX_GPIOS]; @@ -60,8 +60,8 @@ struct lpc32xx_gpio_platdata { static int lpc32xx_gpio_direction_input(struct udevice *dev, unsigned offset) { int port, mask; - struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); - struct gpio_regs *regs = gpio_platdata->regs; + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + struct gpio_regs *regs = gpio_priv->regs; port = GPIO_TO_PORT(offset); mask = GPIO_TO_MASK(offset); @@ -83,7 +83,7 @@ static int lpc32xx_gpio_direction_input(struct udevice *dev, unsigned offset) } /* GPIO FUNCTION: SEE WARNING #2 */ - gpio_platdata->function[offset] = GPIOF_INPUT; + gpio_priv->function[offset] = GPIOF_INPUT; return 0; } @@ -95,8 +95,8 @@ static int lpc32xx_gpio_direction_input(struct udevice *dev, unsigned offset) static int lpc32xx_gpio_get_value(struct udevice *dev, unsigned offset) { int port, rank, mask, value; - struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); - struct gpio_regs *regs = gpio_platdata->regs; + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + struct gpio_regs *regs = gpio_priv->regs; port = GPIO_TO_PORT(offset); @@ -130,8 +130,8 @@ static int lpc32xx_gpio_get_value(struct udevice *dev, unsigned offset) static int gpio_set(struct udevice *dev, unsigned gpio) { int port, mask; - struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); - struct gpio_regs *regs = gpio_platdata->regs; + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + struct gpio_regs *regs = gpio_priv->regs; port = GPIO_TO_PORT(gpio); mask = GPIO_TO_MASK(gpio); @@ -162,8 +162,8 @@ static int gpio_set(struct udevice *dev, unsigned gpio) static int gpio_clr(struct udevice *dev, unsigned gpio) { int port, mask; - struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); - struct gpio_regs *regs = gpio_platdata->regs; + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + struct gpio_regs *regs = gpio_priv->regs; port = GPIO_TO_PORT(gpio); mask = GPIO_TO_MASK(gpio); @@ -208,8 +208,8 @@ static int lpc32xx_gpio_direction_output(struct udevice *dev, unsigned offset, int value) { int port, mask; - struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); - struct gpio_regs *regs = gpio_platdata->regs; + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + struct gpio_regs *regs = gpio_priv->regs; port = GPIO_TO_PORT(offset); mask = GPIO_TO_MASK(offset); @@ -231,7 +231,7 @@ static int lpc32xx_gpio_direction_output(struct udevice *dev, unsigned offset, } /* GPIO FUNCTION: SEE WARNING #2 */ - gpio_platdata->function[offset] = GPIOF_OUTPUT; + gpio_priv->function[offset] = GPIOF_OUTPUT; return lpc32xx_gpio_set_value(dev, offset, value); } @@ -251,8 +251,8 @@ static int lpc32xx_gpio_direction_output(struct udevice *dev, unsigned offset, static int lpc32xx_gpio_get_function(struct udevice *dev, unsigned offset) { - struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); - return gpio_platdata->function[offset]; + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + return gpio_priv->function[offset]; } static const struct dm_gpio_ops gpio_lpc32xx_ops = { @@ -265,7 +265,7 @@ static const struct dm_gpio_ops gpio_lpc32xx_ops = { static int lpc32xx_gpio_probe(struct udevice *dev) { - struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); struct gpio_dev_priv *uc_priv = dev->uclass_priv; if (dev->of_offset == -1) { @@ -274,12 +274,11 @@ static int lpc32xx_gpio_probe(struct udevice *dev) } /* set base address for GPIO registers */ - gpio_platdata->regs = (struct gpio_regs *)GPIO_BASE; + gpio_priv->regs = (struct gpio_regs *)GPIO_BASE; /* all GPIO functions are unknown until requested */ /* GPIO FUNCTION: SEE WARNING #2 */ - memset(gpio_platdata->function, GPIOF_UNKNOWN, - sizeof(gpio_platdata->function)); + memset(gpio_priv->function, GPIOF_UNKNOWN, sizeof(gpio_priv->function)); return 0; } @@ -289,5 +288,5 @@ U_BOOT_DRIVER(gpio_lpc32xx) = { .id = UCLASS_GPIO, .ops = &gpio_lpc32xx_ops, .probe = lpc32xx_gpio_probe, - .priv_auto_alloc_size = sizeof(struct lpc32xx_gpio_platdata), + .priv_auto_alloc_size = sizeof(struct lpc32xx_gpio_priv), }; diff --git a/drivers/mmc/bcm2835_sdhci.c b/drivers/mmc/bcm2835_sdhci.c index 078ef05..227d2df 100644 --- a/drivers/mmc/bcm2835_sdhci.c +++ b/drivers/mmc/bcm2835_sdhci.c @@ -69,11 +69,11 @@ static inline void bcm2835_sdhci_raw_writel(struct sdhci_host *host, u32 val, * (Which is just as well - otherwise we'd have to nobble the DMA engine * too) */ - while (get_timer(bcm_host->last_write) < bcm_host->twoticks_delay) + while (timer_get_us() - bcm_host->last_write < bcm_host->twoticks_delay) ; writel(val, host->ioaddr + reg); - bcm_host->last_write = get_timer(0); + bcm_host->last_write = timer_get_us(); } static inline u32 bcm2835_sdhci_raw_readl(struct sdhci_host *host, int reg) diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c index 3db9669..7aea7e9 100644 --- a/drivers/mmc/mmc_write.c +++ b/drivers/mmc/mmc_write.c @@ -10,6 +10,8 @@ #include <config.h> #include <common.h> #include <part.h> +#include <div64.h> +#include <linux/math64.h> #include "mmc_private.h" static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) @@ -66,6 +68,7 @@ err_out: unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) { int err = 0; + u32 start_rem, blkcnt_rem; struct mmc *mmc = find_mmc_device(dev_num); lbaint_t blk = 0, blk_r = 0; int timeout = 1000; @@ -73,7 +76,14 @@ unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) if (!mmc) return -1; - if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) + /* + * We want to see if the requested start or total block count are + * unaligned. We discard the whole numbers and only care about the + * remainder. + */ + err = div_u64_rem(start, mmc->erase_grp_size, &start_rem); + err = div_u64_rem(blkcnt, mmc->erase_grp_size, &blkcnt_rem); + if (start_rem || blkcnt_rem) printf("\n\nCaution! Your devices Erase group is 0x%x\n" "The erase range would be change to " "0x" LBAF "~0x" LBAF "\n\n", diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 5467a95..a623f4c 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -5,8 +5,8 @@ # SPDX-License-Identifier: GPL-2.0+ # -ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND))) -obj-y += mtdcore.o +ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND)$(CONFIG_CMD_SF))) +obj-y += mtdcore.o mtd_uboot.o endif obj-$(CONFIG_MTD_PARTITIONS) += mtdpart.o obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o diff --git a/drivers/mtd/mtd_uboot.c b/drivers/mtd/mtd_uboot.c new file mode 100644 index 0000000..7197007 --- /dev/null +++ b/drivers/mtd/mtd_uboot.c @@ -0,0 +1,99 @@ +/* + * (C) Copyright 2014 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <linux/mtd/mtd.h> +#include <jffs2/jffs2.h> + +static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size, + loff_t *maxsize, int devtype) +{ +#ifdef CONFIG_CMD_MTDPARTS + struct mtd_device *dev; + struct part_info *part; + u8 pnum; + int ret; + + ret = mtdparts_init(); + if (ret) + return ret; + + ret = find_dev_and_part(partname, &dev, &pnum, &part); + if (ret) + return ret; + + if (dev->id->type != devtype) { + printf("not same typ %d != %d\n", dev->id->type, devtype); + return -1; + } + + *off = part->offset; + *size = part->size; + *maxsize = part->size; + *idx = dev->id->num; + + return 0; +#else + puts("offset is not a number\n"); + return -1; +#endif +} + +int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, + loff_t *maxsize, int devtype, int chipsize) +{ + if (!str2off(arg, off)) + return get_part(arg, idx, off, size, maxsize, devtype); + + if (*off >= chipsize) { + puts("Offset exceeds device limit\n"); + return -1; + } + + *maxsize = chipsize - *off; + *size = *maxsize; + return 0; +} + +int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, + loff_t *size, loff_t *maxsize, int devtype, int chipsize) +{ + int ret; + + if (argc == 0) { + *off = 0; + *size = chipsize; + *maxsize = *size; + goto print; + } + + ret = mtd_arg_off(argv[0], idx, off, size, maxsize, devtype, + chipsize); + if (ret) + return ret; + + if (argc == 1) + goto print; + + if (!str2off(argv[1], size)) { + printf("'%s' is not a number\n", argv[1]); + return -1; + } + + if (*size > *maxsize) { + puts("Size exceeds partition or device limit\n"); + return -1; + } + +print: + printf("device %d ", *idx); + if (*size == chipsize) + puts("whole chip\n"); + else + printf("offset 0x%llx, size 0x%llx\n", + (unsigned long long)*off, (unsigned long long)*size); + return 0; +} diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index a0cf4d5..347ea62 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -73,6 +73,5 @@ obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o -obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o endif # drivers diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 610f969..4372988 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -340,6 +340,125 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, return 0; } +#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH + +#define PREFETCH_CONFIG1_CS_SHIFT 24 +#define PREFETCH_FIFOTHRESHOLD_MAX 0x40 +#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) +#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff) +#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F) +#define ENABLE_PREFETCH (1 << 7) + +/** + * omap_prefetch_enable - configures and starts prefetch transfer + * @fifo_th: fifo threshold to be used for read/ write + * @count: number of bytes to be transferred + * @is_write: prefetch read(0) or write post(1) mode + * @cs: chip select to use + */ +static int omap_prefetch_enable(int fifo_th, unsigned int count, int is_write, int cs) +{ + uint32_t val; + + if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) + return -EINVAL; + + if (readl(&gpmc_cfg->prefetch_control)) + return -EBUSY; + + /* Set the amount of bytes to be prefetched */ + writel(count, &gpmc_cfg->prefetch_config2); + + val = (cs << PREFETCH_CONFIG1_CS_SHIFT) | (is_write & 1) | + PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH; + writel(val, &gpmc_cfg->prefetch_config1); + + /* Start the prefetch engine */ + writel(1, &gpmc_cfg->prefetch_control); + + return 0; +} + +/** + * omap_prefetch_reset - disables and stops the prefetch engine + */ +static void omap_prefetch_reset(void) +{ + writel(0, &gpmc_cfg->prefetch_control); + writel(0, &gpmc_cfg->prefetch_config1); +} + +static int __read_prefetch_aligned(struct nand_chip *chip, uint32_t *buf, int len) +{ + int ret; + uint32_t cnt; + struct omap_nand_info *info = chip->priv; + + ret = omap_prefetch_enable(PREFETCH_FIFOTHRESHOLD_MAX, len, 0, info->cs); + if (ret < 0) + return ret; + + do { + int i; + + cnt = readl(&gpmc_cfg->prefetch_status); + cnt = PREFETCH_STATUS_FIFO_CNT(cnt); + + for (i = 0; i < cnt / 4; i++) { + *buf++ = readl(CONFIG_SYS_NAND_BASE); + len -= 4; + } + } while (len); + + omap_prefetch_reset(); + + return 0; +} + +static inline void omap_nand_read(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->options & NAND_BUSWIDTH_16) + nand_read_buf16(mtd, buf, len); + else + nand_read_buf(mtd, buf, len); +} + +static void omap_nand_read_prefetch(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int ret; + uint32_t head, tail; + struct nand_chip *chip = mtd->priv; + + /* + * If the destination buffer is unaligned, start with reading + * the overlap byte-wise. + */ + head = ((uint32_t) buf) % 4; + if (head) { + omap_nand_read(mtd, buf, head); + buf += head; + len -= head; + } + + /* + * Only transfer multiples of 4 bytes in a pre-fetched fashion. + * If there's a residue, care for it byte-wise afterwards. + */ + tail = len % 4; + + ret = __read_prefetch_aligned(chip, (uint32_t *)buf, len - tail); + if (ret < 0) { + /* fallback in case the prefetch engine is busy */ + omap_nand_read(mtd, buf, len); + } else if (tail) { + buf += len - tail; + omap_nand_read(mtd, buf, tail); + } +} +#endif /* CONFIG_NAND_OMAP_GPMC_PREFETCH */ + #ifdef CONFIG_NAND_OMAP_ELM /* * omap_reverse_list - re-orders list elements in reverse order [internal] @@ -452,115 +571,6 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, return (err) ? err : error_count; } -#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH - -#define PREFETCH_CONFIG1_CS_SHIFT 24 -#define PREFETCH_FIFOTHRESHOLD_MAX 0x40 -#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) -#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff) -#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F) -#define ENABLE_PREFETCH (1 << 7) - -/** - * omap_prefetch_enable - configures and starts prefetch transfer - * @fifo_th: fifo threshold to be used for read/ write - * @count: number of bytes to be transferred - * @is_write: prefetch read(0) or write post(1) mode - * @cs: chip select to use - */ -static int omap_prefetch_enable(int fifo_th, unsigned int count, int is_write, int cs) -{ - uint32_t val; - - if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) - return -EINVAL; - - if (readl(&gpmc_cfg->prefetch_control)) - return -EBUSY; - - /* Set the amount of bytes to be prefetched */ - writel(count, &gpmc_cfg->prefetch_config2); - - val = (cs << PREFETCH_CONFIG1_CS_SHIFT) | (is_write & 1) | - PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH; - writel(val, &gpmc_cfg->prefetch_config1); - - /* Start the prefetch engine */ - writel(1, &gpmc_cfg->prefetch_control); - - return 0; -} - -/** - * omap_prefetch_reset - disables and stops the prefetch engine - */ -static void omap_prefetch_reset(void) -{ - writel(0, &gpmc_cfg->prefetch_control); - writel(0, &gpmc_cfg->prefetch_config1); -} - -static int __read_prefetch_aligned(struct nand_chip *chip, uint32_t *buf, int len) -{ - int ret; - uint32_t cnt; - struct omap_nand_info *info = chip->priv; - - ret = omap_prefetch_enable(PREFETCH_FIFOTHRESHOLD_MAX, len, 0, info->cs); - if (ret < 0) - return ret; - - do { - int i; - - cnt = readl(&gpmc_cfg->prefetch_status); - cnt = PREFETCH_STATUS_FIFO_CNT(cnt); - - for (i = 0; i < cnt / 4; i++) { - *buf++ = readl(CONFIG_SYS_NAND_BASE); - len -= 4; - } - } while (len); - - omap_prefetch_reset(); - - return 0; -} - -static void omap_nand_read_prefetch8(struct mtd_info *mtd, uint8_t *buf, int len) -{ - int ret; - uint32_t head, tail; - struct nand_chip *chip = mtd->priv; - - /* - * If the destination buffer is unaligned, start with reading - * the overlap byte-wise. - */ - head = ((uint32_t) buf) % 4; - if (head) { - nand_read_buf(mtd, buf, head); - buf += head; - len -= head; - } - - /* - * Only transfer multiples of 4 bytes in a pre-fetched fashion. - * If there's a residue, care for it byte-wise afterwards. - */ - tail = len % 4; - - ret = __read_prefetch_aligned(chip, (uint32_t *) buf, len - tail); - if (ret < 0) { - /* fallback in case the prefetch engine is busy */ - nand_read_buf(mtd, buf, len); - } else if (tail) { - buf += len - tail; - nand_read_buf(mtd, buf, tail); - } -} -#endif /* CONFIG_NAND_OMAP_GPMC_PREFETCH */ - /** * omap_read_page_bch - hardware ecc based page read function * @mtd: mtd info structure @@ -1011,13 +1021,11 @@ int board_nand_init(struct nand_chip *nand) if (err) return err; - /* TODO: Implement for 16-bit bus width */ - if (nand->options & NAND_BUSWIDTH_16) - nand->read_buf = nand_read_buf16; #ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH - else - nand->read_buf = omap_nand_read_prefetch8; + nand->read_buf = omap_nand_read_prefetch; #else + if (nand->options & NAND_BUSWIDTH_16) + nand->read_buf = nand_read_buf16; else nand->read_buf = nand_read_buf; #endif diff --git a/drivers/mtd/nand/sunxi_nand_spl.c b/drivers/mtd/nand/sunxi_nand_spl.c deleted file mode 100644 index 75982f5..0000000 --- a/drivers/mtd/nand/sunxi_nand_spl.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (c) 2014, Antmicro Ltd <www.antmicro.com> - * Copyright (c) 2015, Turtle Solutions <www.turtle-solutions.eu> - * Copyright (c) 2015, Roy Spliet <rspliet@ultimaker.com> - * - * SPDX-License-Identifier: GPL-2.0+ - * - * \todo Detect chip parameters (page size, ECC mode, randomisation...) - */ - -#include <common.h> -#include <config.h> -#include <asm/io.h> -#include <nand.h> -#include <asm/arch/cpu.h> -#include <asm/arch/clock.h> -#include <asm/arch/dma.h> -#include <asm/arch/nand.h> - -void -nand_init(void) -{ - struct sunxi_ccm_reg * const ccm = - (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - struct sunxi_nand * const nand = (struct sunxi_nand *)SUNXI_NFC_BASE; - u32 val; - - board_nand_init(); - - /* "un-gate" NAND clock and clock source - * This assumes that the clock was already correctly configured by - * BootROM */ - setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_NAND0)); -#ifdef CONFIG_MACH_SUN9I - setbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA)); -#else - setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA)); -#endif - setbits_le32(&ccm->nand0_clk_cfg, 0x80000000); - - val = readl(&nand->ctl); - val |= SUNXI_NAND_CTL_RST; - writel(val, &nand->ctl); - - /* Wait until reset pin is deasserted */ - do { - val = readl(&nand->ctl); - if (!(val & SUNXI_NAND_CTL_RST)) - break; - } while (1); - - /** \todo Chip select, currently kind of static */ - val = readl(&nand->ctl); - val &= 0xf0fff0f2; - val |= SUNXI_NAND_CTL_EN; - val |= SUNXI_NAND_CTL_PAGE_SIZE(CONFIG_NAND_SUNXI_PAGE_SIZE); - writel(val, &nand->ctl); - - writel(0x100, &nand->timing_ctl); - writel(0x7ff, &nand->timing_cfg); - - /* reset CMD */ - val = SUNXI_NAND_CMD_SEND_CMD1 | SUNXI_NAND_CMD_WAIT_FLAG | - NAND_CMD_RESET; - writel(val, &nand->cmd); - do { - val = readl(&nand->st); - if (val & (1<<1)) - break; - udelay(1000); - } while (1); - - printf("Nand initialised\n"); -} - -int -nand_wait_timeout(u32 *reg, u32 mask, u32 val) -{ - unsigned long tmo = timer_get_us() + 1000000; /* 1s */ - - while ((readl(reg) & mask) != val) { - if (timer_get_us() > tmo) - return -ETIMEDOUT; - } - - return 0; -} - -/* random seed */ -static const uint16_t random_seed[128] = { - 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, - 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, - 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, - 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, - 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, - 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, - 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, - 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, - 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, - 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, - 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, - 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, - 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, - 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, - 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, - 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, -}; - -uint32_t ecc_errors = 0; - -static void -nand_config_ecc(struct sunxi_nand *nand, uint32_t page, int syndrome) -{ - static u8 strength[] = {16, 24, 28, 32, 40, 48, 56, 60, 64}; - int i; - uint32_t ecc_mode; - u32 ecc; - u16 seed = 0; - - for (i = 0; i < ARRAY_SIZE(strength); i++) { - if (CONFIG_NAND_SUNXI_ECC_STRENGTH == strength[i]) { - ecc_mode = i; - break; - } - } - - if (i == ARRAY_SIZE(strength)) { - printf("ECC strength unsupported\n"); - return; - } - - ecc = SUNXI_NAND_ECC_CTL_ECC_EN | - SUNXI_NAND_ECC_CTL_PIPELINE | - SUNXI_NAND_ECC_CTL_RND_EN | - SUNXI_NAND_ECC_CTL_MODE(ecc_mode); - - if (CONFIG_NAND_SUNXI_ECC_STEP == 512) - ecc |= SUNXI_NAND_ECC_CTL_BS_512B; - - if (syndrome) - seed = 0x4A80; - else - seed = random_seed[page % ARRAY_SIZE(random_seed)]; - - ecc |= SUNXI_NAND_ECC_CTL_RND_SEED(seed); - - writel(ecc, &nand->ecc_ctl); -} - -/* read CONFIG_NAND_SUNXI_ECC_STEP bytes from real_addr to temp_buf */ -void -nand_read_block(struct sunxi_nand *nand, phys_addr_t src, dma_addr_t dst, - int syndrome) -{ - struct sunxi_dma * const dma = (struct sunxi_dma *)SUNXI_DMA_BASE; - struct sunxi_dma_cfg * const dma_cfg = &dma->ddma[0]; - - uint32_t shift; - uint32_t page; - uint32_t addr; - uint32_t oob_offset; - uint32_t ecc_bytes; - u32 val; - u32 cmd; - - page = src / CONFIG_NAND_SUNXI_PAGE_SIZE; - if (page > 0xFFFF) { - /* TODO: currently this is not supported */ - printf("Reading from address >= %08X is not allowed.\n", - 0xFFFF * CONFIG_NAND_SUNXI_PAGE_SIZE); - return; - } - - shift = src % CONFIG_NAND_SUNXI_PAGE_SIZE; - writel(0, &nand->ecc_st); - - /* ECC_CTL, randomization */ - ecc_bytes = CONFIG_NAND_SUNXI_ECC_STRENGTH * - fls(CONFIG_NAND_SUNXI_ECC_STEP * 8); - ecc_bytes = DIV_ROUND_UP(ecc_bytes, 8); - ecc_bytes += (ecc_bytes & 1); /* Align to 2-bytes */ - ecc_bytes += 4; - - nand_config_ecc(nand, page, syndrome); - if (syndrome) { - /* shift every 1kB in syndrome */ - shift += (shift / CONFIG_NAND_SUNXI_ECC_STEP) * ecc_bytes; - oob_offset = CONFIG_NAND_SUNXI_ECC_STEP + shift; - } else { - oob_offset = CONFIG_NAND_SUNXI_PAGE_SIZE + - (shift / CONFIG_NAND_SUNXI_ECC_STEP) * ecc_bytes; - } - - addr = (page << 16) | shift; - - /* DMA */ - val = readl(&nand->ctl); - writel(val | SUNXI_NAND_CTL_RAM_METHOD_DMA, &nand->ctl); - - writel(oob_offset, &nand->spare_area); - - /* DMAC - * \todo Separate this into a tidy driver */ - writel(0x0, &dma->irq_en); /* clear dma interrupts */ - writel((uint32_t) &nand->io_data , &dma_cfg->src_addr); - writel(dst , &dma_cfg->dst_addr); - writel(0x00007F0F , &dma_cfg->ddma_para); - writel(CONFIG_NAND_SUNXI_ECC_STEP, &dma_cfg->bc); - - val = SUNXI_DMA_CTL_SRC_DRQ(DDMA_SRC_DRQ_NAND) | - SUNXI_DMA_CTL_MODE_IO | - SUNXI_DMA_CTL_SRC_DATA_WIDTH_32 | - SUNXI_DMA_CTL_DST_DRQ(DDMA_DST_DRQ_SDRAM) | - SUNXI_DMA_CTL_DST_DATA_WIDTH_32 | - SUNXI_DMA_CTL_TRIGGER; - writel(val, &dma_cfg->ctl); - - writel(0x00E00530, &nand->rcmd_set); - nand_wait_timeout(&nand->st, SUNXI_NAND_ST_FIFO_FULL, 0); - - writel(1 , &nand->block_num); - writel(addr, &nand->addr_low); - writel(0 , &nand->addr_high); - - /* CMD (PAGE READ) */ - cmd = 0x85E80000; - cmd |= SUNXI_NAND_CMD_ADDR_CYCLES(CONFIG_NAND_SUNXI_ADDR_CYCLES); - cmd |= (syndrome ? SUNXI_NAND_CMD_ORDER_SEQ : - SUNXI_NAND_CMD_ORDER_INTERLEAVE); - writel(cmd, &nand->cmd); - - if(nand_wait_timeout(&nand->st, SUNXI_NAND_ST_DMA_INT, - SUNXI_NAND_ST_DMA_INT)) { - printf("NAND timeout reading data\n"); - return; - } - - if(nand_wait_timeout(&dma_cfg->ctl, SUNXI_DMA_CTL_TRIGGER, 0)) { - printf("NAND timeout reading data\n"); - return; - } - - if (readl(&nand->ecc_st)) - ecc_errors++; -} - -int -nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) -{ - struct sunxi_nand * const nand = (struct sunxi_nand *)SUNXI_NFC_BASE; - dma_addr_t dst_block; - dma_addr_t dst_end; - phys_addr_t addr = offs; - - dst_end = ((dma_addr_t) dest) + size; - - memset((void *)dest, 0x0, size); - ecc_errors = 0; - for (dst_block = (dma_addr_t) dest; dst_block < dst_end; - dst_block += CONFIG_NAND_SUNXI_ECC_STEP, - addr += CONFIG_NAND_SUNXI_ECC_STEP) { - /* syndrome read first 4MiB to match Allwinner BootROM */ - nand_read_block(nand, addr, dst_block, addr < 0x400000); - } - - if (ecc_errors) - printf("Error: %d ECC failures detected\n", ecc_errors); - return ecc_errors == 0; -} - -void -nand_deselect(void) -{} diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index ac6d09f..4f0c040 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -1,3 +1,5 @@ +menu "SPI Flash Support" + config DM_SPI_FLASH bool "Enable Driver Model for SPI flash" depends on DM && DM_SPI @@ -22,3 +24,49 @@ config SPI_FLASH_SANDBOX bus (see CONFIG_SANDBOX_SPI) and SPI traffic will be routed to this device. Typically the contents of the emulated SPI flash device is stored in a file on the host filesystem. + +config SPI_FLASH + bool "Legacy SPI Flash Interface support" + help + Enable the legacy SPI flash support. This will include basic + standard support for things like probing, read / write, and + erasing through cmd_sf interface. + + If unsure, say N + +config SPI_FLASH_BAR + bool "SPI flash Bank/Extended address register support" + depends on SPI_FLASH + help + Enable the SPI flash Bank/Extended address register support. + Bank/Extended address registers are used to access the flash + which has size > 16MiB in 3-byte addressing. + +config SPI_FLASH_DATAFLASH + bool "AT45xxx DataFlash support" + depends on SPI_FLASH && DM_SPI_FLASH + help + Enable the access for SPI-flash-based AT45xxx DataFlash chips. + DataFlash is a kind of SPI flash. Most AT45 chips have two buffers + in each chip, which may be used for double buffered I/O; but this + driver doesn't (yet) use these for any kind of i/o overlap or prefetching. + + Sometimes DataFlash is packaged in MMC-format cards, although the + MMC stack can't (yet?) distinguish between MMC and DataFlash + protocols during enumeration. + + If unsure, say N + +config SPI_FLASH_MTD + bool "SPI Flash MTD support" + depends on SPI_FLASH + help + Enable the MTD support for spi flash layer, this adapter is for + translating mtd_read/mtd_write commands into spi_flash_read/write + commands. It is not intended to use it within sf_cmd or the SPI + flash subsystem. Such an adapter is needed for subsystems like + UBI which can only operate on top of the MTD layer. + + If unsure, say N + +endmenu # menu "SPI Flash Support" diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index c61b784..ff48b25 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -17,5 +17,7 @@ obj-$(CONFIG_SPI_FLASH) += sf_probe.o #endif obj-$(CONFIG_CMD_SF) += sf.o obj-$(CONFIG_SPI_FLASH) += sf_ops.o sf_params.o +obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o +obj-$(CONFIG_SPI_FLASH_MTD) += sf_mtd.o obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o obj-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o diff --git a/drivers/mtd/spi/sf_dataflash.c b/drivers/mtd/spi/sf_dataflash.c new file mode 100644 index 0000000..3111f4f --- /dev/null +++ b/drivers/mtd/spi/sf_dataflash.c @@ -0,0 +1,701 @@ +/* + * + * Atmel DataFlash probing + * + * Copyright (C) 2004-2009, 2015 Freescale Semiconductor, Inc. + * Haikun Wang (haikun.wang@freescale.com) + * + * SPDX-License-Identifier: GPL-2.0+ +*/ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <spi.h> +#include <spi_flash.h> +#include <div64.h> +#include <linux/err.h> +#include <linux/math64.h> + +#include "sf_internal.h" + +/* reads can bypass the buffers */ +#define OP_READ_CONTINUOUS 0xE8 +#define OP_READ_PAGE 0xD2 + +/* group B requests can run even while status reports "busy" */ +#define OP_READ_STATUS 0xD7 /* group B */ + +/* move data between host and buffer */ +#define OP_READ_BUFFER1 0xD4 /* group B */ +#define OP_READ_BUFFER2 0xD6 /* group B */ +#define OP_WRITE_BUFFER1 0x84 /* group B */ +#define OP_WRITE_BUFFER2 0x87 /* group B */ + +/* erasing flash */ +#define OP_ERASE_PAGE 0x81 +#define OP_ERASE_BLOCK 0x50 + +/* move data between buffer and flash */ +#define OP_TRANSFER_BUF1 0x53 +#define OP_TRANSFER_BUF2 0x55 +#define OP_MREAD_BUFFER1 0xD4 +#define OP_MREAD_BUFFER2 0xD6 +#define OP_MWERASE_BUFFER1 0x83 +#define OP_MWERASE_BUFFER2 0x86 +#define OP_MWRITE_BUFFER1 0x88 /* sector must be pre-erased */ +#define OP_MWRITE_BUFFER2 0x89 /* sector must be pre-erased */ + +/* write to buffer, then write-erase to flash */ +#define OP_PROGRAM_VIA_BUF1 0x82 +#define OP_PROGRAM_VIA_BUF2 0x85 + +/* compare buffer to flash */ +#define OP_COMPARE_BUF1 0x60 +#define OP_COMPARE_BUF2 0x61 + +/* read flash to buffer, then write-erase to flash */ +#define OP_REWRITE_VIA_BUF1 0x58 +#define OP_REWRITE_VIA_BUF2 0x59 + +/* + * newer chips report JEDEC manufacturer and device IDs; chip + * serial number and OTP bits; and per-sector writeprotect. + */ +#define OP_READ_ID 0x9F +#define OP_READ_SECURITY 0x77 +#define OP_WRITE_SECURITY_REVC 0x9A +#define OP_WRITE_SECURITY 0x9B /* revision D */ + + +struct dataflash { + uint8_t command[16]; + unsigned short page_offset; /* offset in flash address */ +}; + +/* + * Return the status of the DataFlash device. + */ +static inline int dataflash_status(struct spi_slave *spi) +{ + int ret; + u8 status; + /* + * NOTE: at45db321c over 25 MHz wants to write + * a dummy byte after the opcode... + */ + ret = spi_flash_cmd(spi, OP_READ_STATUS, &status, 1); + return ret ? -EIO : status; +} + +/* + * Poll the DataFlash device until it is READY. + * This usually takes 5-20 msec or so; more for sector erase. + * ready: return > 0 + */ +static int dataflash_waitready(struct spi_slave *spi) +{ + int status; + int timeout = 2 * CONFIG_SYS_HZ; + int timebase; + + timebase = get_timer(0); + do { + status = dataflash_status(spi); + if (status < 0) + status = 0; + + if (status & (1 << 7)) /* RDY/nBSY */ + return status; + + mdelay(3); + } while (get_timer(timebase) < timeout); + + return -ETIME; +} + +/* + * Erase pages of flash. + */ +static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len) +{ + struct dataflash *dataflash; + struct spi_flash *spi_flash; + struct spi_slave *spi; + unsigned blocksize; + uint8_t *command; + uint32_t rem; + int status; + + dataflash = dev_get_priv(dev); + spi_flash = dev_get_uclass_priv(dev); + spi = spi_flash->spi; + + blocksize = spi_flash->page_size << 3; + + memset(dataflash->command, 0 , sizeof(dataflash->command)); + command = dataflash->command; + + debug("%s: erase addr=0x%x len 0x%x\n", dev->name, offset, len); + + div_u64_rem(len, spi_flash->page_size, &rem); + if (rem) + return -EINVAL; + div_u64_rem(offset, spi_flash->page_size, &rem); + if (rem) + return -EINVAL; + + status = spi_claim_bus(spi); + if (status) { + debug("SPI DATAFLASH: unable to claim SPI bus\n"); + return status; + } + + while (len > 0) { + unsigned int pageaddr; + int do_block; + /* + * Calculate flash page address; use block erase (for speed) if + * we're at a block boundary and need to erase the whole block. + */ + pageaddr = div_u64(offset, spi_flash->page_size); + do_block = (pageaddr & 0x7) == 0 && len >= blocksize; + pageaddr = pageaddr << dataflash->page_offset; + + command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE; + command[1] = (uint8_t)(pageaddr >> 16); + command[2] = (uint8_t)(pageaddr >> 8); + command[3] = 0; + + debug("%s ERASE %s: (%x) %x %x %x [%d]\n", + dev->name, do_block ? "block" : "page", + command[0], command[1], command[2], command[3], + pageaddr); + + status = spi_flash_cmd_write(spi, command, 4, NULL, 0); + if (status < 0) { + debug("%s: erase send command error!\n", dev->name); + return -EIO; + } + + status = dataflash_waitready(spi); + if (status < 0) { + debug("%s: erase waitready error!\n", dev->name); + return status; + } + + if (do_block) { + offset += blocksize; + len -= blocksize; + } else { + offset += spi_flash->page_size; + len -= spi_flash->page_size; + } + } + + spi_release_bus(spi); + + return 0; +} + +/* + * Read from the DataFlash device. + * offset : Start offset in flash device + * len : Amount to read + * buf : Buffer containing the data + */ +static int spi_dataflash_read(struct udevice *dev, u32 offset, size_t len, + void *buf) +{ + struct dataflash *dataflash; + struct spi_flash *spi_flash; + struct spi_slave *spi; + unsigned int addr; + uint8_t *command; + int status; + + dataflash = dev_get_priv(dev); + spi_flash = dev_get_uclass_priv(dev); + spi = spi_flash->spi; + + memset(dataflash->command, 0 , sizeof(dataflash->command)); + command = dataflash->command; + + debug("%s: erase addr=0x%x len 0x%x\n", dev->name, offset, len); + debug("READ: (%x) %x %x %x\n", + command[0], command[1], command[2], command[3]); + + /* Calculate flash page/byte address */ + addr = (((unsigned)offset / spi_flash->page_size) + << dataflash->page_offset) + + ((unsigned)offset % spi_flash->page_size); + + status = spi_claim_bus(spi); + if (status) { + debug("SPI DATAFLASH: unable to claim SPI bus\n"); + return status; + } + + /* + * Continuous read, max clock = f(car) which may be less than + * the peak rate available. Some chips support commands with + * fewer "don't care" bytes. Both buffers stay unchanged. + */ + command[0] = OP_READ_CONTINUOUS; + command[1] = (uint8_t)(addr >> 16); + command[2] = (uint8_t)(addr >> 8); + command[3] = (uint8_t)(addr >> 0); + + /* plus 4 "don't care" bytes, command len: 4 + 4 "don't care" bytes */ + status = spi_flash_cmd_read(spi, command, 8, buf, len); + + spi_release_bus(spi); + + return status; +} + +/* + * Write to the DataFlash device. + * offset : Start offset in flash device + * len : Amount to write + * buf : Buffer containing the data + */ +int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len, + const void *buf) +{ + struct dataflash *dataflash; + struct spi_flash *spi_flash; + struct spi_slave *spi; + uint8_t *command; + unsigned int pageaddr, addr, to, writelen; + size_t remaining = len; + u_char *writebuf = (u_char *)buf; + int status = -EINVAL; + + dataflash = dev_get_priv(dev); + spi_flash = dev_get_uclass_priv(dev); + spi = spi_flash->spi; + + memset(dataflash->command, 0 , sizeof(dataflash->command)); + command = dataflash->command; + + debug("%s: write 0x%x..0x%x\n", dev->name, offset, (offset + len)); + + pageaddr = ((unsigned)offset / spi_flash->page_size); + to = ((unsigned)offset % spi_flash->page_size); + if (to + len > spi_flash->page_size) + writelen = spi_flash->page_size - to; + else + writelen = len; + + status = spi_claim_bus(spi); + if (status) { + debug("SPI DATAFLASH: unable to claim SPI bus\n"); + return status; + } + + while (remaining > 0) { + debug("write @ %d:%d len=%d\n", pageaddr, to, writelen); + + /* + * REVISIT: + * (a) each page in a sector must be rewritten at least + * once every 10K sibling erase/program operations. + * (b) for pages that are already erased, we could + * use WRITE+MWRITE not PROGRAM for ~30% speedup. + * (c) WRITE to buffer could be done while waiting for + * a previous MWRITE/MWERASE to complete ... + * (d) error handling here seems to be mostly missing. + * + * Two persistent bits per page, plus a per-sector counter, + * could support (a) and (b) ... we might consider using + * the second half of sector zero, which is just one block, + * to track that state. (On AT91, that sector should also + * support boot-from-DataFlash.) + */ + + addr = pageaddr << dataflash->page_offset; + + /* (1) Maybe transfer partial page to Buffer1 */ + if (writelen != spi_flash->page_size) { + command[0] = OP_TRANSFER_BUF1; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = 0; + + debug("TRANSFER: (%x) %x %x %x\n", + command[0], command[1], command[2], command[3]); + + status = spi_flash_cmd_write(spi, command, 4, NULL, 0); + if (status < 0) { + debug("%s: write(<pagesize) command error!\n", + dev->name); + return -EIO; + } + + status = dataflash_waitready(spi); + if (status < 0) { + debug("%s: write(<pagesize) waitready error!\n", + dev->name); + return status; + } + } + + /* (2) Program full page via Buffer1 */ + addr += to; + command[0] = OP_PROGRAM_VIA_BUF1; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = (addr & 0x000000FF); + + debug("PROGRAM: (%x) %x %x %x\n", + command[0], command[1], command[2], command[3]); + + status = spi_flash_cmd_write(spi, command, + 4, writebuf, writelen); + if (status < 0) { + debug("%s: write send command error!\n", dev->name); + return -EIO; + } + + status = dataflash_waitready(spi); + if (status < 0) { + debug("%s: write waitready error!\n", dev->name); + return status; + } + +#ifdef CONFIG_SPI_DATAFLASH_WRITE_VERIFY + /* (3) Compare to Buffer1 */ + addr = pageaddr << dataflash->page_offset; + command[0] = OP_COMPARE_BUF1; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = 0; + + debug("COMPARE: (%x) %x %x %x\n", + command[0], command[1], command[2], command[3]); + + status = spi_flash_cmd_write(spi, command, + 4, writebuf, writelen); + if (status < 0) { + debug("%s: write(compare) send command error!\n", + dev->name); + return -EIO; + } + + status = dataflash_waitready(spi); + + /* Check result of the compare operation */ + if (status & (1 << 6)) { + printf("SPI DataFlash: write compare page %u, err %d\n", + pageaddr, status); + remaining = 0; + status = -EIO; + break; + } else { + status = 0; + } + +#endif /* CONFIG_SPI_DATAFLASH_WRITE_VERIFY */ + remaining = remaining - writelen; + pageaddr++; + to = 0; + writebuf += writelen; + + if (remaining > spi_flash->page_size) + writelen = spi_flash->page_size; + else + writelen = remaining; + } + + spi_release_bus(spi); + + return 0; +} + +static int add_dataflash(struct udevice *dev, char *name, int nr_pages, + int pagesize, int pageoffset, char revision) +{ + struct spi_flash *spi_flash; + struct dataflash *dataflash; + + dataflash = dev_get_priv(dev); + spi_flash = dev_get_uclass_priv(dev); + + dataflash->page_offset = pageoffset; + + spi_flash->name = name; + spi_flash->page_size = pagesize; + spi_flash->size = nr_pages * pagesize; + spi_flash->erase_size = pagesize; + +#ifndef CONFIG_SPL_BUILD + printf("SPI DataFlash: Detected %s with page size ", spi_flash->name); + print_size(spi_flash->page_size, ", erase size "); + print_size(spi_flash->erase_size, ", total "); + print_size(spi_flash->size, ""); + printf(", revision %c", revision); + puts("\n"); +#endif + + return 0; +} + +struct flash_info { + char *name; + + /* + * JEDEC id has a high byte of zero plus three data bytes: + * the manufacturer id, then a two byte device id. + */ + uint32_t jedec_id; + + /* The size listed here is what works with OP_ERASE_PAGE. */ + unsigned nr_pages; + uint16_t pagesize; + uint16_t pageoffset; + + uint16_t flags; +#define SUP_POW2PS 0x0002 /* supports 2^N byte pages */ +#define IS_POW2PS 0x0001 /* uses 2^N byte pages */ +}; + +static struct flash_info dataflash_data[] = { + /* + * NOTE: chips with SUP_POW2PS (rev D and up) need two entries, + * one with IS_POW2PS and the other without. The entry with the + * non-2^N byte page size can't name exact chip revisions without + * losing backwards compatibility for cmdlinepart. + * + * Those two entries have different name spelling format in order to + * show their difference obviously. + * The upper case refer to the chip isn't in normal 2^N bytes page-size + * mode. + * The lower case refer to the chip is in normal 2^N bytes page-size + * mode. + * + * These newer chips also support 128-byte security registers (with + * 64 bytes one-time-programmable) and software write-protection. + */ + { "AT45DB011B", 0x1f2200, 512, 264, 9, SUP_POW2PS}, + { "at45db011d", 0x1f2200, 512, 256, 8, SUP_POW2PS | IS_POW2PS}, + + { "AT45DB021B", 0x1f2300, 1024, 264, 9, SUP_POW2PS}, + { "at45db021d", 0x1f2300, 1024, 256, 8, SUP_POW2PS | IS_POW2PS}, + + { "AT45DB041x", 0x1f2400, 2048, 264, 9, SUP_POW2PS}, + { "at45db041d", 0x1f2400, 2048, 256, 8, SUP_POW2PS | IS_POW2PS}, + + { "AT45DB081B", 0x1f2500, 4096, 264, 9, SUP_POW2PS}, + { "at45db081d", 0x1f2500, 4096, 256, 8, SUP_POW2PS | IS_POW2PS}, + + { "AT45DB161x", 0x1f2600, 4096, 528, 10, SUP_POW2PS}, + { "at45db161d", 0x1f2600, 4096, 512, 9, SUP_POW2PS | IS_POW2PS}, + + { "AT45DB321x", 0x1f2700, 8192, 528, 10, 0}, /* rev C */ + + { "AT45DB321x", 0x1f2701, 8192, 528, 10, SUP_POW2PS}, + { "at45db321d", 0x1f2701, 8192, 512, 9, SUP_POW2PS | IS_POW2PS}, + + { "AT45DB642x", 0x1f2800, 8192, 1056, 11, SUP_POW2PS}, + { "at45db642d", 0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS}, +}; + +static struct flash_info *jedec_probe(struct spi_slave *spi, u8 *id) +{ + int tmp; + uint32_t jedec; + struct flash_info *info; + int status; + + /* + * JEDEC also defines an optional "extended device information" + * string for after vendor-specific data, after the three bytes + * we use here. Supporting some chips might require using it. + * + * If the vendor ID isn't Atmel's (0x1f), assume this call failed. + * That's not an error; only rev C and newer chips handle it, and + * only Atmel sells these chips. + */ + if (id[0] != 0x1f) + return NULL; + + jedec = id[0]; + jedec = jedec << 8; + jedec |= id[1]; + jedec = jedec << 8; + jedec |= id[2]; + + for (tmp = 0, info = dataflash_data; + tmp < ARRAY_SIZE(dataflash_data); + tmp++, info++) { + if (info->jedec_id == jedec) { + if (info->flags & SUP_POW2PS) { + status = dataflash_status(spi); + if (status < 0) { + debug("SPI DataFlash: status error %d\n", + status); + return NULL; + } + if (status & 0x1) { + if (info->flags & IS_POW2PS) + return info; + } else { + if (!(info->flags & IS_POW2PS)) + return info; + } + } else { + return info; + } + } + } + + /* + * Treat other chips as errors ... we won't know the right page + * size (it might be binary) even when we can tell which density + * class is involved (legacy chip id scheme). + */ + printf("SPI DataFlash: Unsupported flash IDs: "); + printf("manuf %02x, jedec %04x, ext_jedec %04x\n", + id[0], jedec, id[3] << 8 | id[4]); + return NULL; +} + +/* + * Detect and initialize DataFlash device, using JEDEC IDs on newer chips + * or else the ID code embedded in the status bits: + * + * Device Density ID code #Pages PageSize Offset + * AT45DB011B 1Mbit (128K) xx0011xx (0x0c) 512 264 9 + * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1024 264 9 + * AT45DB041B 4Mbit (512K) xx0111xx (0x1c) 2048 264 9 + * AT45DB081B 8Mbit (1M) xx1001xx (0x24) 4096 264 9 + * AT45DB0161B 16Mbit (2M) xx1011xx (0x2c) 4096 528 10 + * AT45DB0321B 32Mbit (4M) xx1101xx (0x34) 8192 528 10 + * AT45DB0642 64Mbit (8M) xx111xxx (0x3c) 8192 1056 11 + * AT45DB1282 128Mbit (16M) xx0100xx (0x10) 16384 1056 11 + */ +static int spi_dataflash_probe(struct udevice *dev) +{ + struct spi_slave *spi = dev_get_parentdata(dev); + struct spi_flash *spi_flash; + struct flash_info *info; + u8 idcode[5]; + int ret, status = 0; + + spi_flash = dev_get_uclass_priv(dev); + spi_flash->dev = dev; + + ret = spi_claim_bus(spi); + if (ret) + return ret; + + ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); + if (ret) { + printf("SPI DataFlash: Failed to get idcodes\n"); + goto err_read_cmd; + } + + /* + * Try to detect dataflash by JEDEC ID. + * If it succeeds we know we have either a C or D part. + * D will support power of 2 pagesize option. + * Both support the security register, though with different + * write procedures. + */ + info = jedec_probe(spi, idcode); + if (info != NULL) + add_dataflash(dev, info->name, info->nr_pages, + info->pagesize, info->pageoffset, + (info->flags & SUP_POW2PS) ? 'd' : 'c'); + else { + /* + * Older chips support only legacy commands, identifing + * capacity using bits in the status byte. + */ + status = dataflash_status(spi); + if (status <= 0 || status == 0xff) { + printf("SPI DataFlash: read status error %d\n", status); + if (status == 0 || status == 0xff) + status = -ENODEV; + goto err_read_cmd; + } + /* + * if there's a device there, assume it's dataflash. + * board setup should have set spi->max_speed_max to + * match f(car) for continuous reads, mode 0 or 3. + */ + switch (status & 0x3c) { + case 0x0c: /* 0 0 1 1 x x */ + status = add_dataflash(dev, "AT45DB011B", + 512, 264, 9, 0); + break; + case 0x14: /* 0 1 0 1 x x */ + status = add_dataflash(dev, "AT45DB021B", + 1024, 264, 9, 0); + break; + case 0x1c: /* 0 1 1 1 x x */ + status = add_dataflash(dev, "AT45DB041x", + 2048, 264, 9, 0); + break; + case 0x24: /* 1 0 0 1 x x */ + status = add_dataflash(dev, "AT45DB081B", + 4096, 264, 9, 0); + break; + case 0x2c: /* 1 0 1 1 x x */ + status = add_dataflash(dev, "AT45DB161x", + 4096, 528, 10, 0); + break; + case 0x34: /* 1 1 0 1 x x */ + status = add_dataflash(dev, "AT45DB321x", + 8192, 528, 10, 0); + break; + case 0x38: /* 1 1 1 x x x */ + case 0x3c: + status = add_dataflash(dev, "AT45DB642x", + 8192, 1056, 11, 0); + break; + /* obsolete AT45DB1282 not (yet?) supported */ + default: + dev_info(&spi->dev, "unsupported device (%x)\n", + status & 0x3c); + status = -ENODEV; + goto err_read_cmd; + } + } + + /* Assign spi data */ + spi_flash->spi = spi; + spi_flash->memory_map = spi->memory_map; + spi_flash->dual_flash = spi->option; + + spi_release_bus(spi); + + return 0; + +err_read_cmd: + spi_release_bus(spi); + + return status; +} + +static const struct dm_spi_flash_ops spi_dataflash_ops = { + .read = spi_dataflash_read, + .write = spi_dataflash_write, + .erase = spi_dataflash_erase, +}; + +static const struct udevice_id spi_dataflash_ids[] = { + { .compatible = "atmel,at45", }, + { .compatible = "atmel,dataflash", }, + { } +}; + +U_BOOT_DRIVER(spi_dataflash) = { + .name = "spi_dataflash", + .id = UCLASS_SPI_FLASH, + .of_match = spi_dataflash_ids, + .probe = spi_dataflash_probe, + .priv_auto_alloc_size = sizeof(struct dataflash), + .ops = &spi_dataflash_ops, +}; diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 4158e13..9fb5557 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -31,9 +31,9 @@ enum spi_read_cmds { }; /* Normal - Extended - Full command set */ -#define RD_NORM (ARRAY_SLOW | ARRAY_FAST) -#define RD_EXTN (RD_NORM | DUAL_OUTPUT_FAST | DUAL_IO_FAST) -#define RD_FULL (RD_EXTN | QUAD_OUTPUT_FAST | QUAD_IO_FAST) +#define RD_NORM (ARRAY_SLOW | ARRAY_FAST) +#define RD_EXTN (RD_NORM | DUAL_OUTPUT_FAST | DUAL_IO_FAST) +#define RD_FULL (RD_EXTN | QUAD_OUTPUT_FAST | QUAD_IO_FAST) /* sf param flags */ enum { @@ -67,12 +67,12 @@ enum { #define CMD_WRITE_STATUS 0x01 #define CMD_PAGE_PROGRAM 0x02 #define CMD_WRITE_DISABLE 0x04 -#define CMD_READ_STATUS 0x05 +#define CMD_READ_STATUS 0x05 #define CMD_QUAD_PAGE_PROGRAM 0x32 #define CMD_READ_STATUS1 0x35 #define CMD_WRITE_ENABLE 0x06 -#define CMD_READ_CONFIG 0x35 -#define CMD_FLAG_STATUS 0x70 +#define CMD_READ_CONFIG 0x35 +#define CMD_FLAG_STATUS 0x70 /* Read commands */ #define CMD_READ_ARRAY_SLOW 0x03 @@ -99,13 +99,13 @@ enum { /* Flash timeout values */ #define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ) -#define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ) +#define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ) #define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CONFIG_SYS_HZ) /* SST specific */ #ifdef CONFIG_SPI_FLASH_SST # define CMD_SST_BP 0x02 /* Byte Program */ -# define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ +# define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, const void *buf); @@ -121,7 +121,7 @@ int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len, * @ext_jedec: Device ext_jedec ID * @sector_size: Isn't necessarily a sector size from vendor, * the size listed here is what works with CMD_ERASE_64K - * @nr_sectors: No.of sectors on this device + * @nr_sectors: No.of sectors on this device * @e_rd_cmd: Enum list for read commands * @flags: Important param, for flash specific behaviour */ @@ -218,4 +218,9 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, size_t len, void *data); +#ifdef CONFIG_SPI_FLASH_MTD +int spi_flash_mtd_register(struct spi_flash *flash); +void spi_flash_mtd_unregister(void); +#endif + #endif /* _SF_INTERNAL_H_ */ diff --git a/drivers/mtd/spi/sf_mtd.c b/drivers/mtd/spi/sf_mtd.c new file mode 100644 index 0000000..0b9cb62 --- /dev/null +++ b/drivers/mtd/spi/sf_mtd.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2012-2014 Daniel Schwierzeck, daniel.schwierzeck@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <asm/errno.h> +#include <linux/mtd/mtd.h> +#include <spi_flash.h> + +static struct mtd_info sf_mtd_info; +static char sf_mtd_name[8]; + +static int spi_flash_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct spi_flash *flash = mtd->priv; + int err; + + instr->state = MTD_ERASING; + + err = spi_flash_erase(flash, instr->addr, instr->len); + if (err) { + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; + return -EIO; + } + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + +static int spi_flash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct spi_flash *flash = mtd->priv; + int err; + + err = spi_flash_read(flash, from, len, buf); + if (!err) + *retlen = len; + + return err; +} + +static int spi_flash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct spi_flash *flash = mtd->priv; + int err; + + err = spi_flash_write(flash, to, len, buf); + if (!err) + *retlen = len; + + return err; +} + +static void spi_flash_mtd_sync(struct mtd_info *mtd) +{ +} + +static int spi_flash_mtd_number(void) +{ +#ifdef CONFIG_SYS_MAX_FLASH_BANKS + return CONFIG_SYS_MAX_FLASH_BANKS; +#else + return 0; +#endif +} + +int spi_flash_mtd_register(struct spi_flash *flash) +{ + memset(&sf_mtd_info, 0, sizeof(sf_mtd_info)); + sprintf(sf_mtd_name, "nor%d", spi_flash_mtd_number()); + + sf_mtd_info.name = sf_mtd_name; + sf_mtd_info.type = MTD_NORFLASH; + sf_mtd_info.flags = MTD_CAP_NORFLASH; + sf_mtd_info.writesize = 1; + sf_mtd_info.writebufsize = flash->page_size; + + sf_mtd_info._erase = spi_flash_mtd_erase; + sf_mtd_info._read = spi_flash_mtd_read; + sf_mtd_info._write = spi_flash_mtd_write; + sf_mtd_info._sync = spi_flash_mtd_sync; + + sf_mtd_info.size = flash->size; + sf_mtd_info.priv = flash; + + /* Only uniform flash devices for now */ + sf_mtd_info.numeraseregions = 0; + sf_mtd_info.erasesize = flash->sector_size; + + return add_mtd_device(&sf_mtd_info); +} + +void spi_flash_mtd_unregister(void) +{ + del_mtd_device(&sf_mtd_info); +} diff --git a/drivers/mtd/spi/sf_params.c b/drivers/mtd/spi/sf_params.c index c12e8c6..4a4a3af 100644 --- a/drivers/mtd/spi/sf_params.c +++ b/drivers/mtd/spi/sf_params.c @@ -100,7 +100,7 @@ const struct spi_flash_params spi_flash_params_table[] = { {"SST25WF010", 0xbf2502, 0x0, 64 * 1024, 2, RD_NORM, SECT_4K | SST_WR}, {"SST25WF020", 0xbf2503, 0x0, 64 * 1024, 4, RD_NORM, SECT_4K | SST_WR}, {"SST25WF040", 0xbf2504, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K | SST_WR}, - {"SST25WF040B", 0x621613, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K | SST_WR}, + {"SST25WF040B", 0x621613, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K}, {"SST25WF080", 0xbf2505, 0x0, 64 * 1024, 16, RD_NORM, SECT_4K | SST_WR}, #endif #ifdef CONFIG_SPI_FLASH_WINBOND /* WINBOND */ diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index 201471c..e0283dc 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -372,11 +372,9 @@ int spi_flash_probe_slave(struct spi_slave *spi, struct spi_flash *flash) puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); } #endif - - /* Release spi bus */ - spi_release_bus(spi); - - return 0; +#ifdef CONFIG_SPI_FLASH_MTD + ret = spi_flash_mtd_register(flash); +#endif err_read_id: spi_release_bus(spi); @@ -430,6 +428,9 @@ struct spi_flash *spi_flash_probe_fdt(const void *blob, int slave_node, void spi_flash_free(struct spi_flash *flash) { +#ifdef CONFIG_SPI_FLASH_MTD + spi_flash_mtd_unregister(); +#endif spi_free_slave(flash->spi); free(flash); } diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 973258a..ce76a02 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -11,6 +11,7 @@ config DM_ETH menuconfig NETDEVICES bool "Network device support" depends on NET + default y if DM_ETH help You must select Y to enable any network device support Generally if you have any networking support this is a given diff --git a/drivers/net/designware.c b/drivers/net/designware.c index ae51cf3..645ca64 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -243,6 +243,12 @@ static int _dw_eth_init(struct dw_eth_dev *priv, u8 *enetaddr) mdelay(100); }; + /* + * Soft reset above clears HW address registers. + * So we have to set it here once again. + */ + _dw_write_hwaddr(priv, enetaddr); + rx_descs_init(priv); tx_descs_init(priv); diff --git a/drivers/serial/arm_dcc.c b/drivers/serial/arm_dcc.c index e777737..df7eb05 100644 --- a/drivers/serial/arm_dcc.c +++ b/drivers/serial/arm_dcc.c @@ -61,6 +61,22 @@ #define status_dcc(x) \ __asm__ volatile ("mrc p14, 0, %0, c14, c0, 0\n" : "=r" (x)) +#elif defined(CONFIG_CPU_ARMV8) +/* + * ARMV8 + */ +#define DCC_RBIT (1 << 30) +#define DCC_WBIT (1 << 29) + +#define write_dcc(x) \ + __asm__ volatile ("msr dbgdtrtx_el0, %0\n" : : "r" (x)) + +#define read_dcc(x) \ + __asm__ volatile ("mrs %0, dbgdtrrx_el0\n" : "=r" (x)) + +#define status_dcc(x) \ + __asm__ volatile ("mrs %0, mdccsr_el0\n" : "=r" (x)) + #else #define DCC_RBIT (1 << 0) #define DCC_WBIT (1 << 1) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 357a335..c84a7b7 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -1,3 +1,5 @@ +menu "SPI Support" + config DM_SPI bool "Enable Driver Model for SPI drivers" depends on DM @@ -11,6 +13,51 @@ config DM_SPI typically use driver-private data instead of extending the spi_slave structure. +if DM_SPI + +config CADENCE_QSPI + bool "Cadence QSPI driver" + help + Enable the Cadence Quad-SPI (QSPI) driver. This driver can be + used to access the SPI NOR flash on platforms embedding this + Cadence IP core. + +config DESIGNWARE_SPI + bool "Designware SPI driver" + help + Enable the Designware SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding this Designware + IP core. + +config EXYNOS_SPI + bool "Samsung Exynos SPI driver" + help + Enable the Samsung Exynos SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding this Samsung + Exynos IP core. + +config FSL_DSPI + bool "Freescale DSPI driver" + help + Enable the Freescale DSPI driver. This driver can be used to + access the SPI NOR flash and SPI Data flash on platforms embedding + this Freescale DSPI IP core. LS102xA and Colibri VF50/VF61 platforms + use this driver. + +config FSL_QSPI + bool "Freescale QSPI driver" + help + Enable the Freescale Quad-SPI (QSPI) driver. This driver can be + used to access the SPI NOR flash on platforms embedding this + Freescale IP core. + +config ICH_SPI + bool "Intel ICH SPI driver" + help + Enable the Intel ICH SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding this Intel + ICH IP core. + config SANDBOX_SPI bool "Sandbox SPI driver" depends on SANDBOX && DM @@ -34,20 +81,61 @@ config SANDBOX_SPI spi-max-frequency = <40000000>; sandbox,filename = "spi.bin"; }; - }; + }; -config DESIGNWARE_SPI - bool "Designware SPI driver" - depends on DM_SPI +config TEGRA114_SPI + bool "nVidia Tegra114 SPI driver" help - Enable the Designware SPI driver. This driver can be used to - access the SPI NOR flash on platforms embedding this Designware + Enable the nVidia Tegra114 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding this nVidia Tegra114 IP core. -config CADENCE_QSPI - bool "Cadence QSPI driver" - depends on DM_SPI + This controller is different than the older SoCs SPI controller and + also register interface get changed with this controller. + +config TEGRA20_SFLASH + bool "nVidia Tegra20 Serial Flash controller driver" help - Enable the Cadence Quad-SPI (QSPI) driver. This driver can be - used to access the SPI NOR flash on platforms embedding this - Cadence IP core. + Enable the nVidia Tegra20 Serial Flash controller driver. This driver + can be used to access the SPI NOR flash on platforms embedding this + nVidia Tegra20 IP core. + +config TEGRA20_SLINK + bool "nVidia Tegra20/Tegra30 SLINK driver" + help + Enable the nVidia Tegra20/Tegra30 SLINK driver. This driver can + be used to access the SPI NOR flash on platforms embedding this + nVidia Tegra20/Tegra30 IP cores. + +config XILINX_SPI + bool "Xilinx SPI driver" + help + Enable the Xilinx SPI driver from the Xilinx EDK. This SPI + controller support 8 bit SPI transfers only, with or w/o FIFO. + For more info on Xilinx SPI Register Definitions and Overview + see driver file - drivers/spi/xilinx_spi.c + +config ZYNQ_SPI + bool "Zynq SPI driver" + depends on ARCH_ZYNQ || TARGET_XILINX_ZYNQMP + help + Enable the Zynq SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding this Zynq + SPI IP core. + +endif # if DM_SPI + +config FSL_ESPI + bool "Freescale eSPI driver" + help + Enable the Freescale eSPI driver. This driver can be used to + access the SPI interface and SPI NOR flash on platforms embedding + this Freescale eSPI IP core. + +config TI_QSPI + bool "TI QSPI driver" + help + Enable the TI Quad-SPI (QSPI) driver for DRA7xx and AM43xx evms. + This driver support spi flash single, quad and memory reads. + +endmenu # menu "SPI Support" diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e288692..ee88aa1 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -15,9 +15,7 @@ obj-y += spi.o obj-$(CONFIG_SOFT_SPI) += soft_spi_legacy.o endif -obj-$(CONFIG_EP93XX_SPI) += ep93xx_spi.o obj-$(CONFIG_ALTERA_SPI) += altera_spi.o -obj-$(CONFIG_ANDES_SPI) += andes_spi.o obj-$(CONFIG_ARMADA100_SPI) += armada100_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o @@ -28,8 +26,11 @@ obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_CF_QSPI) += cf_qspi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o obj-$(CONFIG_DESIGNWARE_SPI) += designware_spi.o +obj-$(CONFIG_EP93XX_SPI) += ep93xx_spi.o obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o -obj-$(CONFIG_FTSSP010_SPI) += ftssp010_spi.o +obj-$(CONFIG_FSL_DSPI) += fsl_dspi.o +obj-$(CONFIG_FSL_ESPI) += fsl_espi.o +obj-$(CONFIG_FSL_QSPI) += fsl_qspi.o obj-$(CONFIG_ICH_SPI) += ich.o obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o obj-$(CONFIG_LPC32XX_SSP) += lpc32xx_ssp.o @@ -37,17 +38,13 @@ obj-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o obj-$(CONFIG_MXC_SPI) += mxc_spi.o obj-$(CONFIG_MXS_SPI) += mxs_spi.o -obj-$(CONFIG_OC_TINY_SPI) += oc_tiny_spi.o obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o obj-$(CONFIG_SH_SPI) += sh_spi.o obj-$(CONFIG_SH_QSPI) += sh_qspi.o -obj-$(CONFIG_FSL_ESPI) += fsl_espi.o +obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o obj-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o obj-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o -obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o obj-$(CONFIG_TI_QSPI) += ti_qspi.o obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o obj-$(CONFIG_ZYNQ_SPI) += zynq_spi.o -obj-$(CONFIG_FSL_QSPI) += fsl_qspi.o -obj-$(CONFIG_FSL_DSPI) += fsl_dspi.o diff --git a/drivers/spi/andes_spi.c b/drivers/spi/andes_spi.c deleted file mode 100644 index 82aed75..0000000 --- a/drivers/spi/andes_spi.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Driver of Andes SPI Controller - * - * (C) Copyright 2011 Andes Technology - * Macpaul Lin <macpaul@andestech.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <malloc.h> -#include <spi.h> - -#include <asm/io.h> -#include "andes_spi.h" - -void spi_init(void) -{ - /* do nothing */ -} - -static void andes_spi_spit_en(struct andes_spi_slave *ds) -{ - unsigned int dcr = readl(&ds->regs->dcr); - - debug("%s: dcr: %x, write value: %x\n", - __func__, dcr, (dcr | ANDES_SPI_DCR_SPIT)); - - writel((dcr | ANDES_SPI_DCR_SPIT), &ds->regs->dcr); -} - -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) -{ - struct andes_spi_slave *ds; - - if (!spi_cs_is_valid(bus, cs)) - return NULL; - - ds = spi_alloc_slave(struct andes_spi_slave, bus, cs); - if (!ds) - return NULL; - - ds->regs = (struct andes_spi_regs *)CONFIG_SYS_SPI_BASE; - - /* - * The hardware of andes_spi will set its frequency according - * to APB/AHB bus clock. Hence the hardware doesn't allow changing of - * requency and so the user requested speed is always ignored. - */ - ds->freq = max_hz; - - return &ds->slave; -} - -void spi_free_slave(struct spi_slave *slave) -{ - struct andes_spi_slave *ds = to_andes_spi(slave); - - free(ds); -} - -int spi_claim_bus(struct spi_slave *slave) -{ - struct andes_spi_slave *ds = to_andes_spi(slave); - unsigned int apb; - unsigned int baud; - - /* Enable the SPI hardware */ - writel(ANDES_SPI_CR_SPIRST, &ds->regs->cr); - udelay(1000); - - /* setup format */ - baud = ((CONFIG_SYS_CLK_FREQ / CONFIG_SYS_SPI_CLK / 2) - 1) & 0xFF; - - /* - * SPI_CLK = AHB bus clock / ((BAUD + 1)*2) - * BAUD = AHB bus clock / SPI_CLK / 2) - 1 - */ - apb = (readl(&ds->regs->apb) & 0xffffff00) | baud; - writel(apb, &ds->regs->apb); - - /* no interrupts */ - writel(0, &ds->regs->ie); - - return 0; -} - -void spi_release_bus(struct spi_slave *slave) -{ - struct andes_spi_slave *ds = to_andes_spi(slave); - - /* Disable the SPI hardware */ - writel(ANDES_SPI_CR_SPIRST, &ds->regs->cr); -} - -static int andes_spi_read(struct spi_slave *slave, unsigned int len, - u8 *rxp, unsigned long flags) -{ - struct andes_spi_slave *ds = to_andes_spi(slave); - unsigned int i, left; - unsigned int data; - - debug("%s: slave: %x, len: %d, rxp: %x, flags: %d\n", - __func__, slave, len, rxp, flags); - - debug("%s: data: ", __func__); - while (len > 0) { - left = min(len, 4); - data = readl(&ds->regs->data); - - debug(" "); - for (i = 0; i < left; i++) { - debug("%02x ", data & 0xff); - *rxp++ = data; - data >>= 8; - len--; - } - } - debug("\n"); - - return 0; -} - -static int andes_spi_write(struct spi_slave *slave, unsigned int wlen, - unsigned int rlen, const u8 *txp, unsigned long flags) -{ - struct andes_spi_slave *ds = to_andes_spi(slave); - unsigned int data; - unsigned int i, left; - unsigned int spit_enabled = 0; - - debug("%s: slave: %x, wlen: %d, rlen: %d, txp: %x, flags: %x\n", - __func__, slave, wlen, rlen, txp, flags); - - /* The value of wlen and rlen wrote to register must minus 1 */ - if (rlen == 0) /* write only */ - writel(ANDES_SPI_DCR_MODE_WO | ANDES_SPI_DCR_WCNT(wlen-1) | - ANDES_SPI_DCR_RCNT(0), &ds->regs->dcr); - else /* write then read */ - writel(ANDES_SPI_DCR_MODE_WR | ANDES_SPI_DCR_WCNT(wlen-1) | - ANDES_SPI_DCR_RCNT(rlen-1), &ds->regs->dcr); - - /* wait till SPIBSY is cleared */ - while (readl(&ds->regs->st) & ANDES_SPI_ST_SPIBSY) - ; - - /* data write process */ - debug("%s: txp: ", __func__); - while (wlen > 0) { - /* clear the data */ - data = 0; - - /* data are usually be read 32bits once a time */ - left = min(wlen, 4); - - for (i = 0; i < left; i++) { - debug("%x ", *txp); - data |= *txp++ << (i * 8); - wlen--; - } - debug("\n"); - - debug("data: %08x\n", data); - debug("streg before write: %08x\n", readl(&ds->regs->st)); - /* wait till TXFULL is deasserted */ - while (readl(&ds->regs->st) & ANDES_SPI_ST_TXFEL) - ; - writel(data, &ds->regs->data); - debug("streg after write: %08x\n", readl(&ds->regs->st)); - - - if (spit_enabled == 0) { - /* enable SPIT bit - trigger the tx and rx progress */ - andes_spi_spit_en(ds); - spit_enabled = 1; - } - - } - debug("\n"); - - return 0; -} - -/* - * spi_xfer: - * Since andes_spi doesn't support independent command transaction, - * that is, write and than read must be operated in continuous - * execution, there is no need to set dcr and trigger spit again in - * RX process. - */ -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, - const void *dout, void *din, unsigned long flags) -{ - unsigned int len; - static int op_nextime; - static u8 tmp_cmd[5]; - static int tmp_wlen; - unsigned int i; - - if (bitlen == 0) - /* Finish any previously submitted transfers */ - goto out; - - if (bitlen % 8) { - /* Errors always terminate an ongoing transfer */ - flags |= SPI_XFER_END; - goto out; - } - - len = bitlen / 8; - - debug("%s: slave: %08x, bitlen: %d, dout: " - "%08x, din: %08x, flags: %d, len: %d\n", - __func__, slave, bitlen, dout, din, flags, len); - - /* - * Important: - * andes_spi's hardware doesn't support 2 data channel. The read - * and write cmd/data share the same register (data register). - * - * If a command has write and read transaction, you cannot do write - * this time and then do read on next time. - * - * A command writes first with a read response must indicating - * the read length in write operation. Hence the write action must - * be stored temporary and wait until the next read action has been - * arrived. Then we flush the write and read action out together. - */ - if (!dout) { - if (op_nextime == 1) { - /* flags should be SPI_XFER_END, value is 2 */ - op_nextime = 0; - andes_spi_write(slave, tmp_wlen, len, tmp_cmd, flags); - } - return andes_spi_read(slave, len, din, flags); - } else if (!din) { - if (flags == SPI_XFER_BEGIN) { - /* store the write command and do operation next time */ - op_nextime = 1; - memset(tmp_cmd, 0, sizeof(tmp_cmd)); - memcpy(tmp_cmd, dout, len); - - debug("%s: tmp_cmd: ", __func__); - for (i = 0; i < len; i++) - debug("%x ", *(tmp_cmd + i)); - debug("\n"); - - tmp_wlen = len; - } else { - /* - * flags should be (SPI_XFER_BEGIN | SPI_XFER_END), - * the value is 3. - */ - if (op_nextime == 1) { - /* flags should be SPI_XFER_END, value is 2 */ - op_nextime = 0; - /* flags 3 implies write only */ - andes_spi_write(slave, tmp_wlen, 0, tmp_cmd, 3); - } - - debug("flags: %x\n", flags); - return andes_spi_write(slave, len, 0, dout, flags); - } - } - -out: - return 0; -} - -int spi_cs_is_valid(unsigned int bus, unsigned int cs) -{ - return bus == 0 && cs == 0; -} - -void spi_cs_activate(struct spi_slave *slave) -{ - /* do nothing */ -} - -void spi_cs_deactivate(struct spi_slave *slave) -{ - /* do nothing */ -} diff --git a/drivers/spi/andes_spi.h b/drivers/spi/andes_spi.h deleted file mode 100644 index b7d2945..0000000 --- a/drivers/spi/andes_spi.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Register definitions for the Andes SPI Controller - * - * (C) Copyright 2011 Andes Technology - * Macpaul Lin <macpaul@andestech.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#ifndef __ANDES_SPI_H -#define __ANDES_SPI_H - -struct andes_spi_regs { - unsigned int apb; /* 0x00 - APB SPI interface setting */ - unsigned int pio; /* 0x04 - PIO reg */ - unsigned int cr; /* 0x08 - SPI Control reg */ - unsigned int st; /* 0x0c - SPI Status reg */ - unsigned int ie; /* 0x10 - Interrupt Enable reg */ - unsigned int ist; /* 0x14 - Interrupt Status reg */ - unsigned int dcr; /* 0x18 - data control reg */ - unsigned int data; /* 0x1c - data register */ - unsigned int ahb; /* 0x20 - AHB SPI interface setting */ - unsigned int ver; /* 0x3c - SPI version reg */ -}; - -#define BIT(x) (1 << (x)) - -/* 0x00 - APB SPI interface setting register */ -#define ANDES_SPI_APB_BAUD(x) (((x) & 0xff) < 0) -#define ANDES_SPI_APB_CSHT(x) (((x) & 0xf) < 16) -#define ANDES_SPI_APB_SPNTS BIT(20) /* 0: normal, 1: delay */ -#define ANDES_SPI_APB_CPHA BIT(24) /* 0: Sampling at odd edges */ -#define ANDES_SPI_APB_CPOL BIT(25) /* 0: SCK low, 1: SCK high */ -#define ANDES_SPI_APB_MSSL BIT(26) /* 0: SPI Master, 1: slave */ - -/* 0x04 - PIO register */ -#define ANDES_SPI_PIO_MISO BIT(0) /* input value of pin MISO */ -#define ANDES_SPI_PIO_MOSI BIT(1) /* I/O value of pin MOSI */ -#define ANDES_SPI_PIO_SCK BIT(2) /* I/O value of pin SCK */ -#define ANDES_SPI_PIO_CS BIT(3) /* I/O value of pin CS */ -#define ANDES_SPI_PIO_PIOE BIT(4) /* Programming IO Enable */ - -/* 0x08 - SPI Control register */ -#define ANDES_SPI_CR_SPIRST BIT(0) /* SPI mode reset */ -#define ANDES_SPI_CR_RXFRST BIT(1) /* RxFIFO reset */ -#define ANDES_SPI_CR_TXFRST BIT(2) /* TxFIFO reset */ -#define ANDES_SPI_CR_RXFTH(x) (((x) & 0x1f) << 10) /* RxFIFO Threshold */ -#define ANDES_SPI_CR_TXFTH(x) (((x) & 0x1f) << 18) /* TxFIFO Threshold */ - -/* 0x0c - SPI Status register */ -#define ANDES_SPI_ST_SPIBSY BIT(0) /* SPI Transfer is active */ -#define ANDES_SPI_ST_RXFEM BIT(8) /* RxFIFO Empty Flag */ -#define ANDES_SPI_ST_RXFEL BIT(9) /* RxFIFO Full Flag */ -#define ANDES_SPI_ST_RXFVE(x) (((x) >> 10) & 0x1f) -#define ANDES_SPI_ST_TXFEM BIT(16) /* TxFIFO Empty Flag */ -#define ANDES_SPI_ST_TXFEL BIT(7) /* TxFIFO Full Flag */ -#define ANDES_SPI_ST_TXFVE(x) (((x) >> 18) & 0x1f) - -/* 0x10 - Interrupt Enable register */ -#define ANDES_SPI_IE_RXFORIE BIT(0) /* RxFIFO overrun intr */ -#define ANDES_SPI_IE_TXFURIE BIT(1) /* TxFOFO underrun intr */ -#define ANDES_SPI_IE_RXFTHIE BIT(2) /* RxFIFO threshold intr */ -#define ANDES_SPI_IE_TXFTHIE BIT(3) /* TxFIFO threshold intr */ -#define ANDES_SPI_IE_SPIEIE BIT(4) /* SPI transmit END intr */ -#define ANDES_SPI_IE_SPCFIE BIT(5) /* AHB/APB TxReq conflict */ - -/* 0x14 - Interrupt Status Register */ -#define ANDES_SPI_IST_RXFORI BIT(0) /* has RxFIFO overrun */ -#define ANDES_SPI_IST_TXFURI BIT(1) /* has TxFOFO underrun */ -#define ANDES_SPI_IST_RXFTHI BIT(2) /* has RxFIFO threshold */ -#define ANDES_SPI_IST_TXFTHI BIT(3) /* has TxFIFO threshold */ -#define ANDES_SPI_IST_SPIEI BIT(4) /* has SPI transmit END */ -#define ANDES_SPI_IST_SPCFI BIT(5) /* has AHB/APB TxReq conflict */ - -/* 0x18 - Data Control Register */ -#define ANDES_SPI_DCR_RCNT(x) (((x) & 0x3ff) << 0) -#define ANDES_SPI_DCR_DYCNT(x) (((x) & 0x7) << 12) -#define ANDES_SPI_DCR_WCNT(x) (((x) & 0x3ff) << 16) -#define ANDES_SPI_DCR_TRAMODE(x) (((x) & 0x7) << 28) -#define ANDES_SPI_DCR_SPIT BIT(31) /* SPI bus trigger */ - -#define ANDES_SPI_DCR_MODE_WRCON ANDES_SPI_DCR_TRAMODE(0) /* w/r at the same time */ -#define ANDES_SPI_DCR_MODE_WO ANDES_SPI_DCR_TRAMODE(1) /* write only */ -#define ANDES_SPI_DCR_MODE_RO ANDES_SPI_DCR_TRAMODE(2) /* read only */ -#define ANDES_SPI_DCR_MODE_WR ANDES_SPI_DCR_TRAMODE(3) /* write, read */ -#define ANDES_SPI_DCR_MODE_RW ANDES_SPI_DCR_TRAMODE(4) /* read, write */ -#define ANDES_SPI_DCR_MODE_WDR ANDES_SPI_DCR_TRAMODE(5) /* write, dummy, read */ -#define ANDES_SPI_DCR_MODE_RDW ANDES_SPI_DCR_TRAMODE(6) /* read, dummy, write */ -#define ANDES_SPI_DCR_MODE_RECEIVE ANDES_SPI_DCR_TRAMODE(7) /* receive */ - -/* 0x20 - AHB SPI interface setting register */ -#define ANDES_SPI_AHB_BAUD(x) (((x) & 0xff) < 0) -#define ANDES_SPI_AHB_CSHT(x) (((x) & 0xf) < 16) -#define ANDES_SPI_AHB_SPNTS BIT(20) /* 0: normal, 1: delay */ -#define ANDES_SPI_AHB_CPHA BIT(24) /* 0: Sampling at odd edges */ -#define ANDES_SPI_AHB_CPOL BIT(25) /* 0: SCK low, 1: SCK high */ -#define ANDES_SPI_AHB_MSSL BIT(26) /* only Master mode */ - -/* 0x3c - Version Register - (Year V.MAJOR.MINOR) */ -#define ANDES_SPI_VER_MINOR(x) (((x) >> 0) & 0xf) -#define ANDES_SPI_VER_MAJOR(x) (((x) >> 8) & 0xf) -#define ANDES_SPI_VER_YEAR(x) (((x) >> 16) & 0xf) - -struct andes_spi_slave { - struct spi_slave slave; - struct andes_spi_regs *regs; - unsigned int freq; -}; - -static inline struct andes_spi_slave *to_andes_spi(struct spi_slave *slave) -{ - return container_of(slave, struct andes_spi_slave, slave); -} - -#endif /* __ANDES_SPI_H */ diff --git a/drivers/spi/cf_qspi.c b/drivers/spi/cf_qspi.c index 834c5bd..e57e63e 100644 --- a/drivers/spi/cf_qspi.c +++ b/drivers/spi/cf_qspi.c @@ -19,7 +19,6 @@ DECLARE_GLOBAL_DATA_PTR; -#define clamp(x, low, high) (min(max(low, x), high)) #define to_cf_qspi_slave(s) container_of(s, struct cf_qspi_slave, slave) struct cf_qspi_slave { @@ -120,7 +119,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, dev->qmr = 2u; else /* Get the closest baud rate */ dev->qmr = clamp(((gd->bus_clk >> 2) + max_hz - 1)/max_hz, - 2u, 255u); + 2lu, 255lu); /* Map mode to QMR[CPOL] and QMR[CPHA] */ if (mode & SPI_CPOL) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index bf18362..0a036cc 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -8,116 +8,122 @@ * * SPDX-License-Identifier: GPL-2.0+ */ + #include <common.h> #include <spi.h> #include <malloc.h> #include <asm/io.h> #include <asm/arch/hardware.h> -#include "davinci_spi.h" -void spi_init() -{ - /* do nothing */ -} +#define BIT(x) (1 << (x)) -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) -{ - struct davinci_spi_slave *ds; +/* SPIGCR0 */ +#define SPIGCR0_SPIENA_MASK 0x1 +#define SPIGCR0_SPIRST_MASK 0x0 - if (!spi_cs_is_valid(bus, cs)) - return NULL; +/* SPIGCR0 */ +#define SPIGCR1_CLKMOD_MASK BIT(1) +#define SPIGCR1_MASTER_MASK BIT(0) +#define SPIGCR1_SPIENA_MASK BIT(24) - ds = spi_alloc_slave(struct davinci_spi_slave, bus, cs); - if (!ds) - return NULL; +/* SPIPC0 */ +#define SPIPC0_DIFUN_MASK BIT(11) /* SIMO */ +#define SPIPC0_DOFUN_MASK BIT(10) /* SOMI */ +#define SPIPC0_CLKFUN_MASK BIT(9) /* CLK */ +#define SPIPC0_EN0FUN_MASK BIT(0) - switch (bus) { - case SPI0_BUS: - ds->regs = (struct davinci_spi_regs *)SPI0_BASE; - break; -#ifdef CONFIG_SYS_SPI1 - case SPI1_BUS: - ds->regs = (struct davinci_spi_regs *)SPI1_BASE; - break; -#endif -#ifdef CONFIG_SYS_SPI2 - case SPI2_BUS: - ds->regs = (struct davinci_spi_regs *)SPI2_BASE; - break; -#endif - default: /* Invalid bus number */ - return NULL; - } +/* SPIFMT0 */ +#define SPIFMT_SHIFTDIR_SHIFT 20 +#define SPIFMT_POLARITY_SHIFT 17 +#define SPIFMT_PHASE_SHIFT 16 +#define SPIFMT_PRESCALE_SHIFT 8 - ds->freq = max_hz; +/* SPIDAT1 */ +#define SPIDAT1_CSHOLD_SHIFT 28 +#define SPIDAT1_CSNR_SHIFT 16 - return &ds->slave; -} +/* SPIDELAY */ +#define SPI_C2TDELAY_SHIFT 24 +#define SPI_T2CDELAY_SHIFT 16 -void spi_free_slave(struct spi_slave *slave) -{ - struct davinci_spi_slave *ds = to_davinci_spi(slave); +/* SPIBUF */ +#define SPIBUF_RXEMPTY_MASK BIT(31) +#define SPIBUF_TXFULL_MASK BIT(29) - free(ds); -} - -int spi_claim_bus(struct spi_slave *slave) -{ - struct davinci_spi_slave *ds = to_davinci_spi(slave); - unsigned int scalar; - - /* Enable the SPI hardware */ - writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0); - udelay(1000); - writel(SPIGCR0_SPIENA_MASK, &ds->regs->gcr0); +/* SPIDEF */ +#define SPIDEF_CSDEF0_MASK BIT(0) - /* Set master mode, powered up and not activated */ - writel(SPIGCR1_MASTER_MASK | SPIGCR1_CLKMOD_MASK, &ds->regs->gcr1); - - /* CS, CLK, SIMO and SOMI are functional pins */ - writel(((1 << slave->cs) | SPIPC0_CLKFUN_MASK | - SPIPC0_DOFUN_MASK | SPIPC0_DIFUN_MASK), &ds->regs->pc0); - - /* setup format */ - scalar = ((CONFIG_SYS_SPI_CLK / ds->freq) - 1) & 0xFF; - - /* - * Use following format: - * character length = 8, - * clock signal delayed by half clk cycle, - * clock low in idle state - Mode 0, - * MSB shifted out first - */ - writel(8 | (scalar << SPIFMT_PRESCALE_SHIFT) | - (1 << SPIFMT_PHASE_SHIFT), &ds->regs->fmt0); - - /* - * Including a minor delay. No science here. Should be good even with - * no delay - */ - writel((50 << SPI_C2TDELAY_SHIFT) | - (50 << SPI_T2CDELAY_SHIFT), &ds->regs->delay); - - /* default chip select register */ - writel(SPIDEF_CSDEF0_MASK, &ds->regs->def); - - /* no interrupts */ - writel(0, &ds->regs->int0); - writel(0, &ds->regs->lvl); +#define SPI0_BUS 0 +#define SPI0_BASE CONFIG_SYS_SPI_BASE +/* + * Define default SPI0_NUM_CS as 1 for existing platforms that uses this + * driver. Platform can configure number of CS using CONFIG_SYS_SPI0_NUM_CS + * if more than one CS is supported and by defining CONFIG_SYS_SPI0. + */ +#ifndef CONFIG_SYS_SPI0 +#define SPI0_NUM_CS 1 +#else +#define SPI0_NUM_CS CONFIG_SYS_SPI0_NUM_CS +#endif - /* enable SPI */ - writel((readl(&ds->regs->gcr1) | SPIGCR1_SPIENA_MASK), &ds->regs->gcr1); +/* + * define CONFIG_SYS_SPI1 when platform has spi-1 device (bus #1) and + * CONFIG_SYS_SPI1_NUM_CS defines number of CS on this bus + */ +#ifdef CONFIG_SYS_SPI1 +#define SPI1_BUS 1 +#define SPI1_NUM_CS CONFIG_SYS_SPI1_NUM_CS +#define SPI1_BASE CONFIG_SYS_SPI1_BASE +#endif - return 0; -} +/* + * define CONFIG_SYS_SPI2 when platform has spi-2 device (bus #2) and + * CONFIG_SYS_SPI2_NUM_CS defines number of CS on this bus + */ +#ifdef CONFIG_SYS_SPI2 +#define SPI2_BUS 2 +#define SPI2_NUM_CS CONFIG_SYS_SPI2_NUM_CS +#define SPI2_BASE CONFIG_SYS_SPI2_BASE +#endif -void spi_release_bus(struct spi_slave *slave) +/* davinci spi register set */ +struct davinci_spi_regs { + dv_reg gcr0; /* 0x00 */ + dv_reg gcr1; /* 0x04 */ + dv_reg int0; /* 0x08 */ + dv_reg lvl; /* 0x0c */ + dv_reg flg; /* 0x10 */ + dv_reg pc0; /* 0x14 */ + dv_reg pc1; /* 0x18 */ + dv_reg pc2; /* 0x1c */ + dv_reg pc3; /* 0x20 */ + dv_reg pc4; /* 0x24 */ + dv_reg pc5; /* 0x28 */ + dv_reg rsvd[3]; + dv_reg dat0; /* 0x38 */ + dv_reg dat1; /* 0x3c */ + dv_reg buf; /* 0x40 */ + dv_reg emu; /* 0x44 */ + dv_reg delay; /* 0x48 */ + dv_reg def; /* 0x4c */ + dv_reg fmt0; /* 0x50 */ + dv_reg fmt1; /* 0x54 */ + dv_reg fmt2; /* 0x58 */ + dv_reg fmt3; /* 0x5c */ + dv_reg intvec0; /* 0x60 */ + dv_reg intvec1; /* 0x64 */ +}; + +/* davinci spi slave */ +struct davinci_spi_slave { + struct spi_slave slave; + struct davinci_spi_regs *regs; + unsigned int freq; +}; + +static inline struct davinci_spi_slave *to_davinci_spi(struct spi_slave *slave) { - struct davinci_spi_slave *ds = to_davinci_spi(slave); - - /* Disable the SPI hardware */ - writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0); + return container_of(slave, struct davinci_spi_slave, slave); } /* @@ -235,6 +241,149 @@ static int davinci_spi_read_write(struct spi_slave *slave, unsigned int len, } #endif +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + int ret = 0; + + switch (bus) { + case SPI0_BUS: + if (cs < SPI0_NUM_CS) + ret = 1; + break; +#ifdef CONFIG_SYS_SPI1 + case SPI1_BUS: + if (cs < SPI1_NUM_CS) + ret = 1; + break; +#endif +#ifdef CONFIG_SYS_SPI2 + case SPI2_BUS: + if (cs < SPI2_NUM_CS) + ret = 1; + break; +#endif + default: + /* Invalid bus number. Do nothing */ + break; + } + return ret; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + /* do nothing */ +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + /* do nothing */ +} + +void spi_init(void) +{ + /* do nothing */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct davinci_spi_slave *ds; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + ds = spi_alloc_slave(struct davinci_spi_slave, bus, cs); + if (!ds) + return NULL; + + switch (bus) { + case SPI0_BUS: + ds->regs = (struct davinci_spi_regs *)SPI0_BASE; + break; +#ifdef CONFIG_SYS_SPI1 + case SPI1_BUS: + ds->regs = (struct davinci_spi_regs *)SPI1_BASE; + break; +#endif +#ifdef CONFIG_SYS_SPI2 + case SPI2_BUS: + ds->regs = (struct davinci_spi_regs *)SPI2_BASE; + break; +#endif + default: /* Invalid bus number */ + return NULL; + } + + ds->freq = max_hz; + + return &ds->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + + free(ds); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + unsigned int scalar; + + /* Enable the SPI hardware */ + writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0); + udelay(1000); + writel(SPIGCR0_SPIENA_MASK, &ds->regs->gcr0); + + /* Set master mode, powered up and not activated */ + writel(SPIGCR1_MASTER_MASK | SPIGCR1_CLKMOD_MASK, &ds->regs->gcr1); + + /* CS, CLK, SIMO and SOMI are functional pins */ + writel(((1 << slave->cs) | SPIPC0_CLKFUN_MASK | + SPIPC0_DOFUN_MASK | SPIPC0_DIFUN_MASK), &ds->regs->pc0); + + /* setup format */ + scalar = ((CONFIG_SYS_SPI_CLK / ds->freq) - 1) & 0xFF; + + /* + * Use following format: + * character length = 8, + * clock signal delayed by half clk cycle, + * clock low in idle state - Mode 0, + * MSB shifted out first + */ + writel(8 | (scalar << SPIFMT_PRESCALE_SHIFT) | + (1 << SPIFMT_PHASE_SHIFT), &ds->regs->fmt0); + + /* + * Including a minor delay. No science here. Should be good even with + * no delay + */ + writel((50 << SPI_C2TDELAY_SHIFT) | + (50 << SPI_T2CDELAY_SHIFT), &ds->regs->delay); + + /* default chip select register */ + writel(SPIDEF_CSDEF0_MASK, &ds->regs->def); + + /* no interrupts */ + writel(0, &ds->regs->int0); + writel(0, &ds->regs->lvl); + + /* enable SPI */ + writel((readl(&ds->regs->gcr1) | SPIGCR1_SPIENA_MASK), &ds->regs->gcr1); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + + /* Disable the SPI hardware */ + writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0); +} + int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { @@ -278,41 +427,3 @@ out: } return 0; } - -int spi_cs_is_valid(unsigned int bus, unsigned int cs) -{ - int ret = 0; - - switch (bus) { - case SPI0_BUS: - if (cs < SPI0_NUM_CS) - ret = 1; - break; -#ifdef CONFIG_SYS_SPI1 - case SPI1_BUS: - if (cs < SPI1_NUM_CS) - ret = 1; - break; -#endif -#ifdef CONFIG_SYS_SPI2 - case SPI2_BUS: - if (cs < SPI2_NUM_CS) - ret = 1; - break; -#endif - default: - /* Invalid bus number. Do nothing */ - break; - } - return ret; -} - -void spi_cs_activate(struct spi_slave *slave) -{ - /* do nothing */ -} - -void spi_cs_deactivate(struct spi_slave *slave) -{ - /* do nothing */ -} diff --git a/drivers/spi/davinci_spi.h b/drivers/spi/davinci_spi.h deleted file mode 100644 index d4612d3..0000000 --- a/drivers/spi/davinci_spi.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * Register definitions for the DaVinci SPI Controller - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#ifndef _DAVINCI_SPI_H_ -#define _DAVINCI_SPI_H_ - -struct davinci_spi_regs { - dv_reg gcr0; /* 0x00 */ - dv_reg gcr1; /* 0x04 */ - dv_reg int0; /* 0x08 */ - dv_reg lvl; /* 0x0c */ - dv_reg flg; /* 0x10 */ - dv_reg pc0; /* 0x14 */ - dv_reg pc1; /* 0x18 */ - dv_reg pc2; /* 0x1c */ - dv_reg pc3; /* 0x20 */ - dv_reg pc4; /* 0x24 */ - dv_reg pc5; /* 0x28 */ - dv_reg rsvd[3]; - dv_reg dat0; /* 0x38 */ - dv_reg dat1; /* 0x3c */ - dv_reg buf; /* 0x40 */ - dv_reg emu; /* 0x44 */ - dv_reg delay; /* 0x48 */ - dv_reg def; /* 0x4c */ - dv_reg fmt0; /* 0x50 */ - dv_reg fmt1; /* 0x54 */ - dv_reg fmt2; /* 0x58 */ - dv_reg fmt3; /* 0x5c */ - dv_reg intvec0; /* 0x60 */ - dv_reg intvec1; /* 0x64 */ -}; - -#define BIT(x) (1 << (x)) - -/* SPIGCR0 */ -#define SPIGCR0_SPIENA_MASK 0x1 -#define SPIGCR0_SPIRST_MASK 0x0 - -/* SPIGCR0 */ -#define SPIGCR1_CLKMOD_MASK BIT(1) -#define SPIGCR1_MASTER_MASK BIT(0) -#define SPIGCR1_SPIENA_MASK BIT(24) - -/* SPIPC0 */ -#define SPIPC0_DIFUN_MASK BIT(11) /* SIMO */ -#define SPIPC0_DOFUN_MASK BIT(10) /* SOMI */ -#define SPIPC0_CLKFUN_MASK BIT(9) /* CLK */ -#define SPIPC0_EN0FUN_MASK BIT(0) - -/* SPIFMT0 */ -#define SPIFMT_SHIFTDIR_SHIFT 20 -#define SPIFMT_POLARITY_SHIFT 17 -#define SPIFMT_PHASE_SHIFT 16 -#define SPIFMT_PRESCALE_SHIFT 8 - -/* SPIDAT1 */ -#define SPIDAT1_CSHOLD_SHIFT 28 -#define SPIDAT1_CSNR_SHIFT 16 - -/* SPIDELAY */ -#define SPI_C2TDELAY_SHIFT 24 -#define SPI_T2CDELAY_SHIFT 16 - -/* SPIBUF */ -#define SPIBUF_RXEMPTY_MASK BIT(31) -#define SPIBUF_TXFULL_MASK BIT(29) - -/* SPIDEF */ -#define SPIDEF_CSDEF0_MASK BIT(0) - -#define SPI0_BUS 0 -#define SPI0_BASE CONFIG_SYS_SPI_BASE -/* - * Define default SPI0_NUM_CS as 1 for existing platforms that uses this - * driver. Platform can configure number of CS using CONFIG_SYS_SPI0_NUM_CS - * if more than one CS is supported and by defining CONFIG_SYS_SPI0. - */ -#ifndef CONFIG_SYS_SPI0 -#define SPI0_NUM_CS 1 -#else -#define SPI0_NUM_CS CONFIG_SYS_SPI0_NUM_CS -#endif - -/* - * define CONFIG_SYS_SPI1 when platform has spi-1 device (bus #1) and - * CONFIG_SYS_SPI1_NUM_CS defines number of CS on this bus - */ -#ifdef CONFIG_SYS_SPI1 -#define SPI1_BUS 1 -#define SPI1_NUM_CS CONFIG_SYS_SPI1_NUM_CS -#define SPI1_BASE CONFIG_SYS_SPI1_BASE -#endif - -/* - * define CONFIG_SYS_SPI2 when platform has spi-2 device (bus #2) and - * CONFIG_SYS_SPI2_NUM_CS defines number of CS on this bus - */ -#ifdef CONFIG_SYS_SPI2 -#define SPI2_BUS 2 -#define SPI2_NUM_CS CONFIG_SYS_SPI2_NUM_CS -#define SPI2_BASE CONFIG_SYS_SPI2_BASE -#endif - -struct davinci_spi_slave { - struct spi_slave slave; - struct davinci_spi_regs *regs; - unsigned int freq; -}; - -static inline struct davinci_spi_slave *to_davinci_spi(struct spi_slave *slave) -{ - return container_of(slave, struct davinci_spi_slave, slave); -} - -#endif /* _DAVINCI_SPI_H_ */ diff --git a/drivers/spi/ftssp010_spi.c b/drivers/spi/ftssp010_spi.c deleted file mode 100644 index c7d6480..0000000 --- a/drivers/spi/ftssp010_spi.c +++ /dev/null @@ -1,498 +0,0 @@ -/* - * (C) Copyright 2013 - * Faraday Technology Corporation. <http://www.faraday-tech.com/tw/> - * Kuo-Jung Su <dantesu@gmail.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <linux/compat.h> -#include <asm/io.h> -#include <malloc.h> -#include <spi.h> - -#ifndef CONFIG_FTSSP010_BASE_LIST -#define CONFIG_FTSSP010_BASE_LIST { CONFIG_FTSSP010_BASE } -#endif - -#ifndef CONFIG_FTSSP010_GPIO_BASE -#define CONFIG_FTSSP010_GPIO_BASE 0 -#endif - -#ifndef CONFIG_FTSSP010_GPIO_LIST -#define CONFIG_FTSSP010_GPIO_LIST { CONFIG_FTSSP010_GPIO_BASE } -#endif - -#ifndef CONFIG_FTSSP010_CLOCK -#define CONFIG_FTSSP010_CLOCK clk_get_rate("SSP"); -#endif - -#ifndef CONFIG_FTSSP010_TIMEOUT -#define CONFIG_FTSSP010_TIMEOUT 100 -#endif - -/* FTSSP010 chip registers */ -struct ftssp010_regs { - uint32_t cr[3];/* control register */ - uint32_t sr; /* status register */ - uint32_t icr; /* interrupt control register */ - uint32_t isr; /* interrupt status register */ - uint32_t dr; /* data register */ - uint32_t rsvd[17]; - uint32_t revr; /* revision register */ - uint32_t fear; /* feature register */ -}; - -/* Control Register 0 */ -#define CR0_FFMT_MASK (7 << 12) -#define CR0_FFMT_SSP (0 << 12) -#define CR0_FFMT_SPI (1 << 12) -#define CR0_FFMT_MICROWIRE (2 << 12) -#define CR0_FFMT_I2S (3 << 12) -#define CR0_FFMT_AC97 (4 << 12) -#define CR0_FLASH (1 << 11) -#define CR0_FSDIST(x) (((x) & 0x03) << 8) -#define CR0_LOOP (1 << 7) /* loopback mode */ -#define CR0_LSB (1 << 6) /* LSB */ -#define CR0_FSPO (1 << 5) /* fs atcive low (I2S only) */ -#define CR0_FSJUSTIFY (1 << 4) -#define CR0_OPM_SLAVE (0 << 2) -#define CR0_OPM_MASTER (3 << 2) -#define CR0_OPM_I2S_MSST (3 << 2) /* master stereo mode */ -#define CR0_OPM_I2S_MSMO (2 << 2) /* master mono mode */ -#define CR0_OPM_I2S_SLST (1 << 2) /* slave stereo mode */ -#define CR0_OPM_I2S_SLMO (0 << 2) /* slave mono mode */ -#define CR0_SCLKPO (1 << 1) /* clock polarity */ -#define CR0_SCLKPH (1 << 0) /* clock phase */ - -/* Control Register 1 */ -#define CR1_PDL(x) (((x) & 0xff) << 24) /* padding length */ -#define CR1_SDL(x) ((((x) - 1) & 0x1f) << 16) /* data length */ -#define CR1_DIV(x) (((x) - 1) & 0xffff) /* clock divider */ - -/* Control Register 2 */ -#define CR2_CS(x) (((x) & 3) << 10) /* CS/FS select */ -#define CR2_FS (1 << 9) /* CS/FS signal level */ -#define CR2_TXEN (1 << 8) /* tx enable */ -#define CR2_RXEN (1 << 7) /* rx enable */ -#define CR2_RESET (1 << 6) /* chip reset */ -#define CR2_TXFC (1 << 3) /* tx fifo Clear */ -#define CR2_RXFC (1 << 2) /* rx fifo Clear */ -#define CR2_TXDOE (1 << 1) /* tx data output enable */ -#define CR2_EN (1 << 0) /* chip enable */ - -/* Status Register */ -#define SR_RFF (1 << 0) /* rx fifo full */ -#define SR_TFNF (1 << 1) /* tx fifo not full */ -#define SR_BUSY (1 << 2) /* chip busy */ -#define SR_RFVE(reg) (((reg) >> 4) & 0x1f) /* rx fifo valid entries */ -#define SR_TFVE(reg) (((reg) >> 12) & 0x1f) /* tx fifo valid entries */ - -/* Feature Register */ -#define FEAR_BITS(reg) ((((reg) >> 0) & 0xff) + 1) /* data width */ -#define FEAR_RFSZ(reg) ((((reg) >> 8) & 0xff) + 1) /* rx fifo size */ -#define FEAR_TFSZ(reg) ((((reg) >> 16) & 0xff) + 1) /* tx fifo size */ -#define FEAR_AC97 (1 << 24) -#define FEAR_I2S (1 << 25) -#define FEAR_SPI_MWR (1 << 26) -#define FEAR_SSP (1 << 27) -#define FEAR_SPDIF (1 << 28) - -/* FTGPIO010 chip registers */ -struct ftgpio010_regs { - uint32_t out; /* 0x00: Data Output */ - uint32_t in; /* 0x04: Data Input */ - uint32_t dir; /* 0x08: Direction */ - uint32_t bypass; /* 0x0c: Bypass */ - uint32_t set; /* 0x10: Data Set */ - uint32_t clr; /* 0x14: Data Clear */ - uint32_t pull_up; /* 0x18: Pull-Up Enabled */ - uint32_t pull_st; /* 0x1c: Pull State (0=pull-down, 1=pull-up) */ -}; - -struct ftssp010_gpio { - struct ftgpio010_regs *regs; - uint32_t pin; -}; - -struct ftssp010_spi { - struct spi_slave slave; - struct ftssp010_gpio gpio; - struct ftssp010_regs *regs; - uint32_t fifo; - uint32_t mode; - uint32_t div; - uint32_t clk; - uint32_t speed; - uint32_t revision; -}; - -static inline struct ftssp010_spi *to_ftssp010_spi(struct spi_slave *slave) -{ - return container_of(slave, struct ftssp010_spi, slave); -} - -static int get_spi_chip(int bus, struct ftssp010_spi *chip) -{ - uint32_t fear, base[] = CONFIG_FTSSP010_BASE_LIST; - - if (bus >= ARRAY_SIZE(base) || !base[bus]) - return -1; - - chip->regs = (struct ftssp010_regs *)base[bus]; - - chip->revision = readl(&chip->regs->revr); - - fear = readl(&chip->regs->fear); - chip->fifo = min_t(uint32_t, FEAR_TFSZ(fear), FEAR_RFSZ(fear)); - - return 0; -} - -static int get_spi_gpio(int bus, struct ftssp010_gpio *chip) -{ - uint32_t base[] = CONFIG_FTSSP010_GPIO_LIST; - - if (bus >= ARRAY_SIZE(base) || !base[bus]) - return -1; - - chip->regs = (struct ftgpio010_regs *)(base[bus] & 0xfff00000); - chip->pin = base[bus] & 0x1f; - - /* make it an output pin */ - setbits_le32(&chip->regs->dir, 1 << chip->pin); - - return 0; -} - -static int ftssp010_wait(struct ftssp010_spi *chip) -{ - struct ftssp010_regs *regs = chip->regs; - ulong t; - - /* wait until device idle */ - for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) { - if (!(readl(®s->sr) & SR_BUSY)) - return 0; - } - - puts("ftspi010: busy timeout\n"); - - return -1; -} - -static int ftssp010_wait_tx(struct ftssp010_spi *chip) -{ - struct ftssp010_regs *regs = chip->regs; - ulong t; - - /* wait until tx fifo not full */ - for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) { - if (readl(®s->sr) & SR_TFNF) - return 0; - } - - puts("ftssp010: tx timeout\n"); - - return -1; -} - -static int ftssp010_wait_rx(struct ftssp010_spi *chip) -{ - struct ftssp010_regs *regs = chip->regs; - ulong t; - - /* wait until rx fifo not empty */ - for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) { - if (SR_RFVE(readl(®s->sr))) - return 0; - } - - puts("ftssp010: rx timeout\n"); - - return -1; -} - -static int ftssp010_spi_work_transfer_v2(struct ftssp010_spi *chip, - const void *tx_buf, void *rx_buf, int len, uint flags) -{ - struct ftssp010_regs *regs = chip->regs; - const uint8_t *txb = tx_buf; - uint8_t *rxb = rx_buf; - - while (len > 0) { - int i, depth = min(chip->fifo >> 2, len); - uint32_t xmsk = 0; - - if (tx_buf) { - for (i = 0; i < depth; ++i) { - ftssp010_wait_tx(chip); - writel(*txb++, ®s->dr); - } - xmsk |= CR2_TXEN | CR2_TXDOE; - if ((readl(®s->cr[2]) & xmsk) != xmsk) - setbits_le32(®s->cr[2], xmsk); - } - if (rx_buf) { - xmsk |= CR2_RXEN; - if ((readl(®s->cr[2]) & xmsk) != xmsk) - setbits_le32(®s->cr[2], xmsk); - for (i = 0; i < depth; ++i) { - ftssp010_wait_rx(chip); - *rxb++ = (uint8_t)readl(®s->dr); - } - } - - len -= depth; - } - - return 0; -} - -static int ftssp010_spi_work_transfer_v1(struct ftssp010_spi *chip, - const void *tx_buf, void *rx_buf, int len, uint flags) -{ - struct ftssp010_regs *regs = chip->regs; - const uint8_t *txb = tx_buf; - uint8_t *rxb = rx_buf; - - while (len > 0) { - int i, depth = min(chip->fifo >> 2, len); - uint32_t tmp; - - for (i = 0; i < depth; ++i) { - ftssp010_wait_tx(chip); - writel(txb ? (*txb++) : 0, ®s->dr); - } - for (i = 0; i < depth; ++i) { - ftssp010_wait_rx(chip); - tmp = readl(®s->dr); - if (rxb) - *rxb++ = (uint8_t)tmp; - } - - len -= depth; - } - - return 0; -} - -static void ftssp010_cs_set(struct ftssp010_spi *chip, int high) -{ - struct ftssp010_regs *regs = chip->regs; - struct ftssp010_gpio *gpio = &chip->gpio; - uint32_t mask; - - /* cs pull high/low */ - if (chip->revision >= 0x11900) { - mask = CR2_CS(chip->slave.cs) | (high ? CR2_FS : 0); - writel(mask, ®s->cr[2]); - } else if (gpio->regs) { - mask = 1 << gpio->pin; - if (high) - writel(mask, &gpio->regs->set); - else - writel(mask, &gpio->regs->clr); - } - - /* extra delay for signal propagation */ - udelay_masked(1); -} - -/* - * Determine if a SPI chipselect is valid. - * This function is provided by the board if the low-level SPI driver - * needs it to determine if a given chipselect is actually valid. - * - * Returns: 1 if bus:cs identifies a valid chip on this board, 0 - * otherwise. - */ -int spi_cs_is_valid(unsigned int bus, unsigned int cs) -{ - struct ftssp010_spi chip; - - if (get_spi_chip(bus, &chip)) - return 0; - - if (!cs) - return 1; - else if ((cs < 4) && (chip.revision >= 0x11900)) - return 1; - - return 0; -} - -/* - * Activate a SPI chipselect. - * This function is provided by the board code when using a driver - * that can't control its chipselects automatically (e.g. - * common/soft_spi.c). When called, it should activate the chip select - * to the device identified by "slave". - */ -void spi_cs_activate(struct spi_slave *slave) -{ - struct ftssp010_spi *chip = to_ftssp010_spi(slave); - struct ftssp010_regs *regs = chip->regs; - - /* cs pull */ - if (chip->mode & SPI_CS_HIGH) - ftssp010_cs_set(chip, 1); - else - ftssp010_cs_set(chip, 0); - - /* chip enable + fifo clear */ - setbits_le32(®s->cr[2], CR2_EN | CR2_TXFC | CR2_RXFC); -} - -/* - * Deactivate a SPI chipselect. - * This function is provided by the board code when using a driver - * that can't control its chipselects automatically (e.g. - * common/soft_spi.c). When called, it should deactivate the chip - * select to the device identified by "slave". - */ -void spi_cs_deactivate(struct spi_slave *slave) -{ - struct ftssp010_spi *chip = to_ftssp010_spi(slave); - - /* wait until chip idle */ - ftssp010_wait(chip); - - /* cs pull */ - if (chip->mode & SPI_CS_HIGH) - ftssp010_cs_set(chip, 0); - else - ftssp010_cs_set(chip, 1); -} - -void spi_init(void) -{ - /* nothing to do */ -} - -struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode) -{ - struct ftssp010_spi *chip; - - if (mode & SPI_3WIRE) { - puts("ftssp010: can't do 3-wire\n"); - return NULL; - } - - if (mode & SPI_SLAVE) { - puts("ftssp010: can't do slave mode\n"); - return NULL; - } - - if (mode & SPI_PREAMBLE) { - puts("ftssp010: can't skip preamble bytes\n"); - return NULL; - } - - if (!spi_cs_is_valid(bus, cs)) { - puts("ftssp010: invalid (bus, cs)\n"); - return NULL; - } - - chip = spi_alloc_slave(struct ftssp010_spi, bus, cs); - if (!chip) - return NULL; - - if (get_spi_chip(bus, chip)) - goto free_out; - - if (chip->revision < 0x11900 && get_spi_gpio(bus, &chip->gpio)) { - puts("ftssp010: Before revision 1.19.0, its clock & cs are\n" - "controlled by tx engine which is not synced with rx engine,\n" - "so the clock & cs might be shutdown before rx engine\n" - "finishs its jobs.\n" - "If possible, please add a dedicated gpio for it.\n"); - } - - chip->mode = mode; - chip->clk = CONFIG_FTSSP010_CLOCK; - chip->div = 2; - if (max_hz) { - while (chip->div < 0xffff) { - if ((chip->clk / (2 * chip->div)) <= max_hz) - break; - chip->div += 1; - } - } - chip->speed = chip->clk / (2 * chip->div); - - return &chip->slave; - -free_out: - free(chip); - return NULL; -} - -void spi_free_slave(struct spi_slave *slave) -{ - struct ftssp010_spi *chip = to_ftssp010_spi(slave); - - free(chip); -} - -int spi_claim_bus(struct spi_slave *slave) -{ - struct ftssp010_spi *chip = to_ftssp010_spi(slave); - struct ftssp010_regs *regs = chip->regs; - - writel(CR1_SDL(8) | CR1_DIV(chip->div), ®s->cr[1]); - - if (chip->revision >= 0x11900) { - writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO | CR0_FLASH, - ®s->cr[0]); - writel(CR2_TXFC | CR2_RXFC, - ®s->cr[2]); - } else { - writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO, - ®s->cr[0]); - writel(CR2_TXFC | CR2_RXFC | CR2_EN | CR2_TXDOE, - ®s->cr[2]); - } - - if (chip->mode & SPI_LOOP) - setbits_le32(®s->cr[0], CR0_LOOP); - - if (chip->mode & SPI_CPOL) - setbits_le32(®s->cr[0], CR0_SCLKPO); - - if (chip->mode & SPI_CPHA) - setbits_le32(®s->cr[0], CR0_SCLKPH); - - spi_cs_deactivate(slave); - - return 0; -} - -void spi_release_bus(struct spi_slave *slave) -{ - struct ftssp010_spi *chip = to_ftssp010_spi(slave); - struct ftssp010_regs *regs = chip->regs; - - writel(0, ®s->cr[2]); -} - -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, - const void *dout, void *din, unsigned long flags) -{ - struct ftssp010_spi *chip = to_ftssp010_spi(slave); - uint32_t len = bitlen >> 3; - - if (flags & SPI_XFER_BEGIN) - spi_cs_activate(slave); - - if (chip->revision >= 0x11900) - ftssp010_spi_work_transfer_v2(chip, dout, din, len, flags); - else - ftssp010_spi_work_transfer_v1(chip, dout, din, len, flags); - - if (flags & SPI_XFER_END) - spi_cs_deactivate(slave); - - return 0; -} diff --git a/drivers/spi/oc_tiny_spi.c b/drivers/spi/oc_tiny_spi.c deleted file mode 100644 index 4de5d00..0000000 --- a/drivers/spi/oc_tiny_spi.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Opencore tiny_spi driver - * - * http://opencores.org/project,tiny_spi - * - * based on bfin_spi.c - * Copyright (c) 2005-2008 Analog Devices Inc. - * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <asm/io.h> -#include <malloc.h> -#include <spi.h> -#include <asm/gpio.h> - -#define TINY_SPI_STATUS_TXE 0x1 -#define TINY_SPI_STATUS_TXR 0x2 - -struct tiny_spi_regs { - unsigned rxdata; /* Rx data reg */ - unsigned txdata; /* Tx data reg */ - unsigned status; /* Status reg */ - unsigned control; /* Control reg */ - unsigned baud; /* Baud reg */ -}; - -struct tiny_spi_host { - uint base; - uint freq; - uint baudwidth; -}; -static const struct tiny_spi_host tiny_spi_host_list[] = - CONFIG_SYS_TINY_SPI_LIST; - -struct tiny_spi_slave { - struct spi_slave slave; - const struct tiny_spi_host *host; - uint mode; - uint baud; - uint flg; -}; -#define to_tiny_spi_slave(s) container_of(s, struct tiny_spi_slave, slave) - -int spi_cs_is_valid(unsigned int bus, unsigned int cs) -{ - return bus < ARRAY_SIZE(tiny_spi_host_list) && gpio_is_valid(cs); -} - -void spi_cs_activate(struct spi_slave *slave) -{ - struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); - unsigned int cs = slave->cs; - - gpio_set_value(cs, tiny_spi->flg); - debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs)); -} - -void spi_cs_deactivate(struct spi_slave *slave) -{ - struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); - unsigned int cs = slave->cs; - - gpio_set_value(cs, !tiny_spi->flg); - debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs)); -} - -void spi_set_speed(struct spi_slave *slave, uint hz) -{ - struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); - const struct tiny_spi_host *host = tiny_spi->host; - - tiny_spi->baud = min(DIV_ROUND_UP(host->freq, hz * 2), - (1 << host->baudwidth)) - 1; - debug("%s: speed %u actual %u\n", __func__, hz, - host->freq / ((tiny_spi->baud + 1) * 2)); -} - -void spi_init(void) -{ -} - -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int hz, unsigned int mode) -{ - struct tiny_spi_slave *tiny_spi; - - if (!spi_cs_is_valid(bus, cs) || gpio_request(cs, "tiny_spi")) - return NULL; - - tiny_spi = spi_alloc_slave(struct tiny_spi_slave, bus, cs); - if (!tiny_spi) - return NULL; - - tiny_spi->host = &tiny_spi_host_list[bus]; - tiny_spi->mode = mode & (SPI_CPOL | SPI_CPHA); - tiny_spi->flg = mode & SPI_CS_HIGH ? 1 : 0; - spi_set_speed(&tiny_spi->slave, hz); - - debug("%s: bus:%i cs:%i base:%lx\n", __func__, - bus, cs, tiny_spi->host->base); - return &tiny_spi->slave; -} - -void spi_free_slave(struct spi_slave *slave) -{ - struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); - - gpio_free(slave->cs); - free(tiny_spi); -} - -int spi_claim_bus(struct spi_slave *slave) -{ - struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); - struct tiny_spi_regs *regs = (void *)tiny_spi->host->base; - - debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); - gpio_direction_output(slave->cs, !tiny_spi->flg); - writel(tiny_spi->mode, ®s->control); - writel(tiny_spi->baud, ®s->baud); - return 0; -} - -void spi_release_bus(struct spi_slave *slave) -{ - debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); -} - -#ifndef CONFIG_TINY_SPI_IDLE_VAL -# define CONFIG_TINY_SPI_IDLE_VAL 0xff -#endif - -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, - void *din, unsigned long flags) -{ - struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); - struct tiny_spi_regs *regs = (void *)tiny_spi->host->base; - const u8 *txp = dout; - u8 *rxp = din; - uint bytes = bitlen / 8; - uint i; - - debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, - slave->bus, slave->cs, bitlen, bytes, flags); - if (bitlen == 0) - goto done; - - /* assume to do 8 bits transfers */ - if (bitlen % 8) { - flags |= SPI_XFER_END; - goto done; - } - - if (flags & SPI_XFER_BEGIN) - spi_cs_activate(slave); - - /* we need to tighten the transfer loop */ - if (txp && rxp) { - writeb(*txp++, ®s->txdata); - if (bytes > 1) { - writeb(*txp++, ®s->txdata); - for (i = 2; i < bytes; i++) { - u8 rx, tx = *txp++; - while (!(readb(®s->status) & - TINY_SPI_STATUS_TXR)) - ; - rx = readb(®s->txdata); - writeb(tx, ®s->txdata); - *rxp++ = rx; - } - while (!(readb(®s->status) & - TINY_SPI_STATUS_TXR)) - ; - *rxp++ = readb(®s->txdata); - } - while (!(readb(®s->status) & - TINY_SPI_STATUS_TXE)) - ; - *rxp++ = readb(®s->rxdata); - } else if (rxp) { - writeb(CONFIG_TINY_SPI_IDLE_VAL, ®s->txdata); - if (bytes > 1) { - writeb(CONFIG_TINY_SPI_IDLE_VAL, - ®s->txdata); - for (i = 2; i < bytes; i++) { - u8 rx; - while (!(readb(®s->status) & - TINY_SPI_STATUS_TXR)) - ; - rx = readb(®s->txdata); - writeb(CONFIG_TINY_SPI_IDLE_VAL, - ®s->txdata); - *rxp++ = rx; - } - while (!(readb(®s->status) & - TINY_SPI_STATUS_TXR)) - ; - *rxp++ = readb(®s->txdata); - } - while (!(readb(®s->status) & - TINY_SPI_STATUS_TXE)) - ; - *rxp++ = readb(®s->rxdata); - } else if (txp) { - writeb(*txp++, ®s->txdata); - if (bytes > 1) { - writeb(*txp++, ®s->txdata); - for (i = 2; i < bytes; i++) { - u8 tx = *txp++; - while (!(readb(®s->status) & - TINY_SPI_STATUS_TXR)) - ; - writeb(tx, ®s->txdata); - } - } - while (!(readb(®s->status) & - TINY_SPI_STATUS_TXE)) - ; - } else { - writeb(CONFIG_TINY_SPI_IDLE_VAL, ®s->txdata); - if (bytes > 1) { - writeb(CONFIG_TINY_SPI_IDLE_VAL, - ®s->txdata); - for (i = 2; i < bytes; i++) { - while (!(readb(®s->status) & - TINY_SPI_STATUS_TXR)) - ; - writeb(CONFIG_TINY_SPI_IDLE_VAL, - ®s->txdata); - } - } - while (!(readb(®s->status) & - TINY_SPI_STATUS_TXE)) - ; - } - - done: - if (flags & SPI_XFER_END) - spi_cs_deactivate(slave); - - return 0; -} diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 56d99d1..6c21acd 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -1,149 +1,175 @@ /* * Xilinx SPI driver * - * supports 8 bit SPI transfers only, with or w/o FIFO + * Supports 8 bit SPI transfers only, with or w/o FIFO * - * based on bfin_spi.c, by way of altera_spi.c - * Copyright (c) 2005-2008 Analog Devices Inc. - * Copyright (c) 2010 Thomas Chou <thomas@wytron.com.tw> - * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca> + * Based on bfin_spi.c, by way of altera_spi.c + * Copyright (c) 2015 Jagan Teki <jteki@openedev.com> * Copyright (c) 2012 Stephan Linz <linz@li-pro.net> + * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca> + * Copyright (c) 2010 Thomas Chou <thomas@wytron.com.tw> + * Copyright (c) 2005-2008 Analog Devices Inc. * * SPDX-License-Identifier: GPL-2.0+ - * - * [0]: http://www.xilinx.com/support/documentation - * - * [S]: [0]/ip_documentation/xps_spi.pdf - * [0]/ip_documentation/axi_spi_ds742.pdf */ + #include <config.h> #include <common.h> +#include <dm.h> +#include <errno.h> #include <malloc.h> #include <spi.h> +#include <asm/io.h> -#include "xilinx_spi.h" +/* + * [0]: http://www.xilinx.com/support/documentation + * + * Xilinx SPI Register Definitions + * [1]: [0]/ip_documentation/xps_spi.pdf + * page 8, Register Descriptions + * [2]: [0]/ip_documentation/axi_spi_ds742.pdf + * page 7, Register Overview Table + */ -#ifndef CONFIG_SYS_XILINX_SPI_LIST -#define CONFIG_SYS_XILINX_SPI_LIST { CONFIG_SYS_SPI_BASE } -#endif +/* SPI Control Register (spicr), [1] p9, [2] p8 */ +#define SPICR_LSB_FIRST (1 << 9) +#define SPICR_MASTER_INHIBIT (1 << 8) +#define SPICR_MANUAL_SS (1 << 7) +#define SPICR_RXFIFO_RESEST (1 << 6) +#define SPICR_TXFIFO_RESEST (1 << 5) +#define SPICR_CPHA (1 << 4) +#define SPICR_CPOL (1 << 3) +#define SPICR_MASTER_MODE (1 << 2) +#define SPICR_SPE (1 << 1) +#define SPICR_LOOP (1 << 0) + +/* SPI Status Register (spisr), [1] p11, [2] p10 */ +#define SPISR_SLAVE_MODE_SELECT (1 << 5) +#define SPISR_MODF (1 << 4) +#define SPISR_TX_FULL (1 << 3) +#define SPISR_TX_EMPTY (1 << 2) +#define SPISR_RX_FULL (1 << 1) +#define SPISR_RX_EMPTY (1 << 0) + +/* SPI Data Transmit Register (spidtr), [1] p12, [2] p12 */ +#define SPIDTR_8BIT_MASK (0xff << 0) +#define SPIDTR_16BIT_MASK (0xffff << 0) +#define SPIDTR_32BIT_MASK (0xffffffff << 0) + +/* SPI Data Receive Register (spidrr), [1] p12, [2] p12 */ +#define SPIDRR_8BIT_MASK (0xff << 0) +#define SPIDRR_16BIT_MASK (0xffff << 0) +#define SPIDRR_32BIT_MASK (0xffffffff << 0) + +/* SPI Slave Select Register (spissr), [1] p13, [2] p13 */ +#define SPISSR_MASK(cs) (1 << (cs)) +#define SPISSR_ACT(cs) ~SPISSR_MASK(cs) +#define SPISSR_OFF ~0UL + +/* SPI Software Reset Register (ssr) */ +#define SPISSR_RESET_VALUE 0x0a + +#define XILSPI_MAX_XFER_BITS 8 +#define XILSPI_SPICR_DFLT_ON (SPICR_MANUAL_SS | SPICR_MASTER_MODE | \ + SPICR_SPE) +#define XILSPI_SPICR_DFLT_OFF (SPICR_MASTER_INHIBIT | SPICR_MANUAL_SS) #ifndef CONFIG_XILINX_SPI_IDLE_VAL #define CONFIG_XILINX_SPI_IDLE_VAL 0xff #endif -#define XILSPI_SPICR_DFLT_ON (SPICR_MANUAL_SS | \ - SPICR_MASTER_MODE | \ - SPICR_SPE) - -#define XILSPI_SPICR_DFLT_OFF (SPICR_MASTER_INHIBIT | \ - SPICR_MANUAL_SS) +#ifndef CONFIG_SYS_XILINX_SPI_LIST +#define CONFIG_SYS_XILINX_SPI_LIST { CONFIG_SYS_SPI_BASE } +#endif -#define XILSPI_MAX_XFER_BITS 8 +/* xilinx spi register set */ +struct xilinx_spi_regs { + u32 __space0__[7]; + u32 dgier; /* Device Global Interrupt Enable Register (DGIER) */ + u32 ipisr; /* IP Interrupt Status Register (IPISR) */ + u32 __space1__; + u32 ipier; /* IP Interrupt Enable Register (IPIER) */ + u32 __space2__[5]; + u32 srr; /* Softare Reset Register (SRR) */ + u32 __space3__[7]; + u32 spicr; /* SPI Control Register (SPICR) */ + u32 spisr; /* SPI Status Register (SPISR) */ + u32 spidtr; /* SPI Data Transmit Register (SPIDTR) */ + u32 spidrr; /* SPI Data Receive Register (SPIDRR) */ + u32 spissr; /* SPI Slave Select Register (SPISSR) */ + u32 spitfor; /* SPI Transmit FIFO Occupancy Register (SPITFOR) */ + u32 spirfor; /* SPI Receive FIFO Occupancy Register (SPIRFOR) */ +}; + +/* xilinx spi priv */ +struct xilinx_spi_priv { + struct xilinx_spi_regs *regs; + unsigned int freq; + unsigned int mode; +}; static unsigned long xilinx_spi_base_list[] = CONFIG_SYS_XILINX_SPI_LIST; - -__attribute__((weak)) -int spi_cs_is_valid(unsigned int bus, unsigned int cs) +static int xilinx_spi_probe(struct udevice *bus) { - return bus < ARRAY_SIZE(xilinx_spi_base_list) && cs < 32; -} + struct xilinx_spi_priv *priv = dev_get_priv(bus); + struct xilinx_spi_regs *regs = priv->regs; -__attribute__((weak)) -void spi_cs_activate(struct spi_slave *slave) -{ - struct xilinx_spi_slave *xilspi = to_xilinx_spi_slave(slave); + priv->regs = (struct xilinx_spi_regs *)xilinx_spi_base_list[bus->seq]; - writel(SPISSR_ACT(slave->cs), &xilspi->regs->spissr); -} + writel(SPISSR_RESET_VALUE, ®s->srr); -__attribute__((weak)) -void spi_cs_deactivate(struct spi_slave *slave) -{ - struct xilinx_spi_slave *xilspi = to_xilinx_spi_slave(slave); - - writel(SPISSR_OFF, &xilspi->regs->spissr); + return 0; } -void spi_init(void) +static void spi_cs_activate(struct udevice *dev, uint cs) { - /* do nothing */ -} + struct udevice *bus = dev_get_parent(dev); + struct xilinx_spi_priv *priv = dev_get_priv(bus); + struct xilinx_spi_regs *regs = priv->regs; -void spi_set_speed(struct spi_slave *slave, uint hz) -{ - /* xilinx spi core does not support programmable speed */ + writel(SPISSR_ACT(cs), ®s->spissr); } -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) +static void spi_cs_deactivate(struct udevice *dev) { - struct xilinx_spi_slave *xilspi; - - if (!spi_cs_is_valid(bus, cs)) { - printf("XILSPI error: %s: unsupported bus %d / cs %d\n", - __func__, bus, cs); - return NULL; - } - - xilspi = spi_alloc_slave(struct xilinx_spi_slave, bus, cs); - if (!xilspi) { - printf("XILSPI error: %s: malloc of SPI structure failed\n", - __func__); - return NULL; - } - xilspi->regs = (struct xilinx_spi_reg *)xilinx_spi_base_list[bus]; - xilspi->freq = max_hz; - xilspi->mode = mode; - debug("%s: bus:%i cs:%i base:%p mode:%x max_hz:%d\n", __func__, - bus, cs, xilspi->regs, xilspi->mode, xilspi->freq); + struct udevice *bus = dev_get_parent(dev); + struct xilinx_spi_priv *priv = dev_get_priv(bus); + struct xilinx_spi_regs *regs = priv->regs; - writel(SPISSR_RESET_VALUE, &xilspi->regs->srr); - - return &xilspi->slave; -} - -void spi_free_slave(struct spi_slave *slave) -{ - struct xilinx_spi_slave *xilspi = to_xilinx_spi_slave(slave); - - free(xilspi); + writel(SPISSR_OFF, ®s->spissr); } -int spi_claim_bus(struct spi_slave *slave) +static int xilinx_spi_claim_bus(struct udevice *dev) { - struct xilinx_spi_slave *xilspi = to_xilinx_spi_slave(slave); - u32 spicr; + struct udevice *bus = dev_get_parent(dev); + struct xilinx_spi_priv *priv = dev_get_priv(bus); + struct xilinx_spi_regs *regs = priv->regs; - debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); - writel(SPISSR_OFF, &xilspi->regs->spissr); + writel(SPISSR_OFF, ®s->spissr); + writel(XILSPI_SPICR_DFLT_ON, ®s->spicr); - spicr = XILSPI_SPICR_DFLT_ON; - if (xilspi->mode & SPI_LSB_FIRST) - spicr |= SPICR_LSB_FIRST; - if (xilspi->mode & SPI_CPHA) - spicr |= SPICR_CPHA; - if (xilspi->mode & SPI_CPOL) - spicr |= SPICR_CPOL; - if (xilspi->mode & SPI_LOOP) - spicr |= SPICR_LOOP; - - writel(spicr, &xilspi->regs->spicr); return 0; } -void spi_release_bus(struct spi_slave *slave) +static int xilinx_spi_release_bus(struct udevice *dev) { - struct xilinx_spi_slave *xilspi = to_xilinx_spi_slave(slave); + struct udevice *bus = dev_get_parent(dev); + struct xilinx_spi_priv *priv = dev_get_priv(bus); + struct xilinx_spi_regs *regs = priv->regs; + + writel(SPISSR_OFF, ®s->spissr); + writel(XILSPI_SPICR_DFLT_OFF, ®s->spicr); - debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); - writel(SPISSR_OFF, &xilspi->regs->spissr); - writel(XILSPI_SPICR_DFLT_OFF, &xilspi->regs->spicr); + return 0; } -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, - void *din, unsigned long flags) +static int xilinx_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) { - struct xilinx_spi_slave *xilspi = to_xilinx_spi_slave(slave); + struct udevice *bus = dev_get_parent(dev); + struct xilinx_spi_priv *priv = dev_get_priv(bus); + struct xilinx_spi_regs *regs = priv->regs; + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); /* assume spi core configured to do 8 bit transfers */ unsigned int bytes = bitlen / XILSPI_MAX_XFER_BITS; const unsigned char *txp = dout; @@ -151,65 +177,125 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, unsigned rxecount = 17; /* max. 16 elements in FIFO, leftover 1 */ unsigned global_timeout; - debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, - slave->bus, slave->cs, bitlen, bytes, flags); + debug("spi_xfer: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", + bus->seq, slave_plat->cs, bitlen, bytes, flags); + if (bitlen == 0) goto done; if (bitlen % XILSPI_MAX_XFER_BITS) { - printf("XILSPI warning: %s: Not a multiple of %d bits\n", - __func__, XILSPI_MAX_XFER_BITS); + printf("XILSPI warning: Not a multiple of %d bits\n", + XILSPI_MAX_XFER_BITS); flags |= SPI_XFER_END; goto done; } /* empty read buffer */ - while (rxecount && !(readl(&xilspi->regs->spisr) & SPISR_RX_EMPTY)) { - readl(&xilspi->regs->spidrr); + while (rxecount && !(readl(®s->spisr) & SPISR_RX_EMPTY)) { + readl(®s->spidrr); rxecount--; } if (!rxecount) { - printf("XILSPI error: %s: Rx buffer not empty\n", __func__); + printf("XILSPI error: Rx buffer not empty\n"); return -1; } if (flags & SPI_XFER_BEGIN) - spi_cs_activate(slave); + spi_cs_activate(dev, slave_plat->cs); /* at least 1usec or greater, leftover 1 */ - global_timeout = xilspi->freq > XILSPI_MAX_XFER_BITS * 1000000 ? 2 : - (XILSPI_MAX_XFER_BITS * 1000000 / xilspi->freq) + 1; + global_timeout = priv->freq > XILSPI_MAX_XFER_BITS * 1000000 ? 2 : + (XILSPI_MAX_XFER_BITS * 1000000 / priv->freq) + 1; while (bytes--) { unsigned timeout = global_timeout; /* get Tx element from data out buffer and count up */ unsigned char d = txp ? *txp++ : CONFIG_XILINX_SPI_IDLE_VAL; - debug("%s: tx:%x ", __func__, d); + debug("spi_xfer: tx:%x ", d); /* write out and wait for processing (receive data) */ - writel(d & SPIDTR_8BIT_MASK, &xilspi->regs->spidtr); - while (timeout && readl(&xilspi->regs->spisr) + writel(d & SPIDTR_8BIT_MASK, ®s->spidtr); + while (timeout && readl(®s->spisr) & SPISR_RX_EMPTY) { timeout--; udelay(1); } if (!timeout) { - printf("XILSPI error: %s: Xfer timeout\n", __func__); + printf("XILSPI error: Xfer timeout\n"); return -1; } /* read Rx element and push into data in buffer */ - d = readl(&xilspi->regs->spidrr) & SPIDRR_8BIT_MASK; + d = readl(®s->spidrr) & SPIDRR_8BIT_MASK; if (rxp) *rxp++ = d; - debug("rx:%x\n", d); + debug("spi_xfer: rx:%x\n", d); } done: if (flags & SPI_XFER_END) - spi_cs_deactivate(slave); + spi_cs_deactivate(dev); return 0; } + +static int xilinx_spi_set_speed(struct udevice *bus, uint speed) +{ + struct xilinx_spi_priv *priv = dev_get_priv(bus); + + priv->freq = speed; + + debug("xilinx_spi_set_speed: regs=%p, mode=%d\n", priv->regs, + priv->freq); + + return 0; +} + +static int xilinx_spi_set_mode(struct udevice *bus, uint mode) +{ + struct xilinx_spi_priv *priv = dev_get_priv(bus); + struct xilinx_spi_regs *regs = priv->regs; + uint32_t spicr; + + spicr = readl(®s->spicr); + if (priv->mode & SPI_LSB_FIRST) + spicr |= SPICR_LSB_FIRST; + if (priv->mode & SPI_CPHA) + spicr |= SPICR_CPHA; + if (priv->mode & SPI_CPOL) + spicr |= SPICR_CPOL; + if (priv->mode & SPI_LOOP) + spicr |= SPICR_LOOP; + + writel(spicr, ®s->spicr); + priv->mode = mode; + + debug("xilinx_spi_set_mode: regs=%p, mode=%d\n", priv->regs, + priv->mode); + + return 0; +} + +static const struct dm_spi_ops xilinx_spi_ops = { + .claim_bus = xilinx_spi_claim_bus, + .release_bus = xilinx_spi_release_bus, + .xfer = xilinx_spi_xfer, + .set_speed = xilinx_spi_set_speed, + .set_mode = xilinx_spi_set_mode, +}; + +static const struct udevice_id xilinx_spi_ids[] = { + { .compatible = "xlnx,xilinx-spi" }, + { } +}; + +U_BOOT_DRIVER(xilinx_spi) = { + .name = "xilinx_spi", + .id = UCLASS_SPI, + .of_match = xilinx_spi_ids, + .ops = &xilinx_spi_ops, + .priv_auto_alloc_size = sizeof(struct xilinx_spi_priv), + .probe = xilinx_spi_probe, +}; diff --git a/drivers/spi/xilinx_spi.h b/drivers/spi/xilinx_spi.h deleted file mode 100644 index ce7d82c..0000000 --- a/drivers/spi/xilinx_spi.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Xilinx SPI driver - * - * XPS/AXI bus interface - * - * based on bfin_spi.c, by way of altera_spi.c - * Copyright (c) 2005-2008 Analog Devices Inc. - * Copyright (c) 2010 Thomas Chou <thomas@wytron.com.tw> - * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca> - * Copyright (c) 2012 Stephan Linz <linz@li-pro.net> - * - * SPDX-License-Identifier: GPL-2.0+ - * - * [0]: http://www.xilinx.com/support/documentation - * - * [S]: [0]/ip_documentation/xps_spi.pdf - * [0]/ip_documentation/axi_spi_ds742.pdf - */ -#ifndef _XILINX_SPI_ -#define _XILINX_SPI_ - -#include <asm/types.h> -#include <asm/io.h> - -/* - * Xilinx SPI Register Definition - * - * [1]: [0]/ip_documentation/xps_spi.pdf - * page 8, Register Descriptions - * [2]: [0]/ip_documentation/axi_spi_ds742.pdf - * page 7, Register Overview Table - */ -struct xilinx_spi_reg { - u32 __space0__[7]; - u32 dgier; /* Device Global Interrupt Enable Register (DGIER) */ - u32 ipisr; /* IP Interrupt Status Register (IPISR) */ - u32 __space1__; - u32 ipier; /* IP Interrupt Enable Register (IPIER) */ - u32 __space2__[5]; - u32 srr; /* Softare Reset Register (SRR) */ - u32 __space3__[7]; - u32 spicr; /* SPI Control Register (SPICR) */ - u32 spisr; /* SPI Status Register (SPISR) */ - u32 spidtr; /* SPI Data Transmit Register (SPIDTR) */ - u32 spidrr; /* SPI Data Receive Register (SPIDRR) */ - u32 spissr; /* SPI Slave Select Register (SPISSR) */ - u32 spitfor; /* SPI Transmit FIFO Occupancy Register (SPITFOR) */ - u32 spirfor; /* SPI Receive FIFO Occupancy Register (SPIRFOR) */ -}; - -/* Device Global Interrupt Enable Register (dgier), [1] p15, [2] p15 */ -#define DGIER_GIE (1 << 31) - -/* IP Interrupt Status Register (ipisr), [1] p15, [2] p15 */ -#define IPISR_DRR_NOT_EMPTY (1 << 8) -#define IPISR_SLAVE_SELECT (1 << 7) -#define IPISR_TXF_HALF_EMPTY (1 << 6) -#define IPISR_DRR_OVERRUN (1 << 5) -#define IPISR_DRR_FULL (1 << 4) -#define IPISR_DTR_UNDERRUN (1 << 3) -#define IPISR_DTR_EMPTY (1 << 2) -#define IPISR_SLAVE_MODF (1 << 1) -#define IPISR_MODF (1 << 0) - -/* IP Interrupt Enable Register (ipier), [1] p17, [2] p18 */ -#define IPIER_DRR_NOT_EMPTY (1 << 8) -#define IPIER_SLAVE_SELECT (1 << 7) -#define IPIER_TXF_HALF_EMPTY (1 << 6) -#define IPIER_DRR_OVERRUN (1 << 5) -#define IPIER_DRR_FULL (1 << 4) -#define IPIER_DTR_UNDERRUN (1 << 3) -#define IPIER_DTR_EMPTY (1 << 2) -#define IPIER_SLAVE_MODF (1 << 1) -#define IPIER_MODF (1 << 0) - -/* Softare Reset Register (srr), [1] p9, [2] p8 */ -#define SRR_RESET_CODE 0x0000000A - -/* SPI Control Register (spicr), [1] p9, [2] p8 */ -#define SPICR_LSB_FIRST (1 << 9) -#define SPICR_MASTER_INHIBIT (1 << 8) -#define SPICR_MANUAL_SS (1 << 7) -#define SPICR_RXFIFO_RESEST (1 << 6) -#define SPICR_TXFIFO_RESEST (1 << 5) -#define SPICR_CPHA (1 << 4) -#define SPICR_CPOL (1 << 3) -#define SPICR_MASTER_MODE (1 << 2) -#define SPICR_SPE (1 << 1) -#define SPICR_LOOP (1 << 0) - -/* SPI Status Register (spisr), [1] p11, [2] p10 */ -#define SPISR_SLAVE_MODE_SELECT (1 << 5) -#define SPISR_MODF (1 << 4) -#define SPISR_TX_FULL (1 << 3) -#define SPISR_TX_EMPTY (1 << 2) -#define SPISR_RX_FULL (1 << 1) -#define SPISR_RX_EMPTY (1 << 0) - -/* SPI Data Transmit Register (spidtr), [1] p12, [2] p12 */ -#define SPIDTR_8BIT_MASK (0xff << 0) -#define SPIDTR_16BIT_MASK (0xffff << 0) -#define SPIDTR_32BIT_MASK (0xffffffff << 0) - -/* SPI Data Receive Register (spidrr), [1] p12, [2] p12 */ -#define SPIDRR_8BIT_MASK (0xff << 0) -#define SPIDRR_16BIT_MASK (0xffff << 0) -#define SPIDRR_32BIT_MASK (0xffffffff << 0) - -/* SPI Slave Select Register (spissr), [1] p13, [2] p13 */ -#define SPISSR_MASK(cs) (1 << (cs)) -#define SPISSR_ACT(cs) ~SPISSR_MASK(cs) -#define SPISSR_OFF ~0UL - -/* SPI Transmit FIFO Occupancy Register (spitfor), [1] p13, [2] p14 */ -#define SPITFOR_OCYVAL_POS 0 -#define SPITFOR_OCYVAL_MASK (0xf << SPITFOR_OCYVAL_POS) - -/* SPI Receive FIFO Occupancy Register (spirfor), [1] p14, [2] p14 */ -#define SPIRFOR_OCYVAL_POS 0 -#define SPIRFOR_OCYVAL_MASK (0xf << SPIRFOR_OCYVAL_POS) - -/* SPI Software Reset Register (ssr) */ -#define SPISSR_RESET_VALUE 0x0a - -struct xilinx_spi_slave { - struct spi_slave slave; - struct xilinx_spi_reg *regs; - unsigned int freq; - unsigned int mode; -}; - -static inline struct xilinx_spi_slave *to_xilinx_spi_slave( - struct spi_slave *slave) -{ - return container_of(slave, struct xilinx_spi_slave, slave); -} - -#endif /* _XILINX_SPI_ */ diff --git a/drivers/spi/zynq_spi.c b/drivers/spi/zynq_spi.c index e9129da..c5c3e10 100644 --- a/drivers/spi/zynq_spi.c +++ b/drivers/spi/zynq_spi.c @@ -1,5 +1,6 @@ /* * (C) Copyright 2013 Inc. + * (C) Copyright 2015 Jagan Teki <jteki@openedev.com> * * Xilinx Zynq PS SPI controller driver (master mode only) * @@ -8,11 +9,16 @@ #include <config.h> #include <common.h> +#include <dm.h> +#include <errno.h> #include <malloc.h> #include <spi.h> +#include <fdtdec.h> #include <asm/io.h> #include <asm/arch/hardware.h> +DECLARE_GLOBAL_DATA_PTR; + /* zynq spi register bit masks ZYNQ_SPI_<REG>_<BIT>_MASK */ #define ZYNQ_SPI_CR_MSA_MASK (1 << 15) /* Manual start enb */ #define ZYNQ_SPI_CR_MCS_MASK (1 << 14) /* Manual chip select */ @@ -44,180 +50,141 @@ struct zynq_spi_regs { u32 rxdr; /* 0x20 */ }; -/* zynq spi slave */ -struct zynq_spi_slave { - struct spi_slave slave; - struct zynq_spi_regs *base; + +/* zynq spi platform data */ +struct zynq_spi_platdata { + struct zynq_spi_regs *regs; + u32 frequency; /* input frequency */ + u32 speed_hz; +}; + +/* zynq spi priv */ +struct zynq_spi_priv { + struct zynq_spi_regs *regs; u8 mode; u8 fifo_depth; - u32 speed_hz; - u32 input_hz; - u32 req_hz; + u32 freq; /* required frequency */ }; -static inline struct zynq_spi_slave *to_zynq_spi_slave(struct spi_slave *slave) +static int zynq_spi_ofdata_to_platdata(struct udevice *bus) { - return container_of(slave, struct zynq_spi_slave, slave); -} + struct zynq_spi_platdata *plat = bus->platdata; + const void *blob = gd->fdt_blob; + int node = bus->of_offset; -static inline struct zynq_spi_regs *get_zynq_spi_base(int dev) -{ - if (dev) - return (struct zynq_spi_regs *)ZYNQ_SPI_BASEADDR1; - else - return (struct zynq_spi_regs *)ZYNQ_SPI_BASEADDR0; + plat->regs = (struct zynq_spi_regs *)fdtdec_get_addr(blob, node, "reg"); + + /* FIXME: Use 250MHz as a suitable default */ + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 250000000); + plat->speed_hz = plat->frequency / 2; + + debug("zynq_spi_ofdata_to_platdata: regs=%p max-frequency=%d\n", + plat->regs, plat->frequency); + + return 0; } -static void zynq_spi_init_hw(struct zynq_spi_slave *zslave) +static void zynq_spi_init_hw(struct zynq_spi_priv *priv) { + struct zynq_spi_regs *regs = priv->regs; u32 confr; /* Disable SPI */ - writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, ®s->enr); /* Disable Interrupts */ - writel(ZYNQ_SPI_IXR_ALL_MASK, &zslave->base->idr); + writel(ZYNQ_SPI_IXR_ALL_MASK, ®s->idr); /* Clear RX FIFO */ - while (readl(&zslave->base->isr) & + while (readl(®s->isr) & ZYNQ_SPI_IXR_RXNEMPTY_MASK) - readl(&zslave->base->rxdr); + readl(®s->rxdr); /* Clear Interrupts */ - writel(ZYNQ_SPI_IXR_ALL_MASK, &zslave->base->isr); + writel(ZYNQ_SPI_IXR_ALL_MASK, ®s->isr); /* Manual slave select and Auto start */ confr = ZYNQ_SPI_CR_MCS_MASK | ZYNQ_SPI_CR_CS_MASK | ZYNQ_SPI_CR_MSTREN_MASK; confr &= ~ZYNQ_SPI_CR_MSA_MASK; - writel(confr, &zslave->base->cr); + writel(confr, ®s->cr); /* Enable SPI */ - writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + writel(ZYNQ_SPI_ENR_SPI_EN_MASK, ®s->enr); } -int spi_cs_is_valid(unsigned int bus, unsigned int cs) +static int zynq_spi_probe(struct udevice *bus) { - /* 2 bus with 3 chipselect */ - return bus < 2 && cs < 3; + struct zynq_spi_platdata *plat = dev_get_platdata(bus); + struct zynq_spi_priv *priv = dev_get_priv(bus); + + priv->regs = plat->regs; + priv->fifo_depth = ZYNQ_SPI_FIFO_DEPTH; + + /* init the zynq spi hw */ + zynq_spi_init_hw(priv); + + return 0; } -void spi_cs_activate(struct spi_slave *slave) +static void spi_cs_activate(struct udevice *dev, uint cs) { - struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + struct udevice *bus = dev->parent; + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; u32 cr; - debug("spi_cs_activate: 0x%08x\n", (u32)slave); - - clrbits_le32(&zslave->base->cr, ZYNQ_SPI_CR_CS_MASK); - cr = readl(&zslave->base->cr); + clrbits_le32(®s->cr, ZYNQ_SPI_CR_CS_MASK); + cr = readl(®s->cr); /* * CS cal logic: CS[13:10] * xxx0 - cs0 * xx01 - cs1 * x011 - cs2 */ - cr |= (~(0x1 << slave->cs) << 10) & ZYNQ_SPI_CR_CS_MASK; - writel(cr, &zslave->base->cr); + cr |= (~(0x1 << cs) << 10) & ZYNQ_SPI_CR_CS_MASK; + writel(cr, ®s->cr); } -void spi_cs_deactivate(struct spi_slave *slave) +static void spi_cs_deactivate(struct udevice *dev) { - struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); - - debug("spi_cs_deactivate: 0x%08x\n", (u32)slave); + struct udevice *bus = dev->parent; + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; - setbits_le32(&zslave->base->cr, ZYNQ_SPI_CR_CS_MASK); -} - -void spi_init() -{ - /* nothing to do */ + setbits_le32(®s->cr, ZYNQ_SPI_CR_CS_MASK); } -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) +static int zynq_spi_claim_bus(struct udevice *dev) { - struct zynq_spi_slave *zslave; + struct udevice *bus = dev->parent; + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; - if (!spi_cs_is_valid(bus, cs)) - return NULL; + writel(ZYNQ_SPI_ENR_SPI_EN_MASK, ®s->enr); - zslave = spi_alloc_slave(struct zynq_spi_slave, bus, cs); - if (!zslave) { - printf("SPI_error: Fail to allocate zynq_spi_slave\n"); - return NULL; - } - - zslave->base = get_zynq_spi_base(bus); - zslave->mode = mode; - zslave->fifo_depth = ZYNQ_SPI_FIFO_DEPTH; - zslave->input_hz = 166666700; - zslave->speed_hz = zslave->input_hz / 2; - zslave->req_hz = max_hz; - - /* init the zynq spi hw */ - zynq_spi_init_hw(zslave); - - return &zslave->slave; -} - -void spi_free_slave(struct spi_slave *slave) -{ - struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); - - debug("spi_free_slave: 0x%08x\n", (u32)slave); - free(zslave); + return 0; } -int spi_claim_bus(struct spi_slave *slave) +static int zynq_spi_release_bus(struct udevice *dev) { - struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); - u32 confr = 0; - u8 baud_rate_val = 0; + struct udevice *bus = dev->parent; + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; - writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); - - /* Set the SPI Clock phase and polarities */ - confr = readl(&zslave->base->cr); - confr &= ~(ZYNQ_SPI_CR_CPHA_MASK | ZYNQ_SPI_CR_CPOL_MASK); - if (zslave->mode & SPI_CPHA) - confr |= ZYNQ_SPI_CR_CPHA_MASK; - if (zslave->mode & SPI_CPOL) - confr |= ZYNQ_SPI_CR_CPOL_MASK; - - /* Set the clock frequency */ - if (zslave->req_hz == 0) { - /* Set baudrate x8, if the req_hz is 0 */ - baud_rate_val = 0x2; - } else if (zslave->speed_hz != zslave->req_hz) { - while ((baud_rate_val < 8) && - ((zslave->input_hz / - (2 << baud_rate_val)) > zslave->req_hz)) - baud_rate_val++; - zslave->speed_hz = zslave->req_hz / (2 << baud_rate_val); - } - confr &= ~ZYNQ_SPI_CR_BRD_MASK; - confr |= (baud_rate_val << 3); - writel(confr, &zslave->base->cr); - - writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, ®s->enr); return 0; } -void spi_release_bus(struct spi_slave *slave) +static int zynq_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) { - struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); - - debug("spi_release_bus: 0x%08x\n", (u32)slave); - writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); -} - -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, - void *din, unsigned long flags) -{ - struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + struct udevice *bus = dev->parent; + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); u32 len = bitlen / 8; u32 tx_len = len, rx_len = len, tx_tvl; const u8 *tx_buf = dout; @@ -225,7 +192,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, u32 ts, status; debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n", - slave->bus, slave->cs, bitlen, len, flags); + bus->seq, slave_plat->cs, bitlen, len, flags); if (bitlen % 8) { debug("spi_xfer: Non byte aligned SPI transfer\n"); @@ -233,45 +200,126 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, } if (flags & SPI_XFER_BEGIN) - spi_cs_activate(slave); + spi_cs_activate(dev, slave_plat->cs); while (rx_len > 0) { /* Write the data into TX FIFO - tx threshold is fifo_depth */ tx_tvl = 0; - while ((tx_tvl < zslave->fifo_depth) && tx_len) { + while ((tx_tvl < priv->fifo_depth) && tx_len) { if (tx_buf) buf = *tx_buf++; else buf = 0; - writel(buf, &zslave->base->txdr); + writel(buf, ®s->txdr); tx_len--; tx_tvl++; } /* Check TX FIFO completion */ ts = get_timer(0); - status = readl(&zslave->base->isr); + status = readl(®s->isr); while (!(status & ZYNQ_SPI_IXR_TXOW_MASK)) { if (get_timer(ts) > CONFIG_SYS_ZYNQ_SPI_WAIT) { printf("spi_xfer: Timeout! TX FIFO not full\n"); return -1; } - status = readl(&zslave->base->isr); + status = readl(®s->isr); } /* Read the data from RX FIFO */ - status = readl(&zslave->base->isr); + status = readl(®s->isr); while (status & ZYNQ_SPI_IXR_RXNEMPTY_MASK) { - buf = readl(&zslave->base->rxdr); + buf = readl(®s->rxdr); if (rx_buf) *rx_buf++ = buf; - status = readl(&zslave->base->isr); + status = readl(®s->isr); rx_len--; } } if (flags & SPI_XFER_END) - spi_cs_deactivate(slave); + spi_cs_deactivate(dev); return 0; } + +static int zynq_spi_set_speed(struct udevice *bus, uint speed) +{ + struct zynq_spi_platdata *plat = bus->platdata; + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; + uint32_t confr; + u8 baud_rate_val = 0; + + if (speed > plat->frequency) + speed = plat->frequency; + + /* Set the clock frequency */ + confr = readl(®s->cr); + if (speed == 0) { + /* Set baudrate x8, if the freq is 0 */ + baud_rate_val = 0x2; + } else if (plat->speed_hz != speed) { + while ((baud_rate_val < 8) && + ((plat->frequency / + (2 << baud_rate_val)) > speed)) + baud_rate_val++; + plat->speed_hz = speed / (2 << baud_rate_val); + } + confr &= ~ZYNQ_SPI_CR_BRD_MASK; + confr |= (baud_rate_val << 3); + + writel(confr, ®s->cr); + priv->freq = speed; + + debug("zynq_spi_set_speed: regs=%p, mode=%d\n", priv->regs, priv->freq); + + return 0; +} + +static int zynq_spi_set_mode(struct udevice *bus, uint mode) +{ + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; + uint32_t confr; + + /* Set the SPI Clock phase and polarities */ + confr = readl(®s->cr); + confr &= ~(ZYNQ_SPI_CR_CPHA_MASK | ZYNQ_SPI_CR_CPOL_MASK); + + if (priv->mode & SPI_CPHA) + confr |= ZYNQ_SPI_CR_CPHA_MASK; + if (priv->mode & SPI_CPOL) + confr |= ZYNQ_SPI_CR_CPOL_MASK; + + writel(confr, ®s->cr); + priv->mode = mode; + + debug("zynq_spi_set_mode: regs=%p, mode=%d\n", priv->regs, priv->mode); + + return 0; +} + +static const struct dm_spi_ops zynq_spi_ops = { + .claim_bus = zynq_spi_claim_bus, + .release_bus = zynq_spi_release_bus, + .xfer = zynq_spi_xfer, + .set_speed = zynq_spi_set_speed, + .set_mode = zynq_spi_set_mode, +}; + +static const struct udevice_id zynq_spi_ids[] = { + { .compatible = "xlnx,zynq-spi" }, + { } +}; + +U_BOOT_DRIVER(zynq_spi) = { + .name = "zynq_spi", + .id = UCLASS_SPI, + .of_match = zynq_spi_ids, + .ops = &zynq_spi_ops, + .ofdata_to_platdata = zynq_spi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct zynq_spi_platdata), + .priv_auto_alloc_size = sizeof(struct zynq_spi_priv), + .probe = zynq_spi_probe, +}; diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index e455a52..02bb216 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -6,3 +6,4 @@ dwc3-y += gadget.o ep0.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o +obj-$(CONFIG_USB_DWC3_PHY_SAMSUNG) += samsung_usb_phy.o diff --git a/drivers/usb/dwc3/samsung_usb_phy.c b/drivers/usb/dwc3/samsung_usb_phy.c new file mode 100644 index 0000000..4220986 --- /dev/null +++ b/drivers/usb/dwc3/samsung_usb_phy.c @@ -0,0 +1,78 @@ +/** + * samsung_usb_phy.c - DesignWare USB3 (DWC3) PHY handling file + * + * Copyright (C) 2015 Samsung Electronics + * + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <asm/arch/power.h> +#include <asm/arch/xhci-exynos.h> + +void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy) +{ + u32 reg; + + /* Reset USB 3.0 PHY */ + writel(0x0, &phy->phy_reg0); + + clrbits_le32(&phy->phy_param0, + /* Select PHY CLK source */ + PHYPARAM0_REF_USE_PAD | + /* Set Loss-of-Signal Detector sensitivity */ + PHYPARAM0_REF_LOSLEVEL_MASK); + setbits_le32(&phy->phy_param0, PHYPARAM0_REF_LOSLEVEL); + + + writel(0x0, &phy->phy_resume); + + /* + * Setting the Frame length Adj value[6:1] to default 0x20 + * See xHCI 1.0 spec, 5.2.4 + */ + setbits_le32(&phy->link_system, + LINKSYSTEM_XHCI_VERSION_CONTROL | + LINKSYSTEM_FLADJ(0x20)); + + /* Set Tx De-Emphasis level */ + clrbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH_MASK); + setbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH); + + setbits_le32(&phy->phy_batchg, PHYBATCHG_UTMI_CLKSEL); + + /* PHYTEST POWERDOWN Control */ + clrbits_le32(&phy->phy_test, + PHYTEST_POWERDOWN_SSP | + PHYTEST_POWERDOWN_HSP); + + /* UTMI Power Control */ + writel(PHYUTMI_OTGDISABLE, &phy->phy_utmi); + + /* Use core clock from main PLL */ + reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK | + /* Default 24Mhz crystal clock */ + PHYCLKRST_FSEL(FSEL_CLKSEL_24M) | + PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0) | + /* Force PortReset of PHY */ + PHYCLKRST_PORTRESET | + /* Digital power supply in normal operating mode */ + PHYCLKRST_RETENABLEN | + /* Enable ref clock for SS function */ + PHYCLKRST_REF_SSP_EN | + /* Enable spread spectrum */ + PHYCLKRST_SSC_EN | + /* Power down HS Bias and PLL blocks in suspend mode */ + PHYCLKRST_COMMONONN; + + writel(reg, &phy->phy_clk_rst); + + /* giving time to Phy clock to settle before resetting */ + udelay(10); + + reg &= ~PHYCLKRST_PORTRESET; + writel(reg, &phy->phy_clk_rst); +} diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index d1bc5ef..abe9391 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -671,7 +671,7 @@ static int sleep_thread(struct fsg_common *common) if (common->thread_wakeup_needed) break; - if (++i == 50000) { + if (++i == 20000) { busy_indicator(); i = 0; k++; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 5fd618d..97b7f14 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -76,7 +76,7 @@ int ehci_hcd_init(int index, enum usb_init_type init, break; default: printf("ERROR: wrong controller index!!\n"); - break; + return -EINVAL; }; *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 1e5a6e2..bf02221 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1214,6 +1214,7 @@ static int _ehci_submit_control_msg(struct usb_device *dev, unsigned long pipe, struct int_queue { int elementsize; + unsigned long pipe; struct QH *first; struct QH *current; struct QH *last; @@ -1269,7 +1270,7 @@ static struct int_queue *_ehci_create_int_queue(struct usb_device *dev, { struct ehci_ctrl *ctrl = ehci_get_ctrl(dev); struct int_queue *result = NULL; - int i; + uint32_t i, toggle; /* * Interrupt transfers requiring several transactions are not supported @@ -1309,6 +1310,7 @@ static struct int_queue *_ehci_create_int_queue(struct usb_device *dev, goto fail1; } result->elementsize = elementsize; + result->pipe = pipe; result->first = memalign(USB_DMA_MINALIGN, sizeof(struct QH) * queuesize); if (!result->first) { @@ -1326,6 +1328,8 @@ static struct int_queue *_ehci_create_int_queue(struct usb_device *dev, memset(result->first, 0, sizeof(struct QH) * queuesize); memset(result->tds, 0, sizeof(struct qTD) * queuesize); + toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + for (i = 0; i < queuesize; i++) { struct QH *qh = result->first + i; struct qTD *td = result->tds + i; @@ -1357,7 +1361,9 @@ static struct int_queue *_ehci_create_int_queue(struct usb_device *dev, td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); debug("communication direction is '%s'\n", usb_pipein(pipe) ? "in" : "out"); - td->qt_token = cpu_to_hc32((elementsize << 16) | + td->qt_token = cpu_to_hc32( + QT_TOKEN_DT(toggle) | + (elementsize << 16) | ((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */ 0x80); /* active */ td->qt_buffer[0] = @@ -1372,6 +1378,7 @@ static struct int_queue *_ehci_create_int_queue(struct usb_device *dev, cpu_to_hc32((td->qt_buffer[0] + 0x4000) & ~0xfff); *buf = buffer + i * elementsize; + toggle ^= 1; } flush_dcache_range((unsigned long)buffer, @@ -1426,6 +1433,8 @@ static void *_ehci_poll_int_queue(struct usb_device *dev, { struct QH *cur = queue->current; struct qTD *cur_td; + uint32_t token, toggle; + unsigned long pipe = queue->pipe; /* depleted queue */ if (cur == NULL) { @@ -1436,12 +1445,15 @@ static void *_ehci_poll_int_queue(struct usb_device *dev, cur_td = &queue->tds[queue->current - queue->first]; invalidate_dcache_range((unsigned long)cur_td, ALIGN_END_ADDR(struct qTD, cur_td, 1)); - if (QT_TOKEN_GET_STATUS(hc32_to_cpu(cur_td->qt_token)) & - QT_TOKEN_STATUS_ACTIVE) { - debug("Exit poll_int_queue with no completed intr transfer. token is %x\n", - hc32_to_cpu(cur_td->qt_token)); + token = hc32_to_cpu(cur_td->qt_token); + if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE) { + debug("Exit poll_int_queue with no completed intr transfer. token is %x\n", token); return NULL; } + + toggle = QT_TOKEN_GET_DT(token); + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle); + if (!(cur->qh_link & QH_LINK_TERMINATE)) queue->current++; else @@ -1452,7 +1464,7 @@ static void *_ehci_poll_int_queue(struct usb_device *dev, queue->elementsize)); debug("Exit poll_int_queue with completed intr transfer. token is %x at %p (first at %p)\n", - hc32_to_cpu(cur_td->qt_token), cur, queue->first); + token, cur, queue->first); return cur->buffer; } diff --git a/drivers/usb/musb-new/sunxi.c b/drivers/usb/musb-new/sunxi.c index e8a3a23..052e065 100644 --- a/drivers/usb/musb-new/sunxi.c +++ b/drivers/usb/musb-new/sunxi.c @@ -105,16 +105,6 @@ static void USBC_EnableIdPullUp(__iomem void *base) musb_writel(base, USBC_REG_o_ISCR, reg_val); } -static void USBC_DisableIdPullUp(__iomem void *base) -{ - u32 reg_val; - - reg_val = musb_readl(base, USBC_REG_o_ISCR); - reg_val &= ~(1 << USBC_BP_ISCR_ID_PULLUP_EN); - reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); - musb_writel(base, USBC_REG_o_ISCR, reg_val); -} - static void USBC_EnableDpDmPullUp(__iomem void *base) { u32 reg_val; @@ -125,34 +115,35 @@ static void USBC_EnableDpDmPullUp(__iomem void *base) musb_writel(base, USBC_REG_o_ISCR, reg_val); } -static void USBC_DisableDpDmPullUp(__iomem void *base) +static void USBC_ForceIdToLow(__iomem void *base) { u32 reg_val; reg_val = musb_readl(base, USBC_REG_o_ISCR); - reg_val &= ~(1 << USBC_BP_ISCR_DPDM_PULLUP_EN); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID); reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); musb_writel(base, USBC_REG_o_ISCR, reg_val); } -static void USBC_ForceIdToLow(__iomem void *base) +static void USBC_ForceIdToHigh(__iomem void *base) { u32 reg_val; reg_val = musb_readl(base, USBC_REG_o_ISCR); reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); - reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID); + reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID); reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); musb_writel(base, USBC_REG_o_ISCR, reg_val); } -static void USBC_ForceIdToHigh(__iomem void *base) +static void USBC_ForceVbusValidToLow(__iomem void *base) { u32 reg_val; reg_val = musb_readl(base, USBC_REG_o_ISCR); - reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); - reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val |= (0x02 << USBC_BP_ISCR_FORCE_VBUS_VALID); reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); musb_writel(base, USBC_REG_o_ISCR, reg_val); } @@ -205,42 +196,41 @@ static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) return retval; } +/* musb_core does not call enable / disable in a balanced manner <sigh> */ +static bool enabled = false; + static void sunxi_musb_enable(struct musb *musb) { pr_debug("%s():\n", __func__); + if (enabled) + return; + /* select PIO mode */ musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); - if (is_host_enabled(musb)) { - /* port power on */ - sunxi_usb_phy_power_on(0); - } + if (is_host_enabled(musb)) + sunxi_usb_phy_power_on(0); /* port power on */ + + USBC_ForceVbusValidToHigh(musb->mregs); + + enabled = true; } static void sunxi_musb_disable(struct musb *musb) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - pr_debug("%s():\n", __func__); - /* Put the controller back in a pristane state for "usb reset" */ - if (musb->is_active) { - sunxi_usb_phy_exit(0); -#ifdef CONFIG_SUNXI_GEN_SUN6I - clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0); -#endif - clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0); + if (!enabled) + return; - mdelay(10); + if (is_host_enabled(musb)) + sunxi_usb_phy_power_off(0); /* port power off */ - setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0); -#ifdef CONFIG_SUNXI_GEN_SUN6I - setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0); -#endif - sunxi_usb_phy_init(0); - musb->is_active = 0; - } + USBC_ForceVbusValidToLow(musb->mregs); + mdelay(200); /* Wait for the current session to timeout */ + + enabled = false; } static int sunxi_musb_init(struct musb *musb) @@ -282,22 +272,8 @@ static int sunxi_musb_init(struct musb *musb) return 0; } -static int sunxi_musb_exit(struct musb *musb) -{ - pr_debug("%s():\n", __func__); - - USBC_DisableDpDmPullUp(musb->mregs); - USBC_DisableIdPullUp(musb->mregs); - sunxi_usb_phy_power_off(0); - sunxi_usb_phy_exit(0); - - return 0; -} - const struct musb_platform_ops sunxi_musb_ops = { .init = sunxi_musb_init, - .exit = sunxi_musb_exit, - .enable = sunxi_musb_enable, .disable = sunxi_musb_disable, }; |