summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/Kconfig3
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-mxs.c186
-rw-r--r--drivers/mci/imx-esdhc.c3
-rw-r--r--drivers/mci/mxs.c50
-rw-r--r--drivers/mci/sdhci.h1
-rw-r--r--drivers/mtd/nand/Kconfig7
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/nand_imx_bbm.c2
-rw-r--r--drivers/mtd/nand/nand_mrvl_nfc.c1020
-rw-r--r--drivers/mtd/nand/nand_mxs.c7
-rw-r--r--drivers/net/smc91111.c254
-rw-r--r--drivers/of/of_path.c2
-rw-r--r--drivers/of/platform.c4
-rw-r--r--drivers/pinctrl/Kconfig7
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/pinctrl-mxs.c171
-rw-r--r--drivers/pinctrl/pinctrl.c3
-rw-r--r--drivers/serial/serial_imx.c4
-rw-r--r--drivers/serial/stm-serial.c9
-rw-r--r--drivers/spi/imx_spi.c41
-rw-r--r--drivers/usb/core/hub.c8
-rw-r--r--drivers/usb/core/usb.c2
-rw-r--r--drivers/usb/gadget/at91_udc.c2
-rw-r--r--drivers/usb/gadget/u_serial.c1
-rw-r--r--drivers/usb/host/ehci-hcd.c6
-rw-r--r--drivers/usb/musb/Kconfig1
-rw-r--r--drivers/usb/storage/transport.c8
-rw-r--r--drivers/usb/storage/usb.c2
29 files changed, 1678 insertions, 129 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 0ca7df450a..c8b1efb1f2 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -41,6 +41,9 @@ config GPIO_GENERIC_PLATFORM
config GPIO_IMX
def_bool ARCH_IMX
+config GPIO_MXS
+ def_bool ARCH_MXS
+
config GPIO_JZ4740
bool "GPIO support for Ingenic SoCs"
depends on MACH_MIPS_XBURST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 510d146ff5..508e228bac 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_DIGIC) += gpio-digic.o
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
obj-$(CONFIG_GPIO_IMX) += gpio-imx.o
+obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
obj-$(CONFIG_GPIO_JZ4740) += gpio-jz4740.o
obj-$(CONFIG_GPIO_MALTA_FPGA_I2C) += gpio-malta-fpga-i2c.o
obj-$(CONFIG_GPIO_ORION) += gpio-orion.o
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
new file mode 100644
index 0000000000..30f9589831
--- /dev/null
+++ b/drivers/gpio/gpio-mxs.c
@@ -0,0 +1,186 @@
+/*
+ * Freescale MXS gpio support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <io.h>
+#include <of.h>
+#include <gpio.h>
+#include <init.h>
+#include <stmp-device.h>
+#include <linux/err.h>
+
+struct mxs_gpio_chip {
+ void __iomem *base;
+ void __iomem *din;
+ void __iomem *doe;
+ void __iomem *dout;
+ struct gpio_chip chip;
+ struct mxs_gpio_regs *regs;
+};
+
+struct mxs_gpio_regs {
+ unsigned int din;
+ unsigned int doe;
+ unsigned int dout;
+};
+
+static struct mxs_gpio_regs regs_mxs23 = {
+ .din = 0x0600,
+ .dout = 0x0500,
+ .doe = 0x0700,
+};
+
+static struct mxs_gpio_regs regs_mxs28 = {
+ .din = 0x0900,
+ .dout = 0x0700,
+ .doe = 0x0b00,
+};
+
+static void mxs_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value)
+{
+ struct mxs_gpio_chip *mxsgpio = container_of(chip, struct mxs_gpio_chip, chip);
+
+ if (value)
+ writel(0x1 << gpio, mxsgpio->dout + STMP_OFFSET_REG_SET);
+ else
+ writel(0x1 << gpio, mxsgpio->dout + STMP_OFFSET_REG_CLR);
+}
+
+static int mxs_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+ struct mxs_gpio_chip *mxsgpio = container_of(chip, struct mxs_gpio_chip, chip);
+
+ writel(0x1 << gpio, mxsgpio->doe + STMP_OFFSET_REG_CLR);
+
+ return 0;
+}
+
+
+static int mxs_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value)
+{
+ struct mxs_gpio_chip *mxsgpio = container_of(chip, struct mxs_gpio_chip, chip);
+
+ mxs_gpio_set_value(chip, gpio, value);
+ writel(0x1 << gpio, mxsgpio->doe + STMP_OFFSET_REG_SET);
+
+ return 0;
+}
+
+static int mxs_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct mxs_gpio_chip *mxsgpio = container_of(chip, struct mxs_gpio_chip, chip);
+
+ if (readl(mxsgpio->din) & (1 << gpio))
+ return 1;
+ else
+ return 0;
+}
+
+static int mxs_get_direction(struct gpio_chip *chip, unsigned gpio)
+{
+ struct mxs_gpio_chip *mxsgpio = container_of(chip, struct mxs_gpio_chip, chip);
+
+ if (readl(mxsgpio->doe) & (1 << gpio))
+ return GPIOF_DIR_OUT;
+ else
+ return GPIOF_DIR_IN;
+}
+
+static struct gpio_ops mxs_gpio_ops = {
+ .direction_input = mxs_gpio_direction_input,
+ .direction_output = mxs_gpio_direction_output,
+ .get = mxs_gpio_get_value,
+ .set = mxs_gpio_set_value,
+ .get_direction = mxs_get_direction,
+};
+
+static int mxs_gpio_probe(struct device_d *dev)
+{
+ struct mxs_gpio_chip *mxsgpio;
+ struct mxs_gpio_regs *regs;
+ int ret, id;
+
+ ret = dev_get_drvdata(dev, (unsigned long *)&regs);
+ if (ret)
+ return ret;
+
+ mxsgpio = xzalloc(sizeof(*mxsgpio));
+ mxsgpio->chip.ops = &mxs_gpio_ops;
+ if (dev->id < 0) {
+ id = of_alias_get_id(dev->device_node, "gpio");
+ if (id < 0)
+ return id;
+ mxsgpio->base = dev_get_mem_region(dev->parent, 0);
+ mxsgpio->chip.base = id * 32;
+ } else {
+ id = dev->id;
+ mxsgpio->base = dev_get_mem_region(dev, 0);
+ mxsgpio->chip.base = dev->id * 32;
+ }
+
+ if (IS_ERR(mxsgpio->base))
+ return PTR_ERR(mxsgpio->base);
+
+ mxsgpio->doe = mxsgpio->base + regs->doe + id * 0x10;
+ mxsgpio->dout = mxsgpio->base + regs->dout + id * 0x10;
+ mxsgpio->din = mxsgpio->base + regs->din + id * 0x10;
+
+ mxsgpio->chip.ngpio = 32;
+ mxsgpio->chip.dev = dev;
+ mxsgpio->regs = regs;
+ gpiochip_add(&mxsgpio->chip);
+
+ dev_dbg(dev, "probed gpiochip%d with base %d\n", dev->id, mxsgpio->chip.base);
+
+ return 0;
+}
+
+static __maybe_unused struct of_device_id mxs_gpio_dt_ids[] = {
+ {
+ .compatible = "fsl,imx23-gpio",
+ .data = (unsigned long)&regs_mxs23,
+ }, {
+ .compatible = "fsl,imx28-gpio",
+ .data = (unsigned long)&regs_mxs28,
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct platform_device_id mxs_gpio_ids[] = {
+ {
+ .name = "imx23-gpio",
+ .driver_data = (unsigned long)&regs_mxs23,
+ }, {
+ .name = "imx28-gpio",
+ .driver_data = (unsigned long)&regs_mxs28,
+ }, {
+ /* sentinel */
+ },
+};
+
+static struct driver_d mxs_gpio_driver = {
+ .name = "gpio-mxs",
+ .probe = mxs_gpio_probe,
+ .of_compatible = DRV_OF_COMPAT(mxs_gpio_dt_ids),
+ .id_table = mxs_gpio_ids,
+};
+
+static int mxs_gpio_add(void)
+{
+ platform_driver_register(&mxs_gpio_driver);
+ return 0;
+}
+core_initcall(mxs_gpio_add);
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index 239cd375fa..23bdc1fb15 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -471,6 +471,9 @@ static int esdhc_init(struct mci_host *mci, struct device_d *dev)
esdhc_write32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
SYSCTL_HCKEN | SYSCTL_IPGEN);
+ /* RSTA doesn't reset MMC_BOOT register, so manually reset it */
+ esdhc_write32(regs + SDHCI_MMC_BOOT, 0);
+
/* Set the initial clock speed */
set_sysctl(mci, 400000);
diff --git a/drivers/mci/mxs.c b/drivers/mci/mxs.c
index 367964c1d0..b36fb13254 100644
--- a/drivers/mci/mxs.c
+++ b/drivers/mci/mxs.c
@@ -549,11 +549,6 @@ static int mxs_mci_probe(struct device_d *hw_dev)
struct mci_host *host;
unsigned long rate;
- if (hw_dev->platform_data == NULL) {
- dev_err(hw_dev, "Missing platform data\n");
- return -EINVAL;
- }
-
mxs_mci = xzalloc(sizeof(*mxs_mci));
host = &mxs_mci->host;
@@ -567,9 +562,16 @@ static int mxs_mci_probe(struct device_d *hw_dev)
return PTR_ERR(mxs_mci->regs);
/* feed forward the platform specific values */
- host->voltages = pd->voltages;
- host->host_caps = pd->caps;
- host->devname = pd->devname;
+ if (pd) {
+ host->voltages = pd->voltages;
+ host->host_caps = pd->caps;
+ host->devname = pd->devname;
+ host->f_min = pd->f_min;
+ host->f_max = pd->f_max;
+ } else {
+ host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34, /* fixed to 3.3 V */
+ mci_of_parse(host);
+ }
mxs_mci->clk = clk_get(hw_dev, NULL);
if (IS_ERR(mxs_mci->clk))
@@ -579,22 +581,13 @@ static int mxs_mci_probe(struct device_d *hw_dev)
rate = clk_get_rate(mxs_mci->clk);
- if (pd->f_min == 0) {
- host->f_min = rate / 254 / 256;
- dev_dbg(hw_dev, "Min. frequency is %u Hz\n", host->f_min);
- } else {
- host->f_min = pd->f_min;
- dev_dbg(hw_dev, "Min. frequency is %u Hz, could be %lu Hz\n",
- host->f_min, rate / 254 / 256);
- }
- if (pd->f_max == 0) {
+ if (host->f_min < 400000)
+ host->f_min = 400000;
+ if (host->f_max == 0)
host->f_max = rate / 2 / 1;
- dev_dbg(hw_dev, "Max. frequency is %u Hz\n", host->f_max);
- } else {
- host->f_max = pd->f_max;
- dev_dbg(hw_dev, "Max. frequency is %u Hz, could be %lu Hz\n",
- host->f_max, rate / 2 / 1);
- }
+
+ dev_dbg(hw_dev, "Max. frequency is %u Hz\n", host->f_max);
+ dev_dbg(hw_dev, "Min. frequency is %u Hz\n", host->f_min);
if (IS_ENABLED(CONFIG_MCI_INFO)) {
mxs_mci->f_min = host->f_min;
@@ -605,8 +598,19 @@ static int mxs_mci_probe(struct device_d *hw_dev)
return mci_register(host);
}
+static __maybe_unused struct of_device_id mxs_mmc_compatible[] = {
+ {
+ .compatible = "fsl,imx23-mmc",
+ }, {
+ .compatible = "fsl,imx28-mmc",
+ }, {
+ /* sentinel */
+ }
+};
+
static struct driver_d mxs_mci_driver = {
.name = "mxs_mci",
.probe = mxs_mci_probe,
+ .of_compatible = DRV_OF_COMPAT(mxs_mmc_compatible),
};
device_platform_driver(mxs_mci_driver);
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index b67818485b..82a692e732 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -18,6 +18,7 @@
#define SDHCI_SIGNAL_ENABLE 0x38
#define SDHCI_ACMD12_ERR__HOST_CONTROL2 0x3C
#define SDHCI_CAPABILITIES 0x40
+#define SDHCI_MMC_BOOT 0xC4
#define COMMAND_CMD(x) ((x & 0x3f) << 24)
#define COMMAND_CMDTYP_NORMAL 0x0
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index c345847d8f..a75540b4ec 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -97,6 +97,13 @@ config NAND_ORION
help
Support for the Orion NAND controller, present in Kirkwood SoCs.
+config NAND_MRVL_NFC
+ bool
+ prompt "Marvell NAND driver"
+ depends on ARCH_PXA3XX
+ help
+ Support for the PXA3xx NAND controller, present in pxa3xx SoCs.
+
config NAND_ATMEL
bool
prompt "Atmel (AT91SAM9xxx) NAND driver"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 02dacde962..a0b31989ff 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_NAND_IMX) += nand_imx.o
obj-$(CONFIG_NAND_IMX_BBM) += nand_imx_bbm.o
obj-$(CONFIG_NAND_OMAP_GPMC) += nand_omap_gpmc.o nand_omap_bch_decoder.o
obj-$(CONFIG_NAND_ORION) += nand_orion.o
+obj-$(CONFIG_NAND_MRVL_NFC) += nand_mrvl_nfc.o
obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o
obj-$(CONFIG_NAND_S3C24XX) += nand_s3c24xx.o
pbl-$(CONFIG_NAND_S3C24XX) += nand_s3c24xx.o
diff --git a/drivers/mtd/nand/nand_imx_bbm.c b/drivers/mtd/nand/nand_imx_bbm.c
index f419759b18..5caa0a26c3 100644
--- a/drivers/mtd/nand/nand_imx_bbm.c
+++ b/drivers/mtd/nand/nand_imx_bbm.c
@@ -43,7 +43,7 @@
* To preserve the factory bad block information we take the following
* strategy:
*
- * - If the NAND driver detects that no flasg BBT is present on 2k NAND
+ * - If the NAND driver detects that no flash BBT is present on 2k NAND
* chips it will not create one because it would do so based on the wrong
* BBM position
* - This command is used to create a flash BBT then.
diff --git a/drivers/mtd/nand/nand_mrvl_nfc.c b/drivers/mtd/nand/nand_mrvl_nfc.c
new file mode 100644
index 0000000000..258ff75953
--- /dev/null
+++ b/drivers/mtd/nand/nand_mrvl_nfc.c
@@ -0,0 +1,1020 @@
+/*
+ * drivers/mtd/nand/mrvl_nand.c
+ *
+ * Copyright © 2005 Intel Corporation
+ * Copyright © 2006 Marvell International Ltd.
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * See Documentation/mtd/nand/pxa3xx-nand.txt for more details.
+ */
+#include <common.h>
+
+#include <driver.h>
+#include <dma/apbh-dma.h>
+#include <errno.h>
+#include <clock.h>
+#include <init.h>
+#include <io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <mach/clock.h>
+#include <malloc.h>
+#include <of_mtd.h>
+#include <stmp-device.h>
+
+#include <platform_data/mtd-nand-mrvl.h>
+
+#define CHIP_DELAY_TIMEOUT_US 500000
+#define PAGE_CHUNK_SIZE (2048)
+
+/*
+ * Define a buffer size for the initial command that detects the flash device:
+ * STATUS, READID and PARAM. The largest of these is the PARAM command,
+ * needing 256 bytes.
+ */
+#define INIT_BUFFER_SIZE 256
+
+/* registers and bit definitions */
+#define NDCR (0x00) /* Control register */
+#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */
+#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */
+#define NDSR (0x14) /* Status Register */
+#define NDPCR (0x18) /* Page Count Register */
+#define NDBDR0 (0x1C) /* Bad Block Register 0 */
+#define NDBDR1 (0x20) /* Bad Block Register 1 */
+#define NDECCCTRL (0x28) /* ECC control */
+#define NDDB (0x40) /* Data Buffer */
+#define NDCB0 (0x48) /* Command Buffer0 */
+#define NDCB1 (0x4C) /* Command Buffer1 */
+#define NDCB2 (0x50) /* Command Buffer2 */
+
+#define NDCR_SPARE_EN (0x1 << 31)
+#define NDCR_ECC_EN (0x1 << 30)
+#define NDCR_DMA_EN (0x1 << 29)
+#define NDCR_ND_RUN (0x1 << 28)
+#define NDCR_DWIDTH_C (0x1 << 27)
+#define NDCR_DWIDTH_M (0x1 << 26)
+#define NDCR_PAGE_SZ (0x1 << 24)
+#define NDCR_NCSX (0x1 << 23)
+#define NDCR_ND_MODE (0x3 << 21)
+#define NDCR_NAND_MODE (0x0)
+#define NDCR_CLR_PG_CNT (0x1 << 20)
+#define NDCR_STOP_ON_UNCOR (0x1 << 19)
+#define NDCR_RD_ID_CNT_MASK (0x7 << 16)
+#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
+
+#define NDCR_RA_START (0x1 << 15)
+#define NDCR_PG_PER_BLK (0x1 << 14)
+#define NDCR_ND_ARB_EN (0x1 << 12)
+#define NDCR_INT_MASK (0xFFF)
+
+#define NDSR_MASK (0xfff)
+#define NDSR_ERR_CNT_OFF (16)
+#define NDSR_ERR_CNT_MASK (0x1f)
+#define NDSR_ERR_CNT(sr) ((sr >> NDSR_ERR_CNT_OFF) & NDSR_ERR_CNT_MASK)
+#define NDSR_RDY (0x1 << 12)
+#define NDSR_FLASH_RDY (0x1 << 11)
+#define NDSR_CS0_PAGED (0x1 << 10)
+#define NDSR_CS1_PAGED (0x1 << 9)
+#define NDSR_CS0_CMDD (0x1 << 8)
+#define NDSR_CS1_CMDD (0x1 << 7)
+#define NDSR_CS0_BBD (0x1 << 6)
+#define NDSR_CS1_BBD (0x1 << 5)
+#define NDSR_UNCORERR (0x1 << 4)
+#define NDSR_CORERR (0x1 << 3)
+#define NDSR_WRDREQ (0x1 << 2)
+#define NDSR_RDDREQ (0x1 << 1)
+#define NDSR_WRCMDREQ (0x1)
+
+#define NDCB0_LEN_OVRD (0x1 << 28)
+#define NDCB0_ST_ROW_EN (0x1 << 26)
+#define NDCB0_AUTO_RS (0x1 << 25)
+#define NDCB0_CSEL (0x1 << 24)
+#define NDCB0_EXT_CMD_TYPE_MASK (0x7 << 29)
+#define NDCB0_EXT_CMD_TYPE(x) (((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK)
+#define NDCB0_CMD_TYPE_MASK (0x7 << 21)
+#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK)
+#define NDCB0_NC (0x1 << 20)
+#define NDCB0_DBC (0x1 << 19)
+#define NDCB0_ADDR_CYC_MASK (0x7 << 16)
+#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK)
+#define NDCB0_CMD2_MASK (0xff << 8)
+#define NDCB0_CMD1_MASK (0xff)
+#define NDCB0_ADDR_CYC_SHIFT (16)
+
+#define EXT_CMD_TYPE_DISPATCH 6 /* Command dispatch */
+#define EXT_CMD_TYPE_NAKED_RW 5 /* Naked read or Naked write */
+#define EXT_CMD_TYPE_READ 4 /* Read */
+#define EXT_CMD_TYPE_DISP_WR 4 /* Command dispatch with write */
+#define EXT_CMD_TYPE_FINAL 3 /* Final command */
+#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */
+#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */
+
+/* macros for registers read/write */
+#define nand_writel(host, off, val) \
+ _nand_writel(__func__, __LINE__, (host), (off), (val))
+
+#define nand_writesl(host, off, buf, nbbytes) \
+ writesl((host)->mmio_base + (off), buf, nbbytes)
+
+#define nand_readl(host, off) \
+ _nand_readl(__func__, __LINE__, (host), (off))
+
+#define nand_readsl(host, off, buf, nbbytes) \
+ readsl((host)->mmio_base + (off), buf, nbbytes)
+
+struct mrvl_nand_host {
+ struct mtd_info mtd;
+ struct nand_chip chip;
+ struct mtd_partition *parts;
+ struct device_d *dev;
+
+ /* calculated from mrvl_nand_flash data */
+ unsigned int col_addr_cycles;
+ unsigned int row_addr_cycles;
+ size_t read_id_bytes;
+
+ void __iomem *mmio_base;
+
+ unsigned int buf_start;
+ unsigned int buf_count;
+ unsigned int buf_size;
+
+ unsigned char *data_buff;
+
+ int keep_config;
+ int ecc_strength;
+ int ecc_step;
+
+ int cs; /* selected chip 0/1 */
+ int use_ecc; /* use HW ECC ? */
+ int use_spare; /* use spare ? */
+ int flash_bbt;
+
+ unsigned int data_size; /* data to be read from FIFO */
+ unsigned int chunk_size; /* split commands chunk size */
+ unsigned int oob_size;
+ unsigned int spare_size;
+ unsigned int ecc_size;
+ unsigned int max_bitflips;
+ int cmd_ongoing;
+
+ /* cached register value */
+ uint32_t reg_ndcr;
+ uint32_t ndtr0cs0_chip0;
+ uint32_t ndtr1cs0_chip0;
+ uint32_t ndtr0cs0_chip1;
+ uint32_t ndtr1cs0_chip1;
+
+ /* generated NDCBx register values */
+ uint32_t ndcb0;
+ uint32_t ndcb1;
+ uint32_t ndcb2;
+ uint32_t ndcb3;
+};
+
+static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' };
+static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 8,
+ .len = 6,
+ .veroffs = 14,
+ .maxblocks = 8, /* Last 8 blocks in each chip */
+ .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 8,
+ .len = 6,
+ .veroffs = 14,
+ .maxblocks = 8, /* Last 8 blocks in each chip */
+ .pattern = bbt_mirror_pattern,
+};
+
+static struct nand_ecclayout ecc_layout_512B_hwecc = {
+ .eccbytes = 6,
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15 },
+ .oobfree = { {0, 8} }
+};
+
+static struct nand_ecclayout ecc_layout_2KB_hwecc = {
+ .eccbytes = 24,
+ .eccpos = {
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63 },
+ .oobfree = { {0, 40} }
+};
+
+#define NDTR0_tCH(c) (min((c), 7) << 19)
+#define NDTR0_tCS(c) (min((c), 7) << 16)
+#define NDTR0_tWH(c) (min((c), 7) << 11)
+#define NDTR0_tWP(c) (min((c), 7) << 8)
+#define NDTR0_tRH(c) (min((c), 7) << 3)
+#define NDTR0_tRP(c) (min((c), 7) << 0)
+
+#define NDTR1_tR(c) (min((c), 65535) << 16)
+#define NDTR1_tWHR(c) (min((c), 15) << 4)
+#define NDTR1_tAR(c) (min((c), 15) << 0)
+
+#define mtd_info_to_host(mtd) ((struct mrvl_nand_host *) \
+ (((struct nand_chip *)((mtd)->priv))->priv))
+
+static struct of_device_id mrvl_nand_dt_ids[] = {
+ {
+ .compatible = "marvell,pxa3xx-nand",
+ },
+ {}
+};
+
+/* convert nano-seconds to nand flash controller clock cycles */
+static int ns2cycle(int ns, unsigned long clk_rate)
+{
+ int clk_mhz = clk_rate / 1000000;
+
+ return roundup(ns * clk_mhz, 1000) / 1000;
+}
+
+static volatile u32 _nand_readl(const char *func, const int line,
+ struct mrvl_nand_host *host, int off)
+{
+ volatile u32 val = readl((host)->mmio_base + (off));
+
+ dev_vdbg(host->dev, "\treadl %s:%d reg=0x%08x => 0x%08x\n",
+ func, line, off, val);
+ return val;
+}
+
+static void _nand_writel(const char *func, const int line,
+ struct mrvl_nand_host *host, int off, u32 val)
+{
+ dev_vdbg(host->dev, "\twritel %s:%d reg=0x%08x val=0x%08x\n",
+ func, line, off, val);
+ writel(val, (host)->mmio_base + off);
+}
+
+static struct mrvl_nand_timing timings[] = {
+ { 0x46ec, 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
+ { 0xdaec, 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
+ { 0xd7ec, 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
+ { 0xa12c, 10, 25, 15, 25, 15, 30, 25000, 60, 10, },
+ { 0xb12c, 10, 25, 15, 25, 15, 30, 25000, 60, 10, },
+ { 0xdc2c, 10, 25, 15, 25, 15, 30, 25000, 60, 10, },
+ { 0xcc2c, 10, 25, 15, 25, 15, 30, 25000, 60, 10, },
+ { 0xba20, 10, 35, 15, 25, 15, 25, 25000, 60, 10, },
+ { 0x0000, 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
+};
+
+static void mrvl_nand_set_timing(struct mrvl_nand_host *host, bool use_default)
+{
+ struct mtd_info *mtd = &host->mtd;
+ struct mrvl_nand_timing *t;
+ uint32_t ndtr0, ndtr1;
+ u16 id;
+ unsigned long nand_clk = pxa_get_nandclk();
+
+ if (use_default) {
+ id = 0;
+ } else {
+ host->chip.cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+ host->chip.read_buf(mtd, (unsigned char *)&id, sizeof(id));
+ }
+ for (t = &timings[0]; t->id; t++)
+ if (t->id == id)
+ break;
+ ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
+ NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
+ NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
+ NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) |
+ NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) |
+ NDTR0_tRP(ns2cycle(t->tRP, nand_clk));
+
+ ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) |
+ NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
+ NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
+ nand_writel(host, NDTR0CS0, ndtr0);
+ nand_writel(host, NDTR1CS0, ndtr1);
+}
+
+static int mrvl_nand_ready(struct mtd_info *mtd)
+{
+ struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+ u32 ndcr;
+
+ ndcr = nand_readl(host, NDSR);
+ if (host->cs == 0)
+ return ndcr & NDSR_FLASH_RDY;
+ if (host->cs == 1)
+ return ndcr & NDSR_RDY;
+ return 0;
+}
+
+/*
+ * Claims all blocks are good.
+ *
+ * In principle, this function is *only* called when the NAND Flash MTD system
+ * isn't allowed to keep an in-memory bad block table, so it is forced to ask
+ * the driver for bad block information.
+ *
+ * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so
+ * this function is *only* called when we take it away.
+ *
+ * Thus, this function is only called when we want *all* blocks to look good,
+ * so it *always* return success.
+ */
+static int mrvl_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+ return 0;
+}
+
+static void mrvl_nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+
+ if (chipnr <= 0 || chipnr >= 3 || chipnr == host->cs)
+ return;
+ host->cs = chipnr - 1;
+}
+
+/*
+ * Set the data and OOB size, depending on the selected
+ * spare and ECC configuration.
+ * Only applicable to READ0, READOOB and PAGEPROG commands.
+ */
+static unsigned int mrvl_datasize(struct mrvl_nand_host *host)
+{
+ unsigned int datasize;
+
+ datasize = host->mtd.writesize;
+ if (host->use_spare) {
+ datasize += host->spare_size;
+ if (!host->use_ecc)
+ datasize += host->ecc_size;
+ }
+ return datasize;
+}
+
+/**
+ * NOTE: it is a must to set ND_RUN firstly, then write
+ * command buffer, otherwise, it does not work.
+ * We enable all the interrupt at the same time, and
+ * let mrvl_nand_irq to handle all logic.
+ */
+static void mrvl_nand_start(struct mrvl_nand_host *host)
+{
+ uint32_t ndcr;
+
+ ndcr = host->reg_ndcr;
+ if (host->use_ecc)
+ ndcr |= NDCR_ECC_EN;
+ else
+ ndcr &= ~NDCR_ECC_EN;
+
+ ndcr &= ~NDCR_DMA_EN;
+
+ if (host->use_spare)
+ ndcr |= NDCR_SPARE_EN;
+ else
+ ndcr &= ~NDCR_SPARE_EN;
+
+ ndcr &= ~NDCR_ND_RUN;
+ ndcr |= NDCR_INT_MASK;
+
+ /* clear status bits and run */
+ nand_writel(host, NDCR, ndcr);
+ nand_writel(host, NDSR, NDSR_MASK);
+ nand_writel(host, NDCR, ndcr | NDCR_ND_RUN);
+
+ if (wait_on_timeout(host->chip.chip_delay * USECOND,
+ nand_readl(host, NDSR) & NDSR_WRCMDREQ)) {
+ dev_err(host->dev, "Waiting for command request failed\n");
+ } else {
+ /*
+ * Writing 12 bytes to NDBC0 sets NDBC0, NDBC1 and NDBC2 !
+ */
+ nand_writel(host, NDSR, NDSR_WRCMDREQ);
+ nand_writel(host, NDCB0, host->ndcb0);
+ nand_writel(host, NDCB0, host->ndcb1);
+ nand_writel(host, NDCB0, host->ndcb2);
+ }
+}
+
+static void disable_int(struct mrvl_nand_host *host, uint32_t int_mask)
+{
+ uint32_t ndcr;
+
+ ndcr = nand_readl(host, NDCR);
+ nand_writel(host, NDCR, ndcr | int_mask);
+}
+
+static inline int is_buf_blank(uint8_t *buf, size_t len)
+{
+ for (; len > 0; len--)
+ if (*buf++ != 0xff)
+ return 0;
+ return 1;
+}
+
+static void set_command_address(struct mrvl_nand_host *host,
+ unsigned int page_size, uint16_t column, int page_addr)
+{
+ /* small page addr setting */
+ if (page_size < PAGE_CHUNK_SIZE) {
+ host->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
+ | (column & 0xFF);
+
+ host->ndcb2 = 0;
+ } else {
+ host->ndcb1 = ((page_addr & 0xFFFF) << 16)
+ | (column & 0xFFFF);
+
+ if (page_addr & 0xFF0000)
+ host->ndcb2 = (page_addr & 0xFF0000) >> 16;
+ else
+ host->ndcb2 = 0;
+ }
+}
+
+static void prepare_start_command(struct mrvl_nand_host *host, int command)
+{
+ /* reset data and oob column point to handle data */
+ host->buf_start = 0;
+ host->buf_count = 0;
+ host->oob_size = 0;
+ host->use_ecc = 0;
+ host->use_spare = 1;
+ host->ndcb3 = 0;
+ host->cmd_ongoing = command;
+
+ switch (command) {
+ case NAND_CMD_SEQIN:
+ /*
+ * This command is a no-op, as merged with PROGPAGE.
+ */
+ break;
+ case NAND_CMD_READOOB:
+ host->data_size = mrvl_datasize(host);
+ break;
+ case NAND_CMD_READ0:
+ host->use_ecc = 1;
+ host->data_size = mrvl_datasize(host);
+ break;
+ case NAND_CMD_PAGEPROG:
+ host->use_ecc = 1;
+ host->data_size = mrvl_datasize(host);
+ break;
+ case NAND_CMD_PARAM:
+ host->use_spare = 0;
+ break;
+ default:
+ host->ndcb1 = 0;
+ host->ndcb2 = 0;
+ break;
+ }
+
+ /*
+ * If we are about to issue a read command, or about to set
+ * the write address, then clean the data buffer.
+ */
+ if (command == NAND_CMD_READ0 ||
+ command == NAND_CMD_READOOB ||
+ command == NAND_CMD_SEQIN) {
+ host->buf_count = host->mtd.writesize + host->mtd.oobsize;
+ memset(host->data_buff, 0xFF, host->buf_count);
+ }
+
+}
+
+/**
+ * prepare_set_command - Prepare a NAND command
+ *
+ * Prepare data for a NAND command. If the command will not be executed, but
+ * instead merged into a "bi-command", returns 0.
+ *
+ * Returns if the command should be launched on the NFC
+ */
+static int prepare_set_command(struct mrvl_nand_host *host, int command,
+ int ext_cmd_type, uint16_t column, int page_addr)
+{
+ int addr_cycle, exec_cmd;
+ struct mtd_info *mtd;
+
+ mtd = &host->mtd;
+ exec_cmd = 1;
+
+ if (host->cs != 0)
+ host->ndcb0 = NDCB0_CSEL;
+ else
+ host->ndcb0 = 0;
+
+ addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+ + host->col_addr_cycles);
+ switch (command) {
+ case NAND_CMD_READOOB:
+ case NAND_CMD_READ0:
+ host->ndcb0 |= NDCB0_CMD_TYPE(0)
+ | addr_cycle
+ | NAND_CMD_READ0;
+
+ if (command == NAND_CMD_READOOB)
+ host->buf_start = column + mtd->writesize;
+ else
+ host->buf_start = column;
+
+ /*
+ * Multiple page read needs an 'extended command type' field,
+ * which is either naked-read or last-read according to the
+ * state.
+ */
+ if (mtd->writesize == PAGE_CHUNK_SIZE) {
+ host->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8);
+ } else if (mtd->writesize > PAGE_CHUNK_SIZE) {
+ host->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8)
+ | NDCB0_LEN_OVRD
+ | NDCB0_EXT_CMD_TYPE(ext_cmd_type);
+ host->ndcb3 = host->chunk_size +
+ host->oob_size;
+ }
+
+ set_command_address(host, mtd->writesize, column, page_addr);
+ break;
+
+ case NAND_CMD_SEQIN:
+ host->buf_start = column;
+ set_command_address(host, mtd->writesize, 0, page_addr);
+ /* Data transfer will occur in write_page */
+ host->data_size = 0;
+ exec_cmd = 0;
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ host->ndcb0 |= NDCB0_CMD_TYPE(0x1)
+ | NDCB0_DBC
+ | (NAND_CMD_PAGEPROG << 8)
+ | NAND_CMD_SEQIN
+ | addr_cycle;
+ break;
+
+ case NAND_CMD_PARAM:
+ host->buf_count = 256;
+ host->ndcb0 |= NDCB0_CMD_TYPE(0)
+ | NDCB0_ADDR_CYC(1)
+ | NDCB0_LEN_OVRD
+ | command;
+ host->ndcb1 = (column & 0xFF);
+ host->ndcb3 = 256;
+ host->data_size = 256;
+ break;
+
+ case NAND_CMD_READID:
+ host->buf_count = host->read_id_bytes;
+ host->ndcb0 |= NDCB0_CMD_TYPE(3)
+ | NDCB0_ADDR_CYC(1)
+ | command;
+ host->ndcb1 = (column & 0xFF);
+
+ host->data_size = 8;
+ break;
+ case NAND_CMD_STATUS:
+ host->buf_count = 1;
+ host->ndcb0 |= NDCB0_CMD_TYPE(4)
+ | NDCB0_ADDR_CYC(1)
+ | command;
+
+ host->data_size = 8;
+ break;
+
+ case NAND_CMD_ERASE1:
+ host->ndcb0 |= NDCB0_CMD_TYPE(2)
+ | NDCB0_ADDR_CYC(3)
+ | NDCB0_DBC
+ | (NAND_CMD_ERASE2 << 8)
+ | NAND_CMD_ERASE1;
+ host->ndcb1 = page_addr;
+ host->ndcb2 = 0;
+
+ break;
+ case NAND_CMD_RESET:
+ host->ndcb0 |= NDCB0_CMD_TYPE(5)
+ | command;
+ break;
+
+ case NAND_CMD_ERASE2:
+ exec_cmd = 0;
+ break;
+
+ default:
+ exec_cmd = 0;
+ dev_err(host->dev, "non-supported command %x\n",
+ command);
+ break;
+ }
+
+ return exec_cmd;
+}
+
+static void mrvl_data_stage(struct mrvl_nand_host *host)
+{
+ unsigned int i, mask = NDSR_RDDREQ | NDSR_WRDREQ;
+ u32 *src, ndsr;
+
+ dev_dbg(host->dev, "%s() ndsr=0x%08x\n", __func__,
+ nand_readl(host, NDSR));
+ if (!host->data_size)
+ return;
+
+ wait_on_timeout(host->chip.chip_delay * USECOND,
+ nand_readl(host, NDSR) & mask);
+ if (!(nand_readl(host, NDSR) & mask)) {
+ dev_err(host->dev, "Timeout waiting for data ndsr=0x%08x\n",
+ nand_readl(host, NDSR));
+ return;
+ }
+
+ ndsr = nand_readl(host, NDSR);
+ mask &= ndsr;
+ src = (u32 *)host->data_buff;
+
+ for (i = 0; i < host->data_size; i += 4) {
+ if (ndsr & NDSR_RDDREQ)
+ *src++ = nand_readl(host, NDDB);
+ if (ndsr & NDSR_WRDREQ)
+ nand_writel(host, NDDB, *src++);
+ }
+
+ host->data_size = 0;
+ nand_writel(host, NDSR, mask);
+}
+
+static void mrvl_nand_wait_cmd_done(struct mrvl_nand_host *host,
+ unsigned command)
+{
+ unsigned int mask;
+ static unsigned int nb_done;
+
+ if (host->cs == 0)
+ mask = NDSR_CS0_CMDD;
+ else
+ mask = NDSR_CS1_CMDD;
+ wait_on_timeout(host->chip.chip_delay * USECOND,
+ (nand_readl(host, NDSR) & mask) == mask);
+ if ((nand_readl(host, NDSR) & mask) != mask) {
+ dev_err(host->dev, "Waiting end of command %dth %d timeout, ndsr=0x%08x ndcr=0x%08x\n",
+ nb_done++, command, nand_readl(host, NDSR),
+ nand_readl(host, NDCR));
+ }
+}
+
+static void mrvl_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+
+ /*
+ * if this is a x16 device ,then convert the input
+ * "byte" address into a "word" address appropriate
+ * for indexing a word-oriented device
+ */
+ dev_dbg(host->dev, "%s(cmd=%d, col=%d, page=%d)\n", __func__,
+ command, column, page_addr);
+ if ((host->reg_ndcr & NDCR_DWIDTH_M) && (command != NAND_CMD_READID))
+ column /= 2;
+
+ prepare_start_command(host, command);
+ if (prepare_set_command(host, command, 0, column, page_addr)) {
+ mrvl_nand_start(host);
+ mrvl_data_stage(host);
+ mrvl_nand_wait_cmd_done(host, command);
+ }
+}
+
+/**
+ * mrvl_nand_write_page_hwecc - prepare page for write
+ *
+ * Fills in the host->data_buff. The actual write will be done by the PAGEPROG
+ * command, which will trigger a mrvl_data_stage().
+ *
+ * Returns 0
+ */
+static int mrvl_nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required)
+{
+ struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+
+ memcpy(host->data_buff, buf, mtd->writesize);
+ if (oob_required)
+ memcpy(host->data_buff + mtd->writesize, chip->oob_poi,
+ mtd->oobsize);
+ else
+ memset(host->data_buff + mtd->writesize, 0, mtd->oobsize);
+ dev_dbg(host->dev, "%s(buf=%p, oob_required=%d) => 0\n",
+ __func__, buf, oob_required);
+ return 0;
+}
+
+static int mrvl_nand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required,
+ int page)
+{
+ struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+ u32 ndsr;
+ int ret = 0;
+
+ chip->read_buf(mtd, buf, mtd->writesize);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ ndsr = nand_readl(host, NDSR);
+
+ if (ndsr & NDSR_UNCORERR) {
+ if (is_buf_blank(buf, mtd->writesize))
+ ret = 0;
+ else
+ ret = -EBADMSG;
+ }
+ if (ndsr & NDSR_CORERR)
+ ret = 1;
+ dev_dbg(host->dev, "%s(buf=%p, page=%d, oob_required=%d) => %d\n",
+ __func__, buf, page, oob_required, ret);
+ return ret;
+}
+
+static void mrvl_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+ int xfer;
+
+ xfer = min_t(int, len, host->buf_count);
+ memcpy(buf, host->data_buff + host->buf_start, xfer);
+ host->buf_start += xfer;
+ host->buf_count -= xfer;
+}
+
+static uint8_t mrvl_nand_read_byte(struct mtd_info *mtd)
+{
+ uint8_t ret;
+
+ mrvl_nand_read_buf(mtd, (uint8_t *)&ret, sizeof(ret));
+ return ret;
+}
+
+static u16 mrvl_nand_read_word(struct mtd_info *mtd)
+{
+ u16 ret;
+
+ mrvl_nand_read_buf(mtd, (uint8_t *)&ret, sizeof(ret));
+ return ret;
+}
+
+static void mrvl_nand_write_buf(struct mtd_info *mtd,
+ const uint8_t *buf, int len)
+{
+ struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+
+ memcpy(host->data_buff + host->buf_start, buf, len);
+ host->buf_start += len;
+ host->buf_count -= len;
+}
+
+static void mrvl_nand_config_flash(struct mrvl_nand_host *host)
+{
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = &host->mtd;
+ uint32_t ndcr = host->reg_ndcr;
+
+ /* calculate flash information */
+ host->read_id_bytes = (mtd->writesize == 2048) ? 4 : 2;
+
+ /* calculate addressing information */
+ host->col_addr_cycles = (mtd->writesize == 2048) ? 2 : 1;
+ if ((mtd->size >> chip->page_shift) > 65536)
+ host->row_addr_cycles = 3;
+ else
+ host->row_addr_cycles = 2;
+
+ ndcr |= NDCR_ND_ARB_EN;
+ ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+ ndcr |= ((mtd->erasesize / mtd->writesize) == 64) ? NDCR_PG_PER_BLK : 0;
+ ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0;
+
+ ndcr &= ~NDCR_RD_ID_CNT_MASK;
+ ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
+ ndcr |= NDCR_SPARE_EN; /* enable spare by default */
+ ndcr &= ~NDCR_DMA_EN;
+
+ if (chip->options & NAND_BUSWIDTH_16)
+ ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C;
+ else
+ ndcr &= ~(NDCR_DWIDTH_M | NDCR_DWIDTH_C);
+
+ host->reg_ndcr = ndcr;
+}
+
+static int pxa_ecc_init(struct mrvl_nand_host *host,
+ struct nand_ecc_ctrl *ecc,
+ int strength, int ecc_stepsize, int page_size)
+{
+ if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) {
+ host->chunk_size = 2048;
+ host->spare_size = 40;
+ host->ecc_size = 24;
+ ecc->mode = NAND_ECC_HW;
+ ecc->size = 512;
+ ecc->strength = 1;
+ ecc->layout = &ecc_layout_2KB_hwecc;
+
+ } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
+ host->chunk_size = 512;
+ host->spare_size = 8;
+ host->ecc_size = 8;
+ ecc->mode = NAND_ECC_HW;
+ ecc->size = 512;
+ ecc->layout = &ecc_layout_512B_hwecc;
+ ecc->strength = 1;
+ } else {
+ dev_err(host->dev,
+ "ECC strength %d at page size %d is not supported\n",
+ strength, page_size);
+ return -ENODEV;
+ }
+
+ dev_info(host->dev, "ECC strength %d, ECC step size %d\n",
+ ecc->strength, ecc->size);
+ return 0;
+}
+
+static int mrvl_nand_scan(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct mrvl_nand_host *host = chip->priv;
+ int ret;
+ unsigned int ndcr;
+ uint16_t ecc_strength = host->ecc_strength;
+ uint16_t ecc_step = host->ecc_step;
+
+ host->read_id_bytes = 4;
+ ndcr = NDCR_ND_ARB_EN | NDCR_SPARE_EN;
+ ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
+ host->reg_ndcr = ndcr;
+
+ mrvl_nand_set_timing(host, true);
+ if (nand_scan_ident(mtd, 1, NULL)) {
+ host->reg_ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C;
+ if (nand_scan_ident(mtd, 1, NULL))
+ return -ENODEV;
+ }
+ mrvl_nand_config_flash(host);
+ mrvl_nand_set_timing(host, false);
+ if (host->flash_bbt) {
+ /*
+ * We'll use a bad block table stored in-flash and don't
+ * allow writing the bad block marker to the flash.
+ */
+ chip->bbt_options |= NAND_BBT_USE_FLASH |
+ NAND_BBT_NO_OOB_BBM;
+ chip->bbt_td = &bbt_main_descr;
+ chip->bbt_md = &bbt_mirror_descr;
+ }
+
+ /*
+ * If the page size is bigger than the FIFO size, let's check
+ * we are given the right variant and then switch to the extended
+ * (aka split) command handling,
+ */
+ if (mtd->writesize > PAGE_CHUNK_SIZE) {
+ dev_err(host->dev,
+ "unsupported page size on this variant\n");
+ return -ENODEV;
+ }
+
+ /* Set default ECC strength requirements on non-ONFI devices */
+ if (ecc_strength < 1 && ecc_step < 1) {
+ ecc_strength = 1;
+ ecc_step = 512;
+ }
+
+ ret = pxa_ecc_init(host, &chip->ecc, ecc_strength,
+ ecc_step, mtd->writesize);
+ if (ret)
+ return ret;
+ mtd->oobsize = host->spare_size + host->ecc_size;
+
+ /* allocate the real data + oob buffer */
+ host->buf_size = mtd->writesize + mtd->oobsize;
+ host->data_buff = xmalloc(host->buf_size);
+
+ return nand_scan_tail(mtd);
+}
+
+static struct mrvl_nand_host *alloc_nand_resource(struct device_d *dev)
+{
+ struct mrvl_nand_platform_data *pdata;
+ struct mrvl_nand_host *host;
+ struct nand_chip *chip = NULL;
+ struct mtd_info *mtd;
+
+ pdata = dev->platform_data;
+ host = xzalloc(sizeof(*host));
+ host->cs = 0;
+ mtd = &host->mtd;
+ mtd->priv = &host->chip;
+ mtd->parent = dev;
+ mtd->name = "mrvl_nand";
+
+ chip = &host->chip;
+ chip->read_byte = mrvl_nand_read_byte;
+ chip->read_word = mrvl_nand_read_word;
+ chip->ecc.read_page = mrvl_nand_read_page_hwecc;
+ chip->ecc.write_page = mrvl_nand_write_page_hwecc;
+ chip->dev_ready = mrvl_nand_ready;
+ chip->select_chip = mrvl_nand_select_chip;
+ chip->block_bad = mrvl_nand_block_bad;
+ chip->read_buf = mrvl_nand_read_buf;
+ chip->write_buf = mrvl_nand_write_buf;
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+ chip->cmdfunc = mrvl_nand_cmdfunc;
+ chip->priv = host;
+ chip->chip_delay = CHIP_DELAY_TIMEOUT_US;
+
+ host->dev = dev;
+ host->mmio_base = dev_request_mem_region(dev, 0);
+ if (IS_ERR(host->mmio_base)) {
+ free(host);
+ return host->mmio_base;
+ }
+ if (pdata) {
+ host->keep_config = pdata->keep_config;
+ host->flash_bbt = pdata->flash_bbt;
+ host->ecc_strength = pdata->ecc_strength;
+ host->ecc_step = pdata->ecc_step_size;
+ }
+
+ /* Allocate a buffer to allow flash detection */
+ host->buf_size = INIT_BUFFER_SIZE;
+ host->data_buff = xmalloc(host->buf_size);
+
+ /* initialize all interrupts to be disabled */
+ disable_int(host, NDSR_MASK);
+ return host;
+}
+
+static int mrvl_nand_probe_dt(struct mrvl_nand_host *host)
+{
+ struct device_node *np = host->dev->device_node;
+
+ if (of_get_property(np, "marvell,nand-keep-config", NULL))
+ host->keep_config = 1;
+ of_property_read_u32(np, "num-cs", &host->cs);
+ if (of_get_nand_on_flash_bbt(np))
+ host->flash_bbt = 1;
+
+ return 0;
+}
+
+static int mrvl_nand_probe(struct device_d *dev)
+{
+ struct mrvl_nand_host *host;
+ int ret;
+
+ host = alloc_nand_resource(dev);
+ if (IS_ERR(host)) {
+ dev_err(dev, "alloc nand resource failed\n");
+ return PTR_ERR(host);
+ }
+
+ ret = mrvl_nand_probe_dt(host);
+ if (ret)
+ return ret;
+
+ host->chip.controller = &host->chip.hwcontrol;
+ ret = mrvl_nand_scan(&host->mtd);
+ if (ret) {
+ dev_warn(dev, "failed to scan nand at cs %d\n",
+ host->cs);
+ return -ENODEV;
+ }
+
+ ret = add_mtd_nand_device(&host->mtd, "nand");
+ return ret;
+}
+
+static struct driver_d mrvl_nand_driver = {
+ .name = "mrvl_nand",
+ .probe = mrvl_nand_probe,
+ .of_compatible = DRV_OF_COMPAT(mrvl_nand_dt_ids),
+};
+device_platform_driver(mrvl_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Marvell NAND controller driver");
diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c
index 94101a3d98..4e38e09186 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/nand_mxs.c
@@ -20,6 +20,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_mxs.h>
#include <linux/types.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -231,7 +232,7 @@ static uint32_t mxs_nand_aux_status_offset(void)
return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3;
}
-static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
+uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
uint32_t page_oob_size)
{
int ecc_chunk_count = mxs_nand_ecc_chunk_cnt(page_data_size);
@@ -294,14 +295,14 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size,
return block_mark_bit_offset;
}
-static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
{
uint32_t ecc_strength;
ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) >> 3;
}
-static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
{
uint32_t ecc_strength;
ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
diff --git a/drivers/net/smc91111.c b/drivers/net/smc91111.c
index 100688ccf8..698c74add1 100644
--- a/drivers/net/smc91111.c
+++ b/drivers/net/smc91111.c
@@ -435,14 +435,14 @@
#define MEMORY_WAIT_TIME 16
struct accessors {
- void (*ob)(unsigned, void __iomem *);
- void (*ow)(unsigned, void __iomem *);
- void (*ol)(unsigned long, void __iomem *);
- void (*osl)(void __iomem *, const void *, int);
- unsigned (*ib)(void __iomem *);
- unsigned (*iw)(void __iomem *);
- unsigned long (*il)(void __iomem *);
- void (*isl)(void __iomem *, void*, int);
+ void (*ob)(unsigned, void __iomem *, unsigned, unsigned);
+ void (*ow)(unsigned, void __iomem *, unsigned, unsigned);
+ void (*ol)(unsigned long, void __iomem *, unsigned, unsigned);
+ void (*osl)(void __iomem *, unsigned, const void *, int, unsigned);
+ unsigned (*ib)(void __iomem *, unsigned, unsigned);
+ unsigned (*iw)(void __iomem *, unsigned, unsigned);
+ unsigned long (*il)(void __iomem *, unsigned, unsigned);
+ void (*isl)(void __iomem *, unsigned, void*, int, unsigned);
};
struct smc91c111_priv {
@@ -450,6 +450,11 @@ struct smc91c111_priv {
struct accessors a;
void __iomem *base;
int qemu_fixup;
+ unsigned shift;
+ int version;
+ int revision;
+ unsigned int control_setup;
+ unsigned int config_setup;
};
#if (SMC_DEBUG > 2 )
@@ -479,109 +484,176 @@ struct smc91c111_priv {
#define ETH_ZLEN 60
-static void a_outb(unsigned value, void __iomem *offset)
+static void a8_outb(unsigned value, void __iomem *base, unsigned int offset,
+ unsigned shift)
{
- writeb(value, offset);
+ writeb(value, base + (offset << shift));
}
-static void a_outw(unsigned value, void __iomem *offset)
+static void a16_outw(unsigned value, void __iomem *base, unsigned int offset,
+ unsigned shift)
{
- writew(value, offset);
+ writew(value, base + (offset << shift));
}
-static void a_outl(unsigned long value, void __iomem *offset)
+static void a32_outl(unsigned long value, void __iomem *base,
+ unsigned int offset, unsigned shift)
{
- writel(value, offset);
+ writel(value, base + (offset << shift));
}
-static void a_outsl(void __iomem *offset, const void *data, int count)
+static void a16_outsl(void __iomem *base, unsigned int offset,
+ const void *data, int count, unsigned shift)
{
- writesl(offset, data, count);
+ writesw(base + (offset << shift), data, count * 2);
}
-static unsigned a_inb(void __iomem *offset)
+static void a16_outl(unsigned long value, void __iomem *base,
+ unsigned int offset, unsigned shift)
{
- return readb(offset);
+ writew(value & 0xffff, base + (offset << shift));
+ writew(value >> 16, base + ((offset + 2) << shift));
}
-static unsigned a_inw(void __iomem *offset)
+static void a32_outsl(void __iomem *base, unsigned int offset,
+ const void *data, int count, unsigned shift)
{
- return readw(offset);
+ writesw(base + (offset << shift), data, count * 2);
}
-static unsigned long a_inl(void __iomem *offset)
+static unsigned a8_inb(void __iomem *base, unsigned int offset, unsigned shift)
{
- return readl(offset);
+ return readb(base + (offset << shift));
}
-static inline void a_insl(void __iomem *offset, void *data, int count)
+static unsigned a16_inw(void __iomem *base, unsigned int offset, unsigned shift)
{
- readsl(offset, data, count);
+ return readw(base + (offset << shift));
}
-/* access happens via a 32 bit bus */
-static const struct accessors access_via_32bit = {
- .ob = a_outb,
- .ow = a_outw,
- .ol = a_outl,
- .osl = a_outsl,
- .ib = a_inb,
- .iw = a_inw,
- .il = a_inl,
- .isl = a_insl,
-};
-
-/* ------------------------------------------------------------------------ */
-
-static inline void SMC_outb(struct smc91c111_priv *p, unsigned value,
- unsigned offset)
+static unsigned long a16_inl(void __iomem *base, unsigned int offset,
+ unsigned shift)
{
- (p->a.ob)(value, p->base + offset);
-}
+ u32 value;
-static inline void SMC_outw(struct smc91c111_priv *p, unsigned value,
- unsigned offset)
-{
- (p->a.ow)(value, p->base + offset);
-}
+ value = readw(base + (offset << shift));
+ value |= readw(base + (offset << shift)) << 16;
-static inline void SMC_outl(struct smc91c111_priv *p, unsigned long value,
- unsigned offset)
-{
- (p->a.ol)(value, p->base + offset);
+ return value;
}
-static inline void SMC_outsl(struct smc91c111_priv *p, unsigned offset,
- const void *data, int count)
+static inline void a16_insl(void __iomem *base, unsigned int offset, void *data,
+ int count, unsigned shift)
{
- (p->a.osl)(p->base + offset, data, count);
+ readsw(base + (offset << shift), data, count * 2);
}
-static inline unsigned SMC_inb(struct smc91c111_priv *p, unsigned offset)
+static unsigned long a32_inl(void __iomem *base, unsigned int offset,
+ unsigned shift)
{
- return (p->a.ib)(p->base + offset);
+ return readl(base + (offset << shift));
}
-static inline unsigned SMC_inw(struct smc91c111_priv *p, unsigned offset)
+static inline void a32_insl(void __iomem *base, unsigned int offset, void *data,
+ int count, unsigned shift)
{
- return (p->a.iw)(p->base + offset);
+ readsl(base + (offset << shift), data, count);
}
-static inline unsigned long SMC_inl(struct smc91c111_priv *p, unsigned offset)
-{
- return (p->a.il)(p->base + offset);
-}
+static const struct accessors access_via_16bit = {
+ .ob = a8_outb,
+ .ow = a16_outw,
+ .ol = a16_outl,
+ .osl = a16_outsl,
+ .ib = a8_inb,
+ .iw = a16_inw,
+ .il = a16_inl,
+ .isl = a16_insl,
+};
-static inline void SMC_insl(struct smc91c111_priv *p, unsigned offset,
- void *data, int count)
-{
- (p->a.isl)(p->base + offset, data, count);
-}
+/* access happens via a 32 bit bus */
+static const struct accessors access_via_32bit = {
+ .ob = a8_outb,
+ .ow = a16_outw,
+ .ol = a32_outl,
+ .osl = a32_outsl,
+ .ib = a8_inb,
+ .iw = a16_inw,
+ .il = a32_inl,
+ .isl = a32_insl,
+};
-static inline void SMC_SELECT_BANK(struct smc91c111_priv *p, int bank)
-{
- SMC_outw(p, bank, BANK_SELECT);
-}
+/* ------------------------------------------------------------------------ */
+
+static unsigned last_bank;
+#define SMC_outb(p, value, offset) \
+ do { \
+ PRINTK3("\t%s:%d outb: %s[%1d:0x%04x] = 0x%02x\n", \
+ __func__, __LINE__, #offset, last_bank, \
+ (offset), (value)); \
+ ((p)->a.ob)((value), (p)->base, (offset), (p)->shift); \
+ } while (0)
+
+#define SMC_outw(p, value, offset) \
+ do { \
+ PRINTK3("\t%s:%d outw: %s[%1d:0x%04x] = 0x%04x\n", \
+ __func__, __LINE__, #offset, last_bank, \
+ (offset), (value)); \
+ ((p)->a.ow)((value), (p)->base, (offset), (p)->shift); \
+ } while (0)
+
+#define SMC_outl(p, value, offset) \
+ do { \
+ PRINTK3("\t%s:%d outl: %s[%1d:0x%04x] = 0x%08lx\n", \
+ __func__, __LINE__, #offset, last_bank, \
+ (offset), (unsigned long)(value)); \
+ ((p)->a.ol)((value), (p)->base, (offset), (p)->shift); \
+ } while (0)
+
+#define SMC_outsl(p, offset, data, count)\
+ do { \
+ PRINTK3("\t%s:%d outsl: %5d@%p -> [%1d:0x%04x]\n", \
+ __func__, __LINE__, (count) * 4, data, \
+ last_bank, (offset)); \
+ ((p)->a.osl)((p)->base, (offset), data, (count), \
+ (p)->shift); \
+ } while (0)
+
+#define SMC_inb(p, offset) \
+ ({ \
+ unsigned _v = ((p)->a.ib)((p)->base, (offset), \
+ (p)->shift); \
+ PRINTK3("\t%s:%d inb: %s[%1d:0x%04x] -> 0x%02x\n", \
+ __func__, __LINE__, #offset, last_bank, \
+ (offset), _v); \
+ _v; })
+
+#define SMC_inw(p, offset) \
+ ({ \
+ unsigned _v = ((p)->a.iw)((p)->base, (offset), \
+ (p)->shift); \
+ PRINTK3("\t%s:%d inw: %s[%1d:0x%04x] -> 0x%04x\n", \
+ __func__, __LINE__, #offset, last_bank, \
+ (offset), _v); \
+ _v; })
+
+#define SMC_inl(p, offset) \
+ ({ \
+ unsigned long _v = ((p)->a.il)((p)->base, (offset), \
+ (p)->shift); \
+ PRINTK3("\t%s:%d inl: %s[%1d:0x%04x] -> 0x%08lx\n", \
+ __func__, __LINE__, #offset, last_bank, \
+ (offset), _v); \
+ _v; })
+
+#define SMC_insl(p, offset, data, count) \
+ ((p)->a.isl)((p)->base, (offset), data, (count), (p)->shift)
+
+#define SMC_SELECT_BANK(p, bank) \
+ do { \
+ SMC_outw(p, bank & 0xf, BANK_SELECT); \
+ last_bank = bank & 0xf; \
+ } while (0)
#if SMC_DEBUG > 2
static void print_packet( unsigned char * buf, int length )
@@ -863,6 +935,7 @@ static int smc91c111_phy_read(struct mii_bus *bus, int phyaddr, int phyreg)
static void smc91c111_reset(struct eth_device *edev)
{
struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv;
+ int rev_vers;
/* This resets the registers mostly to defaults, but doesn't
affect EEPROM. That seems unnecessary */
@@ -878,8 +951,11 @@ static void smc91c111_reset(struct eth_device *edev)
/* Release from possible power-down state */
/* Configuration register is not affected by Soft Reset */
- SMC_outw(priv, SMC_inw(priv, CONFIG_REG) | CONFIG_EPH_POWER_EN,
- CONFIG_REG);
+ if (priv->config_setup)
+ SMC_outw(priv, priv->config_setup, CONFIG_REG);
+ else
+ SMC_outw(priv, SMC_inw(priv, CONFIG_REG) | CONFIG_EPH_POWER_EN,
+ CONFIG_REG);
SMC_SELECT_BANK(priv, 0);
@@ -892,7 +968,10 @@ static void smc91c111_reset(struct eth_device *edev)
/* set the control register */
SMC_SELECT_BANK(priv, 1);
- SMC_outw(priv, CTL_DEFAULT, CTL_REG);
+ if (priv->control_setup)
+ SMC_outw(priv, priv->control_setup, CTL_REG);
+ else
+ SMC_outw(priv, CTL_DEFAULT, CTL_REG);
/* Reset the MMU */
SMC_SELECT_BANK(priv, 2);
@@ -908,6 +987,14 @@ static void smc91c111_reset(struct eth_device *edev)
/* Disable all interrupts */
SMC_outb(priv, 0, IM_REG);
+
+ /* Check chip revision (91c94, 91c96, 91c100, ...) */
+ SMC_SELECT_BANK(priv, 3);
+ rev_vers = SMC_inb(priv, REV_REG);
+ priv->revision = (rev_vers >> 4) & 0xf;
+ priv->version = rev_vers & 0xf;
+ dev_info(edev->parent, "chip is revision=%2d, version=%2d\n",
+ priv->revision, priv->version);
}
static void smc91c111_enable(struct eth_device *edev)
@@ -927,10 +1014,16 @@ static int smc91c111_eth_open(struct eth_device *edev)
/* Configure the Receive/Phy Control register */
SMC_SELECT_BANK(priv, 0);
- SMC_outw(priv, RPC_DEFAULT, RPC_REG);
+ if (priv->revision > 4)
+ SMC_outw(priv, RPC_DEFAULT, RPC_REG);
smc91c111_enable(edev);
+ if (priv->revision <= 4) {
+ dev_info(edev->parent, "force link at 10Mpbs on internal phy\n");
+ return 0;
+ }
+
ret = phy_device_connect(edev, &priv->miibus, 0, NULL,
0, PHY_INTERFACE_MODE_NA);
@@ -1333,15 +1426,19 @@ static int smc91c111_probe(struct device_d *dev)
edev->priv = (struct smc91c111_priv *)(edev + 1);
priv = edev->priv;
+ priv->a = access_via_32bit;
if (dev->platform_data) {
struct smc91c111_pdata *pdata = dev->platform_data;
priv->qemu_fixup = pdata->qemu_fixup;
+ priv->shift = pdata->addr_shift;
+ if (pdata->bus_width == 16)
+ priv->a = access_via_16bit;
+ pdata->config_setup = pdata->config_setup;
+ pdata->control_setup = pdata->control_setup;
}
- priv->a = access_via_32bit;
-
edev->init = smc91c111_init_dev;
edev->open = smc91c111_eth_open;
edev->send = smc91c111_eth_send;
@@ -1361,7 +1458,8 @@ static int smc91c111_probe(struct device_d *dev)
smc91c111_reset(edev);
- mdiobus_register(&priv->miibus);
+ if (priv->revision > 4)
+ mdiobus_register(&priv->miibus);
eth_register(edev);
return 0;
diff --git a/drivers/of/of_path.c b/drivers/of/of_path.c
index 6a2d634dcf..f0fd917ada 100644
--- a/drivers/of/of_path.c
+++ b/drivers/of/of_path.c
@@ -113,7 +113,7 @@ out:
* @outpath: if this function returns 0 outpath will contain the path belonging
* to the input path description. Must be freed with free().
*
- * pathes in the devicetree have the form of a multistring property. The first
+ * paths in the devicetree have the form of a multistring property. The first
* string contains the full path to the physical device containing the path.
* The remaining strings have the form "<type>:<options>". Currently supported
* for <type> are:
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 92ef53443f..ab3ccab3b6 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -312,8 +312,8 @@ static int of_platform_bus_create(struct device_node *bus,
}
if (of_device_is_compatible(bus, "arm,primecell")) {
- of_amba_device_create(bus);
- return 0;
+ if (of_amba_device_create(bus))
+ return 0;
}
dev = of_platform_device_create(bus, parent);
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 4cbc167cd0..5c69928e75 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -37,6 +37,13 @@ comment "OFDEVICE is not enabled."
comment "Without device tree support PINCTRL won't do anything"
endif
+config PINCTRL_MXS
+ bool "MXS pinctrl"
+ depends on ARCH_MXS
+ default ARCH_MXS
+ help
+ This pinmux controller is found on i.MX23,28
+
config PINCTRL_ROCKCHIP
select GPIO_GENERIC
select MFD_SYSCON
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 724e6d7d06..af9b30df0f 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o
obj-$(CONFIG_PINCTRL_IMX_IOMUX_V1) += imx-iomux-v1.o
obj-$(CONFIG_PINCTRL_IMX_IOMUX_V2) += imx-iomux-v2.o
obj-$(CONFIG_PINCTRL_IMX_IOMUX_V3) += imx-iomux-v3.o
+obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o
obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o
diff --git a/drivers/pinctrl/pinctrl-mxs.c b/drivers/pinctrl/pinctrl-mxs.c
new file mode 100644
index 0000000000..ec0aaec67f
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-mxs.c
@@ -0,0 +1,171 @@
+/*
+ * pinctrl-mxs.c - MXS pinctrl support
+ *
+ * Copyright (c) 2015 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <pinctrl.h>
+#include <malloc.h>
+#include <stmp-device.h>
+
+struct mxs_pinctrl {
+ void __iomem *base;
+ struct pinctrl_device pinctrl;
+};
+
+#define PINID(bank, pin) ((bank) * 32 + (pin))
+#define MUXID_TO_PINID(m) PINID((m) >> 12 & 0xf, (m) >> 4 & 0xff)
+#define MUXID_TO_MUXSEL(m) ((m) & 0xf)
+#define PINID_TO_BANK(p) ((p) >> 5)
+#define PINID_TO_PIN(p) ((p) % 32)
+
+static int mxs_pinctrl_set_state(struct pinctrl_device *pdev, struct device_node *np)
+{
+ struct mxs_pinctrl *iomux = container_of(pdev, struct mxs_pinctrl, pinctrl);
+ const __be32 *list;
+ int npins, size, i;
+ u32 val, ma, vol, pull;
+ int ret;
+ int ma_present = 0, vol_present = 0, pull_present = 0;
+
+ dev_dbg(iomux->pinctrl.dev, "set state: %s\n", np->full_name);
+
+ list = of_get_property(np, "fsl,pinmux-ids", &size);
+ if (!list)
+ return -EINVAL;
+
+ if (!size || size % 4) {
+ dev_err(iomux->pinctrl.dev, "Invalid fsl,pins property in %s\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ npins = size / 4;
+
+ ret = of_property_read_u32(np, "fsl,drive-strength", &ma);
+ if (!ret)
+ ma_present = 1;
+
+ ret = of_property_read_u32(np, "fsl,voltage", &vol);
+ if (!ret)
+ vol_present = 1;
+
+ ret = of_property_read_u32(np, "fsl,pull-up", &pull);
+ if (!ret)
+ pull_present = 1;
+
+ for (i = 0; i < npins; i++) {
+ int muxsel, pinid, bank, pin, shift;
+ void __iomem *reg;
+
+ val = be32_to_cpu(*list++);
+
+ muxsel = MUXID_TO_MUXSEL(val);
+ pinid = MUXID_TO_PINID(val);
+
+ bank = PINID_TO_BANK(pinid);
+ pin = PINID_TO_PIN(pinid);
+ reg = iomux->base + 0x100;
+ reg += bank * 0x20 + pin / 16 * 0x10;
+ shift = pin % 16 * 2;
+
+ writel(0x3 << shift, reg + STMP_OFFSET_REG_CLR);
+ writel(muxsel << shift, reg + STMP_OFFSET_REG_SET);
+
+ dev_dbg(iomux->pinctrl.dev, "pin %d, mux %d, ma: %d, vol: %d, pull: %d\n",
+ pinid, muxsel, ma, vol, pull);
+
+ /* drive */
+ reg = iomux->base + 0x300;
+ reg += bank * 0x40 + pin / 8 * 0x10;
+
+ /* mA */
+ if (ma_present) {
+ shift = pin % 8 * 4;
+ writel(0x3 << shift, reg + STMP_OFFSET_REG_CLR);
+ writel(ma << shift, reg + STMP_OFFSET_REG_SET);
+ }
+
+ /* vol */
+ if (vol_present) {
+ shift = pin % 8 * 4 + 2;
+ if (vol)
+ writel(1 << shift, reg + STMP_OFFSET_REG_SET);
+ else
+ writel(1 << shift, reg + STMP_OFFSET_REG_CLR);
+ }
+
+ /* pull */
+ if (pull_present) {
+ reg = iomux->base + 0x600;
+ reg += bank * 0x10;
+ shift = pin;
+ if (pull)
+ writel(1 << shift, reg + STMP_OFFSET_REG_SET);
+ else
+ writel(1 << shift, reg + STMP_OFFSET_REG_CLR);
+ }
+ }
+
+ return 0;
+}
+
+static struct pinctrl_ops mxs_pinctrl_ops = {
+ .set_state = mxs_pinctrl_set_state,
+};
+
+static int mxs_pinctrl_probe(struct device_d *dev)
+{
+ struct mxs_pinctrl *iomux;
+ int ret = 0;
+
+ iomux = xzalloc(sizeof(*iomux));
+
+ iomux->base = dev_get_mem_region(dev, 0);
+
+ iomux->pinctrl.dev = dev;
+ iomux->pinctrl.ops = &mxs_pinctrl_ops;
+
+ ret = pinctrl_register(&iomux->pinctrl);
+ if (ret)
+ free(iomux);
+
+ return ret;
+}
+
+static __maybe_unused struct of_device_id mxs_pinctrl_dt_ids[] = {
+ {
+ .compatible = "fsl,imx28-pinctrl",
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d mxs_pinctrl_driver = {
+ .name = "mxs-pinctrl",
+ .probe = mxs_pinctrl_probe,
+ .of_compatible = DRV_OF_COMPAT(mxs_pinctrl_dt_ids),
+};
+
+static int mxs_pinctrl_init(void)
+{
+ return platform_driver_register(&mxs_pinctrl_driver);
+}
+postcore_initcall(mxs_pinctrl_init);
diff --git a/drivers/pinctrl/pinctrl.c b/drivers/pinctrl/pinctrl.c
index d6479b9cd5..10ce186a12 100644
--- a/drivers/pinctrl/pinctrl.c
+++ b/drivers/pinctrl/pinctrl.c
@@ -62,9 +62,6 @@ int of_pinctrl_select_state(struct device_node *np, const char *name)
struct device_node *np_config;
const char *statename;
- if (!IS_ENABLED(CONFIG_PINCTRL))
- return -ENOSYS;
-
if (!of_find_property(np, "pinctrl-0", NULL))
return 0;
diff --git a/drivers/serial/serial_imx.c b/drivers/serial/serial_imx.c
index 21189cbbf8..cd954c282d 100644
--- a/drivers/serial/serial_imx.c
+++ b/drivers/serial/serial_imx.c
@@ -340,8 +340,10 @@ static int imx_serial_probe(struct device_d *dev)
cdev->linux_console_name = "ttymxc";
if (dev->device_node) {
devname = of_alias_get(dev->device_node);
- if (devname)
+ if (devname) {
cdev->devname = xstrdup(devname);
+ cdev->devid = DEVICE_ID_SINGLE;
+ }
}
imx_serial_init_port(cdev);
diff --git a/drivers/serial/stm-serial.c b/drivers/serial/stm-serial.c
index e5f57dc6e7..8bb242b14d 100644
--- a/drivers/serial/stm-serial.c
+++ b/drivers/serial/stm-serial.c
@@ -189,9 +189,18 @@ static void stm_serial_remove(struct device_d *dev)
free(priv);
}
+static __maybe_unused struct of_device_id stm_serial_dt_ids[] = {
+ {
+ .compatible = "arm,pl011",
+ }, {
+ /* sentinel */
+ }
+};
+
static struct driver_d stm_serial_driver = {
.name = "stm_serial",
.probe = stm_serial_probe,
.remove = stm_serial_remove,
+ .of_compatible = DRV_OF_COMPAT(stm_serial_dt_ids),
};
console_platform_driver(stm_serial_driver);
diff --git a/drivers/spi/imx_spi.c b/drivers/spi/imx_spi.c
index 3146339949..a1f19eba8b 100644
--- a/drivers/spi/imx_spi.c
+++ b/drivers/spi/imx_spi.c
@@ -137,6 +137,25 @@ static int imx_spi_setup(struct spi_device *spi)
return 0;
}
+static unsigned int imx_spi_maybe_reverse_bits(struct spi_device *spi, unsigned int word)
+{
+ unsigned int result = word;
+
+ if (spi->mode & SPI_LSB_FIRST) {
+ size_t bits_left = spi->bits_per_word - 1;
+
+ for (word >>= 1; word; word >>= 1) {
+ result <<= 1;
+ result |= word & 1;
+ bits_left--;
+ }
+
+ result <<= bits_left;
+ }
+
+ return result;
+}
+
static unsigned int cspi_0_0_xchg_single(struct imx_spi *imx, unsigned int data)
{
void __iomem *base = imx->regs;
@@ -382,9 +401,20 @@ static void cspi_2_3_chipselect(struct spi_device *spi, int is_active)
gpio_set_value(gpio, gpio_cs);
}
-static void imx_spi_do_transfer(struct spi_device *spi, struct spi_transfer *t)
+static u32 imx_xchg_single(struct spi_device *spi, u32 tx_val)
{
+ u32 rx_val;
struct imx_spi *imx = container_of(spi->master, struct imx_spi, master);
+
+
+ tx_val = imx_spi_maybe_reverse_bits(spi, tx_val);
+ rx_val = imx->xchg_single(imx, tx_val);
+
+ return imx_spi_maybe_reverse_bits(spi, rx_val);
+}
+
+static void imx_spi_do_transfer(struct spi_device *spi, struct spi_transfer *t)
+{
unsigned i;
if (spi->bits_per_word <= 8) {
@@ -393,7 +423,8 @@ static void imx_spi_do_transfer(struct spi_device *spi, struct spi_transfer *t)
u8 rx_val;
for (i = 0; i < t->len; i++) {
- rx_val = imx->xchg_single(imx, tx_buf ? tx_buf[i] : 0);
+ rx_val = imx_xchg_single(spi, tx_buf ? tx_buf[i] : 0);
+
if (rx_buf)
rx_buf[i] = rx_val;
}
@@ -403,7 +434,8 @@ static void imx_spi_do_transfer(struct spi_device *spi, struct spi_transfer *t)
u16 rx_val;
for (i = 0; i < t->len >> 1; i++) {
- rx_val = imx->xchg_single(imx, tx_buf ? tx_buf[i] : 0);
+ rx_val = imx_xchg_single(spi, tx_buf ? tx_buf[i] : 0);
+
if (rx_buf)
rx_buf[i] = rx_val;
}
@@ -413,7 +445,8 @@ static void imx_spi_do_transfer(struct spi_device *spi, struct spi_transfer *t)
u32 rx_val;
for (i = 0; i < t->len >> 2; i++) {
- rx_val = imx->xchg_single(imx, tx_buf ? tx_buf[i] : 0);
+ rx_val = imx_xchg_single(spi, tx_buf ? tx_buf[i] : 0);
+
if (rx_buf)
rx_buf[i] = rx_val;
}
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index d42b47cbe0..dd3c10e825 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -122,7 +122,7 @@ int hub_port_reset(struct usb_device *dev, int port,
for (tries = 0; tries < MAX_TRIES; tries++) {
usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET);
- wait_ms(200);
+ mdelay(200);
if (usb_get_port_status(dev, port + 1, &portsts) < 0) {
dev_dbg(&dev->dev, "get_port_status failed status %lX\n",
@@ -149,7 +149,7 @@ int hub_port_reset(struct usb_device *dev, int port,
if (portstatus & USB_PORT_STAT_ENABLE)
break;
- wait_ms(200);
+ mdelay(200);
}
if (tries == MAX_TRIES) {
@@ -196,7 +196,7 @@ static void usb_hub_port_connect_change(struct usb_device *dev, int port)
if (dev->children[port] && !(portstatus & USB_PORT_STAT_ENABLE))
usb_remove_device(dev->children[port]);
- wait_ms(200);
+ mdelay(200);
/* Reset the port */
if (hub_port_reset(dev, port, &portstatus) < 0) {
@@ -204,7 +204,7 @@ static void usb_hub_port_connect_change(struct usb_device *dev, int port)
return;
}
- wait_ms(200);
+ mdelay(200);
/* Allocate a new device struct for it */
usb = usb_alloc_new_device();
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 3f3d59577c..d1c3e0394d 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -372,7 +372,7 @@ int usb_new_device(struct usb_device *dev)
goto err_out;
}
- wait_ms(10); /* Let the SET_ADDRESS settle */
+ mdelay(10); /* Let the SET_ADDRESS settle */
tmp = sizeof(*dev->descriptor);
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index 60e1ff79d3..b36ef19c8a 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -1464,6 +1464,8 @@ static int __init at91udc_probe(struct device_d *dev)
DBG(udc, "VBUS detection: host:%s \n",
udc->vbus ? "present":"absent");
+ udc->gpio_vbus_val = udc->vbus;
+
dev_add_param_bool(dev, "vbus",
at91_udc_vbus_set, NULL, &udc->gpio_vbus_val, udc);
} else {
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index 8c58746cbd..1e5e8093c0 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -530,6 +530,7 @@ int gserial_connect(struct gserial *gser, u8 port_num)
cdev->flush = serial_flush;
cdev->setbrg = serial_setbaudrate;
cdev->devname = "usbserial";
+ cdev->devid = DEVICE_ID_SINGLE;
status = console_register(cdev);
if (status)
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 133561659c..7b913270be 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -646,7 +646,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
* root
*/
ehci_powerup_fixup(ehci);
- wait_ms(50);
+ mdelay(50);
ehci->portreset |= 1 << le16_to_cpu(req->index);
/* terminate the reset */
ehci_writel(status_reg, reg & ~EHCI_PS_PR);
@@ -709,7 +709,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
goto unknown;
}
- wait_ms(1);
+ mdelay(1);
len = min3(srclen, (int)le16_to_cpu(req->length), length);
if (srcptr != NULL && len > 0)
memcpy(buffer, srcptr, len);
@@ -804,7 +804,7 @@ static int ehci_init(struct usb_host *host)
ehci_writel(&ehci->hcor->or_configflag, cmd);
/* unblock posted write */
cmd = ehci_readl(&ehci->hcor->or_usbcmd);
- wait_ms(5);
+ mdelay(5);
ehci->rootdev = 0;
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index e0a1139bc0..b795f30275 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -5,6 +5,7 @@ if USB_MUSB
config USB_MUSB_DSPS
tristate
+ select OFDEVICE
config USB_MUSB_AM335X
tristate "AM335x USB support"
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 1cceeccf8a..ac1fe79418 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -133,7 +133,7 @@ int usb_stor_Bulk_transport(ccb *srb, struct us_data *us)
/* DATA STAGE */
/* send/receive data payload, if there is any */
- wait_ms(1);
+ mdelay(1);
data_actlen = 0;
if (srb->datalen) {
@@ -229,17 +229,17 @@ int usb_stor_Bulk_reset(struct us_data *us)
US_DEBUGP("Soft reset stalled: %d\n", result);
return result;
}
- wait_ms(150);
+ mdelay(150);
/* clear the bulk endpoints halt */
US_DEBUGP("Soft reset: clearing %s endpoint halt\n", "bulk-in");
pipe = usb_rcvbulkpipe(us->pusb_dev, us->recv_bulk_ep);
result = usb_clear_halt(us->pusb_dev, pipe);
- wait_ms(150);
+ mdelay(150);
US_DEBUGP("Soft reset: clearing %s endpoint halt\n", "bulk-out");
pipe = usb_sndbulkpipe(us->pusb_dev, us->send_bulk_ep);
result2 = usb_clear_halt(us->pusb_dev, pipe);
- wait_ms(150);
+ mdelay(150);
if (result >= 0)
result = result2;
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 5149761a83..9d1ffa3070 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -101,7 +101,7 @@ static int usb_stor_test_unit_ready(ccb *srb, struct us_data *us)
if (result == USB_STOR_TRANSPORT_GOOD)
return 0;
usb_stor_request_sense(srb, us);
- wait_ms(100);
+ mdelay(100);
} while (retries--);
return -1;