summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/uniphier/Makefile1
-rw-r--r--drivers/clk/uniphier/clk-uniphier-core.c30
-rw-r--r--drivers/clk/uniphier/clk-uniphier-sys.c34
-rw-r--r--drivers/clk/uniphier/clk-uniphier.h2
-rw-r--r--drivers/core/ofnode.c12
-rw-r--r--drivers/core/read.c6
-rw-r--r--drivers/mmc/Kconfig4
-rw-r--r--drivers/mmc/sunxi_mmc.c29
-rw-r--r--drivers/mtd/nand/Kconfig7
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/denali.c4
-rw-r--r--drivers/mtd/nand/denali.h2
-rw-r--r--drivers/mtd/nand/denali_dt.c68
-rw-r--r--drivers/nvme/nvme-uclass.c35
-rw-r--r--drivers/nvme/nvme.c179
-rw-r--r--drivers/nvme/nvme.h71
-rw-r--r--drivers/nvme/nvme_show.c7
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx.c2
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx7ulp.c10
-rw-r--r--drivers/reset/reset-uniphier.c29
-rw-r--r--drivers/usb/dwc3/Kconfig4
-rw-r--r--drivers/usb/gadget/Kconfig7
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/f_sdp.c737
24 files changed, 1073 insertions, 210 deletions
diff --git a/drivers/clk/uniphier/Makefile b/drivers/clk/uniphier/Makefile
index ed623aa56f7..54c7e09bd60 100644
--- a/drivers/clk/uniphier/Makefile
+++ b/drivers/clk/uniphier/Makefile
@@ -1,2 +1,3 @@
obj-y += clk-uniphier-core.o
+obj-y += clk-uniphier-sys.o
obj-y += clk-uniphier-mio.o
diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c
index eed21b9a687..722cd6b060c 100644
--- a/drivers/clk/uniphier/clk-uniphier-core.c
+++ b/drivers/clk/uniphier/clk-uniphier-core.c
@@ -146,6 +146,36 @@ static int uniphier_clk_probe(struct udevice *dev)
}
static const struct udevice_id uniphier_clk_match[] = {
+ /* System clock */
+ {
+ .compatible = "socionext,uniphier-ld4-clock",
+ .data = (ulong)&uniphier_pxs2_sys_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro4-clock",
+ .data = (ulong)&uniphier_pxs2_sys_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-sld8-clock",
+ .data = (ulong)&uniphier_pxs2_sys_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro5-clock",
+ .data = (ulong)&uniphier_pxs2_sys_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs2-clock",
+ .data = (ulong)&uniphier_pxs2_sys_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld11-clock",
+ .data = (ulong)&uniphier_ld20_sys_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-clock",
+ .data = (ulong)&uniphier_ld20_sys_clk_data,
+ },
+ /* Media I/O clock */
{
.compatible = "socionext,uniphier-ld4-mio-clock",
.data = (ulong)&uniphier_mio_clk_data,
diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c
new file mode 100644
index 00000000000..709fa5081a4
--- /dev/null
+++ b/drivers/clk/uniphier/clk-uniphier-sys.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016-2017 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "clk-uniphier.h"
+
+const struct uniphier_clk_gate_data uniphier_pxs2_sys_clk_gate[] = {
+ UNIPHIER_CLK_GATE(8, 0x2104, 10), /* stdmac */
+ UNIPHIER_CLK_GATE(12, 0x2104, 6), /* gio (Pro4, Pro5) */
+ UNIPHIER_CLK_GATE(14, 0x2104, 16), /* usb30 (Pro4, Pro5, PXs2) */
+ UNIPHIER_CLK_GATE(15, 0x2104, 17), /* usb31 (Pro4, Pro5, PXs2) */
+ UNIPHIER_CLK_GATE(16, 0x2104, 19), /* usb30-phy (PXs2) */
+ UNIPHIER_CLK_GATE(20, 0x2104, 20), /* usb31-phy (PXs2) */
+ UNIPHIER_CLK_END
+};
+
+const struct uniphier_clk_data uniphier_pxs2_sys_clk_data = {
+ .gate = uniphier_pxs2_sys_clk_gate,
+};
+
+const struct uniphier_clk_gate_data uniphier_ld20_sys_clk_gate[] = {
+ UNIPHIER_CLK_GATE(8, 0x210c, 8), /* stdmac */
+ UNIPHIER_CLK_GATE(14, 0x210c, 14), /* usb30 (LD20) */
+ UNIPHIER_CLK_GATE(16, 0x210c, 12), /* usb30-phy0 (LD20) */
+ UNIPHIER_CLK_GATE(17, 0x210c, 13), /* usb30-phy1 (LD20) */
+ UNIPHIER_CLK_END
+};
+
+const struct uniphier_clk_data uniphier_ld20_sys_clk_data = {
+ .gate = uniphier_ld20_sys_clk_gate,
+};
diff --git a/drivers/clk/uniphier/clk-uniphier.h b/drivers/clk/uniphier/clk-uniphier.h
index f9a560ee73d..770a3225e1a 100644
--- a/drivers/clk/uniphier/clk-uniphier.h
+++ b/drivers/clk/uniphier/clk-uniphier.h
@@ -50,6 +50,8 @@ struct uniphier_clk_data {
.rates = {(_reg),}, \
}
+extern const struct uniphier_clk_data uniphier_pxs2_sys_clk_data;
+extern const struct uniphier_clk_data uniphier_ld20_sys_clk_data;
extern const struct uniphier_clk_data uniphier_mio_clk_data;
#endif /* __CLK_UNIPHIER_H__ */
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index c1a2e9f0dae..0685b689d84 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -627,3 +627,15 @@ int ofnode_read_resource(ofnode node, uint index, struct resource *res)
return 0;
}
}
+
+int ofnode_read_resource_byname(ofnode node, const char *name,
+ struct resource *res)
+{
+ int index;
+
+ index = ofnode_stringlist_search(node, "reg-names", name);
+ if (index < 0)
+ return index;
+
+ return ofnode_read_resource(node, index, res);
+}
diff --git a/drivers/core/read.c b/drivers/core/read.c
index fe40bed64de..6acb33388f5 100644
--- a/drivers/core/read.c
+++ b/drivers/core/read.c
@@ -164,3 +164,9 @@ int dev_read_resource(struct udevice *dev, uint index, struct resource *res)
{
return ofnode_read_resource(dev_ofnode(dev), index, res);
}
+
+int dev_read_resource_byname(struct udevice *dev, const char *name,
+ struct resource *res)
+{
+ return ofnode_read_resource_byname(dev_ofnode(dev), name, res);
+}
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 56c352e72a0..6de927b8c6c 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -369,6 +369,10 @@ config MMC_SUNXI
This selects support for the SD/MMC Host Controller on
Allwinner sunxi SoCs.
+config MMC_SUNXI_HAS_NEW_MODE
+ bool
+ depends on MMC_SUNXI
+
config GENERIC_ATMEL_MCI
bool "Atmel Multimedia Card Interface support"
depends on DM_MMC && BLK && ARCH_AT91
diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
index 588574fab6a..4edb4be46c8 100644
--- a/drivers/mmc/sunxi_mmc.c
+++ b/drivers/mmc/sunxi_mmc.c
@@ -96,6 +96,18 @@ static int mmc_resource_init(int sdc_no)
static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
{
unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly;
+ bool new_mode = false;
+ u32 val = 0;
+
+ if (IS_ENABLED(CONFIG_MMC_SUNXI_HAS_NEW_MODE) && (priv->mmc_no == 2))
+ new_mode = true;
+
+ /*
+ * The MMC clock has an extra /2 post-divider when operating in the new
+ * mode.
+ */
+ if (new_mode)
+ hz = hz * 2;
if (hz <= 24000000) {
pll = CCM_MMC_CTRL_OSCM24;
@@ -152,9 +164,18 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
#endif
}
- writel(CCM_MMC_CTRL_ENABLE | pll | CCM_MMC_CTRL_SCLK_DLY(sclk_dly) |
- CCM_MMC_CTRL_N(n) | CCM_MMC_CTRL_OCLK_DLY(oclk_dly) |
- CCM_MMC_CTRL_M(div), priv->mclkreg);
+ if (new_mode) {
+#ifdef CONFIG_MMC_SUNXI_HAS_NEW_MODE
+ val = CCM_MMC_CTRL_MODE_SEL_NEW;
+ setbits_le32(&priv->reg->ntsr, SUNXI_MMC_NTSR_MODE_SEL_NEW);
+#endif
+ } else {
+ val = CCM_MMC_CTRL_OCLK_DLY(oclk_dly) |
+ CCM_MMC_CTRL_SCLK_DLY(sclk_dly);
+ }
+
+ writel(CCM_MMC_CTRL_ENABLE| pll | CCM_MMC_CTRL_N(n) |
+ CCM_MMC_CTRL_M(div) | val, priv->mclkreg);
debug("mmc %u set mod-clk req %u parent %u n %u m %u rate %u\n",
priv->mmc_no, hz, pll_hz, 1u << n, div, pll_hz / (1u << n) / div);
@@ -498,7 +519,7 @@ struct mmc *sunxi_mmc_init(int sdc_no)
if (ret)
return NULL;
- return mmc_create(cfg, mmc_host);
+ return mmc_create(cfg, priv);
}
#else
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 71d678fc66b..85b26d60885 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -16,6 +16,13 @@ config NAND_DENALI
help
Enable support for the Denali NAND controller.
+config NAND_DENALI_DT
+ bool "Support Denali NAND controller as a DT device"
+ depends on NAND_DENALI && OF_CONTROL && DM
+ help
+ Enable the driver for NAND flash on platforms using a Denali NAND
+ controller as a DT device.
+
config SYS_NAND_DENALI_64BIT
bool "Use 64-bit variant of Denali NAND controller"
depends on NAND_DENALI
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index c3d4a996f37..9f7d9d6ff7a 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o
obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o
obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o
obj-$(CONFIG_NAND_DENALI) += denali.o
+obj-$(CONFIG_NAND_DENALI_DT) += denali_dt.o
obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o
obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o
obj-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 18280b0b2fe..47cf37d1d9b 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -1175,7 +1175,7 @@ static void denali_hw_init(struct denali_nand_info *denali)
static struct nand_ecclayout nand_oob;
-static int denali_init(struct denali_nand_info *denali)
+int denali_init(struct denali_nand_info *denali)
{
struct mtd_info *mtd = nand_to_mtd(&denali->nand);
int ret;
@@ -1273,6 +1273,7 @@ fail:
return ret;
}
+#ifndef CONFIG_NAND_DENALI_DT
static int __board_nand_init(void)
{
struct denali_nand_info *denali;
@@ -1296,3 +1297,4 @@ void board_nand_init(void)
if (__board_nand_init() < 0)
pr_warn("Failed to initialize Denali NAND controller.\n");
}
+#endif
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 0e098bddf11..694bce53a95 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -464,4 +464,6 @@ struct denali_nand_info {
uint32_t max_banks;
};
+int denali_init(struct denali_nand_info *denali);
+
#endif /* __DENALI_H__ */
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
new file mode 100644
index 00000000000..0a6155c748c
--- /dev/null
+++ b/drivers/mtd/nand/denali_dt.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+
+#include "denali.h"
+
+static const struct udevice_id denali_nand_dt_ids[] = {
+ {
+ .compatible = "altr,socfpga-denali-nand",
+ },
+ {
+ .compatible = "socionext,uniphier-denali-nand-v5a",
+ },
+ {
+ .compatible = "socionext,uniphier-denali-nand-v5b",
+ },
+ { /* sentinel */ }
+};
+
+static int denali_dt_probe(struct udevice *dev)
+{
+ struct denali_nand_info *denali = dev_get_priv(dev);
+ struct resource res;
+ int ret;
+
+ ret = dev_read_resource_byname(dev, "denali_reg", &res);
+ if (ret)
+ return ret;
+
+ denali->flash_reg = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "nand_data", &res);
+ if (ret)
+ return ret;
+
+ denali->flash_mem = devm_ioremap(dev, res.start, resource_size(&res));
+
+ return denali_init(denali);
+}
+
+U_BOOT_DRIVER(denali_nand_dt) = {
+ .name = "denali-nand-dt",
+ .id = UCLASS_MISC,
+ .of_match = denali_nand_dt_ids,
+ .probe = denali_dt_probe,
+ .priv_auto_alloc_size = sizeof(struct denali_nand_info),
+};
+
+void board_nand_init(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_MISC,
+ DM_GET_DRIVER(denali_nand_dt),
+ &dev);
+ if (ret && ret != -ENODEV)
+ printf("Failed to initialize Denali NAND controller. (error %d)\n",
+ ret);
+}
diff --git a/drivers/nvme/nvme-uclass.c b/drivers/nvme/nvme-uclass.c
index 0895bc9c249..56a6171876b 100644
--- a/drivers/nvme/nvme-uclass.c
+++ b/drivers/nvme/nvme-uclass.c
@@ -11,43 +11,26 @@
#include <dm/device.h>
#include "nvme.h"
-static int nvme_info_init(struct uclass *uc)
-{
- struct nvme_info *info = (struct nvme_info *)uc->priv;
-
- info->ns_num = 0;
- info->ndev_num = 0;
- INIT_LIST_HEAD(&info->dev_list);
- nvme_info = info;
-
- return 0;
-}
-
static int nvme_uclass_post_probe(struct udevice *udev)
{
char name[20];
- char *str;
struct udevice *ns_udev;
int i, ret;
struct nvme_dev *ndev = dev_get_priv(udev);
/* Create a blk device for each namespace */
for (i = 0; i < ndev->nn; i++) {
- sprintf(name, "nvme-blk#%d", nvme_info->ns_num);
- str = strdup(name);
- if (!str)
- return -ENOMEM;
+ /*
+ * Encode the namespace id to the device name so that
+ * we can extract it when doing the probe.
+ */
+ sprintf(name, "blk#%d", i);
/* The real blksz and size will be set by nvme_blk_probe() */
- ret = blk_create_device(udev, "nvme-blk", str, IF_TYPE_NVME,
- nvme_info->ns_num++, 512, 0, &ns_udev);
- if (ret) {
- free(str);
- nvme_info->ns_num--;
-
+ ret = blk_create_devicef(udev, "nvme-blk", name, IF_TYPE_NVME,
+ -1, 512, 0, &ns_udev);
+ if (ret)
return ret;
- }
- device_set_name_alloced(ns_udev);
}
return 0;
@@ -56,7 +39,5 @@ static int nvme_uclass_post_probe(struct udevice *udev)
UCLASS_DRIVER(nvme) = {
.name = "nvme",
.id = UCLASS_NVME,
- .init = nvme_info_init,
.post_probe = nvme_uclass_post_probe,
- .priv_auto_alloc_size = sizeof(struct nvme_info),
};
diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c
index 151fe92479a..ec32d0de27a 100644
--- a/drivers/nvme/nvme.c
+++ b/drivers/nvme/nvme.c
@@ -13,8 +13,6 @@
#include <dm/device-internal.h>
#include "nvme.h"
-struct nvme_info *nvme_info;
-
#define NVME_Q_DEPTH 2
#define NVME_AQ_DEPTH 2
#define NVME_SQ_SIZE(depth) (depth * sizeof(struct nvme_command))
@@ -23,6 +21,12 @@ struct nvme_info *nvme_info;
#define IO_TIMEOUT 30
#define MAX_PRP_POOL 512
+enum nvme_queue_id {
+ NVME_ADMIN_Q,
+ NVME_IO_Q,
+ NVME_Q_NUM,
+};
+
/*
* An NVM Express queue. Each device has at least two (one for admin
* commands and one for I/O commands).
@@ -47,11 +51,19 @@ struct nvme_queue {
static int nvme_wait_ready(struct nvme_dev *dev, bool enabled)
{
u32 bit = enabled ? NVME_CSTS_RDY : 0;
+ int timeout;
+ ulong start;
- while ((readl(&dev->bar->csts) & NVME_CSTS_RDY) != bit)
- udelay(10000);
+ /* Timeout field in the CAP register is in 500 millisecond units */
+ timeout = NVME_CAP_TIMEOUT(dev->cap) * 500;
- return 0;
+ start = get_timer(0);
+ while (get_timer(start) < timeout) {
+ if ((readl(&dev->bar->csts) & NVME_CSTS_RDY) == bit)
+ return 0;
+ }
+
+ return -ETIME;
}
static int nvme_setup_prps(struct nvme_dev *dev, u64 *prp2,
@@ -201,7 +213,8 @@ static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq,
static int nvme_submit_admin_cmd(struct nvme_dev *dev, struct nvme_command *cmd,
u32 *result)
{
- return nvme_submit_sync_cmd(dev->queues[0], cmd, result, ADMIN_TIMEOUT);
+ return nvme_submit_sync_cmd(dev->queues[NVME_ADMIN_Q], cmd,
+ result, ADMIN_TIMEOUT);
}
static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev,
@@ -318,7 +331,7 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev)
{
int result;
u32 aqa;
- u64 cap = nvme_readq(&dev->bar->cap);
+ u64 cap = dev->cap;
struct nvme_queue *nvmeq;
/* most architectures use 4KB as the page size */
unsigned page_shift = 12;
@@ -341,7 +354,7 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev)
if (result < 0)
return result;
- nvmeq = dev->queues[0];
+ nvmeq = dev->queues[NVME_ADMIN_Q];
if (!nvmeq) {
nvmeq = nvme_alloc_queue(dev, 0, NVME_AQ_DEPTH);
if (!nvmeq)
@@ -369,7 +382,7 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev)
nvmeq->cq_vector = 0;
- nvme_init_queue(dev->queues[0], 0);
+ nvme_init_queue(dev->queues[NVME_ADMIN_Q], 0);
return result;
@@ -420,6 +433,7 @@ int nvme_identify(struct nvme_dev *dev, unsigned nsid,
u32 page_size = dev->page_size;
int offset = dma_addr & (page_size - 1);
int length = sizeof(struct nvme_id_ctrl);
+ int ret;
memset(&c, 0, sizeof(c));
c.identify.opcode = nvme_admin_identify;
@@ -431,12 +445,17 @@ int nvme_identify(struct nvme_dev *dev, unsigned nsid,
c.identify.prp2 = 0;
} else {
dma_addr += (page_size - offset);
- c.identify.prp2 = dma_addr;
+ c.identify.prp2 = cpu_to_le64(dma_addr);
}
c.identify.cns = cpu_to_le32(cns);
- return nvme_submit_admin_cmd(dev, &c, NULL);
+ ret = nvme_submit_admin_cmd(dev, &c, NULL);
+ if (!ret)
+ invalidate_dcache_range(dma_addr,
+ dma_addr + sizeof(struct nvme_id_ctrl));
+
+ return ret;
}
int nvme_get_features(struct nvme_dev *dev, unsigned fid, unsigned nsid,
@@ -450,6 +469,11 @@ int nvme_get_features(struct nvme_dev *dev, unsigned fid, unsigned nsid,
c.features.prp1 = cpu_to_le64(dma_addr);
c.features.fid = cpu_to_le32(fid);
+ /*
+ * TODO: add cache invalidate operation when the size of
+ * the DMA buffer is known
+ */
+
return nvme_submit_admin_cmd(dev, &c, result);
}
@@ -464,6 +488,11 @@ int nvme_set_features(struct nvme_dev *dev, unsigned fid, unsigned dword11,
c.features.fid = cpu_to_le32(fid);
c.features.dword11 = cpu_to_le32(dword11);
+ /*
+ * TODO: add cache flush operation when the size of
+ * the DMA buffer is known
+ */
+
return nvme_submit_admin_cmd(dev, &c, result);
}
@@ -547,10 +576,10 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
static int nvme_get_info_from_identify(struct nvme_dev *dev)
{
- u16 vendor, device;
- struct nvme_id_ctrl buf, *ctrl = &buf;
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, sizeof(struct nvme_id_ctrl));
+ struct nvme_id_ctrl *ctrl = (struct nvme_id_ctrl *)buf;
int ret;
- int shift = NVME_CAP_MPSMIN(nvme_readq(&dev->bar->cap)) + 12;
+ int shift = NVME_CAP_MPSMIN(dev->cap) + 12;
ret = nvme_identify(dev, 0, 1, (dma_addr_t)ctrl);
if (ret)
@@ -585,22 +614,6 @@ static int nvme_get_info_from_identify(struct nvme_dev *dev)
dev->max_transfer_shift = 20;
}
- /* Apply quirk stuff */
- dm_pci_read_config16(dev->pdev, PCI_VENDOR_ID, &vendor);
- dm_pci_read_config16(dev->pdev, PCI_DEVICE_ID, &device);
- if ((vendor == PCI_VENDOR_ID_INTEL) &&
- (device == 0x0953) && ctrl->vs[3]) {
- unsigned int max_transfer_shift;
- dev->stripe_size = (ctrl->vs[3] + shift);
- max_transfer_shift = (ctrl->vs[3] + 18);
- if (dev->max_transfer_shift) {
- dev->max_transfer_shift = min(max_transfer_shift,
- dev->max_transfer_shift);
- } else {
- dev->max_transfer_shift = max_transfer_shift;
- }
- }
-
return 0;
}
@@ -629,12 +642,14 @@ static int nvme_blk_probe(struct udevice *udev)
struct blk_desc *desc = dev_get_uclass_platdata(udev);
struct nvme_ns *ns = dev_get_priv(udev);
u8 flbas;
- u16 vendor;
- struct nvme_id_ns buf, *id = &buf;
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, sizeof(struct nvme_id_ns));
+ struct nvme_id_ns *id = (struct nvme_id_ns *)buf;
+ struct pci_child_platdata *pplat;
memset(ns, 0, sizeof(*ns));
ns->dev = ndev;
- ns->ns_id = desc->devnum - ndev->blk_dev_start + 1;
+ /* extract the namespace id from the block device name */
+ ns->ns_id = trailing_strtol(udev->name) + 1;
if (nvme_identify(ndev, ns->ns_id, 0, (dma_addr_t)id))
return -EIO;
@@ -649,8 +664,8 @@ static int nvme_blk_probe(struct udevice *udev)
desc->log2blksz = ns->lba_shift;
desc->blksz = 1 << ns->lba_shift;
desc->bdev = udev;
- dm_pci_read_config16(ndev->pdev, PCI_VENDOR_ID, &vendor);
- sprintf(desc->vendor, "0x%.4x", vendor);
+ pplat = dev_get_parent_platdata(udev->parent);
+ sprintf(desc->vendor, "0x%.4x", pplat->vendor);
memcpy(desc->product, ndev->serial, sizeof(ndev->serial));
memcpy(desc->revision, ndev->firmware_rev, sizeof(ndev->firmware_rev));
part_init(desc);
@@ -658,8 +673,8 @@ static int nvme_blk_probe(struct udevice *udev)
return 0;
}
-static ulong nvme_blk_read(struct udevice *udev, lbaint_t blknr,
- lbaint_t blkcnt, void *buffer)
+static ulong nvme_blk_rw(struct udevice *udev, lbaint_t blknr,
+ lbaint_t blkcnt, void *buffer, bool read)
{
struct nvme_ns *ns = dev_get_priv(udev);
struct nvme_dev *dev = ns->dev;
@@ -674,7 +689,11 @@ static ulong nvme_blk_read(struct udevice *udev, lbaint_t blknr,
u16 lbas = 1 << (dev->max_transfer_shift - ns->lba_shift);
u64 total_lbas = blkcnt;
- c.rw.opcode = nvme_cmd_read;
+ if (!read)
+ flush_dcache_range((unsigned long)buffer,
+ (unsigned long)buffer + total_len);
+
+ c.rw.opcode = read ? nvme_cmd_read : nvme_cmd_write;
c.rw.flags = 0;
c.rw.nsid = cpu_to_le32(ns->ns_id);
c.rw.control = 0;
@@ -692,15 +711,15 @@ static ulong nvme_blk_read(struct udevice *udev, lbaint_t blknr,
total_lbas -= lbas;
}
- if (nvme_setup_prps
- (dev, &prp2, lbas << ns->lba_shift, (ulong)buffer))
+ if (nvme_setup_prps(dev, &prp2,
+ lbas << ns->lba_shift, (ulong)buffer))
return -EIO;
c.rw.slba = cpu_to_le64(slba);
slba += lbas;
c.rw.length = cpu_to_le16(lbas - 1);
c.rw.prp1 = cpu_to_le64((ulong)buffer);
c.rw.prp2 = cpu_to_le64(prp2);
- status = nvme_submit_sync_cmd(dev->queues[1],
+ status = nvme_submit_sync_cmd(dev->queues[NVME_IO_Q],
&c, NULL, IO_TIMEOUT);
if (status)
break;
@@ -708,60 +727,23 @@ static ulong nvme_blk_read(struct udevice *udev, lbaint_t blknr,
buffer += lbas << ns->lba_shift;
}
+ if (read)
+ invalidate_dcache_range((unsigned long)buffer,
+ (unsigned long)buffer + total_len);
+
return (total_len - temp_len) >> desc->log2blksz;
}
+static ulong nvme_blk_read(struct udevice *udev, lbaint_t blknr,
+ lbaint_t blkcnt, void *buffer)
+{
+ return nvme_blk_rw(udev, blknr, blkcnt, buffer, true);
+}
+
static ulong nvme_blk_write(struct udevice *udev, lbaint_t blknr,
lbaint_t blkcnt, const void *buffer)
{
- struct nvme_ns *ns = dev_get_priv(udev);
- struct nvme_dev *dev = ns->dev;
- struct nvme_command c;
- struct blk_desc *desc = dev_get_uclass_platdata(udev);
- int status;
- u64 prp2;
- u64 total_len = blkcnt << desc->log2blksz;
- u64 temp_len = total_len;
-
- u64 slba = blknr;
- u16 lbas = 1 << (dev->max_transfer_shift - ns->lba_shift);
- u64 total_lbas = blkcnt;
-
- c.rw.opcode = nvme_cmd_write;
- c.rw.flags = 0;
- c.rw.nsid = cpu_to_le32(ns->ns_id);
- c.rw.control = 0;
- c.rw.dsmgmt = 0;
- c.rw.reftag = 0;
- c.rw.apptag = 0;
- c.rw.appmask = 0;
- c.rw.metadata = 0;
-
- while (total_lbas) {
- if (total_lbas < lbas) {
- lbas = (u16)total_lbas;
- total_lbas = 0;
- } else {
- total_lbas -= lbas;
- }
-
- if (nvme_setup_prps
- (dev, &prp2, lbas << ns->lba_shift, (ulong)buffer))
- return -EIO;
- c.rw.slba = cpu_to_le64(slba);
- slba += lbas;
- c.rw.length = cpu_to_le16(lbas - 1);
- c.rw.prp1 = cpu_to_le64((ulong)buffer);
- c.rw.prp2 = cpu_to_le64(prp2);
- status = nvme_submit_sync_cmd(dev->queues[1],
- &c, NULL, IO_TIMEOUT);
- if (status)
- break;
- temp_len -= lbas << ns->lba_shift;
- buffer += lbas << ns->lba_shift;
- }
-
- return (total_len - temp_len) >> desc->log2blksz;
+ return nvme_blk_rw(udev, blknr, blkcnt, (void *)buffer, false);
}
static const struct blk_ops nvme_blk_ops = {
@@ -779,8 +761,10 @@ U_BOOT_DRIVER(nvme_blk) = {
static int nvme_bind(struct udevice *udev)
{
+ static int ndev_num;
char name[20];
- sprintf(name, "nvme#%d", nvme_info->ndev_num++);
+
+ sprintf(name, "nvme#%d", ndev_num++);
return device_set_name(udev, name);
}
@@ -789,9 +773,7 @@ static int nvme_probe(struct udevice *udev)
{
int ret;
struct nvme_dev *ndev = dev_get_priv(udev);
- u64 cap;
- ndev->pdev = pci_get_controller(udev);
ndev->instance = trailing_strtol(udev->name);
INIT_LIST_HEAD(&ndev->namespaces);
@@ -803,13 +785,14 @@ static int nvme_probe(struct udevice *udev)
goto free_nvme;
}
- ndev->queues = malloc(2 * sizeof(struct nvme_queue));
+ ndev->queues = malloc(NVME_Q_NUM * sizeof(struct nvme_queue *));
if (!ndev->queues) {
ret = -ENOMEM;
printf("Error: %s: Out of memory!\n", udev->name);
goto free_nvme;
}
- memset(ndev->queues, 0, sizeof(2 * sizeof(struct nvme_queue)));
+ memset(ndev->queues, 0,
+ sizeof(NVME_Q_NUM * sizeof(struct nvme_queue *)));
ndev->prp_pool = malloc(MAX_PRP_POOL);
if (!ndev->prp_pool) {
@@ -819,9 +802,9 @@ static int nvme_probe(struct udevice *udev)
}
ndev->prp_entry_num = MAX_PRP_POOL >> 3;
- cap = nvme_readq(&ndev->bar->cap);
- ndev->q_depth = min_t(int, NVME_CAP_MQES(cap) + 1, NVME_Q_DEPTH);
- ndev->db_stride = 1 << NVME_CAP_STRIDE(cap);
+ ndev->cap = nvme_readq(&ndev->bar->cap);
+ ndev->q_depth = min_t(int, NVME_CAP_MQES(ndev->cap) + 1, NVME_Q_DEPTH);
+ ndev->db_stride = 1 << NVME_CAP_STRIDE(ndev->cap);
ndev->dbs = ((void __iomem *)ndev->bar) + 4096;
ret = nvme_configure_admin_queue(ndev);
@@ -833,8 +816,6 @@ static int nvme_probe(struct udevice *udev)
goto free_queue;
nvme_get_info_from_identify(ndev);
- ndev->blk_dev_start = nvme_info->ns_num;
- list_add(&ndev->node, &nvme_info->dev_list);
return 0;
diff --git a/drivers/nvme/nvme.h b/drivers/nvme/nvme.h
index b7fdd0b8e25..67bf6e187fc 100644
--- a/drivers/nvme/nvme.h
+++ b/drivers/nvme/nvme.h
@@ -528,42 +528,6 @@ struct nvme_completion {
__le16 status; /* did the command fail, and if so, why? */
};
-struct nvme_user_io {
- __u8 opcode;
- __u8 flags;
- __u16 control;
- __u16 nblocks;
- __u16 rsvd;
- __u64 metadata;
- __u64 addr;
- __u64 slba;
- __u32 dsmgmt;
- __u32 reftag;
- __u16 apptag;
- __u16 appmask;
-};
-
-struct nvme_passthru_cmd {
- __u8 opcode;
- __u8 flags;
- __u16 rsvd1;
- __u32 nsid;
- __u32 cdw2;
- __u32 cdw3;
- __u64 metadata;
- __u64 addr;
- __u32 metadata_len;
- __u32 data_len;
- __u32 cdw10;
- __u32 cdw11;
- __u32 cdw12;
- __u32 cdw13;
- __u32 cdw14;
- __u32 cdw15;
- __u32 timeout_ms;
- __u32 result;
-};
-
/*
* Registers should always be accessed with double word or quad word
* accesses. Registers with 64-bit address pointers should be written
@@ -644,11 +608,7 @@ struct nvme_dev {
struct list_head node;
struct nvme_queue **queues;
u32 __iomem *dbs;
- unsigned int cardnum;
- struct udevice *pdev;
- pci_dev_t pci_dev;
int instance;
- uint8_t *hw_addr;
unsigned queue_count;
unsigned online_queues;
unsigned max_qid;
@@ -657,42 +617,17 @@ struct nvme_dev {
u32 ctrl_config;
struct nvme_bar __iomem *bar;
struct list_head namespaces;
- const char *name;
char serial[20];
char model[40];
char firmware_rev[8];
u32 max_transfer_shift;
+ u64 cap;
u32 stripe_size;
u32 page_size;
- u16 oncs;
- u16 abort_limit;
- u8 event_limit;
u8 vwc;
u64 *prp_pool;
u32 prp_entry_num;
u32 nn;
- u32 blk_dev_start;
-};
-
-struct nvme_info {
- int ns_num; /*the number of nvme namespaces*/
- int ndev_num; /*the number of nvme devices*/
- struct list_head dev_list;
-};
-
-/*
- * The nvme_iod describes the data in an I/O, including the list of PRP
- * entries. You can't see it in this data structure because C doesn't let
- * me express that. Use nvme_alloc_iod to ensure there's enough space
- * allocated to store the PRP list.
- */
-struct nvme_iod {
- unsigned long private; /* For the use of the submitter of the I/O */
- int npages; /* In the PRP list. 0 means small pool in use */
- int offset; /* Of PRP list */
- int nents; /* Used in scatterlist */
- int length; /* Of data, in bytes */
- dma_addr_t first_dma;
};
/*
@@ -705,13 +640,9 @@ struct nvme_ns {
unsigned ns_id;
int devnum;
int lba_shift;
- u16 ms;
u8 flbas;
- u8 pi_type;
u64 mode_select_num_blocks;
u32 mode_select_block_len;
};
-extern struct nvme_info *nvme_info;
-
#endif /* __DRIVER_NVME_H__ */
diff --git a/drivers/nvme/nvme_show.c b/drivers/nvme/nvme_show.c
index 5577e5de455..52351388e29 100644
--- a/drivers/nvme/nvme_show.c
+++ b/drivers/nvme/nvme_show.c
@@ -8,6 +8,7 @@
#include <common.h>
#include <dm.h>
#include <errno.h>
+#include <memalign.h>
#include <nvme.h>
#include "nvme.h"
@@ -106,8 +107,10 @@ int nvme_print_info(struct udevice *udev)
{
struct nvme_ns *ns = dev_get_priv(udev);
struct nvme_dev *dev = ns->dev;
- struct nvme_id_ns buf_ns, *id = &buf_ns;
- struct nvme_id_ctrl buf_ctrl, *ctrl = &buf_ctrl;
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf_ns, sizeof(struct nvme_id_ns));
+ struct nvme_id_ns *id = (struct nvme_id_ns *)buf_ns;
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf_ctrl, sizeof(struct nvme_id_ctrl));
+ struct nvme_id_ctrl *ctrl = (struct nvme_id_ctrl *)buf_ctrl;
if (nvme_identify(dev, 0, 1, (dma_addr_t)ctrl))
return -EIO;
diff --git a/drivers/pinctrl/nxp/pinctrl-imx.c b/drivers/pinctrl/nxp/pinctrl-imx.c
index 1b6107fae60..32cbac963f7 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx.c
@@ -158,7 +158,7 @@ static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config)
if (!(config_val & IMX_NO_PAD_CTL)) {
if (info->flags & SHARE_MUX_CONF_REG) {
clrsetbits_le32(info->base + conf_reg,
- info->mux_mask, config_val);
+ ~info->mux_mask, config_val);
} else {
writel(config_val, info->base + conf_reg);
}
diff --git a/drivers/pinctrl/nxp/pinctrl-imx7ulp.c b/drivers/pinctrl/nxp/pinctrl-imx7ulp.c
index 4a893e5a65f..618ce6a0e10 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx7ulp.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx7ulp.c
@@ -12,7 +12,11 @@
#include "pinctrl-imx.h"
-static struct imx_pinctrl_soc_info imx7ulp_pinctrl_soc_info = {
+static struct imx_pinctrl_soc_info imx7ulp_pinctrl_soc_info0 = {
+ .flags = ZERO_OFFSET_VALID | SHARE_MUX_CONF_REG | CONFIG_IBE_OBE,
+};
+
+static struct imx_pinctrl_soc_info imx7ulp_pinctrl_soc_info1 = {
.flags = ZERO_OFFSET_VALID | SHARE_MUX_CONF_REG | CONFIG_IBE_OBE,
};
@@ -25,8 +29,8 @@ static int imx7ulp_pinctrl_probe(struct udevice *dev)
}
static const struct udevice_id imx7ulp_pinctrl_match[] = {
- { .compatible = "fsl,imx7ulp-iomuxc-0", .data = (ulong)&imx7ulp_pinctrl_soc_info },
- { .compatible = "fsl,imx7ulp-iomuxc-1", .data = (ulong)&imx7ulp_pinctrl_soc_info },
+ { .compatible = "fsl,imx7ulp-iomuxc-0", .data = (ulong)&imx7ulp_pinctrl_soc_info0 },
+ { .compatible = "fsl,imx7ulp-iomuxc-1", .data = (ulong)&imx7ulp_pinctrl_soc_info1 },
{ /* sentinel */ }
};
diff --git a/drivers/reset/reset-uniphier.c b/drivers/reset/reset-uniphier.c
index ebb2cae5eb3..c74d16fe206 100644
--- a/drivers/reset/reset-uniphier.c
+++ b/drivers/reset/reset-uniphier.c
@@ -77,6 +77,17 @@ static const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = {
UNIPHIER_RESET_END,
};
+static const struct uniphier_reset_data uniphier_pxs3_sys_reset_data[] = {
+ UNIPHIER_RESETX(2, 0x200c, 0), /* NAND */
+ UNIPHIER_RESETX(4, 0x200c, 2), /* eMMC */
+ UNIPHIER_RESETX(8, 0x200c, 12), /* STDMAC */
+ UNIPHIER_RESETX(12, 0x200c, 5), /* USB30 (GIO0) */
+ UNIPHIER_RESETX(13, 0x200c, 6), /* USB31 (GIO1) */
+ UNIPHIER_RESETX(16, 0x200c, 16), /* USB30-PHY */
+ UNIPHIER_RESETX(20, 0x200c, 17), /* USB31-PHY */
+ UNIPHIER_RESET_END,
+};
+
/* Media I/O reset data */
#define UNIPHIER_MIO_RESET_SD(id, ch) \
UNIPHIER_RESETX((id), 0x110 + 0x200 * (ch), 0)
@@ -268,6 +279,10 @@ static const struct udevice_id uniphier_reset_match[] = {
.compatible = "socionext,uniphier-ld20-reset",
.data = (ulong)uniphier_ld20_sys_reset_data,
},
+ {
+ .compatible = "socionext,uniphier-pxs3-reset",
+ .data = (ulong)uniphier_pxs3_sys_reset_data,
+ },
/* Media I/O reset */
{
.compatible = "socionext,uniphier-ld4-mio-reset",
@@ -294,7 +309,15 @@ static const struct udevice_id uniphier_reset_match[] = {
.data = (ulong)uniphier_mio_reset_data,
},
{
- .compatible = "socionext,uniphier-ld20-mio-reset",
+ .compatible = "socionext,uniphier-ld11-sd-reset",
+ .data = (ulong)uniphier_mio_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-sd-reset",
+ .data = (ulong)uniphier_mio_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs3-sd-reset",
.data = (ulong)uniphier_mio_reset_data,
},
/* Peripheral reset */
@@ -326,6 +349,10 @@ static const struct udevice_id uniphier_reset_match[] = {
.compatible = "socionext,uniphier-ld20-peri-reset",
.data = (ulong)uniphier_pro4_peri_reset_data,
},
+ {
+ .compatible = "socionext,uniphier-pxs3-peri-reset",
+ .data = (ulong)uniphier_pro4_peri_reset_data,
+ },
{ /* sentinel */ }
};
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index e93398fe7c1..a291ceb6ae0 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -1,7 +1,6 @@
config USB_DWC3
bool "DesignWare USB3 DRD Core Support"
- depends on (USB && USB_GADGET)
- select USB_GADGET_DUALSPEED
+ depends on USB_HOST || USB_GADGET
help
Say Y here if your system has a Dual Role SuperSpeed
USB controller based on the DesignWare USB3 IP Core.
@@ -21,6 +20,7 @@ config USB_DWC3_HOST
config USB_DWC3_GADGET
bool "Gadget only mode"
depends on USB_GADGET
+ select USB_GADGET_DUALSPEED
help
Select this when you want to use DWC3 in gadget mode only,
thereby the host feature will be regressed.
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 261ed128acd..225b66bc95f 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -103,6 +103,13 @@ config USB_GADGET_DOWNLOAD
if USB_GADGET_DOWNLOAD
+config USB_FUNCTION_SDP
+ bool "Enable USB SDP (Serial Download Protocol)"
+ help
+ Enable Serial Download Protocol (SDP) device support in U-Boot. This
+ allows to download images into memory and execute (jump to) them
+ using the same protocol as implemented by the i.MX family's boot ROM.
+
config G_DNL_MANUFACTURER
string "Vendor name of USB device"
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5e316a7cff9..7258099c1cf 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_USB_ETHER) += epautoconf.o config.o usbstring.o
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_USB_GADGET_SUPPORT) += g_dnl.o
obj-$(CONFIG_SPL_DFU_SUPPORT) += f_dfu.o
+obj-$(CONFIG_SPL_USB_SDP_SUPPORT) += f_sdp.o
endif
# new USB gadget layer dependencies
@@ -28,6 +29,7 @@ obj-$(CONFIG_USB_FUNCTION_THOR) += f_thor.o
obj-$(CONFIG_USB_FUNCTION_DFU) += f_dfu.o
obj-$(CONFIG_USB_FUNCTION_MASS_STORAGE) += f_mass_storage.o
obj-$(CONFIG_USB_FUNCTION_FASTBOOT) += f_fastboot.o
+obj-$(CONFIG_USB_FUNCTION_SDP) += f_sdp.o
endif
endif
ifdef CONFIG_USB_ETHER
diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c
new file mode 100644
index 00000000000..0fae66beaba
--- /dev/null
+++ b/drivers/usb/gadget/f_sdp.c
@@ -0,0 +1,737 @@
+/*
+ * f_sdp.c -- USB HID Serial Download Protocol
+ *
+ * Copyright (C) 2017 Toradex
+ * Author: Stefan Agner <stefan.agner@toradex.com>
+ *
+ * This file implements the Serial Download Protocol (SDP) as specified in
+ * the i.MX 6 Reference Manual. The SDP is a USB HID based protocol and
+ * allows to download images directly to memory. The implementation
+ * works with the imx_loader (imx_usb) USB client software on host side.
+ *
+ * Not all commands are implemented, e.g. WRITE_REGISTER, DCD_WRITE and
+ * SKIP_DCD_HEADER are only stubs.
+ *
+ * Parts of the implementation are based on f_dfu and f_thor.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <errno.h>
+#include <common.h>
+#include <console.h>
+#include <malloc.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+
+#include <asm/io.h>
+#include <g_dnl.h>
+#include <sdp.h>
+#include <spl.h>
+#include <image.h>
+#include <imximage.h>
+
+#define HID_REPORT_ID_MASK 0x000000ff
+
+/*
+ * HID class requests
+ */
+#define HID_REQ_GET_REPORT 0x01
+#define HID_REQ_GET_IDLE 0x02
+#define HID_REQ_GET_PROTOCOL 0x03
+#define HID_REQ_SET_REPORT 0x09
+#define HID_REQ_SET_IDLE 0x0A
+#define HID_REQ_SET_PROTOCOL 0x0B
+
+#define HID_USAGE_PAGE_LEN 76
+
+struct hid_report {
+ u8 usage_page[HID_USAGE_PAGE_LEN];
+} __packed;
+
+#define SDP_READ_REGISTER 0x0101
+#define SDP_WRITE_REGISTER 0x0202
+#define SDP_WRITE_FILE 0x0404
+#define SDP_ERROR_STATUS 0x0505
+#define SDP_DCD_WRITE 0x0a0a
+#define SDP_JUMP_ADDRESS 0x0b0b
+#define SDP_SKIP_DCD_HEADER 0x0c0c
+
+#define SDP_SECURITY_CLOSED 0x12343412
+#define SDP_SECURITY_OPEN 0x56787856
+
+#define SDP_WRITE_FILE_COMPLETE 0x88888888
+#define SDP_WRITE_REGISTER_COMPLETE 0x128A8A12
+#define SDP_SKIP_DCD_HEADER_COMPLETE 0x900DD009
+#define SDP_ERROR_IMXHEADER 0x000a0533
+
+#define SDP_COMMAND_LEN 16
+
+struct sdp_command {
+ u16 cmd;
+ u32 addr;
+ u8 format;
+ u32 cnt;
+ u32 data;
+ u8 rsvd;
+} __packed;
+
+enum sdp_state {
+ SDP_STATE_IDLE,
+ SDP_STATE_RX_DCD_DATA,
+ SDP_STATE_RX_FILE_DATA,
+ SDP_STATE_TX_SEC_CONF,
+ SDP_STATE_TX_SEC_CONF_BUSY,
+ SDP_STATE_TX_REGISTER,
+ SDP_STATE_TX_REGISTER_BUSY,
+ SDP_STATE_TX_STATUS,
+ SDP_STATE_TX_STATUS_BUSY,
+ SDP_STATE_JUMP,
+};
+
+struct f_sdp {
+ struct usb_function usb_function;
+
+ struct usb_descriptor_header **function;
+
+ u8 altsetting;
+ enum sdp_state state;
+ enum sdp_state next_state;
+ u32 dnl_address;
+ u32 dnl_bytes_remaining;
+ u32 jmp_address;
+ bool always_send_status;
+ u32 error_status;
+
+ /* EP0 request */
+ struct usb_request *req;
+
+ /* EP1 IN */
+ struct usb_ep *in_ep;
+ struct usb_request *in_req;
+
+ bool configuration_done;
+};
+
+static struct f_sdp *sdp_func;
+
+static inline struct f_sdp *func_to_sdp(struct usb_function *f)
+{
+ return container_of(f, struct f_sdp, usb_function);
+}
+
+static struct usb_interface_descriptor sdp_intf_runtime = {
+ .bLength = sizeof(sdp_intf_runtime),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ /* .iInterface = DYNAMIC */
+};
+
+/* HID configuration */
+static struct usb_class_hid_descriptor sdp_hid_desc = {
+ .bLength = sizeof(sdp_hid_desc),
+ .bDescriptorType = USB_DT_CS_DEVICE,
+
+ .bcdCDC = __constant_cpu_to_le16(0x0110),
+ .bCountryCode = 0,
+ .bNumDescriptors = 1,
+
+ .bDescriptorType0 = USB_DT_HID_REPORT,
+ .wDescriptorLength0 = HID_USAGE_PAGE_LEN,
+};
+
+static struct usb_endpoint_descriptor in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/
+
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 64,
+ .bInterval = 1,
+};
+
+static struct usb_descriptor_header *sdp_runtime_descs[] = {
+ (struct usb_descriptor_header *)&sdp_intf_runtime,
+ (struct usb_descriptor_header *)&sdp_hid_desc,
+ (struct usb_descriptor_header *)&in_desc,
+ NULL,
+};
+
+/* This is synchronized with what the SoC implementation reports */
+static struct hid_report sdp_hid_report = {
+ .usage_page = {
+ 0x06, 0x00, 0xff, /* Usage Page */
+ 0x09, 0x01, /* Usage (Pointer?) */
+ 0xa1, 0x01, /* Collection */
+
+ 0x85, 0x01, /* Report ID */
+ 0x19, 0x01, /* Usage Minimum */
+ 0x29, 0x01, /* Usage Maximum */
+ 0x15, 0x00, /* Local Minimum */
+ 0x26, 0xFF, 0x00, /* Local Maximum? */
+ 0x75, 0x08, /* Report Size */
+ 0x95, 0x10, /* Report Count */
+ 0x91, 0x02, /* Output Data */
+
+ 0x85, 0x02, /* Report ID */
+ 0x19, 0x01, /* Usage Minimum */
+ 0x29, 0x01, /* Usage Maximum */
+ 0x15, 0x00, /* Local Minimum */
+ 0x26, 0xFF, 0x00, /* Local Maximum? */
+ 0x75, 0x80, /* Report Size 128 */
+ 0x95, 0x40, /* Report Count */
+ 0x91, 0x02, /* Output Data */
+
+ 0x85, 0x03, /* Report ID */
+ 0x19, 0x01, /* Usage Minimum */
+ 0x29, 0x01, /* Usage Maximum */
+ 0x15, 0x00, /* Local Minimum */
+ 0x26, 0xFF, 0x00, /* Local Maximum? */
+ 0x75, 0x08, /* Report Size 8 */
+ 0x95, 0x04, /* Report Count */
+ 0x81, 0x02, /* Input Data */
+
+ 0x85, 0x04, /* Report ID */
+ 0x19, 0x01, /* Usage Minimum */
+ 0x29, 0x01, /* Usage Maximum */
+ 0x15, 0x00, /* Local Minimum */
+ 0x26, 0xFF, 0x00, /* Local Maximum? */
+ 0x75, 0x08, /* Report Size 8 */
+ 0x95, 0x40, /* Report Count */
+ 0x81, 0x02, /* Input Data */
+ 0xc0
+ },
+};
+
+static const char sdp_name[] = "Serial Downloader Protocol";
+
+/*
+ * static strings, in UTF-8
+ */
+static struct usb_string strings_sdp_generic[] = {
+ [0].s = sdp_name,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_sdp_generic = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_sdp_generic,
+};
+
+static struct usb_gadget_strings *sdp_generic_strings[] = {
+ &stringtab_sdp_generic,
+ NULL,
+};
+
+static void sdp_rx_command_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_sdp *sdp = req->context;
+ int status = req->status;
+ u8 *data = req->buf;
+ u8 report = data[0];
+
+ if (status != 0) {
+ error("Status: %d", status);
+ return;
+ }
+
+ if (report != 1) {
+ error("Unexpected report %d", report);
+ return;
+ }
+
+ struct sdp_command *cmd = req->buf + 1;
+
+ debug("%s: command: %04x, addr: %08x, cnt: %u\n",
+ __func__, be16_to_cpu(cmd->cmd),
+ be32_to_cpu(cmd->addr), be32_to_cpu(cmd->cnt));
+
+ switch (be16_to_cpu(cmd->cmd)) {
+ case SDP_READ_REGISTER:
+ sdp->always_send_status = false;
+ sdp->error_status = 0x0;
+
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ sdp->dnl_address = be32_to_cpu(cmd->addr);
+ sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
+ sdp->next_state = SDP_STATE_TX_REGISTER;
+ printf("Reading %d registers at 0x%08x... ",
+ sdp->dnl_bytes_remaining, sdp->dnl_address);
+ break;
+ case SDP_WRITE_FILE:
+ sdp->always_send_status = true;
+ sdp->error_status = SDP_WRITE_FILE_COMPLETE;
+
+ sdp->state = SDP_STATE_RX_FILE_DATA;
+ sdp->dnl_address = be32_to_cpu(cmd->addr);
+ sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
+ sdp->next_state = SDP_STATE_IDLE;
+
+ printf("Downloading file of size %d to 0x%08x... ",
+ sdp->dnl_bytes_remaining, sdp->dnl_address);
+
+ break;
+ case SDP_ERROR_STATUS:
+ sdp->always_send_status = true;
+ sdp->error_status = 0;
+
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ sdp->next_state = SDP_STATE_IDLE;
+ break;
+ case SDP_DCD_WRITE:
+ sdp->always_send_status = true;
+ sdp->error_status = SDP_WRITE_REGISTER_COMPLETE;
+
+ sdp->state = SDP_STATE_RX_DCD_DATA;
+ sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
+ sdp->next_state = SDP_STATE_IDLE;
+ break;
+ case SDP_JUMP_ADDRESS:
+ sdp->always_send_status = false;
+ sdp->error_status = 0;
+
+ sdp->jmp_address = be32_to_cpu(cmd->addr);
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ sdp->next_state = SDP_STATE_JUMP;
+ break;
+ case SDP_SKIP_DCD_HEADER:
+ sdp->always_send_status = true;
+ sdp->error_status = SDP_SKIP_DCD_HEADER_COMPLETE;
+
+ /* Ignore command, DCD not supported anyway */
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ sdp->next_state = SDP_STATE_IDLE;
+ break;
+ default:
+ error("Unknown command: %04x\n", be16_to_cpu(cmd->cmd));
+ }
+}
+
+static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_sdp *sdp = req->context;
+ int status = req->status;
+ u8 *data = req->buf;
+ u8 report = data[0];
+ int datalen = req->length - 1;
+
+ if (status != 0) {
+ error("Status: %d", status);
+ return;
+ }
+
+ if (report != 2) {
+ error("Unexpected report %d", report);
+ return;
+ }
+
+ if (sdp->dnl_bytes_remaining < datalen) {
+ /*
+ * Some USB stacks require to send a complete buffer as
+ * specified in the HID descriptor. This leads to longer
+ * transfers than the file length, no problem for us.
+ */
+ sdp->dnl_bytes_remaining = 0;
+ } else {
+ sdp->dnl_bytes_remaining -= datalen;
+ }
+
+ if (sdp->state == SDP_STATE_RX_FILE_DATA) {
+ memcpy((void *)sdp->dnl_address, req->buf + 1, datalen);
+ sdp->dnl_address += datalen;
+ }
+
+ if (sdp->dnl_bytes_remaining)
+ return;
+
+ printf("done\n");
+
+ switch (sdp->state) {
+ case SDP_STATE_RX_FILE_DATA:
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ break;
+ case SDP_STATE_RX_DCD_DATA:
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ break;
+ default:
+ error("Invalid state: %d", sdp->state);
+ }
+}
+
+static void sdp_tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_sdp *sdp = req->context;
+ int status = req->status;
+
+ if (status != 0) {
+ error("Status: %d", status);
+ return;
+ }
+
+ switch (sdp->state) {
+ case SDP_STATE_TX_SEC_CONF_BUSY:
+ /* Not all commands require status report */
+ if (sdp->always_send_status || sdp->error_status)
+ sdp->state = SDP_STATE_TX_STATUS;
+ else
+ sdp->state = sdp->next_state;
+
+ break;
+ case SDP_STATE_TX_STATUS_BUSY:
+ sdp->state = sdp->next_state;
+ break;
+ case SDP_STATE_TX_REGISTER_BUSY:
+ if (sdp->dnl_bytes_remaining)
+ sdp->state = SDP_STATE_TX_REGISTER;
+ else
+ sdp->state = SDP_STATE_IDLE;
+ break;
+ default:
+ error("Wrong State: %d", sdp->state);
+ sdp->state = SDP_STATE_IDLE;
+ break;
+ }
+ debug("%s complete --> %d, %d/%d\n", ep->name,
+ status, req->actual, req->length);
+}
+
+static int sdp_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_gadget *gadget = f->config->cdev->gadget;
+ struct usb_request *req = f->config->cdev->req;
+ struct f_sdp *sdp = f->config->cdev->req->context;
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ int value = 0;
+ u8 req_type = ctrl->bRequestType & USB_TYPE_MASK;
+
+ debug("w_value: 0x%04x len: 0x%04x\n", w_value, len);
+ debug("req_type: 0x%02x ctrl->bRequest: 0x%02x sdp->state: %d\n",
+ req_type, ctrl->bRequest, sdp->state);
+
+ if (req_type == USB_TYPE_STANDARD) {
+ if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) {
+ /* Send HID report descriptor */
+ value = min(len, (u16) sizeof(sdp_hid_report));
+ memcpy(req->buf, &sdp_hid_report, value);
+ sdp->configuration_done = true;
+ }
+ }
+
+ if (req_type == USB_TYPE_CLASS) {
+ int report = w_value & HID_REPORT_ID_MASK;
+
+ /* HID (SDP) request */
+ switch (ctrl->bRequest) {
+ case HID_REQ_SET_REPORT:
+ switch (report) {
+ case 1:
+ value = SDP_COMMAND_LEN + 1;
+ req->complete = sdp_rx_command_complete;
+ break;
+ case 2:
+ value = len;
+ req->complete = sdp_rx_data_complete;
+ break;
+ }
+ }
+ }
+
+ if (value >= 0) {
+ req->length = value;
+ req->zero = value < len;
+ value = usb_ep_queue(gadget->ep0, req, 0);
+ if (value < 0) {
+ debug("ep_queue --> %d\n", value);
+ req->status = 0;
+ }
+ }
+
+ return value;
+}
+
+static int sdp_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_gadget *gadget = c->cdev->gadget;
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_sdp *sdp = func_to_sdp(f);
+ int rv = 0, id;
+
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ sdp_intf_runtime.bInterfaceNumber = id;
+
+ struct usb_ep *ep;
+
+ /* allocate instance-specific endpoints */
+ ep = usb_ep_autoconfig(gadget, &in_desc);
+ if (!ep) {
+ rv = -ENODEV;
+ goto error;
+ }
+
+ sdp->in_ep = ep; /* Store IN EP for enabling @ setup */
+
+ cdev->req->context = sdp;
+
+error:
+ return rv;
+}
+
+static void sdp_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ free(sdp_func);
+ sdp_func = NULL;
+}
+
+static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, 0);
+ if (!req)
+ return req;
+
+ req->length = length;
+ req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, length);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ req = NULL;
+ }
+
+ return req;
+}
+
+
+static struct usb_request *sdp_start_ep(struct usb_ep *ep)
+{
+ struct usb_request *req;
+
+ req = alloc_ep_req(ep, 64);
+ debug("%s: ep:%p req:%p\n", __func__, ep, req);
+
+ if (!req)
+ return NULL;
+
+ memset(req->buf, 0, req->length);
+ req->complete = sdp_tx_complete;
+
+ return req;
+}
+static int sdp_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_sdp *sdp = func_to_sdp(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int result;
+
+ debug("%s: intf: %d alt: %d\n", __func__, intf, alt);
+
+ result = usb_ep_enable(sdp->in_ep, &in_desc);
+ if (result)
+ return result;
+ sdp->in_req = sdp_start_ep(sdp->in_ep);
+ sdp->in_req->context = sdp;
+
+ sdp->in_ep->driver_data = cdev; /* claim */
+
+ sdp->altsetting = alt;
+ sdp->state = SDP_STATE_IDLE;
+
+ return 0;
+}
+
+static int sdp_get_alt(struct usb_function *f, unsigned intf)
+{
+ struct f_sdp *sdp = func_to_sdp(f);
+
+ return sdp->altsetting;
+}
+
+static void sdp_disable(struct usb_function *f)
+{
+ struct f_sdp *sdp = func_to_sdp(f);
+
+ usb_ep_disable(sdp->in_ep);
+
+ if (sdp->in_req) {
+ free(sdp->in_req);
+ sdp->in_req = NULL;
+ }
+}
+
+static int sdp_bind_config(struct usb_configuration *c)
+{
+ int status;
+
+ if (!sdp_func) {
+ sdp_func = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*sdp_func));
+ if (!sdp_func)
+ return -ENOMEM;
+ }
+
+ memset(sdp_func, 0, sizeof(*sdp_func));
+
+ sdp_func->usb_function.name = "sdp";
+ sdp_func->usb_function.hs_descriptors = sdp_runtime_descs;
+ sdp_func->usb_function.descriptors = sdp_runtime_descs;
+ sdp_func->usb_function.bind = sdp_bind;
+ sdp_func->usb_function.unbind = sdp_unbind;
+ sdp_func->usb_function.set_alt = sdp_set_alt;
+ sdp_func->usb_function.get_alt = sdp_get_alt;
+ sdp_func->usb_function.disable = sdp_disable;
+ sdp_func->usb_function.strings = sdp_generic_strings;
+ sdp_func->usb_function.setup = sdp_setup;
+
+ status = usb_add_function(c, &sdp_func->usb_function);
+
+ return status;
+}
+
+int sdp_init(int controller_index)
+{
+ printf("SDP: initialize...\n");
+ while (!sdp_func->configuration_done) {
+ if (ctrlc()) {
+ puts("\rCTRL+C - Operation aborted.\n");
+ return 1;
+ }
+ usb_gadget_handle_interrupts(controller_index);
+ }
+
+ return 0;
+}
+
+static u32 sdp_jump_imxheader(void *address)
+{
+ flash_header_v2_t *headerv2 = address;
+ ulong (*entry)(void);
+
+ if (headerv2->header.tag != IVT_HEADER_TAG) {
+ printf("Header Tag is not an IMX image\n");
+ return SDP_ERROR_IMXHEADER;
+ }
+
+ printf("Jumping to 0x%08x\n", headerv2->entry);
+ entry = (void *)headerv2->entry;
+ entry();
+
+ /* The image probably never returns hence we won't reach that point */
+ return 0;
+}
+
+static void sdp_handle_in_ep(void)
+{
+ u8 *data = sdp_func->in_req->buf;
+ u32 status;
+ int datalen;
+
+ switch (sdp_func->state) {
+ case SDP_STATE_TX_SEC_CONF:
+ debug("Report 3: HAB security\n");
+ data[0] = 3;
+
+ status = SDP_SECURITY_OPEN;
+ memcpy(&data[1], &status, 4);
+ sdp_func->in_req->length = 5;
+ usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
+ sdp_func->state = SDP_STATE_TX_SEC_CONF_BUSY;
+ break;
+
+ case SDP_STATE_TX_STATUS:
+ debug("Report 4: Status\n");
+ data[0] = 4;
+
+ memcpy(&data[1], &sdp_func->error_status, 4);
+ sdp_func->in_req->length = 65;
+ usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
+ sdp_func->state = SDP_STATE_TX_STATUS_BUSY;
+ break;
+ case SDP_STATE_TX_REGISTER:
+ debug("Report 4: Register Values\n");
+ data[0] = 4;
+
+ datalen = sdp_func->dnl_bytes_remaining;
+
+ if (datalen > 64)
+ datalen = 64;
+
+ memcpy(&data[1], (void *)sdp_func->dnl_address, datalen);
+ sdp_func->in_req->length = 65;
+
+ sdp_func->dnl_bytes_remaining -= datalen;
+ sdp_func->dnl_address += datalen;
+
+ usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
+ sdp_func->state = SDP_STATE_TX_REGISTER_BUSY;
+ break;
+ case SDP_STATE_JUMP:
+ printf("Jumping to header at 0x%08x\n", sdp_func->jmp_address);
+ status = sdp_jump_imxheader((void *)sdp_func->jmp_address);
+
+ /* If imx header fails, try some U-Boot specific headers */
+ if (status) {
+#ifdef CONFIG_SPL_BUILD
+ /* In SPL, allow jumps to U-Boot images */
+ struct spl_image_info spl_image = {};
+ spl_parse_image_header(&spl_image,
+ (struct image_header *)sdp_func->jmp_address);
+ jump_to_image_no_args(&spl_image);
+#else
+ /* In U-Boot, allow jumps to scripts */
+ source(sdp_func->jmp_address, "script@1");
+#endif
+ }
+
+ sdp_func->next_state = SDP_STATE_IDLE;
+ sdp_func->error_status = status;
+
+ /* Only send Report 4 if there was an error */
+ if (status)
+ sdp_func->state = SDP_STATE_TX_STATUS;
+ else
+ sdp_func->state = SDP_STATE_IDLE;
+ break;
+ default:
+ break;
+ };
+}
+
+void sdp_handle(int controller_index)
+{
+ printf("SDP: handle requests...\n");
+ while (1) {
+ if (ctrlc()) {
+ puts("\rCTRL+C - Operation aborted.\n");
+ return;
+ }
+
+ usb_gadget_handle_interrupts(controller_index);
+
+ sdp_handle_in_ep();
+ }
+}
+
+int sdp_add(struct usb_configuration *c)
+{
+ int id;
+
+ id = usb_string_id(c->cdev);
+ if (id < 0)
+ return id;
+ strings_sdp_generic[0].id = id;
+ sdp_intf_runtime.iInterface = id;
+
+ debug("%s: cdev: %p gadget: %p gadget->ep0: %p\n", __func__,
+ c->cdev, c->cdev->gadget, c->cdev->gadget->ep0);
+
+ return sdp_bind_config(c);
+}
+
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_sdp, sdp_add);