diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/uniphier/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/uniphier/clk-uniphier-core.c | 30 | ||||
-rw-r--r-- | drivers/clk/uniphier/clk-uniphier-sys.c | 34 | ||||
-rw-r--r-- | drivers/clk/uniphier/clk-uniphier.h | 2 | ||||
-rw-r--r-- | drivers/core/ofnode.c | 12 | ||||
-rw-r--r-- | drivers/core/read.c | 6 | ||||
-rw-r--r-- | drivers/mmc/Kconfig | 4 | ||||
-rw-r--r-- | drivers/mmc/sunxi_mmc.c | 29 | ||||
-rw-r--r-- | drivers/mtd/nand/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/denali.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/denali.h | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/denali_dt.c | 68 | ||||
-rw-r--r-- | drivers/nvme/nvme-uclass.c | 35 | ||||
-rw-r--r-- | drivers/nvme/nvme.c | 179 | ||||
-rw-r--r-- | drivers/nvme/nvme.h | 71 | ||||
-rw-r--r-- | drivers/nvme/nvme_show.c | 7 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/pinctrl-imx.c | 2 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/pinctrl-imx7ulp.c | 10 | ||||
-rw-r--r-- | drivers/reset/reset-uniphier.c | 29 | ||||
-rw-r--r-- | drivers/usb/dwc3/Kconfig | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/f_sdp.c | 737 |
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); |