diff options
Diffstat (limited to 'drivers')
67 files changed, 6768 insertions, 1001 deletions
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index d2f8ec70e4..dc7b4f276f 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -65,7 +65,7 @@ struct regmap *regmap_init(struct device_d *dev, } /* - * regmap_init - initialize and register a regmap + * dev_get_regmap - get a regmap from a device * * @dev: The device the maps is attached to * @name: Optional name for the map. If given it must match. @@ -406,4 +406,4 @@ void regmap_exit(struct regmap *map) } free(map); -}
\ No newline at end of file +} diff --git a/drivers/clk/imx/clk-imx6.c b/drivers/clk/imx/clk-imx6.c index c47281b16e..ed29e8c271 100644 --- a/drivers/clk/imx/clk-imx6.c +++ b/drivers/clk/imx/clk-imx6.c @@ -708,7 +708,7 @@ static int imx6_ccm_probe(struct device_d *dev) clks[IMX6QDL_CLK_USDHC3_SEL] = imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); clks[IMX6QDL_CLK_USDHC4_SEL] = imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); if (cpu_mx6_is_plus()) - clks[IMX6QDL_CLK_ENFC_SEL] = imx_clk_mux("enfc_sel", base + 0x2c, 16, 2, enfc_sels_plus, ARRAY_SIZE(enfc_sels_plus)); + clks[IMX6QDL_CLK_ENFC_SEL] = imx_clk_mux("enfc_sel", base + 0x2c, 15, 3, enfc_sels_plus, ARRAY_SIZE(enfc_sels_plus)); else clks[IMX6QDL_CLK_ENFC_SEL] = imx_clk_mux("enfc_sel", base + 0x2c, 16, 2, enfc_sels, ARRAY_SIZE(enfc_sels)); clks[IMX6QDL_CLK_EIM_SEL] = imx_clk_mux("eim_sel", base + 0x1c, 27, 2, eim_sels, ARRAY_SIZE(eim_sels)); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7a1503198b..0f924d135f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -67,6 +67,14 @@ config GPIO_MALTA_FPGA_I2C additional drivers must be enabled in order to use the functionality of the device. +config GPIO_MPC8XXX + bool "MPC512x/MPC8xxx/QorIQ GPIO support" + depends on ARCH_LAYERSCAPE + select GPIO_GENERIC + help + Say Y here if you're going to use hardware that connects to the + MPC512x/831x/834x/837x/8572/8610/QorIQ GPIOs. + config GPIO_OMAP def_bool ARCH_OMAP diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 990df01788..bc5c500e4d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_GPIO_LIBFTDI1) += gpio-libftdi1.o obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o obj-$(CONFIG_GPIO_JZ4740) += gpio-jz4740.o obj-$(CONFIG_GPIO_MALTA_FPGA_I2C) += gpio-malta-fpga-i2c.o +obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o obj-$(CONFIG_GPIO_ORION) += gpio-orion.o obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c new file mode 100644 index 0000000000..979f92ad30 --- /dev/null +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -0,0 +1,122 @@ +/* + * GPIOs on MPC512x/8349/8572/8610/QorIQ and compatible + * + * Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk> + * Copyright (C) 2016 Freescale Semiconductor Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <common.h> +#include <init.h> +#include <gpio.h> +#include <linux/basic_mmio_gpio.h> +#include <linux/clk.h> +#include <malloc.h> +#include <of.h> +#include <of_address.h> +#include <of_device.h> + +#define MPC8XXX_GPIO_PINS 32 + +#define GPIO_DIR 0x00 +#define GPIO_ODR 0x04 +#define GPIO_DAT 0x08 +#define GPIO_IER 0x0c +#define GPIO_IMR 0x10 + +struct mpc8xxx_gpio_chip { + struct bgpio_chip bgc; + void __iomem *regs; +}; + +struct mpc8xxx_gpio_devtype { + int (*gpio_dir_out)(struct bgpio_chip *, unsigned int, int); + int (*gpio_get)(struct bgpio_chip *, unsigned int); +}; + +static int mpc8xxx_probe(struct device_d *dev) +{ + struct device_node *np; + struct resource *iores; + struct mpc8xxx_gpio_chip *mpc8xxx_gc; + struct bgpio_chip *bgc; + int ret; + + mpc8xxx_gc = xzalloc(sizeof(*mpc8xxx_gc)); + + if (dev->device_node) { + np = dev->device_node; + } else { + dev_err(dev, "no device_node\n"); + return -ENODEV; + } + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + mpc8xxx_gc->regs = IOMEM(iores->start); + if (!mpc8xxx_gc->regs) + return -ENOMEM; + + bgc = &mpc8xxx_gc->bgc; + + if (of_property_read_bool(np, "little-endian")) { + ret = bgpio_init(bgc, dev, 4, + mpc8xxx_gc->regs + GPIO_DAT, + NULL, NULL, + mpc8xxx_gc->regs + GPIO_DIR, NULL, 0); + if (ret) + goto err; + dev_dbg(dev, "GPIO registers are LITTLE endian\n"); + } else { + ret = bgpio_init(bgc, dev, 4, + mpc8xxx_gc->regs + GPIO_DAT, + NULL, NULL, + mpc8xxx_gc->regs + GPIO_DIR, NULL, + BGPIOF_BIG_ENDIAN); + if (ret) + goto err; + dev_dbg(dev, "GPIO registers are BIG endian\n"); + } + + ret = gpiochip_add(&mpc8xxx_gc->bgc.gc); + if (ret) { + pr_err("%pOF: GPIO chip registration failed with status %d\n", + np, ret); + goto err; + } + + /* ack and mask all irqs */ + bgc->write_reg(mpc8xxx_gc->regs + GPIO_IER, 0xffffffff); + bgc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, 0); + + return 0; + +err: + return ret; +} + +static __maybe_unused struct of_device_id mpc8xxx_gpio_ids[] = { + { + .compatible = "fsl,qoriq-gpio", + }, + { + /* sentinel */ + }, +}; + +static struct driver_d mpc8xxx_driver = { + .name = "mpc8xxx-gpio", + .probe = mpc8xxx_probe, + .of_compatible = DRV_OF_COMPAT(mpc8xxx_gpio_ids), +}; + +static int __init mpc8xxx_init(void) +{ + return platform_driver_register(&mpc8xxx_driver); +} +postcore_initcall(mpc8xxx_init); diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index 42bde5ed1c..aa7dcb8c31 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -170,6 +170,16 @@ static int pca954x_select_chan(struct i2c_adapter *adap, return ret; } +static int pca954x_deselect_chan(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct pca954x *data = i2c_get_clientdata(client); + + /* Deselect active channel */ + data->last_chan = 0; + return pca954x_reg_write(adap, client, data->last_chan); +} + /* * I2C init/probing/exit functions */ @@ -182,6 +192,7 @@ static int pca954x_probe(struct device_d *dev) uintptr_t tmp; int ret = -ENODEV; int gpio; + bool idle_disconnect; data = kzalloc(sizeof(struct pca954x), GFP_KERNEL); if (!data) { @@ -209,6 +220,9 @@ static int pca954x_probe(struct device_d *dev) if (ret) goto exit_free; + idle_disconnect = of_property_read_bool(dev->device_node, + "i2c-mux-idle-disconnect"); + data->last_chan = 0; /* force the first selection */ /* Now create an adapter for each channel */ @@ -216,7 +230,10 @@ static int pca954x_probe(struct device_d *dev) data->virt_adaps[num] = i2c_add_mux_adapter(adap, &client->dev, client, - 0, num, pca954x_select_chan, NULL); + 0, num, pca954x_select_chan, + idle_disconnect ? + pca954x_deselect_chan : + NULL); if (data->virt_adaps[num] == NULL) { ret = -ENODEV; diff --git a/drivers/input/input.c b/drivers/input/input.c index 14e44d1378..1e8f6e178e 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -195,6 +195,8 @@ static int input_init(void) ic->console.tstc = input_console_tstc; ic->console.getc = input_console_getc; ic->console.f_active = CONSOLE_STDIN; + ic->console.devid = DEVICE_ID_DYNAMIC; + ic->console.devname = "input"; ic->fifo = kfifo_alloc(32); ic->notifier.notify = input_console_notify; diff --git a/drivers/input/specialkeys.c b/drivers/input/specialkeys.c index ff29b8455d..a3f2bf4e4f 100644 --- a/drivers/input/specialkeys.c +++ b/drivers/input/specialkeys.c @@ -13,12 +13,12 @@ static void input_specialkeys_notify(struct input_notifier *in, { switch (ev->code) { case KEY_RESTART: - pr_info("Triggering reset due to special key.\n", ev->code); + pr_info("Triggering reset due to special key.\n"); restart_machine(); break; case KEY_POWER: - pr_info("Triggering poweroff due to special key.\n", ev->code); + pr_info("Triggering poweroff due to special key.\n"); poweroff_machine(); break; } diff --git a/drivers/mci/imx-esdhc-pbl.c b/drivers/mci/imx-esdhc-pbl.c index aa93af656c..f93ddfa0d5 100644 --- a/drivers/mci/imx-esdhc-pbl.c +++ b/drivers/mci/imx-esdhc-pbl.c @@ -28,11 +28,13 @@ #include "imx-esdhc.h" #define SECTOR_SIZE 512 +#define SECTOR_WML (SECTOR_SIZE / sizeof(u32)) struct esdhc { void __iomem *regs; bool is_mx6; bool is_be; + bool wrap_wml; }; static uint32_t esdhc_read32(struct esdhc *esdhc, int reg) @@ -107,7 +109,7 @@ static int esdhc_do_data(struct esdhc *esdhc, struct mci_data *data) } } - for (i = 0; i < SECTOR_SIZE / sizeof(uint32_t); i++) { + for (i = 0; i < SECTOR_WML; i++) { databuf = esdhc_read32(esdhc, SDHCI_BUFFER); *((u32 *)buffer) = databuf; buffer += 4; @@ -203,7 +205,7 @@ static int esdhc_read_blocks(struct esdhc *esdhc, void *dst, size_t len) { struct mci_cmd cmd; struct mci_data data; - u32 val; + u32 val, wml; int ret; esdhc_write32(esdhc, SDHCI_INT_ENABLE, @@ -212,7 +214,18 @@ static int esdhc_read_blocks(struct esdhc *esdhc, void *dst, size_t len) IRQSTATEN_DTOE | IRQSTATEN_DCE | IRQSTATEN_DEBE | IRQSTATEN_DINT); - esdhc_write32(esdhc, IMX_SDHCI_WML, 0x0); + wml = FIELD_PREP(WML_WR_BRST_LEN, 16) | + FIELD_PREP(WML_WR_WML_MASK, SECTOR_WML) | + FIELD_PREP(WML_RD_BRST_LEN, 16) | + FIELD_PREP(WML_RD_WML_MASK, SECTOR_WML); + /* + * Some SoCs intrpret 0 as MAX value so for those cases the + * above value translates to zero + */ + if (esdhc->wrap_wml) + wml = 0; + + esdhc_write32(esdhc, IMX_SDHCI_WML, wml); val = esdhc_read32(esdhc, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET); val |= SYSCTL_HCKEN | SYSCTL_IPGEN; @@ -388,6 +401,7 @@ int imx6_esdhc_start_image(int instance) esdhc.is_be = 0; esdhc.is_mx6 = 1; + esdhc.wrap_wml = false; return esdhc_start_image(&esdhc, 0x10000000, 0x10000000, 0); } @@ -421,6 +435,7 @@ int imx8_esdhc_start_image(int instance) esdhc.is_be = 0; esdhc.is_mx6 = 1; + esdhc.wrap_wml = false; return esdhc_start_image(&esdhc, MX8MQ_DDR_CSD1_BASE_ADDR, MX8MQ_ATF_BL33_BASE_ADDR, SZ_32K); @@ -447,6 +462,7 @@ int imx8_esdhc_load_piggy(int instance) esdhc.is_be = 0; esdhc.is_mx6 = 1; + esdhc.wrap_wml = false; /* * We expect to be running at MX8MQ_ATF_BL33_BASE_ADDR where the atf @@ -503,6 +519,7 @@ int ls1046a_esdhc_start_image(unsigned long r0, unsigned long r1, unsigned long struct esdhc esdhc = { .regs = IOMEM(0x01560000), .is_be = true, + .wrap_wml = true, }; unsigned long sdram = 0x80000000; void (*barebox)(unsigned long, unsigned long, unsigned long) = diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c index f71ca539ed..db3450a26d 100644 --- a/drivers/mci/imx-esdhc.c +++ b/drivers/mci/imx-esdhc.c @@ -653,20 +653,24 @@ static int fsl_esdhc_probe(struct device_d *dev) dma_set_mask(dev, DMA_BIT_MASK(32)); host->clk = clk_get(dev, socdata->clkidx); - if (IS_ERR(host->clk)) - return PTR_ERR(host->clk); + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); + goto err_free; + } ret = clk_enable(host->clk); if (ret) { dev_err(dev, "Failed to enable clock: %s\n", strerror(ret)); - return ret; + goto err_clk_put; } host->dev = dev; iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); + if (IS_ERR(iores)) { + ret = PTR_ERR(iores); + goto err_clk_disable; + } host->regs = IOMEM(iores->start); caps = esdhc_read32(host, SDHCI_CAPABILITIES); @@ -709,7 +713,21 @@ static int fsl_esdhc_probe(struct device_d *dev) dev->priv = host; - return mci_register(&host->mci); + ret = mci_register(&host->mci); + if (ret) + goto err_release_res; + + return 0; + +err_release_res: + release_region(iores); +err_clk_disable: + clk_disable(host->clk); +err_clk_put: + clk_put(host->clk); +err_free: + free(host); + return ret; } static struct esdhc_soc_data esdhc_imx25_data = { diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h index 9b79346f90..2d5471969d 100644 --- a/drivers/mci/imx-esdhc.h +++ b/drivers/mci/imx-esdhc.h @@ -24,6 +24,7 @@ #include <errno.h> #include <asm/byteorder.h> +#include <linux/bitfield.h> #define SYSCTL_INITA 0x08000000 #define SYSCTL_TIMEOUT_MASK 0x000f0000 @@ -43,7 +44,9 @@ #define WML_WRITE 0x00010000 #define WML_RD_WML_MASK 0xff +#define WML_WR_BRST_LEN GENMASK(28, 24) #define WML_WR_WML_MASK 0xff0000 +#define WML_RD_BRST_LEN GENMASK(12, 8) #define BLKATTR_CNT(x) ((x & 0xffff) << 16) #define BLKATTR_SIZE(x) (x & 0x1fff) diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c index 67257bcd18..9e39cbbb55 100644 --- a/drivers/mci/mci-core.c +++ b/drivers/mci/mci-core.c @@ -1819,6 +1819,10 @@ int mci_register(struct mci_host *host) host->supply = regulator_get(host->hw_dev, "vmmc"); if (IS_ERR(host->supply)) { + if (host->supply == ERR_PTR(-EPROBE_DEFER)) { + ret = -EPROBE_DEFER; + goto err_free; + } dev_err(&mci->dev, "Failed to get 'vmmc' regulator.\n"); host->supply = NULL; } diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7d924cfca1..f4cc71ef0e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -67,4 +67,19 @@ config MFD_STPMIC1 help Select this to support communication with the STPMIC1. +config MFD_SUPERIO + bool + +config FINTEK_SUPERIO + bool "Fintek Super I/O chip" + select MFD_SUPERIO + help + Select this to probe for IO-port connected Fintek Super I/O chips. + +config SMSC_SUPERIO + bool "SMSC Super I/O chip" + select MFD_SUPERIO + help + Select this to probe for IO-port connected SMSC Super I/O chips. + endmenu diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 16a74abd77..0c24493e3d 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -12,3 +12,6 @@ obj-$(CONFIG_MFD_TWL4030) += twl4030.o obj-$(CONFIG_MFD_TWL6030) += twl6030.o obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o obj-$(CONFIG_MFD_STPMIC1) += stpmic1.o +obj-$(CONFIG_MFD_SUPERIO) += superio.o +obj-$(CONFIG_FINTEK_SUPERIO) += fintek-superio.o +obj-$(CONFIG_SMSC_SUPERIO) += smsc-superio.o diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c index 5f77e5935b..b61e764876 100644 --- a/drivers/mfd/da9063.c +++ b/drivers/mfd/da9063.c @@ -15,8 +15,10 @@ #include <common.h> #include <driver.h> +#include <gpio.h> #include <restart.h> #include <i2c/i2c.h> +#include <linux/bitops.h> #include <malloc.h> #include <notifier.h> #include <reset_source.h> @@ -25,6 +27,7 @@ struct da9063 { struct restart_handler restart; struct watchdog wd; + struct gpio_chip gpio; struct i2c_client *client; /* dummy client for accessing bank #1 */ struct i2c_client *client1; @@ -61,6 +64,19 @@ struct da9063 { /* DA9063_REG_CONTROL_I (addr=0x10e) */ #define DA9062_WATCHDOG_SD BIT(3) +#define DA9062AA_STATUS_B 0x002 +#define DA9062AA_GPIO_0_1 0x015 +#define DA9062AA_GPIO_MODE0_4 0x01D + +/* DA9062AA_GPIO_0_1 (addr=0x015) */ +#define DA9062AA_GPIO0_PIN_MASK 0x03 + +#define DA9062_PIN_SHIFT(offset) (4 * (offset % 2)) +#define DA9062_PIN_ALTERNATE 0x00 /* gpio alternate mode */ +#define DA9062_PIN_GPI 0x01 /* gpio in */ +#define DA9062_PIN_GPO_OD 0x02 /* gpio out open-drain */ +#define DA9062_PIN_GPO_PP 0x03 /* gpio out push-pull */ + struct da906x_device_data { int (*init)(struct da9063 *priv); }; @@ -104,6 +120,118 @@ static int da906x_reg_update(struct da9063 *priv, unsigned int reg, return 0; } +static inline struct da9063 *to_da9063(struct gpio_chip *chip) +{ + return container_of(chip, struct da9063, gpio); +} + +static int da9063_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct da9063 *priv = to_da9063(chip); + u8 mask, mode; + + mode = DA9062_PIN_GPI << DA9062_PIN_SHIFT(offset); + mask = DA9062AA_GPIO0_PIN_MASK << DA9062_PIN_SHIFT(offset); + + return da906x_reg_update(priv, DA9062AA_GPIO_0_1 + (offset >> 1), + mask, mode); +} + +static int da9063_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct da9063 *priv = to_da9063(chip); + + return da906x_reg_update(priv, DA9062AA_GPIO_MODE0_4, BIT(offset), + value << offset); +} + +static int da9063_gpio_get_pin_mode(struct da9063 *priv, unsigned offset) +{ + int ret; + u8 val; + + ret = i2c_read_reg(priv->client, DA9062AA_GPIO_0_1 + (offset >> 1), + &val, 1); + if (ret < 0) + return ret; + + val >>= DA9062_PIN_SHIFT(offset); + val &= DA9062AA_GPIO0_PIN_MASK; + + return val; +} + +static int da9063_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct da9063 *priv = to_da9063(chip); + int gpio_dir; + int ret; + u8 val; + + gpio_dir = da9063_gpio_get_pin_mode(priv, offset); + if (gpio_dir < 0) + return gpio_dir; + + switch (gpio_dir) { + case DA9062_PIN_ALTERNATE: + return -ENOTSUPP; + case DA9062_PIN_GPI: + ret = i2c_read_reg(priv->client, DA9062AA_STATUS_B, &val, 1); + if (ret < 0) + return ret; + break; + case DA9062_PIN_GPO_OD: + /* falltrough */ + case DA9062_PIN_GPO_PP: + ret = i2c_read_reg(priv->client, DA9062AA_GPIO_MODE0_4, &val, 1); + if (ret < 0) + return ret; + } + + return val & BIT(offset); +} + +static int da9063_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct da9063 *priv = to_da9063(chip); + int gpio_dir; + + gpio_dir = da9063_gpio_get_pin_mode(priv, offset); + if (gpio_dir < 0) + return gpio_dir; + + switch (gpio_dir) { + case DA9062_PIN_ALTERNATE: + return -ENOTSUPP; + case DA9062_PIN_GPI: + return 1; + case DA9062_PIN_GPO_OD: + /* falltrough */ + case DA9062_PIN_GPO_PP: + return 0; + } + + return -EINVAL; +} + +static void da9063_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct da9063 *priv = to_da9063(chip); + + da906x_reg_update(priv, DA9062AA_GPIO_MODE0_4, BIT(offset), + value << offset); + +} + +static struct gpio_ops da9063_gpio_ops = { + .direction_input = da9063_gpio_direction_input, + .direction_output = da9063_gpio_direction_output, + .get_direction = da9063_gpio_get_direction, + .get = da9063_gpio_get, + .set = da9063_gpio_set, +}; + static int da9063_watchdog_ping(struct da9063 *priv) { int ret; @@ -262,6 +390,17 @@ static int da9063_probe(struct device_d *dev) restart_handler_register(&priv->restart); + priv->gpio.base = -1; + priv->gpio.ngpio = 5; + priv->gpio.ops = &da9063_gpio_ops; + priv->gpio.dev = dev; + ret = gpiochip_add(&priv->gpio); + if (ret) + goto on_error; + + if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) + return of_platform_populate(dev->device_node, NULL, dev); + return 0; on_error: diff --git a/drivers/mfd/fintek-superio.c b/drivers/mfd/fintek-superio.c new file mode 100644 index 0000000000..60785bce27 --- /dev/null +++ b/drivers/mfd/fintek-superio.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Ahmad Fatoum, Pengutronix + */ + +#define pr_fmt(fmt) "fintek-superio: " fmt + +#include <superio.h> +#include <init.h> +#include <asm/io.h> +#include <common.h> + +#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ +#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ + +#define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */ + +#define SIO_F71808_ID 0x0901 +#define SIO_F71858_ID 0x0507 +#define SIO_F71862_ID 0x0601 +#define SIO_F71868_ID 0x1106 +#define SIO_F71869_ID 0x0814 +#define SIO_F71869A_ID 0x1007 +#define SIO_F71882_ID 0x0541 +#define SIO_F71889_ID 0x0723 +#define SIO_F71889A_ID 0x1005 +#define SIO_F81865_ID 0x0704 +#define SIO_F81866_ID 0x1010 + +static void superio_enter(u16 sioaddr) +{ + /* according to the datasheet the key must be sent twice! */ + outb(SIO_UNLOCK_KEY, sioaddr); + outb(SIO_UNLOCK_KEY, sioaddr); +} + +static void superio_exit(u16 sioaddr) +{ + outb(SIO_LOCK_KEY, sioaddr); +} + +static void fintek_superio_find(u16 sioaddr) +{ + struct superio_chip *chip; + u16 vid; + + superio_enter(sioaddr); + + vid = superio_inw(sioaddr, SIO_REG_MANID); + if (vid != SIO_FINTEK_ID) { + pr_debug("Not a Fintek device (port=0x%02x, vid=0x%04x)\n", + sioaddr, vid); + return; + } + + chip = xzalloc(sizeof(*chip)); + + chip->devid = superio_inw(sioaddr, SIO_REG_DEVID); + chip->vid = vid; + chip->sioaddr = sioaddr; + chip->enter = superio_enter; + chip->exit = superio_exit; + + superio_chip_add(chip); + + switch (chip->devid) { + case SIO_F71808_ID: + superio_func_add(chip, "f71808fg_wdt"); + break; + case SIO_F71862_ID: + superio_func_add(chip, "f71862fg_wdt"); + break; + case SIO_F71868_ID: + superio_func_add(chip, "f71868_wdt"); + break; + case SIO_F71869_ID: + superio_func_add(chip, "f71869_wdt"); + superio_func_add(chip, "gpio-f71869"); + break; + case SIO_F71869A_ID: + superio_func_add(chip, "f71869_wdt"); + superio_func_add(chip, "gpio-f71869a"); + break; + case SIO_F71882_ID: + superio_func_add(chip, "f71882fg_wdt"); + superio_func_add(chip, "gpio-f71882fg"); + break; + case SIO_F71889_ID: + superio_func_add(chip, "f71889fg_wdt"); + superio_func_add(chip, "gpio-f71889f"); + break; + case SIO_F71889A_ID: + superio_func_add(chip, "f71889fg_wdt"); + superio_func_add(chip, "gpio-f71889a"); + break; + case SIO_F71858_ID: + /* Confirmed (by datasheet) not to have a watchdog. */ + break; + case SIO_F81865_ID: + superio_func_add(chip, "f81865_wdt"); + break; + case SIO_F81866_ID: + superio_func_add(chip, "f81866_wdt"); + superio_func_add(chip, "gpio-f81866"); + break; + default: + pr_info("Unrecognized Fintek device: 0x%04x\n", chip->devid); + } + + superio_exit(sioaddr); +} + +static int fintek_superio_detect(void) +{ + fintek_superio_find(0x2e); + fintek_superio_find(0x4e); + + return 0; +} +coredevice_initcall(fintek_superio_detect); diff --git a/drivers/mfd/smsc-superio.c b/drivers/mfd/smsc-superio.c new file mode 100644 index 0000000000..349c878cef --- /dev/null +++ b/drivers/mfd/smsc-superio.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Ahmad Fatoum, Pengutronix + */ + +#define pr_fmt(fmt) "smsc-superio: " fmt + +#include <superio.h> +#include <init.h> +#include <asm/io.h> +#include <common.h> + +#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */ +#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ + +#define SMSC_ID 0x10b8 /* Standard Microsystems Corp PCI ID */ + +static void superio_enter(u16 sioaddr) +{ + outb(SIO_UNLOCK_KEY, sioaddr); + mdelay(1); + outb(SIO_UNLOCK_KEY, sioaddr); +} + +static void superio_exit(u16 sioaddr) +{ + outb(SIO_LOCK_KEY, sioaddr); +} + +static void smsc_superio_find(u16 sioaddr, u16 id_reg) +{ + struct superio_chip *chip; + u16 devid; + + superio_enter(sioaddr); + + devid = superio_inw(sioaddr, id_reg); + switch(devid >> 8) { + case 0x02: + case 0x03: + case 0x07: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0e: + case 0x14: + case 0x30: + case 0x40: + case 0x42: + case 0x43: + case 0x44: + case 0x46: + case 0x47: + case 0x4c: + case 0x4d: + case 0x51: + case 0x52: + case 0x54: + case 0x56: + case 0x57: + case 0x59: + case 0x5d: + case 0x5f: + case 0x60: + case 0x62: + case 0x67: + case 0x6b: + case 0x6e: + case 0x6f: + case 0x74: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7a: + case 0x7c: + case 0x7d: + case 0x7f: + case 0x81: + case 0x83: + case 0x85: + case 0x86: + case 0x89: + case 0x8c: + case 0x90: + break; + default: + pr_debug("Not a SMSC device (port=0x%02x, devid=0x%04x)\n", + sioaddr, devid); + return; + } + + chip = xzalloc(sizeof(*chip)); + + chip->devid = devid; + chip->vid = SMSC_ID; + chip->sioaddr = sioaddr; + chip->enter = superio_enter; + chip->exit = superio_exit; + + superio_chip_add(chip); + + superio_exit(sioaddr); +} + +static int smsc_superio_detect(void) +{ + u16 ports[] = { 0x2e, 0x4e, 0x162e, 0x164e, 0x3f0, 0x370 }; + + for (int i = 0; i < ARRAY_SIZE(ports); i++) + smsc_superio_find(ports[i], SIO_REG_DEVID); + + return 0; +} +coredevice_initcall(smsc_superio_detect); diff --git a/drivers/mfd/superio.c b/drivers/mfd/superio.c new file mode 100644 index 0000000000..0f08d56cb3 --- /dev/null +++ b/drivers/mfd/superio.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Ahmad Fatoum, Pengutronix + */ + +#define pr_fmt(fmt) "superio: " fmt + +#include <common.h> +#include <superio.h> +#include <regmap.h> + +struct device_d *superio_func_add(struct superio_chip *siochip, const char *name) +{ + struct device_d *dev; + int ret; + + dev = device_alloc(name, DEVICE_ID_DYNAMIC); + dev->parent = siochip->dev; + + + ret = platform_device_register(dev); + if (ret) + return NULL; + + return dev; +} +EXPORT_SYMBOL(superio_func_add) + +static int superio_reg_read(void *ctx, unsigned int reg, unsigned int *val) +{ + struct superio_chip *siochip = ctx; + + siochip->enter(siochip->sioaddr); + + *val = superio_inb(siochip->sioaddr, reg); + + siochip->exit(siochip->sioaddr); + + return 0; +} + +static int superio_reg_write(void *ctx, unsigned int reg, unsigned int val) +{ + struct superio_chip *siochip = ctx; + + siochip->enter(siochip->sioaddr); + + superio_outb(siochip->sioaddr, reg, val); + + siochip->exit(siochip->sioaddr); + + return 0; +} + +static struct regmap_bus superio_regmap_bus = { + .reg_write = superio_reg_write, + .reg_read = superio_reg_read, +}; + +static struct regmap_config superio_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .reg_stride = 1, + .max_register = 0xff, +}; + +void superio_chip_add(struct superio_chip *siochip) +{ + struct regmap *regmap; + char *chipname; + char str[5]; + int ret; + + chipname = xasprintf("superio-%04x:%04x@%02x", + siochip->vid, siochip->devid, siochip->sioaddr); + siochip->dev = add_generic_device(chipname, DEVICE_ID_SINGLE, NULL, + siochip->sioaddr, 2, IORESOURCE_IO, + NULL); + + siochip->dev->priv = siochip; + + sprintf(str, "%04x", siochip->vid); + dev_add_param_fixed(siochip->dev, "vendor", str); + sprintf(str, "%04x", siochip->devid); + dev_add_param_fixed(siochip->dev, "device", str); + + regmap = regmap_init(siochip->dev, &superio_regmap_bus, siochip, + &superio_regmap_config); + if (IS_ERR(regmap)) + pr_warn("creating %s regmap failed: %s\n", + chipname, strerror(-PTR_ERR(regmap))); + + ret = regmap_register_cdev(regmap, chipname); + if (ret) + pr_warn("registering %s regmap cdev failed: %s\n", + chipname, strerror(-ret)); +} +EXPORT_SYMBOL(superio_chip_add) diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c index c4d0cdfb57..83fa93b617 100644 --- a/drivers/mtd/nand/nand_omap_gpmc.c +++ b/drivers/mtd/nand/nand_omap_gpmc.c @@ -110,7 +110,6 @@ struct gpmc_nand_info { struct device_d *pdev; struct gpmc_nand_platform_data *pdata; struct nand_chip nand; - struct mtd_info minfo; int gpmc_cs; void *gpmc_command; void *gpmc_address; @@ -582,8 +581,8 @@ static int omap_gpmc_read_buf_manual(struct mtd_info *mtd, struct nand_chip *chi */ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) { - struct gpmc_nand_info *info = container_of(mtd, - struct gpmc_nand_info, minfo); + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct gpmc_nand_info *info = nand_chip->priv; u32 r_count = 0; u32 *p = (u32 *)buf; @@ -623,8 +622,9 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) static void omap_write_buf_pref(struct mtd_info *mtd, const u_char *buf, int len) { - struct gpmc_nand_info *info = container_of(mtd, - struct gpmc_nand_info, minfo); + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct gpmc_nand_info *info = nand_chip->priv; + u32 w_count = 0; u_char *buf1 = (u_char *)buf; u32 *p32 = (u32 *)buf; @@ -1021,8 +1021,8 @@ static int gpmc_read_page_hwecc(struct mtd_info *mtd, static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo, enum gpmc_ecc_mode mode) { - struct mtd_info *minfo = &oinfo->minfo; struct nand_chip *nand = &oinfo->nand; + struct mtd_info *minfo = &nand->mtd; int offset, err; int i, j; @@ -1116,7 +1116,7 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo, omap_oobinfo.eccpos[oinfo->nand.ecc.total - 1] + 1; err = elm_config(BCH16_ECC, - oinfo->minfo.writesize / nand->ecc.size, + minfo->writesize / nand->ecc.size, nand->ecc.size, nand->ecc.bytes); if (err < 0) return err; @@ -1199,7 +1199,7 @@ static int gpmc_nand_probe(struct device_d *pdev) nand = &oinfo->nand; nand->priv = (void *)oinfo; - minfo = &oinfo->minfo; + minfo = &nand->mtd; minfo->parent = pdev; if (pdata->cs >= GPMC_NUM_CS) { diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index 9a344082b7..ed2f13d14c 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -10,23 +10,6 @@ menuconfig MTD_UBI if MTD_UBI -config MTD_UBI_WL_THRESHOLD - int "UBI wear-leveling threshold" - default 4096 - range 2 65536 - help - This parameter defines the maximum difference between the highest - erase counter value and the lowest erase counter value of eraseblocks - of UBI devices. When this threshold is exceeded, UBI starts performing - wear leveling by means of moving data from eraseblock with low erase - counter to eraseblocks with high erase counter. - - The default value should be OK for SLC NAND flashes, NOR flashes and - other flashes which have eraseblock life-cycle 100000 or more. - However, in case of MLC NAND flashes which typically have eraseblock - life-cycle less than 10000, the threshold should be lessened (e.g., - to 128 or 256, although it does not have to be power of 2). - config MTD_UBI_BEB_LIMIT int "Maximum expected bad eraseblock count per 1024 eraseblocks" default 20 diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 493c778c3f..604fe87e53 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -655,7 +655,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, ubi->vol_count - UBI_INT_VOL_COUNT, UBI_INT_VOL_COUNT, ubi->vtbl_slots); ubi_msg(ubi, "max/mean erase counter: %d/%d, WL threshold: %d, image sequence number: %u", - ubi->max_ec, ubi->mean_ec, CONFIG_MTD_UBI_WL_THRESHOLD, + ubi->max_ec, ubi->mean_ec, UBI_WL_THRESHOLD, ubi->image_seq); ubi_msg(ubi, "available PEBs: %d, total reserved PEBs: %d, PEBs reserved for bad PEB handling: %d", ubi->avail_pebs, ubi->rsvd_pebs, ubi->beb_rsvd_pebs); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 922c1a3c8b..7d07bbf197 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -69,6 +69,17 @@ */ #define UBI_PROT_QUEUE_LEN 10 +/* + * Maximum difference between two erase counters. If this threshold is + * exceeded, the WL sub-system starts moving data from used physical + * eraseblocks with low erase counter to free physical eraseblocks with high + * erase counter. + * Extensive wear-leveling in the barebox can lead to stack overflows. Thus + * disable it by setting the threshold to the OS's max configurable value and + * leave wear-leveling to the OS. + */ +#define UBI_WL_THRESHOLD 65536 + /* The volume ID/LEB number/erase counter is unknown */ #define UBI_UNKNOWN -1 diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index cf90ecfb23..013ba3e1ff 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -101,14 +101,6 @@ #define WL_RESERVED_PEBS 1 /* - * Maximum difference between two erase counters. If this threshold is - * exceeded, the WL sub-system starts moving data from used physical - * eraseblocks with low erase counter to free physical eraseblocks with high - * erase counter. - */ -#define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD - -/* * When a physical eraseblock is moved, the WL sub-system has to pick the target * physical eraseblock to move to. The simplest way would be just to pick the * one with the highest erase counter. But in certain workloads this could lead diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index beeb4b8221..57f0b57d64 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -16,9 +16,6 @@ config HAS_DM9000 config HAS_MACB bool -config HAS_NETX_ETHER - bool - config PHYLIB bool @@ -180,11 +177,6 @@ config DRIVER_NET_MVNETA select PHYLIB select MDIO_MVEBU -config DRIVER_NET_NETX - bool "Hilscher Netx ethernet driver" - depends on HAS_NETX_ETHER - select PHYLIB - config DRIVER_NET_ORION bool "Marvell Orion SoC Ethernet" depends on ARCH_MVEBU diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 6ccd22cc10..f6a8213613 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -24,7 +24,6 @@ obj-$(CONFIG_DRIVER_NET_MACB) += macb.o obj-$(CONFIG_DRIVER_NET_MICREL) += ksz8864rmn.o obj-$(CONFIG_DRIVER_NET_MPC5200) += fec_mpc5200.o obj-$(CONFIG_DRIVER_NET_MVNETA) += mvneta.o -obj-$(CONFIG_DRIVER_NET_NETX) += netx_eth.o obj-$(CONFIG_DRIVER_NET_ORION) += orion-gbe.o obj-$(CONFIG_DRIVER_NET_RTL8139) += rtl8139.o obj-$(CONFIG_DRIVER_NET_RTL8169) += rtl8169.o diff --git a/drivers/net/ag71xx.c b/drivers/net/ag71xx.c index 0565c90490..70aaa60f1a 100644 --- a/drivers/net/ag71xx.c +++ b/drivers/net/ag71xx.c @@ -667,7 +667,7 @@ static void ag71xx_remove(struct device_d *dev) } static __maybe_unused struct of_device_id ag71xx_dt_ids[] = { - { .compatible = "qca,ar9331-ge0", .data = &ag71xx_cfg_ar9331_ge0, }, + { .compatible = "qca,ar9330-eth", .data = &ag71xx_cfg_ar9331_ge0, }, { .compatible = "qca,ar9344-gmac0", .data = &ag71xx_cfg_ar9344_gmac0, }, { /* sentinel */ } }; diff --git a/drivers/net/fec_imx.c b/drivers/net/fec_imx.c index 31c9102189..5ef1d4359e 100644 --- a/drivers/net/fec_imx.c +++ b/drivers/net/fec_imx.c @@ -781,7 +781,8 @@ static int fec_probe(struct device_d *dev) if (IS_ERR(fec->reg_phy)) { if (PTR_ERR(fec->reg_phy) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; - goto disable_clk; + fec->reg_phy = NULL; + goto release_res; } fec->reg_phy = NULL; } @@ -789,7 +790,7 @@ static int fec_probe(struct device_d *dev) ret = regulator_enable(fec->reg_phy); if (ret) { dev_err(dev, "Failed to enable phy regulator: %d\n", ret); - goto disable_clk; + goto release_res; } phy_reset = of_get_named_gpio(dev->device_node, "phy-reset-gpios", 0); diff --git a/drivers/net/netx_eth.c b/drivers/net/netx_eth.c deleted file mode 100644 index 64e9886d61..0000000000 --- a/drivers/net/netx_eth.c +++ /dev/null @@ -1,279 +0,0 @@ -#include <common.h> -#include <command.h> -#include <net.h> -#include <io.h> -#include <mach/netx-xc.h> -#include <mach/netx-eth.h> -#include <mach/netx-regs.h> -#include <xfuncs.h> -#include <init.h> -#include <driver.h> -#include <linux/phy.h> - -#define ETH_MAC_LOCAL_CONFIG 0x1560 -#define ETH_MAC_4321 0x1564 -#define ETH_MAC_65 0x1568 - -#define MAC_TRAFFIC_CLASS_ARRANGEMENT_SHIFT 16 -#define MAC_TRAFFIC_CLASS_ARRANGEMENT_MASK (0xf<<MAC_TRAFFIC_CLASS_ARRANGEMENT_SHIFT) -#define MAC_TRAFFIC_CLASS_ARRANGEMENT(x) (((x)<<MAC_TRAFFIC_CLASS_ARRANGEMENT_SHIFT) & MAC_TRAFFIC_CLASS_ARRANGEMENT_MASK) - -#define FIFO_PTR_FRAMELEN_SHIFT 0 -#define FIFO_PTR_FRAMELEN_MASK (0x7ff << 0) -#define FIFO_PTR_FRAMELEN(len) (((len) << 0) & FIFO_PTR_FRAMELEN_MASK) -#define FIFO_PTR_TIMETRIG (1<<11) -#define FIFO_PTR_MULTI_REQ -#define FIFO_PTR_ORIGIN (1<<14) -#define FIFO_PTR_VLAN (1<<15) -#define FIFO_PTR_FRAMENO_SHIFT 16 -#define FIFO_PTR_FRAMENO_MASK (0x3f << 16) -#define FIFO_PTR_FRAMENO(no) ( ((no) << 16) & FIFO_PTR_FRAMENO_MASK) -#define FIFO_PTR_SEGMENT_SHIFT 22 -#define FIFO_PTR_SEGMENT_MASK (0xf << 22) -#define FIFO_PTR_SEGMENT(seg) (((seg) & 0xf) << 22) -#define FIFO_PTR_ERROR_SHIFT 28 -#define FIFO_PTR_ERROR_MASK (0xf << 28) - -/* use sram 0 for now */ -#define SRAM_BASE(xcno) (0x8000 * (xcno)) - -/* XC Fifo Offsets */ -#define EMPTY_PTR_FIFO(xcno) (0 + ((xcno) << 3)) /* Index of the empty pointer FIFO */ -#define IND_FIFO_PORT_HI(xcno) (1 + ((xcno) << 3)) /* Index of the FIFO where received Data packages are indicated by XC */ -#define IND_FIFO_PORT_LO(xcno) (2 + ((xcno) << 3)) /* Index of the FIFO where received Data packages are indicated by XC */ -#define REQ_FIFO_PORT_HI(xcno) (3 + ((xcno) << 3)) /* Index of the FIFO where Data packages have to be indicated by ARM which shall be sent */ -#define REQ_FIFO_PORT_LO(xcno) (4 + ((xcno) << 3)) /* Index of the FIFO where Data packages have to be indicated by ARM which shall be sent */ -#define CON_FIFO_PORT_HI(xcno) (5 + ((xcno) << 3)) /* Index of the FIFO where sent Data packages are confirmed */ -#define CON_FIFO_PORT_LO(xcno) (6 + ((xcno) << 3)) /* Index of the FIFO where sent Data packages are confirmed */ - -struct netx_eth_priv { - struct mii_bus miibus; - int xcno; -}; - -static int netx_eth_send (struct eth_device *edev, - void *packet, int length) -{ - struct netx_eth_priv *priv = (struct netx_eth_priv *)edev->priv; - int xcno = priv->xcno; - unsigned int val; - int timeout = 500; - unsigned char *dst = (unsigned char *)(SRAM_BASE(xcno) + 1560); - - memcpy(dst, (void *)packet, length); - - if( length < 60 ) { - memset(dst + length, 0, 60 - length); - length = 60; - } - - PFIFO_REG(PFIFO_BASE(REQ_FIFO_PORT_LO(xcno))) = - FIFO_PTR_SEGMENT(xcno) | - FIFO_PTR_FRAMENO(1) | - FIFO_PTR_FRAMELEN(length); - - while (!PFIFO_REG( PFIFO_FILL_LEVEL(CON_FIFO_PORT_LO(xcno))) && timeout) { - timeout--; - udelay(100); - } -#if 0 - if (!timeout) { - loadxc(0); - loadxc(1); - eth_init(gd->bd); - return -1; - } -#endif - val = PFIFO_REG( PFIFO_BASE(CON_FIFO_PORT_LO(xcno)) ); - if((val & FIFO_PTR_ERROR_MASK) & 0x8) - printf("error sending frame: %u\n", val); - - return 0; -} - -static int netx_eth_rx (struct eth_device *edev) -{ - struct netx_eth_priv *priv = (struct netx_eth_priv *)edev->priv; - int xcno = priv->xcno; - unsigned int val, frameno, seg, len; - - if(!PFIFO_REG( PFIFO_FILL_LEVEL(IND_FIFO_PORT_LO(xcno)))) { - return 0; - } - - val = PFIFO_REG( PFIFO_BASE(IND_FIFO_PORT_LO(xcno)) ); - - frameno = (val & FIFO_PTR_FRAMENO_MASK) >> FIFO_PTR_FRAMENO_SHIFT; - seg = (val & FIFO_PTR_SEGMENT_MASK) >> FIFO_PTR_SEGMENT_SHIFT; - len = (val & FIFO_PTR_FRAMELEN_MASK) >> FIFO_PTR_FRAMELEN_SHIFT; - - /* get data */ - memcpy((void*)NetRxPackets[0], (void *)(SRAM_BASE(seg) + frameno * 1560), len); - /* pass to barebox */ - net_receive(edev, NetRxPackets[0], len); - - PFIFO_REG(PFIFO_BASE(EMPTY_PTR_FIFO(xcno))) = - FIFO_PTR_SEGMENT(seg) | - FIFO_PTR_FRAMENO(frameno); - return 0; -} - -static int netx_miibus_read(struct mii_bus *bus, int phy_addr, int reg) -{ - int value; - - MIIMU_REG = MIIMU_SNRDY | MIIMU_PREAMBLE | MIIMU_PHYADDR(phy_addr) | - MIIMU_REGADDR(reg) | MIIMU_PHY_NRES; - - while(MIIMU_REG & MIIMU_SNRDY); - - value = MIIMU_REG >> 16; - - debug("%s: addr: 0x%02x reg: 0x%02x val: 0x%04x\n", __func__, - phy_addr, reg, value); - - return value; -} - -static int netx_miibus_write(struct mii_bus *bus, int phy_addr, - int reg, u16 val) -{ - debug("%s: addr: 0x%02x reg: 0x%02x val: 0x%04x\n",__func__, - phy_addr, reg, val); - - MIIMU_REG = MIIMU_SNRDY | MIIMU_PREAMBLE | MIIMU_PHYADDR(phy_addr) | - MIIMU_REGADDR(reg) | MIIMU_PHY_NRES | MIIMU_OPMODE_WRITE | - MIIMU_DATA(val); - - while(MIIMU_REG & MIIMU_SNRDY); - - return 0; -} - -static int netx_eth_init_phy(void) -{ - unsigned int phy_control; - - phy_control = PHY_CONTROL_PHY_ADDRESS(0xe) | - PHY_CONTROL_PHY1_MODE(PHY_MODE_ALL) | - PHY_CONTROL_PHY1_AUTOMDIX | - PHY_CONTROL_PHY1_EN | - PHY_CONTROL_PHY0_MODE(PHY_MODE_ALL) | - PHY_CONTROL_PHY0_AUTOMDIX | - PHY_CONTROL_PHY0_EN | - PHY_CONTROL_CLK_XLATIN; - - /* enable asic control */ - SYSTEM_REG(SYSTEM_IOC_ACCESS_KEY) = SYSTEM_REG(SYSTEM_IOC_ACCESS_KEY); - - SYSTEM_REG(SYSTEM_PHY_CONTROL) = phy_control | PHY_CONTROL_RESET; - udelay(100); - - /* enable asic control */ - SYSTEM_REG(SYSTEM_IOC_ACCESS_KEY) = SYSTEM_REG(SYSTEM_IOC_ACCESS_KEY); - - SYSTEM_REG(SYSTEM_PHY_CONTROL) = phy_control; - - return 0; -} - -static int netx_eth_init_dev(struct eth_device *edev) -{ - struct netx_eth_priv *priv = (struct netx_eth_priv *)edev->priv; - int xcno = priv->xcno; - int i; - - loadxc(xcno); - - /* Fill empty pointer fifo */ - for (i = 2; i <= 18; i++) - PFIFO_REG( PFIFO_BASE(EMPTY_PTR_FIFO(xcno)) ) = FIFO_PTR_FRAMENO(i) | FIFO_PTR_SEGMENT(xcno); - - return 0; -} - -static int netx_eth_open(struct eth_device *edev) -{ - struct netx_eth_priv *priv = (struct netx_eth_priv *)edev->priv; - - return phy_device_connect(edev, &priv->miibus, 0, NULL, - 0, PHY_INTERFACE_MODE_NA); -} - -static void netx_eth_halt (struct eth_device *edev) -{ -} - -static int netx_eth_get_ethaddr(struct eth_device *edev, unsigned char *adr) -{ - /* FIXME: get from crypto flash */ - return -1; -} - -static int netx_eth_set_ethaddr(struct eth_device *edev, const unsigned char *adr) -{ - struct netx_eth_priv *priv = (struct netx_eth_priv *)edev->priv; - int xcno = priv->xcno; - - debug("%s\n", __func__); - - /* set MAC address */ - XMAC_REG(xcno, XMAC_RPU_HOLD_PC) = RPU_HOLD_PC; - XMAC_REG(xcno, XMAC_TPU_HOLD_PC) = TPU_HOLD_PC; - XPEC_REG(xcno, XPEC_XPU_HOLD_PC) = XPU_HOLD_PC; - XPEC_REG(xcno, XPEC_RAM_START + ETH_MAC_4321) = adr[0] | adr[1]<<8 | adr[2]<<16 | adr[3]<<24; - XPEC_REG(xcno, XPEC_RAM_START + ETH_MAC_65) = adr[4] | adr[5]<<8; - XPEC_REG(xcno, XPEC_RAM_START + ETH_MAC_LOCAL_CONFIG) = MAC_TRAFFIC_CLASS_ARRANGEMENT(8); - XMAC_REG(xcno, XMAC_RPU_HOLD_PC) = 0; - XMAC_REG(xcno, XMAC_TPU_HOLD_PC) = 0; - XPEC_REG(xcno, XPEC_XPU_HOLD_PC) = 0; - -#if 0 - for (i = 0; i < 5; i++) - printf ("%02x:", adr[i]); - printf ("%02x\n", adr[5]); -#endif - return -0; -} - -static int netx_eth_probe(struct device_d *dev) -{ - struct eth_device *edev; - struct netx_eth_priv *priv; - struct netx_eth_platform_data *pdata; - - debug("netx_eth_probe()\n"); - - pdata = dev->platform_data; - - edev = xzalloc(sizeof(struct eth_device) + sizeof(struct netx_eth_priv)); - edev->priv = (struct netx_priv *)(edev + 1); - - priv = edev->priv; - priv->xcno = pdata->xcno; - - edev->init = netx_eth_init_dev; - edev->open = netx_eth_open; - edev->send = netx_eth_send; - edev->recv = netx_eth_rx; - edev->halt = netx_eth_halt; - edev->get_ethaddr = netx_eth_get_ethaddr; - edev->set_ethaddr = netx_eth_set_ethaddr; - edev->parent = dev; - - priv->miibus.read = netx_miibus_read; - priv->miibus.write = netx_miibus_write; - priv->miibus.parent = dev; - - netx_eth_init_phy(); - mdiobus_register(&priv->miibus); - eth_register(edev); - - return 0; -} - -static struct driver_d netx_eth_driver = { - .name = "netx-eth", - .probe = netx_eth_probe, -}; -device_platform_driver(netx_eth_driver); diff --git a/drivers/net/phy/mv88e6xxx/Makefile b/drivers/net/phy/mv88e6xxx/Makefile index e09ea0aa47..e1d4b1b9d7 100644 --- a/drivers/net/phy/mv88e6xxx/Makefile +++ b/drivers/net/phy/mv88e6xxx/Makefile @@ -1,5 +1,6 @@ obj-y += mv88e6xxx.o mv88e6xxx-objs := chip.o +mv88e6xxx-objs += global1.o mv88e6xxx-objs += global2.o mv88e6xxx-objs += port.o diff --git a/drivers/net/phy/mv88e6xxx/chip.c b/drivers/net/phy/mv88e6xxx/chip.c index 9688dbd1be..b1bffe5cbc 100644 --- a/drivers/net/phy/mv88e6xxx/chip.c +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -11,6 +11,7 @@ #include <of_gpio.h> #include "chip.h" +#include "global1.h" #include "global2.h" #include "port.h" @@ -342,6 +343,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6085", .num_ports = 10, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6085_ops, }, @@ -352,6 +354,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6095/88E6095F", .num_ports = 11, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6095_ops, }, @@ -362,6 +365,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6097/88E6097F", .num_ports = 11, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6097_ops, }, @@ -372,6 +376,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6123", .num_ports = 3, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6123_ops, }, @@ -382,6 +387,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6131", .num_ports = 8, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6131_ops, }, @@ -392,6 +398,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6341", .num_ports = 6, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6141_ops, }, @@ -402,6 +409,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6161", .num_ports = 6, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6161_ops, }, @@ -412,6 +420,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6165", .num_ports = 6, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6165_ops, }, @@ -422,6 +431,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6171", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6171_ops, }, @@ -432,6 +442,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6172", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6172_ops, }, @@ -442,6 +453,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6175", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6175_ops, }, @@ -452,6 +464,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6176", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6176_ops, }, @@ -462,6 +475,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6185", .num_ports = 10, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6185_ops, }, @@ -472,6 +486,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6190", .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6190_ops, }, @@ -482,6 +497,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6190X", .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6190x_ops, }, @@ -492,6 +508,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6191", .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6191_ops, }, @@ -502,6 +519,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6240", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6240_ops, }, @@ -512,6 +530,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6290", .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6290_ops, }, @@ -522,6 +541,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6320", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6320_ops, }, @@ -532,6 +552,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6321", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6321_ops, }, @@ -542,6 +563,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6341", .num_ports = 6, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6341_ops, }, @@ -552,6 +574,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6350", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6350_ops, }, @@ -562,6 +585,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6351", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6351_ops, }, @@ -572,6 +596,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6352", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6352_ops, }, @@ -582,6 +607,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6390", .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6390_ops, }, @@ -592,6 +618,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6390X", .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6390x_ops, }, @@ -741,6 +768,8 @@ static void mv88e6xxx_hardware_reset(struct mv88e6xxx_chip *chip) mv88e6xxx_hardware_reset_delay(); gpio_set_active(chip->reset, 0); mv88e6xxx_hardware_reset_delay(); + + mv88e6xxx_g1_wait_eeprom_done(chip); } } @@ -797,7 +826,7 @@ static int mv88e6xxx_probe(struct device_d *dev) err = of_property_read_u32(np, "reg", ®); if (err) { - dev_err(dev, "Couldn't determine switch MIDO address\n"); + dev_err(dev, "Couldn't determine switch MDIO address\n"); return err; } @@ -836,6 +865,11 @@ static int mv88e6xxx_probe(struct device_d *dev) */ mv88e6xxx_hardware_reset_delay(); } + /* + * Switch will not return valid data over MDIO until EEPROM is + * loaded + */ + mv88e6xxx_g1_wait_eeprom_done(chip); err = mv88e6xxx_detect(chip); if (err) diff --git a/drivers/net/phy/mv88e6xxx/chip.h b/drivers/net/phy/mv88e6xxx/chip.h index 7548358de0..57f74a39a0 100644 --- a/drivers/net/phy/mv88e6xxx/chip.h +++ b/drivers/net/phy/mv88e6xxx/chip.h @@ -34,6 +34,7 @@ struct mv88e6xxx_info { const char *name; unsigned int num_ports; unsigned int port_base_addr; + unsigned int global1_addr; unsigned int global2_addr; const struct mv88e6xxx_ops *ops; diff --git a/drivers/net/phy/mv88e6xxx/global1.c b/drivers/net/phy/mv88e6xxx/global1.c new file mode 100644 index 0000000000..bace5396e9 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/global1.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Marvell 88E6xxx Switch Global (1) Registers support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2016-2017 Savoir-faire Linux Inc. + * Vivien Didelot <vivien.didelot@savoirfairelinux.com> + */ + +#include <clock.h> +#include <linux/bitfield.h> + +#include "chip.h" +#include "global1.h" + +static int mv88e6xxx_g1_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) +{ + int addr = chip->info->global1_addr; + + return mv88e6xxx_read(chip, addr, reg, val); +} + +void mv88e6xxx_g1_wait_eeprom_done(struct mv88e6xxx_chip *chip) +{ + const uint64_t start = get_time_ns(); + const uint64_t timeout = SECOND; + u16 val; + int err; + + /* Wait up to 1 second for the switch to finish reading the + * EEPROM. + */ + while (!is_timeout(start, timeout)) { + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &val); + if (err) { + dev_err(chip->dev, "Error reading status\n"); + return; + } + + if (val != 0xFFFF && /* switch will return 0xffff until + * EEPROM is loaded + */ + val & BIT(MV88E6XXX_G1_STS_IRQ_EEPROM_DONE)) + return; + + mdelay(2); + } + + dev_err(chip->dev, "Timeout waiting for EEPROM done\n"); +} diff --git a/drivers/net/phy/mv88e6xxx/global1.h b/drivers/net/phy/mv88e6xxx/global1.h new file mode 100644 index 0000000000..a505bae2bf --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/global1.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Marvell 88E6xxx Switch Global (1) Registers support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2016-2017 Savoir-faire Linux Inc. + * Vivien Didelot <vivien.didelot@savoirfairelinux.com> + */ + +#ifndef _MV88E6XXX_GLOBAL1_H +#define _MV88E6XXX_GLOBAL1_H + +#include "chip.h" + +/* Offset 0x00: Switch Global Status Register */ +#define MV88E6XXX_G1_STS 0x00 +#define MV88E6352_G1_STS_PPU_STATE 0x8000 +#define MV88E6185_G1_STS_PPU_STATE_MASK 0xc000 +#define MV88E6185_G1_STS_PPU_STATE_DISABLED_RST 0x0000 +#define MV88E6185_G1_STS_PPU_STATE_INITIALIZING 0x4000 +#define MV88E6185_G1_STS_PPU_STATE_DISABLED 0x8000 +#define MV88E6185_G1_STS_PPU_STATE_POLLING 0xc000 +#define MV88E6XXX_G1_STS_INIT_READY 0x0800 +#define MV88E6XXX_G1_STS_IRQ_AVB 8 +#define MV88E6XXX_G1_STS_IRQ_DEVICE 7 +#define MV88E6XXX_G1_STS_IRQ_STATS 6 +#define MV88E6XXX_G1_STS_IRQ_VTU_PROB 5 +#define MV88E6XXX_G1_STS_IRQ_VTU_DONE 4 +#define MV88E6XXX_G1_STS_IRQ_ATU_PROB 3 +#define MV88E6XXX_G1_STS_IRQ_ATU_DONE 2 +#define MV88E6XXX_G1_STS_IRQ_TCAM_DONE 1 +#define MV88E6XXX_G1_STS_IRQ_EEPROM_DONE 0 + +void mv88e6xxx_g1_wait_eeprom_done(struct mv88e6xxx_chip *chip); + +#endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 25924872ef..06e1414769 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -57,9 +57,14 @@ int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset, static ssize_t nvmem_cdev_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, unsigned long flags) { - struct nvmem_device *nvmem = container_of(cdev, struct nvmem_device, cdev); + struct nvmem_device *nvmem; ssize_t retlen; + if (cdev->master) + nvmem = container_of(cdev->master, struct nvmem_device, cdev); + else + nvmem = container_of(cdev, struct nvmem_device, cdev); + dev_dbg(cdev->dev, "read ofs: 0x%08llx count: 0x%08zx\n", offset, count); @@ -71,9 +76,14 @@ static ssize_t nvmem_cdev_read(struct cdev *cdev, void *buf, size_t count, static ssize_t nvmem_cdev_write(struct cdev *cdev, const void *buf, size_t count, loff_t offset, unsigned long flags) { - struct nvmem_device *nvmem = container_of(cdev, struct nvmem_device, cdev); + struct nvmem_device *nvmem; ssize_t retlen; + if (cdev->master) + nvmem = container_of(cdev->master, struct nvmem_device, cdev); + else + nvmem = container_of(cdev, struct nvmem_device, cdev); + dev_dbg(cdev->dev, "write ofs: 0x%08llx count: 0x%08zx\n", offset, count); diff --git a/drivers/nvmem/ocotp.c b/drivers/nvmem/ocotp.c index 3f9f162860..79693e22e0 100644 --- a/drivers/nvmem/ocotp.c +++ b/drivers/nvmem/ocotp.c @@ -29,6 +29,8 @@ #include <regmap.h> #include <linux/clk.h> #include <mach/ocotp.h> +#include <machine_id.h> +#include <mach/ocotp-fusemap.h> #include <linux/nvmem-provider.h> /* @@ -77,6 +79,7 @@ #define MAC_OFFSET_1 (0x24 * 4) #define MAX_MAC_OFFSETS 2 #define MAC_BYTES 8 +#define UNIQUE_ID_NUM 2 enum imx_ocotp_format_mac_direction { OCOTP_HW_TO_MAC, @@ -548,6 +551,19 @@ static int imx_ocotp_read(struct device_d *dev, const int offset, void *val, return regmap_bulk_read(priv->map, offset, val, bytes); } +static void imx_ocotp_set_unique_machine_id(void) +{ + uint32_t unique_id_parts[UNIQUE_ID_NUM]; + int i; + + for (i = 0; i < UNIQUE_ID_NUM; i++) + if (imx_ocotp_read_field(OCOTP_UNIQUE_ID(i), + &unique_id_parts[i])) + return; + + machine_id_set_hashable(unique_id_parts, sizeof(unique_id_parts)); +} + static const struct nvmem_bus imx_ocotp_nvmem_bus = { .write = imx_ocotp_write, .read = imx_ocotp_read, @@ -633,6 +649,9 @@ static int imx_ocotp_probe(struct device_d *dev) ethaddr->value, ethaddr); } + if (IS_ENABLED(CONFIG_MACHINE_ID)) + imx_ocotp_set_unique_machine_id(); + imx_ocotp_init_dt(priv); dev_add_param_bool(&(priv->dev), "sense_enable", NULL, NULL, &priv->sense_enable, priv); diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index e2fb0af756..95c6708f4a 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -17,6 +17,16 @@ config PINCTRL_AT91 help The pinmux controller found on AT91 SoCs. +config PINCTRL_AT91PIO4 + bool "AT91 PIO4 pinctrl driver" + depends on OFDEVICE + depends on ARCH_AT91 + select GPIOLIB + select OF_GPIO + help + Say Y here to enable the at91 pinctrl/gpio driver for Atmel PIO4 + controller available on sama5d2 SoC. + config PINCTRL_BCM283X bool "GPIO and pinmux support for BCM283X" depends on ARCH_BCM283X diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index e311df7103..e7d8ad8f4b 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_PINCTRL) += pinctrl.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o +obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o obj-$(CONFIG_PINCTRL_BCM283X) += pinctrl-bcm2835.o obj-pbl-$(CONFIG_PINCTRL_IMX_IOMUX_V1) += imx-iomux-v1.o obj-$(CONFIG_PINCTRL_IMX_IOMUX_V2) += imx-iomux-v2.o diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c new file mode 100644 index 0000000000..9bc259f84c --- /dev/null +++ b/drivers/pinctrl/pinctrl-at91-pio4.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sama5d2 pin control and GPIO chip driver + * Copyright (c) 2019 Ahmad Fatoum, Pengutronix + */ + +#include <common.h> +#include <init.h> +#include <io.h> +#include <of.h> +#include <pinctrl.h> +#include <malloc.h> +#include <gpio.h> +#include <mach/gpio.h> +#include <linux/clk.h> + +#include <dt-bindings/pinctrl/at91.h> + +#define ATMEL_GET_PIN_NO(pinfunc) ((pinfunc) & 0xff) +#define ATMEL_GET_PIN_FUNC(pinfunc) ((pinfunc >> 16) & 0xf) +#define ATMEL_GET_PIN_IOSET(pinfunc) ((pinfunc >> 20) & 0xf) + +struct pinctrl_at91_pio4 { + void __iomem *base; + struct pinctrl_device pinctrl; + struct gpio_chip gpiochip; +}; + +struct at91_pinctrl_data { + unsigned nbanks; +}; + +static inline void __iomem *pin_to_pio4(struct pinctrl_device *pdev, + unsigned int *pin) +{ + void __iomem *pio; + struct pinctrl_at91_pio4 *pinctrl = + container_of(pdev, struct pinctrl_at91_pio4, pinctrl); + + pio = pinctrl->base + (*pin / 32) * 0x40; + *pin %= 32; + + return pio; +} + +static int __pinctrl_at91_pio4_set_state(struct pinctrl_device *pdev, + struct device_node *np) +{ + u32 drive_strength, enable = 0, disable = ~0; + int output = -1; + + int npins, i; + int ret; + + ret = of_property_read_u32(np, "drive-strength", &drive_strength); + if (!ret && ATMEL_PIO_DRVSTR_LO <= drive_strength + && drive_strength <= ATMEL_PIO_DRVSTR_HI) { + disable &= ~PIO4_DRVSTR_MASK; + enable |= drive_strength << PIO4_DRVSTR_OFFSET; + } + + if (of_get_property(np, "bias-disable", NULL)) { + disable &= ~PIO4_PUEN_MASK; + disable &= ~PIO4_PDEN_MASK; + } + + if (of_get_property(np, "bias-pull-up", NULL)) { + enable |= PIO4_PUEN_MASK; + disable &= ~PIO4_PDEN_MASK; + } + + if (of_get_property(np, "bias-pull-down", NULL)) { + enable |= PIO4_PDEN_MASK; + disable &= ~PIO4_PUEN_MASK; + } + + if (of_get_property(np, "drive-open-drain", NULL)) + enable |= PIO4_OPD_MASK; + + if (of_get_property(np, "input-schmitt-enable", NULL)) + enable |= PIO4_SCHMITT_MASK; + + if (of_get_property(np, "input-enable", NULL)) + disable &= ~PIO4_DIR_MASK; + + if (of_get_property(np, "output-enable", NULL)) + enable |= PIO4_DIR_MASK; + + if (of_get_property(np, "output-low", NULL)) + output = GPIOF_OUT_INIT_LOW; + + if (of_get_property(np, "output-high", NULL)) + output = GPIOF_OUT_INIT_HIGH; + + of_get_property(np, "pinmux", &npins); + npins /= sizeof(__be32); + + if (!npins) { + dev_err(pdev->dev, "Invalid pinmux property in %s\n", + np->full_name); + return -EINVAL; + } + + for (i = 0; i < npins; i++) { + void __iomem *pio; + u32 conf, no, func, cfgr; + + ret = of_property_read_u32_index(np, "pinmux", i, &conf); + if (ret) + return ret; + + no = ATMEL_GET_PIN_NO(conf); + func = ATMEL_GET_PIN_FUNC(conf); + + pio = pin_to_pio4(pdev, &no); + + if (output == GPIOF_OUT_INIT_HIGH) + at91_mux_gpio4_set(pio, BIT(no), true); + + if (output == GPIOF_OUT_INIT_LOW) + at91_mux_gpio4_set(pio, BIT(no), false); + + writel(BIT(no), pio + PIO4_MSKR); + cfgr = readl(pio + PIO4_CFGR); + cfgr &= disable; + cfgr |= enable; + writel(func | cfgr, pio + PIO4_CFGR); + } + + return 0; +} + +static int pinctrl_at91_pio4_set_state(struct pinctrl_device *pdev, + struct device_node *np) +{ + struct device_node *child; + void *prop; + int ret; + + prop = of_find_property(np, "pinmux", NULL); + if (prop) + return __pinctrl_at91_pio4_set_state(pdev, np); + + for_each_child_of_node(np, child) { + ret = __pinctrl_at91_pio4_set_state(pdev, child); + if (ret) + return ret; + } + + return 0; +} + +static inline void __iomem *pin_to_gpio4(struct gpio_chip *chip, unsigned int *pin) +{ + void __iomem *gpio; + struct pinctrl_at91_pio4 *pinctrl = + container_of(chip, struct pinctrl_at91_pio4, gpiochip); + + gpio = pinctrl->base + (*pin / 32) * 0x40; + *pin %= 32; + + return gpio; +} + +static int at91_gpio4_direction_input(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *gpio = pin_to_gpio4(chip, &offset); + + at91_mux_gpio4_input(gpio, BIT(offset), true); + + return 0; +} + +static int at91_gpio4_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + void __iomem *gpio = pin_to_gpio4(chip, &offset); + + at91_mux_gpio4_set(gpio, BIT(offset), value); + at91_mux_gpio4_input(gpio, BIT(offset), false); + + return 0; +} + +static int at91_gpio4_request(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *gpio = pin_to_gpio4(chip, &offset); + + at91_mux_gpio4_enable(gpio, BIT(offset)); + + return 0; +} + +static int at91_gpio4_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + u32 cfgr; + void __iomem *gpio = pin_to_gpio4(chip, &offset); + + writel(BIT(offset), gpio + PIO4_MSKR); + cfgr = readl(gpio + PIO4_CFGR); + + return cfgr & PIO4_DIR_MASK ? GPIOF_DIR_OUT : GPIOF_DIR_IN; +} + +static void at91_gpio4_set(struct gpio_chip *chip, unsigned offset, int value) +{ + void __iomem *gpio = pin_to_gpio4(chip, &offset); + + at91_mux_gpio4_set(gpio, BIT(offset), value); +} + +static int at91_gpio4_get(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *gpio = pin_to_gpio4(chip, &offset); + + return at91_mux_gpio4_get(gpio, BIT(offset)); +} + +static struct gpio_ops at91_gpio4_ops = { + .request = at91_gpio4_request, + .direction_input = at91_gpio4_direction_input, + .direction_output = at91_gpio4_direction_output, + .get_direction = at91_gpio4_get_direction, + .get = at91_gpio4_get, + .set = at91_gpio4_set, +}; + +static int pinctrl_at91_pio4_gpiochip_add(struct device_d *dev, + struct pinctrl_at91_pio4 *pinctrl) +{ + struct at91_pinctrl_data *drvdata; + struct clk *clk; + int ret; + + clk = clk_get(dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(dev, "clock not found: %d\n", ret); + return ret; + } + + ret = clk_enable(clk); + if (ret < 0) { + dev_err(dev, "clock failed to enable: %d\n", ret); + clk_put(clk); + return ret; + } + + dev_get_drvdata(dev, (const void **)&drvdata); + + pinctrl->gpiochip.ops = &at91_gpio4_ops; + pinctrl->gpiochip.base = 0; + pinctrl->gpiochip.ngpio = drvdata->nbanks * MAX_NB_GPIO_PER_BANK; + pinctrl->gpiochip.dev = dev; + + ret = gpiochip_add(&pinctrl->gpiochip); + if (ret) { + dev_err(dev, "couldn't add gpiochip, ret = %d\n", ret); + return ret; + } + + dev_info(dev, "gpio driver registered\n"); + + return 0; +} + +static struct pinctrl_ops pinctrl_at91_pio4_ops = { + .set_state = pinctrl_at91_pio4_set_state, +}; + +static int pinctrl_at91_pio4_probe(struct device_d *dev) +{ + struct device_node *np = dev->device_node; + struct pinctrl_at91_pio4 *pinctrl; + struct resource *io; + int ret; + + pinctrl = xzalloc(sizeof(*pinctrl)); + + io = dev_request_mem_resource(dev, 0); + if (IS_ERR(io)) + return PTR_ERR(io); + + pinctrl->base = IOMEM(io->start); + pinctrl->pinctrl.dev = dev; + pinctrl->pinctrl.ops = &pinctrl_at91_pio4_ops; + + ret = pinctrl_register(&pinctrl->pinctrl); + if (ret) + return ret; + + dev_info(dev, "pinctrl driver registered\n"); + + if (of_get_property(np, "gpio-controller", NULL)) + return pinctrl_at91_pio4_gpiochip_add(dev, pinctrl); + + return 0; +} + +static const struct at91_pinctrl_data sama5d2_pinctrl_data = { + .nbanks = 4, +}; + +static __maybe_unused struct of_device_id pinctrl_at91_pio4_dt_ids[] = { + { .compatible = "atmel,sama5d2-pinctrl", .data = &sama5d2_pinctrl_data }, + { /* sentinel */ } +}; + +static struct driver_d pinctrl_at91_pio4_driver = { + .name = "pinctrl-at91-pio4", + .probe = pinctrl_at91_pio4_probe, + .of_compatible = DRV_OF_COMPAT(pinctrl_at91_pio4_dt_ids), +}; + +static int pinctrl_at91_pio4_init(void) +{ + return platform_driver_register(&pinctrl_at91_pio4_driver); +} +core_initcall(pinctrl_at91_pio4_init); diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 9b366e4812..0da6332720 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -152,6 +152,8 @@ int at91_mux_pin(unsigned pin, enum at91_mux mux, int use_pullup) return -EINVAL; at91_gpio->ops->mux_D_periph(pio, mask); break; + default: + return -EINVAL; } if (mux) at91_mux_gpio_disable(pio, mask); diff --git a/drivers/regulator/pfuze.c b/drivers/regulator/pfuze.c index dc41e8f55b..1950ffb04c 100644 --- a/drivers/regulator/pfuze.c +++ b/drivers/regulator/pfuze.c @@ -161,16 +161,11 @@ static const struct regmap_config pfuze_regmap_i2c_config = { static int __init pfuze_probe(struct device_d *dev) { - struct pfuze_devtype *devtype; int ret; if (pfuze_dev) return -EBUSY; - ret = dev_get_drvdata(dev, (const void **)&devtype); - if (ret) - return ret; - pfuze_dev = xzalloc(sizeof(*pfuze_dev)); pfuze_dev->dev = dev; @@ -192,26 +187,19 @@ static int __init pfuze_probe(struct device_d *dev) return 0; } -static struct pfuze_devtype pfuze100_devtype = { -}; - -static struct pfuze_devtype pfuze200_devtype = { -}; - -static struct pfuze_devtype pfuze3000_devtype = { -}; - static struct platform_device_id pfuze_ids[] = { - { .name = "pfuze100", .driver_data = (ulong)&pfuze100_devtype, }, - { .name = "pfuze200", .driver_data = (ulong)&pfuze200_devtype, }, - { .name = "pfuze3000", .driver_data = (ulong)&pfuze3000_devtype, }, + { .name = "pfuze100" }, + { .name = "pfuze200" }, + { .name = "pfuze3000" }, + { .name = "pfuze3001" }, { } }; static __maybe_unused struct of_device_id pfuze_dt_ids[] = { - { .compatible = "fsl,pfuze100", .data = &pfuze100_devtype, }, - { .compatible = "fsl,pfuze200", .data = &pfuze200_devtype, }, - { .compatible = "fsl,pfuze3000", .data = &pfuze3000_devtype, }, + { .compatible = "fsl,pfuze100" }, + { .compatible = "fsl,pfuze200" }, + { .compatible = "fsl,pfuze3000" }, + { .compatible = "fsl,pfuze3001" }, { } }; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index f12ff93f6a..7a411d456e 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -50,11 +50,6 @@ config DRIVER_SERIAL_AUART depends on ARCH_MXS bool "i.MX23/i.MX28 application UART serial driver" -config DRIVER_SERIAL_NETX - depends on ARCH_NETX - default y - bool "Netx serial driver" - config DRIVER_SERIAL_LINUX_CONSOLE depends on LINUX default y @@ -69,11 +64,6 @@ config DRIVER_SERIAL_MPC5XXX default y bool "MPC5200 serial driver" -config DRIVER_SERIAL_BLACKFIN - depends on BLACKFIN - default y - bool "Blackfin serial driver" - config DRIVER_SERIAL_CLPS711X depends on ARCH_CLPS711X default y diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 4174cc1ffb..6f9e3b7835 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -5,10 +5,8 @@ obj-$(CONFIG_DRIVER_SERIAL_EFI) += serial_efi.o obj-$(CONFIG_DRIVER_SERIAL_IMX) += serial_imx.o obj-$(CONFIG_DRIVER_SERIAL_STM378X) += stm-serial.o obj-$(CONFIG_DRIVER_SERIAL_ATMEL) += atmel.o -obj-$(CONFIG_DRIVER_SERIAL_NETX) += serial_netx.o obj-$(CONFIG_DRIVER_SERIAL_LINUX_CONSOLE) += linux_console.o obj-$(CONFIG_DRIVER_SERIAL_MPC5XXX) += serial_mpc5xxx.o -obj-$(CONFIG_DRIVER_SERIAL_BLACKFIN) += serial_blackfin.o obj-$(CONFIG_DRIVER_SERIAL_CLPS711X) += serial_clps711x.o obj-$(CONFIG_DRIVER_SERIAL_NS16550) += serial_ns16550.o obj-$(CONFIG_DRIVER_SERIAL_PL010) += serial_pl010.o diff --git a/drivers/serial/serial_blackfin.c b/drivers/serial/serial_blackfin.c deleted file mode 100644 index 2122226734..0000000000 --- a/drivers/serial/serial_blackfin.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * (C) Copyright 2005 - * Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <common.h> -#include <driver.h> -#include <init.h> -#include <malloc.h> -#include <io.h> -#include <asm/blackfin.h> - -#define UART_IER_ERBFI 0x01 -#define UART_IER_ETBEI 0x02 -#define UART_IER_ELSI 0x04 -#define UART_IER_EDDSI 0x08 - -#define UART_IIR_NOINT 0x01 -#define UART_IIR_STATUS 0x06 -#define UART_IIR_LSR 0x06 -#define UART_IIR_RBR 0x04 -#define UART_IIR_THR 0x02 -#define UART_IIR_MSR 0x00 - -#define UART_LCR_WLS5 0 -#define UART_LCR_WLS6 0x01 -#define UART_LCR_WLS7 0x02 -#define UART_LCR_WLS8 0x03 -#define UART_LCR_STB 0x04 -#define UART_LCR_PEN 0x08 -#define UART_LCR_EPS 0x10 -#define UART_LCR_SP 0x20 -#define UART_LCR_SB 0x40 -#define UART_LCR_DLAB 0x80 - -#define UART_LSR_DR 0x01 -#define UART_LSR_OE 0x02 -#define UART_LSR_PE 0x04 -#define UART_LSR_FE 0x08 -#define UART_LSR_BI 0x10 -#define UART_LSR_THRE 0x20 -#define UART_LSR_TEMT 0x40 - -#define UART_GCTL_UCEN 0x01 - -static int blackfin_serial_setbaudrate(struct console_device *cdev, int baudrate) -{ - int divisor, oldlcr; - - oldlcr = readw(UART_LCR); - - divisor = (get_sclk() + (baudrate * 0)) / (baudrate * 16); - - /* Set DLAB in LCR to Access DLL and DLH */ - writew(UART_LCR_DLAB, UART_LCR); - - writew(divisor & 0xff, UART_DLL); - writew((divisor >> 8) & 0xff, UART_DLH); - - /* Clear DLAB in LCR to Access THR RBR IER */ - writew(oldlcr, UART_LCR); - - return 0; -} - -static int blackfin_serial_init_port(struct console_device *cdev) -{ - /* Enable UART */ - writew(UART_GCTL_UCEN, UART_GCTL); - - /* Set LCR to Word Lengh 8-bit word select */ - writew(UART_LCR_WLS8, UART_LCR); - - return 0; -} - -static void blackfin_serial_putc(struct console_device *cdev, char c) -{ - while (!(readw(UART_LSR) & UART_LSR_TEMT)); - - writew(c, UART_THR); -} - -static int blackfin_serial_getc(struct console_device *cdev) -{ - while (!(readw(UART_LSR) & UART_LSR_DR)); - - return readw(UART_RBR); -} - -static int blackfin_serial_tstc(struct console_device *cdev) -{ - return (readw(UART_LSR) & UART_LSR_DR) ? 1 : 0; -} - -static int blackfin_serial_probe(struct device_d *dev) -{ - struct console_device *cdev; - - cdev = xzalloc(sizeof(struct console_device)); - cdev->dev = dev; - cdev->tstc = blackfin_serial_tstc; - cdev->putc = blackfin_serial_putc; - cdev->getc = blackfin_serial_getc; - cdev->setbrg = blackfin_serial_setbaudrate; - - blackfin_serial_init_port(cdev); - - console_register(cdev); - - return 0; -} - -static struct driver_d blackfin_serial_driver = { - .name = "blackfin_serial", - .probe = blackfin_serial_probe, -}; -console_platform_driver(blackfin_serial_driver); diff --git a/drivers/serial/serial_netx.c b/drivers/serial/serial_netx.c deleted file mode 100644 index 55ed89bf92..0000000000 --- a/drivers/serial/serial_netx.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * (C) Copyright 2005 - * Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <common.h> -#include <mach/netx-regs.h> -#include <driver.h> -#include <init.h> -#include <malloc.h> -#include <io.h> - -enum uart_regs { - UART_DR = 0x00, - UART_SR = 0x04, - UART_LINE_CR = 0x08, - UART_BAUDDIV_MSB = 0x0c, - UART_BAUDDIV_LSB = 0x10, - UART_CR = 0x14, - UART_FR = 0x18, - UART_IIR = 0x1c, - UART_ILPR = 0x20, - UART_RTS_CR = 0x24, - UART_RTS_LEAD = 0x28, - UART_RTS_TRAIL = 0x2c, - UART_DRV_ENABLE = 0x30, - UART_BRM_CR = 0x34, - UART_RXFIFO_IRQLEVEL = 0x38, - UART_TXFIFO_IRQLEVEL = 0x3c, -}; - -#define LINE_CR_5BIT (0<<5) -#define LINE_CR_6BIT (1<<5) -#define LINE_CR_7BIT (2<<5) -#define LINE_CR_8BIT (3<<5) -#define LINE_CR_FEN (1<<4) - -#define CR_UARTEN (1<<0) - -#define FR_TXFE (1<<7) -#define FR_RXFF (1<<6) -#define FR_TXFF (1<<5) -#define FR_RXFE (1<<4) -#define FR_BUSY (1<<3) -#define FR_DCD (1<<2) -#define FR_DSR (1<<1) -#define FR_CTS (1<<0) - -#define DRV_ENABLE_TX (1<<1) -#define DRV_ENABLE_RTS (1<<0) - -#define BRM_CR_BAUD_RATE_MODE (1<<0) - -static int netx_serial_init_port(struct console_device *cdev) -{ - struct device_d *dev = cdev->dev; - void __iomem *base = dev->priv; - unsigned int divisor; - - /* disable uart */ - writel(0, base + UART_CR); - writel(BRM_CR_BAUD_RATE_MODE, base + UART_BRM_CR); - - /* set baud rate */ - divisor = 115200 * 4096; - divisor /= 1000; - divisor *= 256; - divisor /= 100000; - - writel(divisor & 0xff, base + UART_BAUDDIV_LSB); - writel((divisor >> 8) & 0xff, base + UART_BAUDDIV_MSB); - writel(DRV_ENABLE_TX | DRV_ENABLE_RTS, base + UART_DRV_ENABLE); - - writel(LINE_CR_8BIT | LINE_CR_FEN, base + UART_LINE_CR); - - /* Finally, enable the UART */ - writel(CR_UARTEN, base + UART_CR); - - return 0; -} - -static int netx_serial_setbaudrate(struct console_device *cdev, int baudrate) -{ - return 0; -} - -static void netx_serial_putc(struct console_device *cdev, char c) -{ - struct device_d *dev = cdev->dev; - void __iomem *base = dev->priv; - - while (readl(base + UART_FR) & FR_TXFF ); - - writel(c, base + UART_DR); -} - -static int netx_serial_getc(struct console_device *cdev) -{ - struct device_d *dev = cdev->dev; - void __iomem *base = dev->priv; - int c; - - while (readl(base + UART_FR) & FR_RXFE ); - - c = readl(base + UART_DR); - - readl(base + UART_SR); - - return c; -} - -static int netx_serial_tstc(struct console_device *cdev) -{ - struct device_d *dev = cdev->dev; - void __iomem *base = dev->priv; - - return (readl(base + UART_FR) & FR_RXFE) ? 0 : 1; -} - -static int netx_serial_probe(struct device_d *dev) -{ - struct resource *iores; - struct console_device *cdev; - - cdev = xzalloc(sizeof(struct console_device)); - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - dev->priv = IOMEM(iores->start); - cdev->dev = dev; - cdev->tstc = netx_serial_tstc; - cdev->putc = netx_serial_putc; - cdev->getc = netx_serial_getc; - cdev->setbrg = netx_serial_setbaudrate; - - netx_serial_init_port(cdev); - - console_register(cdev); - - return 0; -} - -static struct driver_d netx_serial_driver = { - .name = "netx_serial", - .probe = netx_serial_probe, -}; -console_platform_driver(netx_serial_driver); diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index c91fd36a95..09da121374 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -2,8 +2,6 @@ config USB_DWC3 tristate "DesignWare USB3 DRD Core Support" depends on USB && HAS_DMA select USB_XHCI - select USB_DWC3_HOST # Remove this once we support more - # than USB host help Say Y or M here if your system has a Dual Role SuperSpeed USB controller based on the DesignWare USB3 IP Core. @@ -13,10 +11,37 @@ config USB_DWC3 if USB_DWC3 +choice + bool "DWC3 Mode Selection" + config USB_DWC3_HOST bool "Host only mode" help Select this when you want to use DWC3 in host mode only, thereby the gadget feature will be regressed. +config USB_DWC3_GADGET + bool "Gadget only mode" + depends on USB_GADGET + help + Select this when you want to use DWC3 in gadget mode only, + thereby the host feature will be regressed. + +config USB_DWC3_DUAL_ROLE + bool "Dual Role mode" + help + This is the default mode of working of DWC3 controller where + both host and gadget features are enabled. + +endchoice + +config USB_DWC3_OF_SIMPLE + tristate "Generic OF Simple Glue Layer" + depends on COMMON_CLK + default USB_DWC3 + help + Support USB2/3 functionality in simple SoC integrations. + Currently supports Xilinx and Qualcomm DWC USB3 IP. + Say 'Y' or 'M' if you have one such device. + endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index d43b23eb2d..d0c812c883 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -4,7 +4,12 @@ obj-$(CONFIG_USB_DWC3) += dwc3.o dwc3-y := core.o -ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST)),) +ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),) dwc3-y += host.o endif +ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),) + dwc3-y += gadget.o ep0.o +endif + +obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 60fd6318db..d3f9d9ef27 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -11,15 +11,77 @@ #include <common.h> #include <linux/clk.h> #include <linux/phy/phy.h> +#include <dma.h> #include <driver.h> #include <init.h> +#include "gadget.h" #include "core.h" #include "io.h" #define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */ +/** + * dwc3_get_dr_mode - Validates and sets dr_mode + * @dwc: pointer to our context structure + */ +static int dwc3_get_dr_mode(struct dwc3 *dwc) +{ + enum usb_dr_mode mode; + struct device_d *dev = dwc->dev; + unsigned int hw_mode; + + if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) + dwc->dr_mode = USB_DR_MODE_OTG; + + mode = dwc->dr_mode; + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); + + switch (hw_mode) { + case DWC3_GHWPARAMS0_MODE_GADGET: + if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) { + dev_err(dev, + "Controller does not support host mode.\n"); + return -EINVAL; + } + mode = USB_DR_MODE_PERIPHERAL; + break; + case DWC3_GHWPARAMS0_MODE_HOST: + if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) { + dev_err(dev, + "Controller does not support device mode.\n"); + return -EINVAL; + } + mode = USB_DR_MODE_HOST; + break; + default: + if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) + mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) + mode = USB_DR_MODE_PERIPHERAL; + + /* + * DWC_usb31 and DWC_usb3 v3.30a and higher do not support OTG + * mode. If the controller supports DRD but the dr_mode is not + * specified or set to OTG, then set the mode to peripheral. + */ + if (mode == USB_DR_MODE_OTG && + dwc->revision >= DWC3_REVISION_330A) + mode = USB_DR_MODE_PERIPHERAL; + } + + if (mode != dwc->dr_mode) { + dev_warn(dev, + "Configuration mismatch. dr_mode forced to %s\n", + mode == USB_DR_MODE_HOST ? "host" : "gadget"); + + dwc->dr_mode = mode; + } + + return 0; +} + void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) { u32 reg; @@ -89,6 +151,180 @@ done: return 0; } +/** + * dwc3_free_one_event_buffer - Frees one event buffer + * @dwc: Pointer to our controller context structure + * @evt: Pointer to event buffer to be freed + */ +static void dwc3_free_one_event_buffer(struct dwc3 *dwc, + struct dwc3_event_buffer *evt) +{ + dma_free_coherent(evt->buf, 0, sizeof(dma_addr_t)); +} + +/** + * dwc3_alloc_one_event_buffer - Allocates one event buffer structure + * @dwc: Pointer to our controller context structure + * @length: size of the event buffer + * + * Returns a pointer to the allocated event buffer structure on success + * otherwise ERR_PTR(errno). + */ +static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, + unsigned length) +{ + struct dwc3_event_buffer *evt; + + evt = xzalloc(sizeof(*evt)); + if (!evt) + return ERR_PTR(-ENOMEM); + + evt->dwc = dwc; + evt->length = length; + evt->buf = dma_alloc_coherent(length, &evt->dma); + if (!evt->buf) + return ERR_PTR(-ENOMEM); + + return evt; +} + +/** + * dwc3_free_event_buffers - frees all allocated event buffers + * @dwc: Pointer to our controller context structure + */ +static void dwc3_free_event_buffers(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + + evt = dwc->ev_buf; + if (evt) + dwc3_free_one_event_buffer(dwc, evt); +} + +/** + * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length + * @dwc: pointer to our controller context structure + * @length: size of event buffer + * + * Returns 0 on success otherwise negative errno. In the error case, dwc + * may contain some buffers allocated but not all which were requested. + */ +static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) +{ + struct dwc3_event_buffer *evt; + + evt = dwc3_alloc_one_event_buffer(dwc, length); + if (IS_ERR(evt)) { + dev_err(dwc->dev, "can't allocate event buffer\n"); + return PTR_ERR(evt); + } + dwc->ev_buf = evt; + + return 0; +} + +/** + * dwc3_event_buffers_setup - setup our allocated event buffers + * @dwc: pointer to our controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +static int dwc3_event_buffers_setup(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + + evt = dwc->ev_buf; + evt->lpos = 0; + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), + lower_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), + upper_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), + DWC3_GEVNTSIZ_SIZE(evt->length)); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0); + + return 0; +} + + +static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + + evt = dwc->ev_buf; + + evt->lpos = 0; + + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK + | DWC3_GEVNTSIZ_SIZE(0)); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0); +} + +static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc) +{ + if (!dwc->has_hibernation) + return 0; + + if (!dwc->nr_scratch) + return 0; + + dwc->scratchbuf = kmalloc_array(dwc->nr_scratch, + DWC3_SCRATCHBUF_SIZE, GFP_KERNEL); + if (!dwc->scratchbuf) + return -ENOMEM; + + return 0; +} + +static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) +{ + dma_addr_t scratch_addr; + u32 param; + int ret; + + if (!dwc->has_hibernation) + return 0; + + if (!dwc->nr_scratch) + return 0; + + scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf, + dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(dwc->dev, scratch_addr)) { + dev_err(dwc->dev, "failed to map scratch buffer\n"); + ret = -EFAULT; + goto err0; + } + + dwc->scratch_addr = scratch_addr; + + param = lower_32_bits(scratch_addr); + + ret = dwc3_send_gadget_generic_command(dwc, + DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param); + if (ret < 0) + goto err1; + + param = upper_32_bits(scratch_addr); + + ret = dwc3_send_gadget_generic_command(dwc, + DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param); + if (ret < 0) + goto err1; + + return 0; + +err1: + dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + +err0: + return ret; +} + static const struct clk_bulk_data dwc3_core_clks[] = { { .id = "ref" }, { .id = "bus_early" }, @@ -142,6 +378,22 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); } +static int dwc3_core_ulpi_init(struct dwc3 *dwc) +{ + int intf; + int ret = 0; + + intf = DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3); + + if (intf == DWC3_GHWPARAMS3_HSPHY_IFC_ULPI || + (intf == DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI && + dwc->hsphy_interface && + !strncmp(dwc->hsphy_interface, "ulpi", 4))) + ret = dwc3_ulpi_init(dwc); + + return ret; +} + /** * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core * @dwc: Pointer to our controller context structure @@ -228,6 +480,23 @@ static int dwc3_phy_setup(struct dwc3 *dwc) break; } + switch (dwc->hsphy_mode) { + case USBPHY_INTERFACE_MODE_UTMI: + reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK | + DWC3_GUSB2PHYCFG_USBTRDTIM_MASK); + reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_8_BIT) | + DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_8_BIT); + break; + case USBPHY_INTERFACE_MODE_UTMIW: + reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK | + DWC3_GUSB2PHYCFG_USBTRDTIM_MASK); + reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_16_BIT) | + DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_16_BIT); + break; + default: + break; + } + /* * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to * '0' during coreConsultant configuration. So default value will @@ -255,12 +524,16 @@ static int dwc3_phy_setup(struct dwc3 *dwc) static void dwc3_core_exit(struct dwc3 *dwc) { + dwc3_event_buffers_cleanup(dwc); + phy_exit(dwc->usb2_generic_phy); phy_exit(dwc->usb3_generic_phy); phy_power_off(dwc->usb2_generic_phy); phy_power_off(dwc->usb3_generic_phy); clk_bulk_disable(dwc->num_clks, dwc->clks); + + dwc3_free_event_buffers(dwc); } static bool dwc3_core_is_valid(struct dwc3 *dwc) @@ -361,6 +634,105 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc) static int dwc3_core_get_phy(struct dwc3 *dwc); +/* set global incr burst type configuration registers */ +static void dwc3_set_incr_burst_type(struct dwc3 *dwc) +{ + struct device_d *dev = dwc->dev; + /* incrx_mode : for INCR burst type. */ + bool incrx_mode; + /* incrx_size : for size of INCRX burst. */ + u32 incrx_size; + u32 *vals; + u32 cfg; + int ntype = 0; + int ret; + int i; + + cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0); + + /* + * Handle property "snps,incr-burst-type-adjustment". + * Get the number of value from this property: + * result <= 0, means this property is not supported. + * result = 1, means INCRx burst mode supported. + * result > 1, means undefined length burst mode supported. + */ + of_find_property(dev->device_node, "snps,incr-burst-type-adjustment", + &ntype); + + ntype /= sizeof(u32); + + if (ntype <= 0) + return; + + vals = kcalloc(ntype, sizeof(u32), GFP_KERNEL); + if (!vals) { + dev_err(dev, "Error to get memory\n"); + return; + } + + /* Get INCR burst type, and parse it */ + ret = of_property_read_u32_array(dev->device_node, + "snps,incr-burst-type-adjustment", + vals, ntype); + if (ret) { + kfree(vals); + dev_err(dev, "Error to get property\n"); + return; + } + + incrx_size = *vals; + + if (ntype > 1) { + /* INCRX (undefined length) burst mode */ + incrx_mode = INCRX_UNDEF_LENGTH_BURST_MODE; + for (i = 1; i < ntype; i++) { + if (vals[i] > incrx_size) + incrx_size = vals[i]; + } + } else { + /* INCRX burst mode */ + incrx_mode = INCRX_BURST_MODE; + } + + kfree(vals); + + /* Enable Undefined Length INCR Burst and Enable INCRx Burst */ + cfg &= ~DWC3_GSBUSCFG0_INCRBRST_MASK; + if (incrx_mode) + cfg |= DWC3_GSBUSCFG0_INCRBRSTENA; + switch (incrx_size) { + case 256: + cfg |= DWC3_GSBUSCFG0_INCR256BRSTENA; + break; + case 128: + cfg |= DWC3_GSBUSCFG0_INCR128BRSTENA; + break; + case 64: + cfg |= DWC3_GSBUSCFG0_INCR64BRSTENA; + break; + case 32: + cfg |= DWC3_GSBUSCFG0_INCR32BRSTENA; + break; + case 16: + cfg |= DWC3_GSBUSCFG0_INCR16BRSTENA; + break; + case 8: + cfg |= DWC3_GSBUSCFG0_INCR8BRSTENA; + break; + case 4: + cfg |= DWC3_GSBUSCFG0_INCR4BRSTENA; + break; + case 1: + break; + default: + dev_err(dev, "Invalid property\n"); + break; + } + + dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg); +} + /** * dwc3_core_init - Low-level initialization of DWC3 Core * @dwc: Pointer to our controller context structure @@ -395,6 +767,13 @@ static int dwc3_core_init(struct dwc3 *dwc) if (ret) goto err0; + if (!dwc->ulpi_ready) { + ret = dwc3_core_ulpi_init(dwc); + if (ret) + goto err0; + dwc->ulpi_ready = true; + } + if (!dwc->phys_ready) { ret = dwc3_core_get_phy(dwc); if (ret) @@ -409,16 +788,29 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_core_setup_global_control(dwc); dwc3_core_num_eps(dwc); + ret = dwc3_setup_scratch_buffers(dwc); + if (ret) + goto err0a; + /* Adjust Frame Length */ dwc3_frame_length_adjustment(dwc); + dwc3_set_incr_burst_type(dwc); + ret = phy_power_on(dwc->usb2_generic_phy); if (ret < 0) - goto err2; + goto err1; ret = phy_power_on(dwc->usb3_generic_phy); if (ret < 0) + goto err2; + + ret = dwc3_event_buffers_setup(dwc); + if (ret) { + dev_err(dwc->dev, "failed to setup event buffers\n"); goto err3; + } + /* * ENDXFER polling is available on version 3.10a and later of * the DWC_usb3 controller. It is NOT available in the @@ -501,12 +893,11 @@ static int dwc3_core_init(struct dwc3 *dwc) return 0; -/* err4: */ - /* phy_power_off(dwc->usb3_generic_phy); */ err3: - phy_power_off(dwc->usb2_generic_phy); + phy_power_off(dwc->usb3_generic_phy); err2: -/* err1: */ + phy_power_off(dwc->usb2_generic_phy); +err1: phy_exit(dwc->usb2_generic_phy); phy_exit(dwc->usb3_generic_phy); err0a: @@ -556,6 +947,16 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) int ret; switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); + + ret = dwc3_gadget_init(dwc); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize gadget\n"); + return ret; + } + break; case USB_DR_MODE_HOST: dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); @@ -595,10 +996,19 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->maximum_speed = of_usb_get_maximum_speed(dev->device_node, NULL); dwc->dr_mode = of_usb_get_dr_mode(dev->device_node, NULL); + dwc->hsphy_mode = of_usb_get_phy_mode(dev->device_node, NULL); dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; + if (of_get_property(dev->device_node, "snps,dis_rxdet_inp3_quirk", + NULL)) + dwc->dis_rxdet_inp3_quirk = 1; + + of_property_read_u32_array(dev->device_node, + "snps,quirk-frame-length-adjustment", + &dwc->fladj, 1); + dwc->hird_threshold = hird_threshold | (dwc->is_utmi_l1_suspend << 4); @@ -699,15 +1109,13 @@ static int dwc3_probe(struct device_d *dev) if (dev->device_node) { dwc->num_clks = ARRAY_SIZE(dwc3_core_clks); - ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks); - if (ret == -EPROBE_DEFER) - return ret; - /* - * Clocks are optional, but new DT platforms should support all - * clocks as required by the DT-binding. - */ - if (ret) - dwc->num_clks = 0; + if (of_find_property(dev->device_node, "clocks", NULL)) { + ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks); + if (ret == -EPROBE_DEFER) + return ret; + if (ret) + return ret; + } } ret = clk_bulk_enable(dwc->num_clks, dwc->clks); @@ -718,6 +1126,20 @@ static int dwc3_probe(struct device_d *dev) dwc3_cache_hwparams(dwc); + ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); + if (ret) { + dev_err(dwc->dev, "failed to allocate event buffers\n"); + return -ENOMEM; + } + + ret = dwc3_get_dr_mode(dwc); + if (ret) + return ret; + + ret = dwc3_alloc_scratch_buffers(dwc); + if (ret) + return ret; + ret = dwc3_core_init(dwc); if (ret) { if (ret != -EPROBE_DEFER) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index a404e4cd6a..df0a188a63 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -11,7 +11,10 @@ #ifndef __DRIVERS_USB_DWC3_CORE_H #define __DRIVERS_USB_DWC3_CORE_H +#include <linux/spinlock.h> #include <usb/usb.h> +#include <usb/phy.h> +#include <usb/gadget.h> #define DWC3_MSG_MAX 500 @@ -364,6 +367,7 @@ #define DWC3_DCFG_HIGHSPEED (0 << 0) #define DWC3_DCFG_FULLSPEED BIT(0) #define DWC3_DCFG_LOWSPEED (2 << 0) +#define DWC3_DCFG_FULLSPEED1 (3 << 0) #define DWC3_DCFG_NUMP_SHIFT 17 #define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f) @@ -457,6 +461,7 @@ #define DWC3_DSTS_HIGHSPEED (0 << 0) #define DWC3_DSTS_FULLSPEED BIT(0) #define DWC3_DSTS_LOWSPEED (2 << 0) +#define DWC3_DSTS_FULLSPEED1 (3 << 0) /* Device Generic Command Register */ #define DWC3_DGCMD_SET_LMP 0x01 @@ -594,6 +599,22 @@ struct dwc3_trb; +/** + * struct dwc3_event_buffer - Software event buffer representation + * @buf: _THE_ buffer + * @length: size of this buffer + * @lpos: event offset + * @dma: dma_addr_t + * @dwc: pointer to DWC controller + */ +struct dwc3_event_buffer { + void *buf; + unsigned length; + unsigned int lpos; + dma_addr_t dma; + struct dwc3 *dwc; +}; + #define DWC3_EP_FLAG_STALLED BIT(0) #define DWC3_EP_FLAG_WEDGED BIT(1) @@ -601,6 +622,84 @@ struct dwc3_trb; #define DWC3_EP_DIRECTION_RX false #define DWC3_TRB_NUM 256 +#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1) + +/** + * struct dwc3_ep - device side endpoint representation + * @endpoint: usb endpoint + * @pending_list: list of requests for this endpoint + * @started_list: list of started requests on this endpoint + * @trb_pool: array of transaction buffers + * @trb_pool_dma: dma address of @trb_pool + * @free_slot: next slot which is going to be used + * @busy_slot: first slot which is owned by HW + * @desc: usb_endpoint_descriptor pointer + * @dwc: pointer to DWC controller + * @saved_state: ep state saved during hibernation + * @flags: endpoint flags (wedged, stalled, ...) + * @current_trb: index of current used trb + * @number: endpoint number (1 - 15) + * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK + * @resource_index: Resource transfer index + * @interval: the interval on which the ISOC transfer is started + * @name: a human readable name e.g. ep1out-bulk + * @direction: true for TX, false for RX + * @stream_capable: true when streams are enabled + */ +struct dwc3_ep { + struct usb_ep endpoint; + struct list_head cancelled_list; + struct list_head pending_list; + struct list_head started_list; + + void __iomem *regs; + + struct dwc3_trb *trb_pool; + dma_addr_t trb_pool_dma; + u32 free_slot; + u32 busy_slot; + const struct usb_ss_ep_comp_descriptor *comp_desc; + struct dwc3 *dwc; + + u32 saved_state; + unsigned flags; +#define DWC3_EP_ENABLED BIT(0) +#define DWC3_EP_STALL BIT(1) +#define DWC3_EP_WEDGE BIT(2) +#define DWC3_EP_TRANSFER_STARTED BIT(3) +#define DWC3_EP_PENDING_REQUEST BIT(4) + + /* This last one is specific to EP0 */ +#define DWC3_EP0_DIR_IN BIT(31) + + unsigned current_trb; + /* + * IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will + * use a u8 type here. If anybody decides to increase number of TRBs to + * anything larger than 256 - I can't see why people would want to do + * this though - then this type needs to be changed. + * + * By using u8 types we ensure that our % operator when incrementing + * enqueue and dequeue get optimized away by the compiler. + */ + u8 trb_enqueue; + u8 trb_dequeue; + + u8 number; + u8 type; + u8 resource_index; + u32 frame_number; + u32 interval; + + char name[20]; + + unsigned direction:1; + unsigned stream_capable:1; + + /* For isochronous START TRANSFER workaround only */ + u8 combo_num; + int start_cmd_status; +}; enum dwc3_phy { DWC3_PHY_UNKNOWN = 0, @@ -730,6 +829,33 @@ struct dwc3_hwparams { /* HWPARAMS7 */ #define DWC3_RAM1_DEPTH(n) ((n) & 0xffff) +struct dwc3_request { + struct usb_request request; + struct list_head list; + struct dwc3_ep *dep; + u32 start_slot; + + unsigned remaining; + + unsigned int status; +#define DWC3_REQUEST_STATUS_QUEUED 0 +#define DWC3_REQUEST_STATUS_STARTED 1 +#define DWC3_REQUEST_STATUS_CANCELLED 2 +#define DWC3_REQUEST_STATUS_COMPLETED 3 +#define DWC3_REQUEST_STATUS_UNKNOWN -1 + + u8 epnum; + struct dwc3_trb *trb; + dma_addr_t trb_dma; + + unsigned num_trbs; + + unsigned needs_extra_trb:1; + unsigned direction:1; + unsigned mapped:1; + unsigned queued:1; +}; + /** * struct dwc3 - representation of our controller * @drd_work: workqueue used for role swapping @@ -855,9 +981,25 @@ struct dwc3_hwparams { * increments or 0 to disable. */ struct dwc3 { + struct dwc3_trb *ep0_trb; + void *bounce; + void *scratchbuf; + u8 *setup_buf; + dma_addr_t ep0_trb_addr; + dma_addr_t bounce_addr; + dma_addr_t scratch_addr; + struct dwc3_request ep0_usb_req; + struct device_d *dev; + struct device_d *xhci; + struct dwc3_event_buffer *ev_buf; + struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; + + struct usb_gadget gadget; + struct usb_gadget_driver *gadget_driver; + struct clk_bulk_data *clks; int num_clks; @@ -865,12 +1007,14 @@ struct dwc3 { struct phy *usb3_generic_phy; bool phys_ready; + bool ulpi_ready; void __iomem *regs; enum usb_dr_mode dr_mode; u32 current_dr_role; u32 desired_dr_role; + enum usb_phy_interface hsphy_mode; u32 fladj; u32 irq_gadget; @@ -934,6 +1078,11 @@ struct dwc3 { #define DWC31_VERSIONTYPE_EA05 0x65613035 #define DWC31_VERSIONTYPE_EA06 0x65613036 + enum dwc3_ep0_next ep0_next_event; + enum dwc3_ep0_state ep0state; + enum dwc3_link_state link_state; + + u16 isoch_delay; u16 u2sel; u16 u2pel; u8 u1sel; @@ -966,10 +1115,14 @@ struct dwc3 { unsigned sysdev_is_parent:1; unsigned has_lpm_erratum:1; unsigned is_utmi_l1_suspend:1; + unsigned is_selfpowered:1; unsigned is_fpga:1; unsigned pending_events:1; + unsigned needs_fifo_resize:1; unsigned pullups_connected:1; + unsigned resize_fifos:1; unsigned setup_packet_pending:1; + unsigned start_config_issued:1; unsigned three_stage_setup:1; unsigned dis_start_transfer_quirk:1; unsigned usb3_lpm_capable:1; @@ -1020,6 +1173,30 @@ struct dwc3_event_type { #define DWC3_DEPEVT_EPCMDCMPLT 0x07 /** + * dwc3_ep_event_string - returns event name + * @event: then event code + */ +static inline const char *dwc3_ep_event_string(u8 event) +{ + switch (event) { + case DWC3_DEPEVT_XFERCOMPLETE: + return "Transfer Complete"; + case DWC3_DEPEVT_XFERINPROGRESS: + return "Transfer In-Progress"; + case DWC3_DEPEVT_XFERNOTREADY: + return "Transfer Not Ready"; + case DWC3_DEPEVT_RXTXFIFOEVT: + return "FIFO"; + case DWC3_DEPEVT_STREAMEVT: + return "Stream"; + case DWC3_DEPEVT_EPCMDCMPLT: + return "Endpoint Command Complete"; + } + + return "UNKNOWN"; +} + +/** * struct dwc3_event_depvt - Device Endpoint Events * @one_bit: indicates this is an endpoint event (not used) * @endpoint_number: number of the endpoint @@ -1157,6 +1334,7 @@ struct dwc3_gadget_ep_cmd_params { #define DWC3_HAS_OTG BIT(3) /* prototypes */ +int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode); void dwc3_set_mode(struct dwc3 *dwc, u32 mode); @@ -1174,9 +1352,6 @@ static inline bool dwc3_is_usb31(struct dwc3 *dwc) bool dwc3_has_imod(struct dwc3 *dwc); -int dwc3_event_buffers_setup(struct dwc3 *dwc); -void dwc3_event_buffers_cleanup(struct dwc3 *dwc); - #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_host_init(struct dwc3 *dwc); #else diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c new file mode 100644 index 0000000000..e58d9f95fe --- /dev/null +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * dwc3-of-simple.c - OF glue layer for simple integrations + * + * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Felipe Balbi <balbi@ti.com> + * + * This is a combination of the old dwc3-qcom.c by Ivan T. Ivanov + * <iivanov@mm-sol.com> and the original patch adding support for Xilinx' SoC + * by Subbaraya Sundeep Bhatta <subbaraya.sundeep.bhatta@xilinx.com> + */ + +#include <common.h> +#include <driver.h> +#include <errno.h> +#include <init.h> +#include <linux/clk.h> +#include <linux/reset.h> +#include <of.h> + +struct dwc3_of_simple { + struct device_d *dev; + struct clk **clks; + int num_clocks; +}; + +static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) +{ + struct device_d *dev = simple->dev; + struct device_node *np = dev->device_node; + int i; + + simple->num_clocks = count; + + if (!count) + return 0; + + simple->clks = xzalloc(sizeof(struct clk *)); + if (!simple->clks) + return -ENOMEM; + + for (i = 0; i < simple->num_clocks; i++) { + struct clk *clk; + + clk = of_clk_get(np, i); + if (IS_ERR(clk)) { + while (--i >= 0) { + clk_disable(simple->clks[i]); + clk_put(simple->clks[i]); + } + return PTR_ERR(clk); + } + + simple->clks[i] = clk; + } + + return 0; +} + +static int dwc3_of_simple_probe(struct device_d *dev) +{ + struct dwc3_of_simple *simple; + struct device_node *np = dev->device_node; + + int ret; + int i; + + simple = xzalloc(sizeof(*simple)); + if (!simple) + return -ENOMEM; + + dev->priv = simple; + simple->dev = dev; + + ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np, + "clocks", "#clock-cells")); + if (ret) + return ret; + + ret = of_platform_populate(np, NULL, dev); + if (ret) { + for (i = 0; i < simple->num_clocks; i++) { + clk_disable(simple->clks[i]); + clk_put(simple->clks[i]); + } + return ret; + } + + return 0; +} + +static void dwc3_of_simple_remove(struct device_d *dev) +{ + struct dwc3_of_simple *simple = dev->priv; + int i; + + for (i = 0; i < simple->num_clocks; i++) { + clk_disable(simple->clks[i]); + clk_put(simple->clks[i]); + } + simple->num_clocks = 0; +} + +static const struct of_device_id of_dwc3_simple_match[] = { + {.compatible = "rockchip,rk3399-dwc3"}, + {.compatible = "xlnx,zynqmp-dwc3"}, + {.compatible = "fsl,ls1046a-dwc3"}, + {.compatible = "cavium,octeon-7130-usb-uctl"}, + {.compatible = "sprd,sc9860-dwc3"}, + {.compatible = "amlogic,meson-axg-dwc3"}, + {.compatible = "amlogic,meson-gxl-dwc3"}, + {.compatible = "allwinner,sun50i-h6-dwc3"}, + {/* Sentinel */}}; + +static struct driver_d dwc3_of_simple_driver = { + .probe = dwc3_of_simple_probe, + .remove = dwc3_of_simple_remove, + .name = "dwc3-of-simple", + .of_compatible = DRV_OF_COMPAT(of_dwc3_simple_match), +}; +device_platform_driver(dwc3_of_simple_driver); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c new file mode 100644 index 0000000000..b757a57886 --- /dev/null +++ b/drivers/usb/dwc3/ep0.c @@ -0,0 +1,1183 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/ep0.c) and ported + * to uboot. + * + * commit c00552ebaf : Merge 3.18-rc7 into usb-next + */ + +#include <common.h> +#include <dma.h> +#include <linux/kernel.h> +#include <linux/list.h> + +#include <usb/gadget.h> + +#include "core.h" +#include "gadget.h" +#include "io.h" + +static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep); +static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, + struct dwc3_ep *dep, struct dwc3_request *req); + +static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep, + dma_addr_t buf_dma, u32 len, u32 type, + bool chain) +{ + struct dwc3_trb *trb; + struct dwc3 *dwc; + + dwc = dep->dwc; + trb = &dwc->ep0_trb[dep->trb_enqueue]; + + if (chain) + dep->trb_enqueue++; + + trb->bpl = lower_32_bits(buf_dma); + trb->bph = upper_32_bits(buf_dma); + trb->size = len; + trb->ctrl = type; + + trb->ctrl |= (DWC3_TRB_CTRL_HWO + | DWC3_TRB_CTRL_ISP_IMI); + + if (chain) + trb->ctrl |= DWC3_TRB_CTRL_CHN; + else + trb->ctrl |= (DWC3_TRB_CTRL_IOC + | DWC3_TRB_CTRL_LST); +} + +static int dwc3_ep0_start_trans(struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3 *dwc; + int ret; + + dwc = dep->dwc; + + if (dep->flags & DWC3_EP_TRANSFER_STARTED) { + dev_err(dwc->dev, "%s: transfer already started\n", dep->name); + return 0; + } + + memset(¶ms, 0, sizeof(params)); + params.param0 = upper_32_bits(dwc->ep0_trb_addr); + params.param1 = lower_32_bits(dwc->ep0_trb_addr); + + ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_STARTTRANSFER, ¶ms); + if (ret < 0) { + dev_err(dwc->dev, "%s: STARTTRANSFER failed\n", dep->name); + return ret; + } + + dwc->ep0_next_event = DWC3_EP0_COMPLETE; + + return 0; +} + +static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) +{ + switch (state) { + case EP0_UNCONNECTED: + return "Unconnected"; + case EP0_SETUP_PHASE: + return "Setup Phase"; + case EP0_DATA_PHASE: + return "Data Phase"; + case EP0_STATUS_PHASE: + return "Status Phase"; + default: + return "UNKNOWN"; + } +} + +static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, + struct dwc3_request *req) +{ + struct dwc3 *dwc = dep->dwc; + + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->epnum = dep->number; + + list_add_tail(&req->list, &dep->pending_list); + + /* + * Gadget driver might not be quick enough to queue a request + * before we get a Transfer Not Ready event on this endpoint. + * + * In that case, we will set DWC3_EP_PENDING_REQUEST. When that + * flag is set, it's telling us that as soon as Gadget queues the + * required request, we should kick the transfer here because the + * IRQ we were waiting for is long gone. + */ + if (dep->flags & DWC3_EP_PENDING_REQUEST) { + unsigned direction; + + direction = !!(dep->flags & DWC3_EP0_DIR_IN); + + if (dwc->ep0state != EP0_DATA_PHASE) { + dev_warn(dwc->dev, "Unexpected pending request\n"); + return 0; + } + + __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req); + + dep->flags &= ~(DWC3_EP_PENDING_REQUEST | + DWC3_EP0_DIR_IN); + + return 0; + } + + /* + * In case gadget driver asked us to delay the STATUS phase, + * handle it here. + */ + if (dwc->delayed_status) { + unsigned direction; + + direction = !dwc->ep0_expect_in; + dwc->delayed_status = false; + usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED); + + if (dwc->ep0state == EP0_STATUS_PHASE) + __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); + else + dev_dbg(dwc->dev, "too early for delayed status\n"); + + return 0; + } + + /* + * Unfortunately we have uncovered a limitation wrt the Data Phase. + * + * Section 9.4 says we can wait for the XferNotReady(DATA) event to + * come before issueing Start Transfer command, but if we do, we will + * miss situations where the host starts another SETUP phase instead of + * the DATA phase. Such cases happen at least on TD.7.6 of the Link + * Layer Compliance Suite. + * + * The problem surfaces due to the fact that in case of back-to-back + * SETUP packets there will be no XferNotReady(DATA) generated and we + * will be stuck waiting for XferNotReady(DATA) forever. + * + * By looking at tables 9-13 and 9-14 of the Databook, we can see that + * it tells us to start Data Phase right away. It also mentions that if + * we receive a SETUP phase instead of the DATA phase, core will issue + * XferComplete for the DATA phase, before actually initiating it in + * the wire, with the TRB's status set to "SETUP_PENDING". Such status + * can only be used to print some debugging logs, as the core expects + * us to go through to the STATUS phase and start a CONTROL_STATUS TRB, + * just so it completes right away, without transferring anything and, + * only then, we can go back to the SETUP phase. + * + * Because of this scenario, SNPS decided to change the programming + * model of control transfers and support on-demand transfers only for + * the STATUS phase. To fix the issue we have now, we will always wait + * for gadget driver to queue the DATA phase's struct usb_request, then + * start it right away. + * + * If we're actually in a 2-stage transfer, we will wait for + * XferNotReady(STATUS). + */ + if (dwc->three_stage_setup) { + unsigned direction; + + direction = dwc->ep0_expect_in; + dwc->ep0state = EP0_DATA_PHASE; + + __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req); + + dep->flags &= ~DWC3_EP0_DIR_IN; + } + + return 0; +} + +int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + unsigned long flags; + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + if (!dep->endpoint.desc) { + dev_err(dwc->dev, "trying to queue request %p to disabled %s\n", + request, dep->name); + ret = -ESHUTDOWN; + goto out; + } + + /* we share one TRB for ep0/1 */ + if (!list_empty(&dep->pending_list)) { + ret = -EBUSY; + goto out; + } + + dev_dbg(dwc->dev, "queueing request %p to %s length %d state '%s'\n", + request, dep->name, request->length, + dwc3_ep0_state_string(dwc->ep0state)); + + ret = __dwc3_gadget_ep0_queue(dep, req); + +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + + /* reinitialize physical ep1 */ + dep = dwc->eps[1]; + dep->flags = DWC3_EP_ENABLED; + + /* stall is always issued on EP0 */ + dep = dwc->eps[0]; + __dwc3_gadget_ep_set_halt(dep, 1, false); + dep->flags = DWC3_EP_ENABLED; + dwc->delayed_status = false; + + if (!list_empty(&dep->pending_list)) { + struct dwc3_request *req; + + req = next_request(&dep->pending_list); + dwc3_gadget_giveback(dep, req, -ECONNRESET); + } + + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); +} + +int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + dwc3_ep0_stall_and_restart(dwc); + + return 0; +} + +int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep0_set_halt(ep, value); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +void dwc3_ep0_out_start(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + int ret; + + dep = dwc->eps[0]; + dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8, + DWC3_TRBCTL_CONTROL_SETUP, false); + ret = dwc3_ep0_start_trans(dep); + WARN_ON(ret < 0); +} + +static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) +{ + struct dwc3_ep *dep; + u32 windex = le16_to_cpu(wIndex_le); + u32 epnum; + + epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1; + if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) + epnum |= 1; + + dep = dwc->eps[epnum]; + if (dep->flags & DWC3_EP_ENABLED) + return dep; + + return NULL; +} + +static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req) +{ +} +/* + * ch 9.4.5 + */ +static int dwc3_ep0_handle_status(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl) +{ + struct dwc3_ep *dep; + u32 recip; + u32 value; + u32 reg; + u16 usb_status = 0; + __le16 *response_pkt; + + /* We don't support PTM_STATUS */ + value = le16_to_cpu(ctrl->wValue); + if (value != 0) + return -EINVAL; + + recip = ctrl->bRequestType & USB_RECIP_MASK; + switch (recip) { + case USB_RECIP_DEVICE: + /* + * LTM will be set once we know how to set this in HW. + */ + usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED; + + if ((dwc->speed == DWC3_DSTS_SUPERSPEED) || + (dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (reg & DWC3_DCTL_INITU1ENA) + usb_status |= 1 << USB_DEV_STAT_U1_ENABLED; + if (reg & DWC3_DCTL_INITU2ENA) + usb_status |= 1 << USB_DEV_STAT_U2_ENABLED; + } + + break; + + case USB_RECIP_INTERFACE: + /* + * Function Remote Wake Capable D0 + * Function Remote Wakeup D1 + */ + break; + + case USB_RECIP_ENDPOINT: + dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); + if (!dep) + return -EINVAL; + + if (dep->flags & DWC3_EP_STALL) + usb_status = 1 << USB_ENDPOINT_HALT; + break; + default: + return -EINVAL; + } + + response_pkt = (__le16 *) dwc->setup_buf; + *response_pkt = cpu_to_le16(usb_status); + + dep = dwc->eps[0]; + dwc->ep0_usb_req.dep = dep; + dwc->ep0_usb_req.request.length = sizeof(*response_pkt); + dwc->ep0_usb_req.request.buf = dwc->setup_buf; + dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl; + + return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); +} + +static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state, + int set) +{ + u32 reg; + + if (state != USB_STATE_CONFIGURED) + return -EINVAL; + if ((dwc->speed != DWC3_DSTS_SUPERSPEED) && + (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS)) + return -EINVAL; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (set) + reg |= DWC3_DCTL_INITU1ENA; + else + reg &= ~DWC3_DCTL_INITU1ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + return 0; +} + +static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state, + int set) +{ + u32 reg; + + if (state != USB_STATE_CONFIGURED) + return -EINVAL; + if ((dwc->speed != DWC3_DSTS_SUPERSPEED) && + (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS)) + return -EINVAL; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (set) + reg |= DWC3_DCTL_INITU2ENA; + else + reg &= ~DWC3_DCTL_INITU2ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + return 0; +} + +static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state, + u32 wIndex, int set) +{ + if ((wIndex & 0xff) != 0) + return -EINVAL; + if (!set) + return -EINVAL; + + switch (wIndex >> 8) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + case TEST_FORCE_EN: + dwc->test_mode_nr = wIndex >> 8; + dwc->test_mode = true; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int dwc3_ep0_handle_device(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl, int set) +{ + enum usb_device_state state; + u32 wValue; + u32 wIndex; + int ret = 0; + + wValue = le16_to_cpu(ctrl->wValue); + wIndex = le16_to_cpu(ctrl->wIndex); + state = dwc->gadget.state; + + switch (wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + break; + /* + * 9.4.1 says only only for SS, in AddressState only for + * default control pipe + */ + case USB_DEVICE_U1_ENABLE: + ret = dwc3_ep0_handle_u1(dwc, state, set); + break; + case USB_DEVICE_U2_ENABLE: + ret = dwc3_ep0_handle_u2(dwc, state, set); + break; + case USB_DEVICE_LTM_ENABLE: + ret = -EINVAL; + break; + case USB_DEVICE_TEST_MODE: + ret = dwc3_ep0_handle_test(dwc, state, wIndex, set); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int dwc3_ep0_handle_intf(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl, int set) +{ + u32 wValue; + int ret = 0; + + wValue = le16_to_cpu(ctrl->wValue); + + switch (wValue) { + case USB_INTRF_FUNC_SUSPEND: + /* + * REVISIT: Ideally we would enable some low power mode here, + * however it's unclear what we should be doing here. + * + * For now, we're not doing anything, just making sure we return + * 0 so USB Command Verifier tests pass without any errors. + */ + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl, int set) +{ + struct dwc3_ep *dep; + u32 wValue; + int ret; + + wValue = le16_to_cpu(ctrl->wValue); + + switch (wValue) { + case USB_ENDPOINT_HALT: + dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); + if (!dep) + return -EINVAL; + + if (set == 0 && (dep->flags & DWC3_EP_WEDGE)) + break; + + ret = __dwc3_gadget_ep_set_halt(dep, set, true); + if (ret) + return -EINVAL; + break; + default: + return -EINVAL; + } + + return 0; +} + + +static int dwc3_ep0_handle_feature(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl, int set) +{ + u32 recip; + int ret; + + recip = ctrl->bRequestType & USB_RECIP_MASK; + + switch (recip) { + case USB_RECIP_DEVICE: + ret = dwc3_ep0_handle_device(dwc, ctrl, set); + break; + case USB_RECIP_INTERFACE: + ret = dwc3_ep0_handle_intf(dwc, ctrl, set); + break; + case USB_RECIP_ENDPOINT: + ret = dwc3_ep0_handle_endpoint(dwc, ctrl, set); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + enum usb_device_state state = dwc->gadget.state; + u32 addr; + u32 reg; + + addr = le16_to_cpu(ctrl->wValue); + if (addr > 127) { + dev_err(dwc->dev, "invalid device address %d\n", addr); + return -EINVAL; + } + + if (state == USB_STATE_CONFIGURED) { + dev_err(dwc->dev, "trying to set address when configured\n"); + return -EINVAL; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_DEVADDR_MASK); + reg |= DWC3_DCFG_DEVADDR(addr); + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + if (addr) + usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); + else + usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT); + + return 0; +} + +static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + int ret; + + spin_unlock(&dwc->lock); + ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl); + spin_lock(&dwc->lock); + return ret; +} + +#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */ +static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + enum usb_device_state state = dwc->gadget.state; + u32 cfg; + int ret; + u32 reg; + + cfg = le16_to_cpu(ctrl->wValue); + + switch (state) { + case USB_STATE_DEFAULT: + return -EINVAL; + + case USB_STATE_ADDRESS: + ret = dwc3_ep0_delegate_req(dwc, ctrl); + /* if the cfg matches and the cfg is non zero */ + if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { + + /* + * only change state if set_config has already + * been processed. If gadget driver returns + * USB_GADGET_DELAYED_STATUS, we will wait + * to change the state on the next usb_ep_queue() + */ + if (ret == 0) + usb_gadget_set_state(&dwc->gadget, + USB_STATE_CONFIGURED); + + /* + * Enable transition to U1/U2 state when + * nothing is pending from application. + */ + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + } + break; + + case USB_STATE_CONFIGURED: + ret = dwc3_ep0_delegate_req(dwc, ctrl); + if (!cfg && !ret) + usb_gadget_set_state(&dwc->gadget, + USB_STATE_ADDRESS); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + u32 param = 0; + u32 reg; + + struct timing { + u8 u1sel; + u8 u1pel; + __le16 u2sel; + __le16 u2pel; + } __packed timing; + + int ret; + + memcpy(&timing, req->buf, sizeof(timing)); + + dwc->u1sel = timing.u1sel; + dwc->u1pel = timing.u1pel; + dwc->u2sel = le16_to_cpu(timing.u2sel); + dwc->u2pel = le16_to_cpu(timing.u2pel); + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (reg & DWC3_DCTL_INITU2ENA) + param = dwc->u2pel; + if (reg & DWC3_DCTL_INITU1ENA) + param = dwc->u1pel; + + /* + * According to Synopsys Databook, if parameter is + * greater than 125, a value of zero should be + * programmed in the register. + */ + if (param > 125) + param = 0; + + /* now that we have the time, issue DGCMD Set Sel */ + ret = dwc3_send_gadget_generic_command(dwc, + DWC3_DGCMD_SET_PERIODIC_PAR, param); + WARN_ON(ret < 0); +} + +static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + struct dwc3_ep *dep; + enum usb_device_state state = dwc->gadget.state; + u16 wLength; + + if (state == USB_STATE_DEFAULT) + return -EINVAL; + + wLength = le16_to_cpu(ctrl->wLength); + + if (wLength != 6) { + dev_err(dwc->dev, "Set SEL should be 6 bytes, got %d\n", + wLength); + return -EINVAL; + } + + /* + * To handle Set SEL we need to receive 6 bytes from Host. So let's + * queue a usb_request for 6 bytes. + * + * Remember, though, this controller can't handle non-wMaxPacketSize + * aligned transfers on the OUT direction, so we queue a request for + * wMaxPacketSize instead. + */ + dep = dwc->eps[0]; + dwc->ep0_usb_req.dep = dep; + dwc->ep0_usb_req.request.length = dep->endpoint.maxpacket; + dwc->ep0_usb_req.request.buf = dwc->setup_buf; + dwc->ep0_usb_req.request.complete = dwc3_ep0_set_sel_cmpl; + + return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); +} + +static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl) +{ + u16 wLength; + u16 wValue; + u16 wIndex; + + wValue = le16_to_cpu(ctrl->wValue); + wLength = le16_to_cpu(ctrl->wLength); + wIndex = le16_to_cpu(ctrl->wIndex); + + if (wIndex || wLength) + return -EINVAL; + + /* + * REVISIT It's unclear from Databook what to do with this + * value. For now, just cache it. + */ + dwc->isoch_delay = wValue; + + return 0; +} + +static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + int ret; + + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + dev_dbg(dwc->dev, "USB_REQ_GET_STATUS\n"); + ret = dwc3_ep0_handle_status(dwc, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + dev_dbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n"); + ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); + break; + case USB_REQ_SET_FEATURE: + dev_dbg(dwc->dev, "USB_REQ_SET_FEATURE\n"); + ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); + break; + case USB_REQ_SET_ADDRESS: + dev_dbg(dwc->dev, "USB_REQ_SET_ADDRESS\n"); + ret = dwc3_ep0_set_address(dwc, ctrl); + break; + case USB_REQ_SET_CONFIGURATION: + dev_dbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n"); + ret = dwc3_ep0_set_config(dwc, ctrl); + break; + case USB_REQ_SET_SEL: + dev_dbg(dwc->dev, "USB_REQ_SET_SEL\n"); + ret = dwc3_ep0_set_sel(dwc, ctrl); + break; + case USB_REQ_SET_ISOCH_DELAY: + dev_dbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n"); + ret = dwc3_ep0_set_isoch_delay(dwc, ctrl); + break; + default: + dev_dbg(dwc->dev, "Forwarding to gadget driver\n"); + ret = dwc3_ep0_delegate_req(dwc, ctrl); + break; + } + + return ret; +} + +static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct usb_ctrlrequest *ctrl = (void *) dwc->ep0_trb; + int ret = -EINVAL; + u32 len; + + if (!dwc->gadget_driver) + goto out; + + len = le16_to_cpu(ctrl->wLength); + if (!len) { + dwc->three_stage_setup = false; + dwc->ep0_expect_in = false; + dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; + } else { + dwc->three_stage_setup = true; + dwc->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN); + dwc->ep0_next_event = DWC3_EP0_NRDY_DATA; + } + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + ret = dwc3_ep0_std_request(dwc, ctrl); + else + ret = dwc3_ep0_delegate_req(dwc, ctrl); + + if (ret == USB_GADGET_DELAYED_STATUS) + dwc->delayed_status = true; + +out: + if (ret < 0) + dwc3_ep0_stall_and_restart(dwc); +} + +static void dwc3_ep0_complete_data(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_request *r = NULL; + struct usb_request *ur; + struct dwc3_trb *trb; + struct dwc3_ep *ep0; + u32 transferred = 0; + u32 status; + u32 length; + u8 epnum; + + epnum = event->endpoint_number; + ep0 = dwc->eps[0]; + + dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; + trb = dwc->ep0_trb; + + r = next_request(&ep0->pending_list); + if (!r) + return; + + status = DWC3_TRB_SIZE_TRBSTS(trb->size); + if (status == DWC3_TRBSTS_SETUP_PENDING) { + dev_dbg(dwc->dev, "Setup Pending received\n"); + dwc->setup_packet_pending = true; + + if (r) + dwc3_gadget_giveback(ep0, r, -ECONNRESET); + + return; + } + + ur = &r->request; + + length = trb->size & DWC3_TRB_SIZE_MASK; + transferred = ur->length - length; + ur->actual += transferred; + + if ((IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) && + ur->length && ur->zero) || dwc->ep0_bounced) { + trb++; + trb->ctrl &= ~DWC3_TRB_CTRL_HWO; + + if (r->direction) + dwc->eps[1]->trb_enqueue = 0; + else + dwc->eps[0]->trb_enqueue = 0; + + dwc->ep0_bounced = false; + } + + if ((epnum & 1) && ur->actual < ur->length) + dwc3_ep0_stall_and_restart(dwc); + else + dwc3_gadget_giveback(ep0, r, 0); +} + +static void dwc3_ep0_complete_status(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_request *r; + struct dwc3_ep *dep; + struct dwc3_trb *trb; + u32 status; + + dep = dwc->eps[0]; + trb = dwc->ep0_trb; + + if (!list_empty(&dep->pending_list)) { + r = next_request(&dep->pending_list); + + dwc3_gadget_giveback(dep, r, 0); + } + + if (dwc->test_mode) { + int ret; + + ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr); + if (ret < 0) { + dev_dbg(dwc->dev, "Invalid Test #%d\n", + dwc->test_mode_nr); + dwc3_ep0_stall_and_restart(dwc); + return; + } + } + + status = DWC3_TRB_SIZE_TRBSTS(trb->size); + if (status == DWC3_TRBSTS_SETUP_PENDING) { + dev_dbg(dwc->dev, "Setup Pending received\n"); + dwc->setup_packet_pending = true; + } + + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); +} + +static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + dep->resource_index = 0; + dwc->setup_packet_pending = false; + + switch (dwc->ep0state) { + case EP0_SETUP_PHASE: + dev_dbg(dwc->dev, "Setup Phase\n"); + dwc3_ep0_inspect_setup(dwc, event); + break; + + case EP0_DATA_PHASE: + dev_dbg(dwc->dev, "Data Phase\n"); + dwc3_ep0_complete_data(dwc, event); + break; + + case EP0_STATUS_PHASE: + dev_dbg(dwc->dev, "Status Phase\n"); + dwc3_ep0_complete_status(dwc, event); + break; + default: + WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state); + } +} + +static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, + struct dwc3_ep *dep, + struct dwc3_request *req) +{ + dma_addr_t dma_addr; + int ret; + + req->direction = !!dep->number; + + if (req->request.length == 0) { + dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0, + DWC3_TRBCTL_CONTROL_DATA, false); + ret = dwc3_ep0_start_trans(dep); + } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && + (dep->number == 0)) { + u32 maxpacket; + u32 rem; + + dma_addr = dma_map_single(dwc->dev, req->request.buf, + req->request.length, + dep->number ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (dma_mapping_error(dwc->dev, dma_addr)) + return; + + req->request.dma = dma_addr; + + maxpacket = dep->endpoint.maxpacket; + rem = req->request.length % maxpacket; + dwc->ep0_bounced = true; + + /* prepare normal TRB */ + dwc3_ep0_prepare_one_trb(dep, req->request.dma, + req->request.length, + DWC3_TRBCTL_CONTROL_DATA, + true); + + req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1]; + + /* Now prepare one extra TRB to align transfer size */ + dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, + maxpacket - rem, + DWC3_TRBCTL_CONTROL_DATA, + false); + ret = dwc3_ep0_start_trans(dep); + } else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && + req->request.length && req->request.zero) { + dma_addr = dma_map_single(dwc->dev, req->request.buf, + req->request.length, + dep->number ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (dma_mapping_error(dwc->dev, dma_addr)) + return; + + req->request.dma = dma_addr; + + /* prepare normal TRB */ + dwc3_ep0_prepare_one_trb(dep, req->request.dma, + req->request.length, + DWC3_TRBCTL_CONTROL_DATA, + true); + + req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1]; + + /* Now prepare one extra TRB to align transfer size */ + dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, + 0, DWC3_TRBCTL_CONTROL_DATA, + false); + ret = dwc3_ep0_start_trans(dep); + } else { + dma_addr = dma_map_single(dwc->dev, req->request.buf, + req->request.length, + dep->number ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (dma_mapping_error(dwc->dev, dma_addr)) + return; + + req->request.dma = dma_addr; + + dwc3_ep0_prepare_one_trb(dep, req->request.dma, + req->request.length, DWC3_TRBCTL_CONTROL_DATA, + false); + + req->trb = &dwc->ep0_trb[dep->trb_enqueue]; + + ret = dwc3_ep0_start_trans(dep); + } + + WARN_ON(ret < 0); +} + +static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + u32 type; + + type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3 + : DWC3_TRBCTL_CONTROL_STATUS2; + + dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0, type, false); + return dwc3_ep0_start_trans(dep); +} + +static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + WARN_ON(dwc3_ep0_start_control_status(dep)); +} + +static void dwc3_ep0_do_control_status(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + + __dwc3_ep0_do_control_status(dwc, dep); +} + +static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + int ret; + + if (!dep->resource_index) + return; + + cmd = DWC3_DEPCMD_ENDTRANSFER; + cmd |= DWC3_DEPCMD_CMDIOC; + cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); + WARN_ON(ret); + dep->resource_index = 0; +} + +static void dwc3_ep0_xfernotready(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + switch (event->status) { + case DEPEVT_STATUS_CONTROL_DATA: + dev_dbg(dwc->dev, "Control Data\n"); + + /* + * We already have a DATA transfer in the controller's cache, + * if we receive a XferNotReady(DATA) we will ignore it, unless + * it's for the wrong direction. + * + * In that case, we must issue END_TRANSFER command to the Data + * Phase we already have started and issue SetStall on the + * control endpoint. + */ + if (dwc->ep0_expect_in != event->endpoint_number) { + struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in]; + + dev_dbg(dwc->dev, "Wrong direction for Data phase\n"); + dwc3_ep0_end_control_data(dwc, dep); + dwc3_ep0_stall_and_restart(dwc); + return; + } + + break; + + case DEPEVT_STATUS_CONTROL_STATUS: + if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) + return; + + dev_dbg(dwc->dev, "Control Status\n"); + + dwc->ep0state = EP0_STATUS_PHASE; + + if (dwc->delayed_status) { + struct dwc3_ep *dep = dwc->eps[0]; + + WARN_ON(event->endpoint_number != 1); + dev_dbg(dwc->dev, "Delayed Status\n"); + /* + * We should handle the delay STATUS phase here if the + * request for handling delay STATUS has been queued + * into the list. + */ + if (!list_empty(&dep->pending_list)) { + dwc->delayed_status = false; + usb_gadget_set_state(&dwc->gadget, + USB_STATE_CONFIGURED); + dwc3_ep0_do_control_status(dwc, event); + } + + return; + } + + dwc3_ep0_do_control_status(dwc, event); + } +} + +void dwc3_ep0_interrupt(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + u8 epnum = event->endpoint_number; + + dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n", + dwc3_ep_event_string(event->endpoint_event), + epnum >> 1, (epnum & 1) ? "in" : "out", + dwc3_ep0_state_string(dwc->ep0state)); + + switch (event->endpoint_event) { + case DWC3_DEPEVT_XFERCOMPLETE: + dwc3_ep0_xfer_complete(dwc, event); + break; + + case DWC3_DEPEVT_XFERNOTREADY: + dwc3_ep0_xfernotready(dwc, event); + break; + + case DWC3_DEPEVT_XFERINPROGRESS: + case DWC3_DEPEVT_RXTXFIFOEVT: + case DWC3_DEPEVT_STREAMEVT: + case DWC3_DEPEVT_EPCMDCMPLT: + break; + } +} diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c new file mode 100644 index 0000000000..f416acc999 --- /dev/null +++ b/drivers/usb/dwc3/gadget.c @@ -0,0 +1,3015 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.c) and ported + * to uboot. + * + * commit 8e74475b0e : usb: dwc3: gadget: use udc-core's reset notifier + */ +#include <common.h> +#include <dma.h> +#include <io.h> +#include <linux/list.h> + +#include <usb/gadget.h> +#include <usb/ch9.h> + +#include "core.h" +#include "gadget.h" + +#define DWC3_ALIGN_FRAME(d, n) (((d)->frame_number + ((d)->interval * (n))) \ + & ~((d)->interval - 1)) + + +/** + * dwc3_gadget_set_test_mode - Enables USB2 Test Modes + * @dwc: pointer to our context structure + * @mode: the mode to set (J, K SE0 NAK, Force Enable) + * + * Caller should take care of locking. This function will + * return 0 on success or -EINVAL if wrong Test Selector + * is passed + */ +int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_TSTCTRL_MASK; + + switch (mode) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + case TEST_FORCE_EN: + reg |= mode << 1; + break; + default: + return -EINVAL; + } + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + return 0; +} + +/** + * dwc3_gadget_get_link_state - Gets current state of USB Link + * @dwc: pointer to our context structure + * + * Caller should take care of locking. This function will + * return the link state on success (>= 0) or -ETIMEDOUT. + */ +int dwc3_gadget_get_link_state(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + return DWC3_DSTS_USBLNKST(reg); +} + +/** + * dwc3_gadget_set_link_state - Sets USB Link to a particular State + * @dwc: pointer to our context structure + * @state: the state to put link into + * + * Caller should take care of locking. This function will + * return 0 on success or -ETIMEDOUT. + */ +int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) +{ + int retries = 10000; + u32 reg; + + /* + * Wait until device controller is ready. Only applies to 1.94a and + * later RTL. + */ + if (dwc->revision >= DWC3_REVISION_194A) { + while (--retries) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + if (reg & DWC3_DSTS_DCNRD) + udelay(5); + else + break; + } + + if (retries <= 0) + return -ETIMEDOUT; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + + /* set requested state */ + reg |= DWC3_DCTL_ULSTCHNGREQ(state); + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + /* + * The following code is racy when called from dwc3_gadget_wakeup, + * and is not needed, at least on newer versions + */ + if (dwc->revision >= DWC3_REVISION_194A) + return 0; + + /* wait for a change in DSTS */ + retries = 10000; + while (--retries) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + if (DWC3_DSTS_USBLNKST(reg) == state) + return 0; + + udelay(5); + } + + dev_dbg(dwc->dev, "link state change request timed out\n"); + + return -ETIMEDOUT; +} + +/** + * dwc3_ep_inc_trb - increment a trb index. + * @index: Pointer to the TRB index to increment. + * + * The index should never point to the link TRB. After incrementing, + * if it is point to the link TRB, wrap around to the beginning. The + * link TRB is always at the last TRB entry. + */ +static void dwc3_ep_inc_trb(u8 *index) +{ + (*index)++; + if (*index == (DWC3_TRB_NUM - 1)) + *index = 0; +} + +/** + * dwc3_ep_inc_enq - increment endpoint's enqueue pointer + * @dep: The endpoint whose enqueue pointer we're incrementing + */ +static void dwc3_ep_inc_enq(struct dwc3_ep *dep) +{ + dwc3_ep_inc_trb(&dep->trb_enqueue); +} + +/** + * dwc3_ep_inc_deq - increment endpoint's dequeue pointer + * @dep: The endpoint whose enqueue pointer we're incrementing + */ +static void dwc3_ep_inc_deq(struct dwc3_ep *dep) +{ + dwc3_ep_inc_trb(&dep->trb_dequeue); +} + +static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep, + struct dwc3_request *req, + int status) +{ + struct dwc3 *dwc = dep->dwc; + + list_del(&req->list); + req->remaining = 0; + req->needs_extra_trb = false; + + if (req->request.status == -EINPROGRESS) + req->request.status = status; + + if (req->request.length == 0) + return; + + if (req->trb) + dma_unmap_single(dwc->dev, req->request.dma, + req->request.length, + req->direction ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + req->trb = NULL; +} + +/** + * dwc3_gadget_giveback - call struct usb_request's ->complete callback + * @dep: The endpoint to whom the request belongs to + * @req: The request we're giving back + * @status: completion code for the request + * + * Must be called with controller's lock held and interrupts disabled. This + * function will unmap @req and call its ->complete() callback to notify upper + * layers that it has completed. + */ +void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status) +{ + struct dwc3 *dwc = dep->dwc; + + dwc3_gadget_del_and_unmap_request(dep, req, status); + dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n", + req, dep->name, req->request.actual, + req->request.length, status); + req->status = DWC3_REQUEST_STATUS_COMPLETED; + + spin_unlock(&dwc->lock); + req->request.complete(&dep->endpoint, &req->request); + spin_lock(&dwc->lock); +} + +/** + * dwc3_send_gadget_generic_command - issue a generic command for the controller + * @dwc: pointer to the controller context + * @cmd: the command to be issued + * @param: command parameter + * + * Caller should take care of locking. Issue @cmd with a given @param to @dwc + * and wait for its completion. + */ +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) +{ + u32 timeout = 500; + int status = 0; + int ret = 0; + u32 reg; + + dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param); + dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); + + do { + reg = dwc3_readl(dwc->regs, DWC3_DGCMD); + if (!(reg & DWC3_DGCMD_CMDACT)) { + dev_dbg(dwc->dev, "%s: Command Complete --> %d\n", + __func__, + DWC3_DGCMD_STATUS(reg)); + status = DWC3_DGCMD_STATUS(reg); + if (status) + ret = -EINVAL; + break; + } + + udelay(1); + } while (--timeout); + + if (!timeout) { + ret = -ETIMEDOUT; + status = -ETIMEDOUT; + } + + return ret; +} + +static int __dwc3_gadget_wakeup(struct dwc3 *dwc); +/** + * dwc3_send_gadget_ep_cmd - issue an endpoint command + * @dep: the endpoint to which the command is going to be issued + * @cmd: the command to be issued + * @params: parameters to the command + * + * Caller should handle locking. This function will issue @cmd with given + * @params to @dep and wait for its completion. + */ +int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, + struct dwc3_gadget_ep_cmd_params *params) +{ + const struct usb_endpoint_descriptor *desc = dep->endpoint.desc; + struct dwc3 *dwc = dep->dwc; + u32 timeout = 1000; + u32 saved_config = 0; + u32 reg; + + int cmd_status = 0; + int ret = -EINVAL; + + /* + * When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or + * GUSB2PHYCFG.SUSPHY is set, it must be cleared before issuing an + * endpoint command. + * + * Save and clear both GUSB2PHYCFG.ENBLSLPM and GUSB2PHYCFG.SUSPHY + * settings. Restore them after the command is completed. + * + * DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2 + */ + if (dwc->gadget.speed <= USB_SPEED_HIGH) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) { + saved_config |= DWC3_GUSB2PHYCFG_SUSPHY; + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + } + + if (reg & DWC3_GUSB2PHYCFG_ENBLSLPM) { + saved_config |= DWC3_GUSB2PHYCFG_ENBLSLPM; + reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; + } + + if (saved_config) + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } + + if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) { + int needs_wakeup; + + needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 || + dwc->link_state == DWC3_LINK_STATE_U2 || + dwc->link_state == DWC3_LINK_STATE_U3); + + if (unlikely(needs_wakeup)) { + ret = __dwc3_gadget_wakeup(dwc); + dev_warn(dwc->dev, "wakeup failed --> %d\n", ret); + } + } + + dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0); + dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1); + dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2); + + /* + * Synopsys Databook 2.60a states in section 6.3.2.5.6 of that if we're + * not relying on XferNotReady, we can make use of a special "No + * Response Update Transfer" command where we should clear both CmdAct + * and CmdIOC bits. + * + * With this, we don't need to wait for command completion and can + * straight away issue further commands to the endpoint. + * + * NOTICE: We're making an assumption that control endpoints will never + * make use of Update Transfer command. This is a safe assumption + * because we can never have more than one request at a time with + * Control Endpoints. If anybody changes that assumption, this chunk + * needs to be updated accordingly. + */ + if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_UPDATETRANSFER && + !usb_endpoint_xfer_isoc(desc)) + cmd &= ~(DWC3_DEPCMD_CMDIOC | DWC3_DEPCMD_CMDACT); + else + cmd |= DWC3_DEPCMD_CMDACT; + + dwc3_writel(dep->regs, DWC3_DEPCMD, cmd); + do { + reg = dwc3_readl(dep->regs, DWC3_DEPCMD); + if (!(reg & DWC3_DEPCMD_CMDACT)) { + cmd_status = DWC3_DEPCMD_STATUS(reg); + + switch (cmd_status) { + case 0: + ret = 0; + break; + case DEPEVT_TRANSFER_NO_RESOURCE: + ret = -EINVAL; + break; + case DEPEVT_TRANSFER_BUS_EXPIRY: + /* + * SW issues START TRANSFER command to + * isochronous ep with future frame interval. If + * future interval time has already passed when + * core receives the command, it will respond + * with an error status of 'Bus Expiry'. + * + * Instead of always returning -EINVAL, let's + * give a hint to the gadget driver that this is + * the case by returning -EAGAIN. + */ + ret = -EAGAIN; + break; + default: + dev_warn(dwc->dev, "UNKNOWN cmd status\n"); + } + + break; + } + } while (--timeout); + + if (timeout == 0) { + ret = -ETIMEDOUT; + cmd_status = -ETIMEDOUT; + } + + if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) { + dep->flags |= DWC3_EP_TRANSFER_STARTED; + dwc3_gadget_ep_get_transfer_index(dep); + } + + if (saved_config) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= saved_config; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } + + return ret; + +} + +static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + struct dwc3_gadget_ep_cmd_params params; + u32 cmd = DWC3_DEPCMD_CLEARSTALL; + + /* + * As of core revision 2.60a the recommended programming model + * is to set the ClearPendIN bit when issuing a Clear Stall EP + * command for IN endpoints. This is to prevent an issue where + * some (non-compliant) hosts may not send ACK TPs for pending + * IN transfers due to a mishandled error condition. Synopsys + * STAR 9000614252. + */ + if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) && + (dwc->gadget.speed >= USB_SPEED_SUPER)) + cmd |= DWC3_DEPCMD_CLEARPENDIN; + + memset(¶ms, 0, sizeof(params)); + + return dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); +} + +static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, + struct dwc3_trb *trb) +{ + u32 offset = (char *) trb - (char *) dep->trb_pool; + + return dep->trb_pool_dma + offset; +} + +static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) +{ + if (dep->trb_pool) + return 0; + + dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) * + DWC3_TRB_NUM, + &dep->trb_pool_dma); + if (!dep->trb_pool) { + dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n", + dep->name); + return -ENOMEM; + } + + return 0; +} + +static void dwc3_free_trb_pool(struct dwc3_ep *dep) +{ + dma_free_coherent(dep->trb_pool, 0, sizeof(dma_addr_t)); + + dep->trb_pool = NULL; + dep->trb_pool_dma = 0; +} + +static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + + memset(¶ms, 0x00, sizeof(params)); + + params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1); + + return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE, + ¶ms); +} + +/** + * dwc3_gadget_start_config - configure ep resources + * @dep: endpoint that is being enabled + * + * Issue a %DWC3_DEPCMD_DEPSTARTCFG command to @dep. After the command's + * completion, it will set Transfer Resource for all available endpoints. + * + * The assignment of transfer resources cannot perfectly follow the data book + * due to the fact that the controller driver does not have all knowledge of the + * configuration in advance. It is given this information piecemeal by the + * composite gadget framework after every SET_CONFIGURATION and + * SET_INTERFACE. Trying to follow the databook programming model in this + * scenario can cause errors. For two reasons: + * + * 1) The databook says to do %DWC3_DEPCMD_DEPSTARTCFG for every + * %USB_REQ_SET_CONFIGURATION and %USB_REQ_SET_INTERFACE (8.1.5). This is + * incorrect in the scenario of multiple interfaces. + * + * 2) The databook does not mention doing more %DWC3_DEPCMD_DEPXFERCFG for new + * endpoint on alt setting (8.1.6). + * + * The following simplified method is used instead: + * + * All hardware endpoints can be assigned a transfer resource and this setting + * will stay persistent until either a core reset or hibernation. So whenever we + * do a %DWC3_DEPCMD_DEPSTARTCFG(0) we can go ahead and do + * %DWC3_DEPCMD_DEPXFERCFG for every hardware endpoint as well. We are + * guaranteed that there are as many transfer resources as endpoints. + * + * This function is called for each endpoint when it is being enabled but is + * triggered only when called for EP0-out, which always happens first, and which + * should only happen in one of the above conditions. + */ +static int dwc3_gadget_start_config(struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3 *dwc; + u32 cmd; + int i; + int ret; + + if (dep->number) + return 0; + + memset(¶ms, 0x00, sizeof(params)); + cmd = DWC3_DEPCMD_DEPSTARTCFG; + dwc = dep->dwc; + + ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); + if (ret) + return ret; + + for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { + struct dwc3_ep *dep = dwc->eps[i]; + + if (!dep) + continue; + + ret = dwc3_gadget_set_xfer_resource(dep); + if (ret) + return ret; + } + + return 0; +} + +static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) +{ + const struct usb_ss_ep_comp_descriptor *comp_desc; + const struct usb_endpoint_descriptor *desc; + struct dwc3_gadget_ep_cmd_params params; + struct dwc3 *dwc = dep->dwc; + + comp_desc = dep->endpoint.comp_desc; + desc = dep->endpoint.desc; + + memset(¶ms, 0x00, sizeof(params)); + + params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc)) + | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)); + + /* Burst size is only needed in SuperSpeed mode */ + if (dwc->gadget.speed == USB_SPEED_SUPER) { + u32 burst = dep->endpoint.maxburst - 1; + params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst); + } + + params.param0 |= action; + if (action == DWC3_DEPCFG_ACTION_RESTORE) + params.param2 |= dep->saved_state; + + if (usb_endpoint_xfer_control(desc)) + params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN; + + if (dep->number <= 1 || usb_endpoint_xfer_isoc(desc)) + params.param1 |= DWC3_DEPCFG_XFER_NOT_READY_EN; + + if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) { + params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE + | DWC3_DEPCFG_STREAM_EVENT_EN; + dep->stream_capable = true; + } + + if (!usb_endpoint_xfer_control(desc)) + params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN; + + /* + * We are doing 1:1 mapping for endpoints, meaning + * Physical Endpoints 2 maps to Logical Endpoint 2 and + * so on. We consider the direction bit as part of the physical + * endpoint number. So USB endpoint 0x81 is 0x03. + */ + params.param1 |= DWC3_DEPCFG_EP_NUMBER(dep->number); + + /* + * We must use the lower 16 TX FIFOs even though + * HW might have more + */ + if (dep->direction) + params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1); + + if (desc->bInterval) { + params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1); + dep->interval = 1 << (desc->bInterval - 1); + } + + return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms); +} + +/** + * __dwc3_gadget_ep_enable - Initializes a HW endpoint + * @dep: endpoint to be initialized + * @desc: USB Endpoint Descriptor + * + * Caller should take care of locking + */ +static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) +{ + const struct usb_endpoint_descriptor *desc = dep->endpoint.desc; + struct dwc3 *dwc = dep->dwc; + + u32 reg; + int ret; + + dev_dbg(dwc->dev, "Enabling %s\n", dep->name); + + if (!(dep->flags & DWC3_EP_ENABLED)) { + ret = dwc3_gadget_start_config(dep); + if (ret) + return ret; + } + + ret = dwc3_gadget_set_ep_config(dep, action); + if (ret) + return ret; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + struct dwc3_trb *trb_st_hw; + struct dwc3_trb *trb_link; + + dep->type = usb_endpoint_type(desc); + dep->flags |= DWC3_EP_ENABLED; + + reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); + reg |= DWC3_DALEPENA_EP(dep->number); + dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + + if (usb_endpoint_xfer_control(desc)) + return 0; + + /* Initialize the TRB ring */ + dep->trb_dequeue = 0; + dep->trb_enqueue = 0; + memset(dep->trb_pool, 0, + sizeof(struct dwc3_trb) * DWC3_TRB_NUM); + + /* Link TRB. The HWO bit is never reset */ + trb_st_hw = &dep->trb_pool[0]; + + trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1]; + memset(trb_link, 0, sizeof(*trb_link)); + + trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, + trb_st_hw)); + trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, + trb_st_hw)); + trb_link->ctrl |= DWC3_TRBCTL_LINK_TRB; + trb_link->ctrl |= DWC3_TRB_CTRL_HWO; + } + + /* + * Issue StartTransfer here with no-op TRB so we can always rely on No + * Response Update Transfer command. + */ + if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) || + usb_endpoint_xfer_int(desc)) { + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_trb *trb; + dma_addr_t trb_dma; + u32 cmd; + + memset(¶ms, 0, sizeof(params)); + trb = &dep->trb_pool[0]; + trb_dma = dwc3_trb_dma_offset(dep, trb); + + params.param0 = upper_32_bits(trb_dma); + params.param1 = lower_32_bits(trb_dma); + + cmd = DWC3_DEPCMD_STARTTRANSFER; + + ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); + if (ret < 0) + return ret; + } + + return 0; +} + +static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, + bool interrupt); +static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_request *req; + + dwc3_stop_active_transfer(dep, true, false); + + /* - giveback all requests to gadget driver */ + while (!list_empty(&dep->started_list)) { + req = next_request(&dep->started_list); + + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + } + + while (!list_empty(&dep->pending_list)) { + req = next_request(&dep->pending_list); + + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + } +} + +/** + * __dwc3_gadget_ep_disable - Disables a HW endpoint + * @dep: the endpoint to disable + * + * This function also removes requests which are currently processed ny the + * hardware and those which are not yet scheduled. + * Caller should take care of locking. + */ +static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + u32 reg; + + dwc3_remove_requests(dwc, dep); + + /* make sure HW endpoint isn't stalled */ + if (dep->flags & DWC3_EP_STALL) + __dwc3_gadget_ep_set_halt(dep, 0, false); + + reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); + reg &= ~DWC3_DALEPENA_EP(dep->number); + dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + + dep->stream_capable = false; + dep->type = 0; + dep->flags = 0; + + /* Clear out the ep descriptors for non-ep0 */ + if (dep->number > 1) { + dep->endpoint.comp_desc = NULL; + dep->endpoint.desc = NULL; + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_ep0_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + return -EINVAL; +} + +static int dwc3_gadget_ep0_disable(struct usb_ep *ep) +{ + return -EINVAL; +} + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc3_ep *dep; + struct dwc3 *dwc; + unsigned long flags; + int ret; + + if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { + pr_debug("dwc3: invalid parameters\n"); + return -EINVAL; + } + + if (!desc->wMaxPacketSize) { + pr_debug("dwc3: missing wMaxPacketSize\n"); + return -EINVAL; + } + + dep = to_dwc3_ep(ep); + dwc = dep->dwc; + + if (dep->flags & DWC3_EP_ENABLED) { + WARN(true, "%s is already enabled\n", + dep->name); + return 0; + } + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_disable(struct usb_ep *ep) +{ + struct dwc3_ep *dep; + struct dwc3 *dwc; + unsigned long flags; + int ret; + + if (!ep) { + pr_debug("dwc3: invalid parameters\n"); + return -EINVAL; + } + + dep = to_dwc3_ep(ep); + dwc = dep->dwc; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + WARN(true, "%s is already disabled\n", + dep->name); + return 0; + } + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_disable(dep); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep) +{ + struct dwc3_request *req; + struct dwc3_ep *dep = to_dwc3_ep(ep); + + req = xzalloc(sizeof(*req)); + if (!req) + return NULL; + + req->direction = dep->direction; + req->epnum = dep->number; + req->dep = dep; + req->status = DWC3_REQUEST_STATUS_UNKNOWN; + + return &req->request; +} + +static void dwc3_gadget_ep_free_request(struct usb_ep *ep, + struct usb_request *request) +{ + struct dwc3_request *req = to_dwc3_request(request); + + kfree(req); +} + +/** + * dwc3_ep_prev_trb - returns the previous TRB in the ring + * @dep: The endpoint with the TRB ring + * @index: The index of the current TRB in the ring + * + * Returns the TRB prior to the one pointed to by the index. If the + * index is 0, we will wrap backwards, skip the link TRB, and return + * the one just before that. + */ +static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index) +{ + u8 tmp = index; + + if (!tmp) + tmp = DWC3_TRB_NUM - 1; + + return &dep->trb_pool[tmp - 1]; +} + +static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) +{ + struct dwc3_trb *tmp; + u8 trbs_left; + + /* + * If enqueue & dequeue are equal than it is either full or empty. + * + * One way to know for sure is if the TRB right before us has HWO bit + * set or not. If it has, then we're definitely full and can't fit any + * more transfers in our ring. + */ + if (dep->trb_enqueue == dep->trb_dequeue) { + tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue); + if (tmp->ctrl & DWC3_TRB_CTRL_HWO) + return 0; + + return DWC3_TRB_NUM - 1; + } + + trbs_left = dep->trb_dequeue - dep->trb_enqueue; + trbs_left &= (DWC3_TRB_NUM - 1); + + if (dep->trb_dequeue < dep->trb_enqueue) + trbs_left--; + + return trbs_left; +} + +static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, + dma_addr_t dma, unsigned length, + unsigned chain, unsigned node, + unsigned stream_id, unsigned short_not_ok, + unsigned no_interrupt) +{ + struct dwc3 *dwc = dep->dwc; + struct usb_gadget *gadget = &dwc->gadget; + enum usb_device_speed speed = gadget->speed; + + trb->size = DWC3_TRB_SIZE_LENGTH(length); + trb->bpl = lower_32_bits(dma); + trb->bph = upper_32_bits(dma); + + switch (usb_endpoint_type(dep->endpoint.desc)) { + case USB_ENDPOINT_XFER_CONTROL: + trb->ctrl = DWC3_TRBCTL_CONTROL_SETUP; + break; + + case USB_ENDPOINT_XFER_ISOC: + if (!node) { + trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; + + /* + * USB Specification 2.0 Section 5.9.2 states that: "If + * there is only a single transaction in the microframe, + * only a DATA0 data packet PID is used. If there are + * two transactions per microframe, DATA1 is used for + * the first transaction data packet and DATA0 is used + * for the second transaction data packet. If there are + * three transactions per microframe, DATA2 is used for + * the first transaction data packet, DATA1 is used for + * the second, and DATA0 is used for the third." + * + * IOW, we should satisfy the following cases: + * + * 1) length <= maxpacket + * - DATA0 + * + * 2) maxpacket < length <= (2 * maxpacket) + * - DATA1, DATA0 + * + * 3) (2 * maxpacket) < length <= (3 * maxpacket) + * - DATA2, DATA1, DATA0 + */ + if (speed == USB_SPEED_HIGH) { + struct usb_ep *ep = &dep->endpoint; + unsigned int mult = 2; + unsigned int maxp = usb_endpoint_maxp(ep->desc); + + if (length <= (2 * maxp)) + mult--; + + if (length <= maxp) + mult--; + + trb->size |= DWC3_TRB_SIZE_PCM1(mult); + } + } else { + trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS; + } + + /* always enable Interrupt on Missed ISOC */ + trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; + break; + + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + trb->ctrl = DWC3_TRBCTL_NORMAL; + break; + default: + /* + * This is only possible with faulty memory because we + * checked it already :) + */ + dev_warn(dwc->dev, "Unknown endpoint type %d\n", + usb_endpoint_type(dep->endpoint.desc)); + } + + /* + * Enable Continue on Short Packet + * when endpoint is not a stream capable + */ + if (usb_endpoint_dir_out(dep->endpoint.desc)) { + if (!dep->stream_capable) + trb->ctrl |= DWC3_TRB_CTRL_CSP; + + if (short_not_ok) + trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; + } + + if ((!no_interrupt && !chain) || + (dwc3_calc_trbs_left(dep) == 1)) + trb->ctrl |= DWC3_TRB_CTRL_IOC; + + if (chain) + trb->ctrl |= DWC3_TRB_CTRL_CHN; + + if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable) + trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id); + + trb->ctrl |= DWC3_TRB_CTRL_HWO; + + dwc3_ep_inc_enq(dep); +} + +/** + * dwc3_prepare_one_trb - setup one TRB from one request + * @dep: endpoint for which this request is prepared + * @req: dwc3_request pointer + * @chain: should this TRB be chained to the next? + * @node: only for isochronous endpoints. First TRB needs different type. + */ +static void dwc3_prepare_one_trb(struct dwc3_ep *dep, + struct dwc3_request *req, + unsigned chain, unsigned node) +{ + struct dwc3_trb *trb; + unsigned int length; + dma_addr_t dma; + unsigned stream_id = req->request.stream_id; + unsigned short_not_ok = req->request.short_not_ok; + unsigned no_interrupt = req->request.no_interrupt; + + length = req->request.length; + dma = req->request.dma; + + trb = &dep->trb_pool[dep->trb_enqueue]; + + if (!req->trb) { + dwc3_gadget_move_started_request(req); + req->trb = trb; + req->trb_dma = dwc3_trb_dma_offset(dep, trb); + } + + req->num_trbs++; + + __dwc3_prepare_one_trb(dep, trb, dma, length, chain, node, + stream_id, short_not_ok, no_interrupt); +} + +static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, + struct dwc3_request *req) +{ + unsigned int length = req->request.length; + unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); + unsigned int rem = length % maxp; + + if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) { + struct dwc3 *dwc = dep->dwc; + struct dwc3_trb *trb; + + req->needs_extra_trb = true; + + /* prepare normal TRB */ + dwc3_prepare_one_trb(dep, req, true, 0); + + /* Now prepare one extra TRB to align transfer size */ + trb = &dep->trb_pool[dep->trb_enqueue]; + req->num_trbs++; + __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem, + false, 1, req->request.stream_id, + req->request.short_not_ok, + req->request.no_interrupt); + } else if (req->request.zero && req->request.length && + (IS_ALIGNED(req->request.length, maxp))) { + struct dwc3 *dwc = dep->dwc; + struct dwc3_trb *trb; + + req->needs_extra_trb = true; + + /* prepare normal TRB */ + dwc3_prepare_one_trb(dep, req, true, 0); + + /* Now prepare one extra TRB to handle ZLP */ + trb = &dep->trb_pool[dep->trb_enqueue]; + req->num_trbs++; + __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0, + false, 1, req->request.stream_id, + req->request.short_not_ok, + req->request.no_interrupt); + } else { + dwc3_prepare_one_trb(dep, req, false, 0); + } +} + +/* + * dwc3_prepare_trbs - setup TRBs from requests + * @dep: endpoint for which requests are being prepared + * @starting: true if the endpoint is idle and no requests are queued. + * + * The function goes through the requests list and sets up TRBs for the + * transfers. The function returns once there are no more TRBs available or + * it runs out of requests. + */ +static void dwc3_prepare_trbs(struct dwc3_ep *dep) +{ + struct dwc3_request *req, *n; + struct dwc3 *dwc = dep->dwc; + dma_addr_t dma_addr; + + BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); + + list_for_each_entry_safe(req, n, &dep->pending_list, list) { + dma_addr = dma_map_single(dwc->dev, req->request.buf, + req->request.length, + dep->number ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (dma_mapping_error(dwc->dev, dma_addr)) + return; + + req->request.dma = dma_addr; + + dwc3_prepare_one_trb_linear(dep, req); + + if (!dwc3_calc_trbs_left(dep)) + return; + } +} + +static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_request *req; + int starting; + int ret; + u32 cmd; + + if (!dwc3_calc_trbs_left(dep)) + return 0; + + starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED); + + dwc3_prepare_trbs(dep); + + req = next_request(&dep->started_list); + if (!req) { + dep->flags |= DWC3_EP_PENDING_REQUEST; + return 0; + } + + memset(¶ms, 0, sizeof(params)); + + if (starting) { + params.param0 = upper_32_bits(req->trb_dma); + params.param1 = lower_32_bits(req->trb_dma); + cmd = DWC3_DEPCMD_STARTTRANSFER; + + if (dep->stream_capable) + cmd |= DWC3_DEPCMD_PARAM(req->request.stream_id); + + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) + cmd |= DWC3_DEPCMD_PARAM(dep->frame_number); + } else { + cmd = DWC3_DEPCMD_UPDATETRANSFER | + DWC3_DEPCMD_PARAM(dep->resource_index); + } + + ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); + if (ret < 0) { + /* + * FIXME we need to iterate over the list of requests + * here and stop, unmap, free and del each of the linked + * requests instead of what we do now. + */ + if (req->trb) + memset(req->trb, 0, sizeof(struct dwc3_trb)); + dwc3_gadget_del_and_unmap_request(dep, req, ret); + return ret; + } + + return 0; +} + +static int __dwc3_gadget_get_frame(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + return DWC3_DSTS_SOFFN(reg); +} + +/** + * dwc3_gadget_start_isoc_quirk - workaround invalid frame number + * @dep: isoc endpoint + * + * This function tests for the correct combination of BIT[15:14] from the 16-bit + * microframe number reported by the XferNotReady event for the future frame + * number to start the isoc transfer. + * + * In DWC_usb31 version 1.70a-ea06 and prior, for highspeed and fullspeed + * isochronous IN, BIT[15:14] of the 16-bit microframe number reported by the + * XferNotReady event are invalid. The driver uses this number to schedule the + * isochronous transfer and passes it to the START TRANSFER command. Because + * this number is invalid, the command may fail. If BIT[15:14] matches the + * internal 16-bit microframe, the START TRANSFER command will pass and the + * transfer will start at the scheduled time, if it is off by 1, the command + * will still pass, but the transfer will start 2 seconds in the future. For all + * other conditions, the START TRANSFER command will fail with bus-expiry. + * + * In order to workaround this issue, we can test for the correct combination of + * BIT[15:14] by sending START TRANSFER commands with different values of + * BIT[15:14]: 'b00, 'b01, 'b10, and 'b11. Each combination is 2^14 uframe apart + * (or 2 seconds). 4 seconds into the future will result in a bus-expiry status. + * As the result, within the 4 possible combinations for BIT[15:14], there will + * be 2 successful and 2 failure START COMMAND status. One of the 2 successful + * command status will result in a 2-second delay start. The smaller BIT[15:14] + * value is the correct combination. + * + * Since there are only 4 outcomes and the results are ordered, we can simply + * test 2 START TRANSFER commands with BIT[15:14] combinations 'b00 and 'b01 to + * deduce the smaller successful combination. + * + * Let test0 = test status for combination 'b00 and test1 = test status for 'b01 + * of BIT[15:14]. The correct combination is as follow: + * + * if test0 fails and test1 passes, BIT[15:14] is 'b01 + * if test0 fails and test1 fails, BIT[15:14] is 'b10 + * if test0 passes and test1 fails, BIT[15:14] is 'b11 + * if test0 passes and test1 passes, BIT[15:14] is 'b00 + * + * Synopsys STAR 9001202023: Wrong microframe number for isochronous IN + * endpoints. + */ +static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) +{ + int cmd_status = 0; + bool test0; + bool test1; + + while (dep->combo_num < 2) { + struct dwc3_gadget_ep_cmd_params params; + u32 test_frame_number; + u32 cmd; + + /* + * Check if we can start isoc transfer on the next interval or + * 4 uframes in the future with BIT[15:14] as dep->combo_num + */ + test_frame_number = dep->frame_number & 0x3fff; + test_frame_number |= dep->combo_num << 14; + test_frame_number += max_t(u32, 4, dep->interval); + + params.param0 = upper_32_bits(dep->dwc->bounce_addr); + params.param1 = lower_32_bits(dep->dwc->bounce_addr); + + cmd = DWC3_DEPCMD_STARTTRANSFER; + cmd |= DWC3_DEPCMD_PARAM(test_frame_number); + cmd_status = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); + + /* Redo if some other failure beside bus-expiry is received */ + if (cmd_status && cmd_status != -EAGAIN) { + dep->start_cmd_status = 0; + dep->combo_num = 0; + return 0; + } + + /* Store the first test status */ + if (dep->combo_num == 0) + dep->start_cmd_status = cmd_status; + + dep->combo_num++; + + /* + * End the transfer if the START_TRANSFER command is successful + * to wait for the next XferNotReady to test the command again + */ + if (cmd_status == 0) { + dwc3_stop_active_transfer(dep, true, true); + return 0; + } + } + + /* test0 and test1 are both completed at this point */ + test0 = (dep->start_cmd_status == 0); + test1 = (cmd_status == 0); + + if (!test0 && test1) + dep->combo_num = 1; + else if (!test0 && !test1) + dep->combo_num = 2; + else if (test0 && !test1) + dep->combo_num = 3; + else if (test0 && test1) + dep->combo_num = 0; + + dep->frame_number &= 0x3fff; + dep->frame_number |= dep->combo_num << 14; + dep->frame_number += max_t(u32, 4, dep->interval); + + /* Reinitialize test variables */ + dep->start_cmd_status = 0; + dep->combo_num = 0; + + return __dwc3_gadget_kick_transfer(dep); +} + +static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + int ret; + int i; + + if (list_empty(&dep->pending_list)) { + dep->flags |= DWC3_EP_PENDING_REQUEST; + return -EAGAIN; + } + + if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) && + (dwc->revision <= DWC3_USB31_REVISION_160A || + (dwc->revision == DWC3_USB31_REVISION_170A && + dwc->version_type >= DWC31_VERSIONTYPE_EA01 && + dwc->version_type <= DWC31_VERSIONTYPE_EA06))) { + + if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction) + return dwc3_gadget_start_isoc_quirk(dep); + } + + for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) { + dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1); + + ret = __dwc3_gadget_kick_transfer(dep); + if (ret != -EAGAIN) + break; + } + + return ret; +} + +static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) +{ + struct dwc3 *dwc = dep->dwc; + + if (!dep->endpoint.desc) { + dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", + dep->name); + return -ESHUTDOWN; + } + + if (req->dep != dep) { + WARN(true, "request %p belongs to '%s'\n", + &req->request, req->dep->name); + return -EINVAL; + } + + if (req->status < DWC3_REQUEST_STATUS_COMPLETED) { + WARN(true, "request %p already in flight\n", &req->request); + return -EINVAL; + } + + req->request.actual = 0; + req->request.status = -EINPROGRESS; + + list_add_tail(&req->list, &dep->pending_list); + req->status = DWC3_REQUEST_STATUS_QUEUED; + + /* + * NOTICE: Isochronous endpoints should NEVER be prestarted. We must + * wait for a XferNotReady event so we will know what's the current + * (micro-)frame number. + * + * Without this trick, we are very, very likely gonna get Bus Expiry + * errors which will force us issue EndTransfer command. + */ + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + if (!(dep->flags & DWC3_EP_PENDING_REQUEST) && + !(dep->flags & DWC3_EP_TRANSFER_STARTED)) + return 0; + + if ((dep->flags & DWC3_EP_PENDING_REQUEST)) { + if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) { + return __dwc3_gadget_start_isoc(dep); + } + } + } + + return __dwc3_gadget_kick_transfer(dep); +} + +static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + + unsigned long flags; + + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_queue(dep, req); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, + struct dwc3_request *req) +{ + int i; + + /* + * If request was already started, this means we had to + * stop the transfer. With that we also need to ignore + * all TRBs used by the request, however TRBs can only + * be modified after completion of END_TRANSFER + * command. So what we do here is that we wait for + * END_TRANSFER completion and only after that, we jump + * over TRBs by clearing HWO and incrementing dequeue + * pointer. + */ + for (i = 0; i < req->num_trbs; i++) { + struct dwc3_trb *trb; + + trb = req->trb + i; + trb->ctrl &= ~DWC3_TRB_CTRL_HWO; + dwc3_ep_inc_deq(dep); + } + + req->num_trbs = 0; +} + +static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep) +{ + struct dwc3_request *req; + struct dwc3_request *tmp; + + list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) { + dwc3_gadget_ep_skip_trbs(dep, req); + dwc3_gadget_giveback(dep, req, -ECONNRESET); + } +} + +static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, + struct usb_request *request) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_request *r = NULL; + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&dwc->lock, flags); + + list_for_each_entry(r, &dep->pending_list, list) { + if (r == req) + break; + } + + if (r != req) { + list_for_each_entry(r, &dep->started_list, list) { + if (r == req) + break; + } + if (r == req) { + /* wait until it is processed */ + dwc3_stop_active_transfer(dep, true, true); + + if (!r->trb) + goto out0; + + dwc3_gadget_move_cancelled_request(req); + if (dep->flags & DWC3_EP_TRANSFER_STARTED) + goto out0; + else + goto out1; + } + dev_err(dwc->dev, "request %p was not queued to %s\n", + request, ep->name); + ret = -EINVAL; + goto out0; + } + +out1: + /* giveback the request */ + dwc3_gadget_giveback(dep, req, -ECONNRESET); + +out0: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3 *dwc = dep->dwc; + int ret; + + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name); + return -EINVAL; + } + + memset(¶ms, 0x00, sizeof(params)); + + if (value) { + struct dwc3_trb *trb; + unsigned transfer_in_flight; + unsigned started; + + if (dep->number > 1) + trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue); + else + trb = &dwc->ep0_trb[dep->trb_enqueue]; + + transfer_in_flight = trb->ctrl & DWC3_TRB_CTRL_HWO; + started = !list_empty(&dep->started_list); + + if (!protocol && ((dep->direction && transfer_in_flight) || + (!dep->direction && started))) { + return -EAGAIN; + } + + ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETSTALL, + ¶ms); + if (ret) + dev_err(dwc->dev, "failed to set STALL on %s\n", + dep->name); + else + dep->flags |= DWC3_EP_STALL; + } else { + ret = dwc3_send_clear_stall_ep_cmd(dep); + if (ret) + dev_err(dwc->dev, "failed to clear STALL on %s\n", + dep->name); + else + dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); + } + + return ret; +} + +static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + unsigned long flags; + + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_set_halt(dep, value, false); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + unsigned long flags; + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + dep->flags |= DWC3_EP_WEDGE; + + if (dep->number == 0 || dep->number == 1) + ret = __dwc3_gadget_ep0_set_halt(ep, 1); + else + ret = __dwc3_gadget_ep_set_halt(dep, 1, false); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +/* -------------------------------------------------------------------------- */ + +static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +static const struct usb_ep_ops dwc3_gadget_ep0_ops = { + .enable = dwc3_gadget_ep0_enable, + .disable = dwc3_gadget_ep0_disable, + .alloc_request = dwc3_gadget_ep_alloc_request, + .free_request = dwc3_gadget_ep_free_request, + .queue = dwc3_gadget_ep0_queue, + .dequeue = dwc3_gadget_ep_dequeue, + .set_halt = dwc3_gadget_ep0_set_halt, + .set_wedge = dwc3_gadget_ep_set_wedge, +}; + +static const struct usb_ep_ops dwc3_gadget_ep_ops = { + .enable = dwc3_gadget_ep_enable, + .disable = dwc3_gadget_ep_disable, + .alloc_request = dwc3_gadget_ep_alloc_request, + .free_request = dwc3_gadget_ep_free_request, + .queue = dwc3_gadget_ep_queue, + .dequeue = dwc3_gadget_ep_dequeue, + .set_halt = dwc3_gadget_ep_set_halt, + .set_wedge = dwc3_gadget_ep_set_wedge, +}; + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_get_frame(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + + return __dwc3_gadget_get_frame(dwc); +} + +static int __dwc3_gadget_wakeup(struct dwc3 *dwc) +{ + int retries; + + int ret; + u32 reg; + + u8 link_state; + u8 speed; + + /* + * According to the Databook Remote wakeup request should + * be issued only when the device is in early suspend state. + * + * We can check that via USB Link State bits in DSTS register. + */ + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + speed = reg & DWC3_DSTS_CONNECTSPD; + if ((speed == DWC3_DSTS_SUPERSPEED) || + (speed == DWC3_DSTS_SUPERSPEED_PLUS)) + return 0; + + link_state = DWC3_DSTS_USBLNKST(reg); + + switch (link_state) { + case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */ + case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */ + break; + default: + return -EINVAL; + } + + ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV); + if (ret < 0) { + dev_err(dwc->dev, "failed to put link in Recovery\n"); + return ret; + } + + /* Recent versions do this automatically */ + if (dwc->revision < DWC3_REVISION_194A) { + /* write zeroes to Link Change Request */ + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + } + + /* poll until Link State changes to ON */ + retries = 20000; + + while (retries--) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + /* in HS, means ON */ + if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0) + break; + } + + if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) { + dev_err(dwc->dev, "failed to send remote wakeup\n"); + return -EINVAL; + } + + return 0; +} + +static int dwc3_gadget_wakeup(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_wakeup(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, + int is_selfpowered) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->is_selfpowered = !!is_selfpowered; + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} + +static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) +{ + u32 reg; + u32 timeout = 500; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (is_on) { + if (dwc->revision <= DWC3_REVISION_187A) { + reg &= ~DWC3_DCTL_TRGTULST_MASK; + reg |= DWC3_DCTL_TRGTULST_RX_DET; + } + + if (dwc->revision >= DWC3_REVISION_194A) + reg &= ~DWC3_DCTL_KEEP_CONNECT; + reg |= DWC3_DCTL_RUN_STOP; + + if (dwc->has_hibernation) + reg |= DWC3_DCTL_KEEP_CONNECT; + + dwc->pullups_connected = true; + } else { + reg &= ~DWC3_DCTL_RUN_STOP; + + if (dwc->has_hibernation && !suspend) + reg &= ~DWC3_DCTL_KEEP_CONNECT; + + dwc->pullups_connected = false; + } + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + do { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + reg &= DWC3_DSTS_DEVCTRLHLT; + } while (--timeout && !(!is_on ^ !reg)); + + if (!timeout) + return -ETIMEDOUT; + + dev_dbg(dwc->dev, "gadget %s data soft-%s\n", + dwc->gadget_driver + ? dwc->gadget_driver->function : "no-function", + is_on ? "connect" : "disconnect"); + + return 0; +} + +static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + int ret; + + is_on = !!is_on; + + /* + * Per databook, when we want to stop the gadget, if a control transfer + * is still in process, complete it and get the core into setup phase. + */ + if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) + dev_warn(dwc->dev, "not in SETUP phase\n"); + + spin_lock_irqsave(&dwc->lock, flags); + ret = dwc3_gadget_run_stop(dwc, is_on, false); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static void dwc3_gadget_enable_irq(struct dwc3 *dwc) +{ + u32 reg; + + /* Enable all but Start and End of Frame IRQs */ + reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | + DWC3_DEVTEN_EVNTOVERFLOWEN | + DWC3_DEVTEN_CMDCMPLTEN | + DWC3_DEVTEN_ERRTICERREN | + DWC3_DEVTEN_WKUPEVTEN | + DWC3_DEVTEN_ULSTCNGEN | + DWC3_DEVTEN_CONNECTDONEEN | + DWC3_DEVTEN_USBRSTEN | + DWC3_DEVTEN_DISCONNEVTEN); + + if (dwc->revision < DWC3_REVISION_250A) + reg |= DWC3_DEVTEN_ULSTCNGEN; + + dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); +} + +static void dwc3_gadget_disable_irq(struct dwc3 *dwc) +{ + /* mask all interrupts */ + dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); +} + +/** + * dwc3_gadget_setup_nump - calculate and initialize NUMP field of %DWC3_DCFG + * @dwc: pointer to our context structure + * + * The following looks like complex but it's actually very simple. In order to + * calculate the number of packets we can burst at once on OUT transfers, we're + * gonna use RxFIFO size. + * + * To calculate RxFIFO size we need two numbers: + * MDWIDTH = size, in bits, of the internal memory bus + * RAM2_DEPTH = depth, in MDWIDTH, of internal RAM2 (where RxFIFO sits) + * + * Given these two numbers, the formula is simple: + * + * RxFIFO Size = (RAM2_DEPTH * MDWIDTH / 8) - 24 - 16; + * + * 24 bytes is for 3x SETUP packets + * 16 bytes is a clock domain crossing tolerance + * + * Given RxFIFO Size, NUMP = RxFIFOSize / 1024; + */ +static void dwc3_gadget_setup_nump(struct dwc3 *dwc) +{ + u32 ram2_depth; + u32 mdwidth; + u32 nump; + u32 reg; + + ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7); + mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0); + + nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024; + nump = min_t(u32, nump, 16); + + /* update NumP */ + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~DWC3_DCFG_NUMP_MASK; + reg |= nump << DWC3_DCFG_NUMP_SHIFT; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); +} + +static int __dwc3_gadget_start(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + int ret = 0; + u32 reg; + + /* + * We are telling dwc3 that we want to use DCFG.NUMP as ACK TP's NUMP + * field instead of letting dwc3 itself calculate that automatically. + * + * This way, we maximize the chances that we'll be able to get several + * bursts of data without going through any sort of endpoint throttling. + */ + reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); + if (dwc3_is_usb31(dwc)) + reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL; + else + reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL; + + dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); + + dwc3_gadget_setup_nump(dwc); + + /* Start with SuperSpeed Default */ + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + + dep = dwc->eps[0]; + ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + goto err0; + } + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + goto err1; + } + + /* begin to receive SETUP packets */ + dwc->ep0state = EP0_SETUP_PHASE; + dwc->link_state = DWC3_LINK_STATE_SS_DIS; + dwc3_ep0_out_start(dwc); + + dwc3_gadget_enable_irq(dwc); + + return 0; + +err1: + __dwc3_gadget_ep_disable(dwc->eps[0]); + +err0: + return ret; +} + +static int dwc3_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + int ret = 0; + + //dwc3_gadget_wakeup(g); + spin_lock_irqsave(&dwc->lock, flags); + if (dwc->gadget_driver) { + dev_err(dwc->dev, "%s is already bound to %s\n", + dwc->gadget.name, + dwc->gadget_driver->function); + ret = -EBUSY; + goto err1; + } + + dwc->gadget_driver = driver; + + __dwc3_gadget_start(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; + +err1: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + + dwc3_gadget_disable_irq(dwc); + __dwc3_gadget_ep_disable(dwc->eps[0]); + __dwc3_gadget_ep_disable(dwc->eps[1]); + + dwc->gadget_driver = NULL; + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} + +static void dwc3_gadget_set_speed(struct dwc3 *dwc, + enum usb_device_speed speed) +{ + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&dwc->lock, flags); + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_SPEED_MASK); + + /* + * WORKAROUND: DWC3 revision < 2.20a have an issue + * which would cause metastability state on Run/Stop + * bit if we try to force the IP to USB2-only mode. + * + * Because of that, we cannot configure the IP to any + * speed other than the SuperSpeed + * + * Refers to: + * + * STAR#9000525659: Clock Domain Crossing on DCTL in + * USB 2.0 Mode + */ + if (dwc->revision < DWC3_REVISION_220A && + !dwc->dis_metastability_quirk) { + reg |= DWC3_DCFG_SUPERSPEED; + } else { + switch (speed) { + case USB_SPEED_LOW: + reg |= DWC3_DCFG_LOWSPEED; + break; + case USB_SPEED_FULL: + reg |= DWC3_DCFG_FULLSPEED; + break; + case USB_SPEED_HIGH: + reg |= DWC3_DCFG_HIGHSPEED; + break; + case USB_SPEED_SUPER: + reg |= DWC3_DCFG_SUPERSPEED; + break; + case USB_SPEED_SUPER_PLUS: + if (dwc3_is_usb31(dwc)) + reg |= DWC3_DCFG_SUPERSPEED_PLUS; + else + reg |= DWC3_DCFG_SUPERSPEED; + break; + default: + dev_err(dwc->dev, "invalid speed (%d)\n", speed); + + if (dwc->revision & DWC3_REVISION_IS_DWC31) + reg |= DWC3_DCFG_SUPERSPEED_PLUS; + else + reg |= DWC3_DCFG_SUPERSPEED; + } + } + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + spin_unlock_irqrestore(&dwc->lock, flags); +} + +static void dwc3_gadget_poll(struct usb_gadget *g); + +static const struct usb_gadget_ops dwc3_gadget_ops = { + .get_frame = dwc3_gadget_get_frame, + .wakeup = dwc3_gadget_wakeup, + .set_selfpowered = dwc3_gadget_set_selfpowered, + .pullup = dwc3_gadget_pullup, + .udc_start = dwc3_gadget_start, + .udc_stop = dwc3_gadget_stop, + .udc_poll = dwc3_gadget_poll, +}; + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + + usb_ep_set_maxpacket_limit(&dep->endpoint, 512); + dep->endpoint.maxburst = 1; + dep->endpoint.ops = &dwc3_gadget_ep0_ops; + if (!dep->direction) + dwc->gadget.ep0 = &dep->endpoint; + + return 0; +} + +static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + int mdwidth; + int kbytes; + int size; + + mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); + /* MDWIDTH is represented in bits, we need it in bytes */ + mdwidth /= 8; + + size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1)); + if (dwc3_is_usb31(dwc)) + size = DWC31_GTXFIFOSIZ_TXFDEF(size); + else + size = DWC3_GTXFIFOSIZ_TXFDEF(size); + + /* FIFO Depth is in MDWDITH bytes. Multiply */ + size *= mdwidth; + + kbytes = size / 1024; + if (kbytes == 0) + kbytes = 1; + + /* + * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for + * internal overhead. We don't really know how these are used, + * but documentation say it exists. + */ + size -= mdwidth * (kbytes + 1); + size /= kbytes; + + usb_ep_set_maxpacket_limit(&dep->endpoint, size); + + dep->endpoint.max_streams = 15; + dep->endpoint.ops = &dwc3_gadget_ep_ops; + list_add_tail(&dep->endpoint.ep_list, + &dwc->gadget.ep_list); + + return dwc3_alloc_trb_pool(dep); +} + +static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + + usb_ep_set_maxpacket_limit(&dep->endpoint, 1024); + dep->endpoint.max_streams = 15; + dep->endpoint.ops = &dwc3_gadget_ep_ops; + list_add_tail(&dep->endpoint.ep_list, + &dwc->gadget.ep_list); + + return dwc3_alloc_trb_pool(dep); +} + +static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) +{ + struct dwc3_ep *dep; + bool direction = epnum & 1; + int ret; + u8 num = epnum >> 1; + + dep = kzalloc(sizeof(*dep), GFP_KERNEL); + if (!dep) + return -ENOMEM; + + dep->dwc = dwc; + dep->number = epnum; + dep->direction = direction; + dep->regs = dwc->regs + DWC3_DEP_BASE(epnum); + dwc->eps[epnum] = dep; + dep->combo_num = 0; + dep->start_cmd_status = 0; + + snprintf(dep->name, sizeof(dep->name), "ep%u%s", num, + direction ? "in" : "out"); + + dep->endpoint.name = dep->name; + + if (!(dep->number > 1)) { + dep->endpoint.desc = &dwc3_gadget_ep0_desc; + dep->endpoint.comp_desc = NULL; + } + + spin_lock_init(&dep->lock); + + if (num == 0) + ret = dwc3_gadget_init_control_endpoint(dep); + else if (direction) + ret = dwc3_gadget_init_in_endpoint(dep); + else + ret = dwc3_gadget_init_out_endpoint(dep); + + if (ret) + return ret; + + INIT_LIST_HEAD(&dep->pending_list); + INIT_LIST_HEAD(&dep->started_list); + INIT_LIST_HEAD(&dep->cancelled_list); + + return 0; +} + +static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total) +{ + u8 epnum; + + INIT_LIST_HEAD(&dwc->gadget.ep_list); + + for (epnum = 0; epnum < total; epnum++) { + int ret; + + ret = dwc3_gadget_init_endpoint(dwc, epnum); + if (ret) + return ret; + } + + return 0; +} + +static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + u8 epnum; + + for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + dep = dwc->eps[epnum]; + if (!dep) + continue; + /* + * Physical endpoints 0 and 1 are special; they form the + * bi-directional USB endpoint 0. + * + * For those two physical endpoints, we don't allocate a TRB + * pool nor do we add them the endpoints list. Due to that, we + * shouldn't do these two operations otherwise we would end up + * with all sorts of bugs when removing dwc3.ko. + */ + if (epnum != 0 && epnum != 1) { + dwc3_free_trb_pool(dep); + list_del(&dep->endpoint.ep_list); + } + + kfree(dep); + } +} + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, + struct dwc3_request *req, + struct dwc3_trb *trb, + const struct dwc3_event_depevt *event, + int status, int chain) +{ + unsigned int count; + + dwc3_ep_inc_deq(dep); + + req->num_trbs--; + + /* + * If we're in the middle of series of chained TRBs and we + * receive a short transfer along the way, DWC3 will skip + * through all TRBs including the last TRB in the chain (the + * where CHN bit is zero. DWC3 will also avoid clearing HWO + * bit and SW has to do it manually. + * + * We're going to do that here to avoid problems of HW trying + * to use bogus TRBs for transfers. + */ + if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO)) + trb->ctrl &= ~DWC3_TRB_CTRL_HWO; + + /* + * For isochronous transfers, the first TRB in a service interval must + * have the Isoc-First type. Track and report its interval frame number. + */ + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + (trb->ctrl & DWC3_TRBCTL_ISOCHRONOUS_FIRST)) { + unsigned int frame_number; + + frame_number = DWC3_TRB_CTRL_GET_SID_SOFN(trb->ctrl); + frame_number &= ~(dep->interval - 1); + } + + /* + * If we're dealing with unaligned size OUT transfer, we will be left + * with one TRB pending in the ring. We need to manually clear HWO bit + * from that TRB. + */ + + if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) { + trb->ctrl &= ~DWC3_TRB_CTRL_HWO; + return 1; + } + + count = trb->size & DWC3_TRB_SIZE_MASK; + req->remaining += count; + + if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) + return 1; + + if (event->status & DEPEVT_STATUS_SHORT && !chain) + return 1; + + if (event->status & DEPEVT_STATUS_IOC) + return 1; + + return 0; +} + +static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep, + struct dwc3_request *req, + const struct dwc3_event_depevt *event, + int status) +{ + struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue]; + + return dwc3_gadget_ep_reclaim_completed_trb(dep, req, trb, + event, status, false); +} + +static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event, + struct dwc3_request *req, + int status) +{ + int ret; + + ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, + status); + + if (req->needs_extra_trb) { + ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, + status); + req->needs_extra_trb = false; + } + + req->request.actual = req->request.length - req->remaining; + + dwc3_gadget_giveback(dep, req, status); + + return ret; +} + +static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event, + int status) +{ + struct dwc3_request *req; + struct dwc3_request *tmp; + + list_for_each_entry_safe(req, tmp, &dep->started_list, list) { + int ret; + + ret = dwc3_gadget_ep_cleanup_completed_request(dep, event, + req, status); + if (ret) + break; + } +} + +static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + dep->frame_number = event->parameters; +} + +static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + struct dwc3 *dwc = dep->dwc; + unsigned status = 0; + bool stop = false; + + dwc3_gadget_endpoint_frame_from_event(dep, event); + + if (event->status & DEPEVT_STATUS_BUSERR) + status = -ECONNRESET; + + if (event->status & DEPEVT_STATUS_MISSED_ISOC) { + status = -EXDEV; + + if (list_empty(&dep->started_list)) + stop = true; + } + + dwc3_gadget_ep_cleanup_completed_requests(dep, event, status); + + if (stop) { + dwc3_stop_active_transfer(dep, true, true); + dep->flags = DWC3_EP_ENABLED; + } + + /* + * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround. + * See dwc3_gadget_linksts_change_interrupt() for 1st half. + */ + if (dwc->revision < DWC3_REVISION_183A) { + u32 reg; + int i; + + for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { + dep = dwc->eps[i]; + + if (!(dep->flags & DWC3_EP_ENABLED)) + continue; + + if (!list_empty(&dep->started_list)) + return; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= dwc->u1u2; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + dwc->u1u2 = 0; + } +} + +static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + dwc3_gadget_endpoint_frame_from_event(dep, event); + (void) __dwc3_gadget_start_isoc(dep); +} + +static void dwc3_endpoint_interrupt(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep; + u8 epnum = event->endpoint_number; + u8 cmd; + + dep = dwc->eps[epnum]; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) + return; + + /* Handle only EPCMDCMPLT when EP disabled */ + if (event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT) + return; + } + + if (epnum == 0 || epnum == 1) { + dwc3_ep0_interrupt(dwc, event); + return; + } + + switch (event->endpoint_event) { + case DWC3_DEPEVT_XFERINPROGRESS: + dwc3_gadget_endpoint_transfer_in_progress(dep, event); + break; + case DWC3_DEPEVT_XFERNOTREADY: + dwc3_gadget_endpoint_transfer_not_ready(dep, event); + break; + case DWC3_DEPEVT_EPCMDCMPLT: + cmd = DEPEVT_PARAMETER_CMD(event->parameters); + + if (cmd == DWC3_DEPCMD_ENDTRANSFER) { + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + dwc3_gadget_ep_cleanup_cancelled_requests(dep); + } + break; + case DWC3_DEPEVT_STREAMEVT: + case DWC3_DEPEVT_XFERCOMPLETE: + case DWC3_DEPEVT_RXTXFIFOEVT: + break; + } +} + +static void dwc3_disconnect_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->disconnect(&dwc->gadget); + spin_lock(&dwc->lock); + } +} + +static void dwc3_suspend_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->suspend) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->suspend(&dwc->gadget); + spin_lock(&dwc->lock); + } +} + +static void dwc3_resume_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->resume) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->resume(&dwc->gadget); + spin_lock(&dwc->lock); + } +} + +static void dwc3_reset_gadget(struct dwc3 *dwc) +{ + if (!dwc->gadget_driver) + return; + + if (dwc->gadget.speed != USB_SPEED_UNKNOWN) { + spin_unlock(&dwc->lock); + usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver); + spin_lock(&dwc->lock); + } +} + +static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, + bool interrupt) +{ + struct dwc3 *dwc = dep->dwc; + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + int ret; + + if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) + return; + + /* + * NOTICE: We are violating what the Databook says about the + * EndTransfer command. Ideally we would _always_ wait for the + * EndTransfer Command Completion IRQ, but that's causing too + * much trouble synchronizing between us and gadget driver. + * + * We have discussed this with the IP Provider and it was + * suggested to giveback all requests here, but give HW some + * extra time to synchronize with the interconnect. We're using + * an arbitraty 100us delay for that. + * + * Note also that a similar handling was tested by Synopsys + * (thanks a lot Paul) and nothing bad has come out of it. + * In short, what we're doing is: + * + * - Issue EndTransfer WITH CMDIOC bit set + * - Wait 100us + */ + + cmd = DWC3_DEPCMD_ENDTRANSFER; + cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0; + cmd |= DWC3_DEPCMD_CMDIOC; + cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); + dep->resource_index = 0; + + if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) + udelay(100); +} + +static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) +{ + u32 epnum; + + for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + struct dwc3_ep *dep; + + dep = dwc->eps[epnum]; + if (!dep) + continue; + + if (!(dep->flags & DWC3_EP_STALL)) + continue; + + dep->flags &= ~DWC3_EP_STALL; + + dwc3_send_clear_stall_ep_cmd(dep); + } +} + +static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) +{ + int reg; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_INITU1ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + reg &= ~DWC3_DCTL_INITU2ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + dwc3_disconnect_gadget(dwc); + + dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->setup_packet_pending = false; + usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED); + + dwc->connected = false; +} + +static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) +{ + u32 reg; + + dwc->connected = true; + + /* + * WORKAROUND: DWC3 revisions <1.88a have an issue which + * would cause a missing Disconnect Event if there's a + * pending Setup Packet in the FIFO. + * + * There's no suggested workaround on the official Bug + * report, which states that "unless the driver/application + * is doing any special handling of a disconnect event, + * there is no functional issue". + * + * Unfortunately, it turns out that we _do_ some special + * handling of a disconnect event, namely complete all + * pending transfers, notify gadget driver of the + * disconnection, and so on. + * + * Our suggested workaround is to follow the Disconnect + * Event steps here, instead, based on a setup_packet_pending + * flag. Such flag gets set whenever we have a SETUP_PENDING + * status for EP0 TRBs and gets cleared on XferComplete for the + * same endpoint. + * + * Refers to: + * + * STAR#9000466709: RTL: Device : Disconnect event not + * generated if setup packet pending in FIFO + */ + if (dwc->revision < DWC3_REVISION_188A) { + if (dwc->setup_packet_pending) + dwc3_gadget_disconnect_interrupt(dwc); + } + + dwc3_reset_gadget(dwc); + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_TSTCTRL_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc->test_mode = false; + dwc3_clear_stall_all_ep(dwc); + + /* Reset device address to zero */ + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_DEVADDR_MASK); + dwc3_writel(dwc->regs, DWC3_DCFG, reg); +} + +static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + int ret; + u32 reg; + u8 speed; + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + speed = reg & DWC3_DSTS_CONNECTSPD; + dwc->speed = speed; + + switch (speed) { + case DWC3_DSTS_SUPERSPEED_PLUS: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + dwc->gadget.ep0->maxpacket = 512; + dwc->gadget.speed = USB_SPEED_SUPER_PLUS; + break; + case DWC3_DCFG_SUPERSPEED: + /* + * WORKAROUND: DWC3 revisions <1.90a have an issue which + * would cause a missing USB3 Reset event. + * + * In such situations, we should force a USB3 Reset + * event by calling our dwc3_gadget_reset_interrupt() + * routine. + * + * Refers to: + * + * STAR#9000483510: RTL: SS : USB3 reset event may + * not be generated always when the link enters poll + */ + if (dwc->revision < DWC3_REVISION_190A) + dwc3_gadget_reset_interrupt(dwc); + + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + dwc->gadget.ep0->maxpacket = 512; + dwc->gadget.speed = USB_SPEED_SUPER; + break; + case DWC3_DCFG_HIGHSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); + dwc->gadget.ep0->maxpacket = 64; + dwc->gadget.speed = USB_SPEED_HIGH; + break; + case DWC3_DCFG_FULLSPEED: + case DWC3_DCFG_FULLSPEED1: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); + dwc->gadget.ep0->maxpacket = 64; + dwc->gadget.speed = USB_SPEED_FULL; + break; + case DWC3_DCFG_LOWSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); + dwc->gadget.ep0->maxpacket = 8; + dwc->gadget.speed = USB_SPEED_LOW; + break; + } + + dwc->eps[1]->endpoint.maxpacket = dwc->gadget.ep0->maxpacket; + + /* Enable USB2 LPM Capability */ + + if ((dwc->revision > DWC3_REVISION_194A) && + (speed != DWC3_DSTS_SUPERSPEED) && + (speed != DWC3_DSTS_SUPERSPEED_PLUS)) { + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg |= DWC3_DCFG_LPM_CAP; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); + + reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold); + + /* + * When dwc3 revisions >= 2.40a, LPM Erratum is enabled and + * DCFG.LPMCap is set, core responses with an ACK and the + * BESL value in the LPM token is less than or equal to LPM + * NYET threshold. + */ + if (dwc->revision < DWC3_REVISION_240A && dwc->has_lpm_erratum) + WARN(true, "LPM Erratum not available on dwc3 revisisions < 2.40a\n"); + + if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A) + reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold); + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + } else { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_HIRD_THRES_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + } + + dep = dwc->eps[0]; + ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_MODIFY); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + return; + } + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_MODIFY); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + return; + } + + /* + * Configure PHY via GUSB3PIPECTLn if required. + * + * Update GTXFIFOSIZn + * + * In both cases reset values should be sufficient. + */ +} + +static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) +{ + /* + * TODO take core out of low power mode when that's + * implemented. + */ + + if (dwc->gadget_driver && dwc->gadget_driver->resume) + dwc->gadget_driver->resume(&dwc->gadget); +} + +static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, + unsigned int evtinfo) +{ + enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; + unsigned int pwropt; + + /* + * WORKAROUND: DWC3 < 2.50a have an issue when configured without + * Hibernation mode enabled which would show up when device detects + * host-initiated U3 exit. + * + * In that case, device will generate a Link State Change Interrupt + * from U3 to RESUME which is only necessary if Hibernation is + * configured in. + * + * There are no functional changes due to such spurious event and we + * just need to ignore it. + * + * Refers to: + * + * STAR#9000570034 RTL: SS Resume event generated in non-Hibernation + * operational mode + */ + pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1); + if ((dwc->revision < DWC3_REVISION_250A) && + (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) { + if ((dwc->link_state == DWC3_LINK_STATE_U3) && + (next == DWC3_LINK_STATE_RESUME)) { + dev_dbg(dwc->dev, "ignoring transition U3 -> Resume\n"); + return; + } + } + + /* + * WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending + * on the link partner, the USB session might do multiple entry/exit + * of low power states before a transfer takes place. + * + * Due to this problem, we might experience lower throughput. The + * suggested workaround is to disable DCTL[12:9] bits if we're + * transitioning from U1/U2 to U0 and enable those bits again + * after a transfer completes and there are no pending transfers + * on any of the enabled endpoints. + * + * This is the first half of that workaround. + * + * Refers to: + * + * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us + * core send LGO_Ux entering U0 + */ + if (dwc->revision < DWC3_REVISION_183A) { + if (next == DWC3_LINK_STATE_U0) { + u32 u1u2; + u32 reg; + + switch (dwc->link_state) { + case DWC3_LINK_STATE_U1: + case DWC3_LINK_STATE_U2: + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + u1u2 = reg & (DWC3_DCTL_INITU2ENA + | DWC3_DCTL_ACCEPTU2ENA + | DWC3_DCTL_INITU1ENA + | DWC3_DCTL_ACCEPTU1ENA); + + if (!dwc->u1u2) + dwc->u1u2 = reg & u1u2; + + reg &= ~u1u2; + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + break; + default: + /* do nothing */ + break; + } + } + } + + switch (next) { + case DWC3_LINK_STATE_U1: + if (dwc->speed == USB_SPEED_SUPER) + dwc3_suspend_gadget(dwc); + break; + case DWC3_LINK_STATE_U2: + case DWC3_LINK_STATE_U3: + //dwc3_suspend_gadget(dwc); + break; + case DWC3_LINK_STATE_RESUME: + dwc3_resume_gadget(dwc); + break; + default: + /* do nothing */ + break; + } + + dwc->link_state = next; +} + +static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc, + unsigned int evtinfo) +{ + enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; + + if (dwc->link_state != next && next == DWC3_LINK_STATE_U3) + dwc3_suspend_gadget(dwc); + + dwc->link_state = next; +} + +static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc, + unsigned int evtinfo) +{ + unsigned int is_ss = evtinfo & (1UL << 4); + + /** + * WORKAROUND: DWC3 revison 2.20a with hibernation support + * have a known issue which can cause USB CV TD.9.23 to fail + * randomly. + * + * Because of this issue, core could generate bogus hibernation + * events which SW needs to ignore. + * + * Refers to: + * + * STAR#9000546576: Device Mode Hibernation: Issue in USB 2.0 + * Device Fallback from SuperSpeed + */ + if (is_ss ^ (dwc->speed == USB_SPEED_SUPER)) + return; + + /* enter hibernation here */ +} + +static void dwc3_gadget_interrupt(struct dwc3 *dwc, + const struct dwc3_event_devt *event) +{ + + switch (event->type) { + case DWC3_DEVICE_EVENT_DISCONNECT: + dwc3_gadget_disconnect_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_RESET: + dwc3_gadget_reset_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + dwc3_gadget_conndone_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_WAKEUP: + dwc3_gadget_wakeup_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_HIBER_REQ: + if (!dwc->has_hibernation) { + WARN(1 ,"unexpected hibernation event\n"); + break; + } + dwc3_gadget_hibernation_interrupt(dwc, event->event_info); + break; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); + break; + case DWC3_DEVICE_EVENT_EOPF: + dev_dbg(dwc->dev, "End of Periodic Frame\n"); + /* It changed to be suspend event for version 2.30a and above */ + if (dwc->revision >= DWC3_REVISION_230A) { + /* + * Ignore suspend event until the gadget enters into + * USB_STATE_CONFIGURED state. + */ + if (dwc->gadget.state >= USB_STATE_CONFIGURED) + dwc3_gadget_suspend_interrupt(dwc, + event->event_info); + } + break; + case DWC3_DEVICE_EVENT_SOF: + dev_dbg(dwc->dev, "Start of Periodic Frame\n"); + break; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + dev_dbg(dwc->dev, "Erratic Error\n"); + break; + case DWC3_DEVICE_EVENT_CMD_CMPL: + dev_dbg(dwc->dev, "Command Complete\n"); + break; + case DWC3_DEVICE_EVENT_OVERFLOW: + dev_dbg(dwc->dev, "Overflow\n"); + break; + default: + dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type); + } +} + +static void dwc3_process_event_entry(struct dwc3 *dwc, + const union dwc3_event *event) +{ + if (!event->type.is_devspec) + dwc3_endpoint_interrupt(dwc, &event->depevt); + else if (event->type.type == DWC3_EVENT_TYPE_DEV) + dwc3_gadget_interrupt(dwc, &event->devt); +} + +static void dwc3_gadget_poll(struct usb_gadget * g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + struct dwc3_event_buffer *evt = dwc->ev_buf; + u32 amount; + u32 count; + void *buf; + int pos = 0; + + count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); + count &= DWC3_GEVNTCOUNT_MASK; + if (!count) + return; + + buf = xzalloc(count); + + amount = min(count, evt->length - evt->lpos); + memcpy(buf, evt->buf + evt->lpos, amount); + + if (amount < count) + memcpy(buf + amount, evt->buf, count - amount); + + evt->lpos = (evt->lpos + count) % evt->length; + + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count); + + while (count > 0) { + union dwc3_event event; + + event.raw = *(u32 *)(buf + pos); + + dwc3_process_event_entry(dwc, &event); + + count -= 4; + pos += 4; + } + + free(buf); +} + +/** + * dwc3_gadget_init - Initializes gadget related registers + * @dwc: pointer to our controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +int dwc3_gadget_init(struct dwc3 *dwc) +{ + int ret; + + dwc->ep0_trb = dma_alloc_coherent(sizeof(*dwc->ep0_trb) * 2, + &dwc->ep0_trb_addr); + if (!dwc->ep0_trb) { + dev_err(dwc->dev, "failed to allocate ep0 trb\n"); + ret = -ENOMEM; + goto err1; + } + + dwc->setup_buf = xzalloc(DWC3_EP0_SETUP_SIZE); + if (!dwc->setup_buf) { + ret = -ENOMEM; + goto err2; + } + + dwc->bounce = dma_alloc_coherent(DWC3_BOUNCE_SIZE, + &dwc->bounce_addr); + if (!dwc->bounce) { + dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n"); + ret = -ENOMEM; + goto err3; + } + + dwc->gadget.ops = &dwc3_gadget_ops; + dwc->gadget.max_speed = USB_SPEED_SUPER; + dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->gadget.name = "dwc3-gadget"; + + /* + * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize + * on ep out. + */ + dwc->gadget.quirk_ep_out_aligned_size = true; + + if (dwc->revision < DWC3_REVISION_220A && + !dwc->dis_metastability_quirk) + dev_info(dwc->dev, "changing max_speed on rev %08x\n", + dwc->revision); + + dwc->gadget.max_speed = dwc->maximum_speed; + + /* + * REVISIT: Here we should clear all pending IRQs to be + * sure we're starting from a well known location. + */ + + ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps); + if (ret) + goto err4; + + ret = usb_add_gadget_udc((struct device_d *)dwc->dev, &dwc->gadget); + if (ret) { + dev_err(dwc->dev, "failed to register udc\n"); + goto err4; + } + + dwc3_gadget_set_speed(dwc, dwc->maximum_speed); + + return 0; + +err4: + dwc3_gadget_free_endpoints(dwc); +err3: + dma_free_coherent(dwc->bounce, 0, DWC3_BOUNCE_SIZE); + +err2: + kfree(dwc->setup_buf); + +err1: + dma_free_coherent(dwc->ep0_trb, 0, sizeof(*dwc->ep0_trb) * 2); + + return ret; +} diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h new file mode 100644 index 0000000000..3ce748c0b4 --- /dev/null +++ b/drivers/usb/dwc3/gadget.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** + * gadget.h - DesignWare USB3 DRD Gadget Header + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.h) and ported + * to uboot. + * + * commit 7a60855972 : usb: dwc3: gadget: fix set_halt() bug with pending + transfers + * + */ + +#ifndef __DRIVERS_USB_DWC3_GADGET_H +#define __DRIVERS_USB_DWC3_GADGET_H + +#include <usb/gadget.h> +#include <linux/list.h> +#include "io.h" + +struct dwc3; +#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint)) +#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget)) + +/* DEPCFG parameter 1 */ +#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0) +#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8) +#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9) +#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10) +#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11) +#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13) +#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16) +#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24) +#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25) +#define DWC3_DEPCFG_BULK_BASED (1 << 30) +#define DWC3_DEPCFG_FIFO_BASED (1 << 31) + +/* DEPCFG parameter 0 */ +#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1) +#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3) +#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17) +#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22) +#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26) +/* This applies for core versions earlier than 1.94a */ +#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31) +/* These apply for core versions 1.94a and later */ +#define DWC3_DEPCFG_ACTION_INIT (0 << 30) +#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30) +#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30) + +/* DEPXFERCFG parameter 0 */ +#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) + +/* -------------------------------------------------------------------------- */ + +#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) + +static inline struct dwc3_request *next_request(struct list_head *list) +{ + if (list_empty(list)) + return NULL; + + return list_first_entry(list, struct dwc3_request, list); +} + +/** + * dwc3_gadget_move_started_request - move @req to the started_list + * @req: the request to be moved + * + * Caller should take care of locking. This function will move @req from its + * current list to the endpoint's started_list. + */ +static inline void dwc3_gadget_move_started_request(struct dwc3_request *req) +{ + struct dwc3_ep *dep = req->dep; + + req->status = DWC3_REQUEST_STATUS_STARTED; + list_move_tail(&req->list, &dep->started_list); +} + +/** + * dwc3_gadget_move_cancelled_request - move @req to the cancelled_list + * @req: the request to be moved + * + * Caller should take care of locking. This function will move @req from its + * current list to the endpoint's cancelled_list. + */ +static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req) +{ + struct dwc3_ep *dep = req->dep; + + req->status = DWC3_REQUEST_STATUS_CANCELLED; + list_move_tail(&req->list, &dep->cancelled_list); +} + +void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status); + +void dwc3_ep0_interrupt(struct dwc3 *dwc, + const struct dwc3_event_depevt *event); +void dwc3_ep0_out_start(struct dwc3 *dwc); +int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); +int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); +int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request); +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); +void dwc3_gadget_handle_interrupt(struct dwc3 *dwc); + +/** + * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW + * @dep: dwc3 endpoint + * + * Caller should take care of locking. Returns the transfer resource + * index for a given endpoint. + */ +static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep) +{ + u32 res_id; + + res_id = dwc3_readl(dep->regs, DWC3_DEPCMD); + dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id); +} + +#endif /* __DRIVERS_USB_DWC3_GADGET_H */ diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ca1bfc1b4e..9d6a262038 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -19,7 +19,6 @@ config USB_GADGET_DRIVER_AT91 bool prompt "at91 gadget driver" depends on ARCH_AT91 - depends on !ARCH_SAMA5D4 default y select USB_GADGET_DUALSPEED diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index efbc574c23..411464690d 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -31,9 +31,7 @@ #include <asm/byteorder.h> #include <mach/hardware.h> -#if defined CONFIG_ARCH_AT91SAM9261 || defined CONFIG_ARCH_AT91SAM9G10 #include <mach/at91sam9261.h> -#endif #include <mach/board.h> #include <mach/cpu.h> #include <mach/at91sam9261_matrix.h> @@ -693,12 +691,10 @@ static void pullup(struct at91_udc *udc, int is_on) txvc |= AT91_UDP_TXVC_PUON; at91_udp_write(udc, AT91_UDP_TXVC, txvc); } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { -#if defined CONFIG_ARCH_AT91SAM9261 || defined CONFIG_ARCH_AT91SAM9G10 u32 usbpucr; usbpucr = readl(AT91SAM9261_BASE_MATRIX + AT91SAM9261_MATRIX_USBPUCR); usbpucr |= AT91SAM9261_MATRIX_USBPUCR_PUON; writel(usbpucr, AT91SAM9261_BASE_MATRIX + AT91SAM9261_MATRIX_USBPUCR); -#endif } } else { stop_activity(udc); @@ -712,12 +708,10 @@ static void pullup(struct at91_udc *udc, int is_on) txvc &= ~AT91_UDP_TXVC_PUON; at91_udp_write(udc, AT91_UDP_TXVC, txvc); } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { -#if defined CONFIG_ARCH_AT91SAM9261 || defined CONFIG_ARCH_AT91SAM9G10 u32 usbpucr; usbpucr = readl(AT91SAM9261_BASE_MATRIX + AT91SAM9261_MATRIX_USBPUCR); usbpucr &= ~AT91SAM9261_MATRIX_USBPUCR_PUON; writel(usbpucr, AT91SAM9261_BASE_MATRIX + AT91SAM9261_MATRIX_USBPUCR); -#endif } clk_off(udc); } diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 1cfc49d1c5..b66aa6be97 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1194,10 +1194,45 @@ EXPORT_SYMBOL_GPL(usb_string_ids_n); static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) { + struct usb_composite_dev *cdev; + if (req->status || req->actual != req->length) DBG((struct usb_composite_dev *) ep->driver_data, "setup complete --> %d, %d/%d\n", req->status, req->actual, req->length); + + /* + * REVIST The same ep0 requests are shared with function drivers + * so they don't have to maintain the same ->complete() stubs. + * + * Because of that, we need to check for the validity of ->context + * here, even though we know we've set it to something useful. + */ + if (!req->context) + return; + + cdev = req->context; + + if (cdev->req == req) + cdev->setup_pending = false; + else + WARN(1, "unknown request %p\n", req); +} + +static int composite_ep0_queue(struct usb_composite_dev *cdev, + struct usb_request *req) +{ + int ret; + + ret = usb_ep_queue(cdev->gadget->ep0, req); + if (ret == 0) { + if (cdev->req == req) + cdev->setup_pending = true; + else + WARN(1, "unknown request %p\n", req); + } + + return ret; } /* @@ -1226,6 +1261,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) * when we delegate to it. */ req->zero = 0; + req->context = cdev; req->complete = composite_setup_complete; req->length = 0; gadget->ep0->driver_data = cdev; @@ -1469,7 +1505,7 @@ unknown: if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) { req->length = value; req->zero = value < w_length; - value = usb_ep_queue(gadget->ep0, req); + value = composite_ep0_queue(cdev, req); if (value < 0) { DBG(cdev, "ep_queue --> %d\n", value); req->status = 0; @@ -1621,7 +1657,8 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) kfree(uc); } if (cdev->req) { - usb_ep_dequeue(cdev->gadget->ep0, cdev->req); + if (cdev->setup_pending) + usb_ep_dequeue(cdev->gadget->ep0, cdev->req); kfree(cdev->req->buf); usb_ep_free_request(cdev->gadget->ep0, cdev->req); } @@ -1753,7 +1790,7 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev) } else if (--cdev->delayed_status == 0) { DBG(cdev, "%s: Completing delayed status\n", __func__); req->length = 0; - value = usb_ep_queue(cdev->gadget->ep0, req); + value = composite_ep0_queue(cdev, req); if (value < 0) { DBG(cdev, "ep_queue --> %d\n", value); req->status = 0; diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index f0f576d708..ced568921b 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -279,25 +279,6 @@ struct usb_ep *usb_ep_autoconfig_ss( ep_comp)) goto found_ep; } - -#ifdef CONFIG_BLACKFIN - } else if (gadget_is_musbhdrc(gadget)) { - if ((USB_ENDPOINT_XFER_BULK == type) || - (USB_ENDPOINT_XFER_ISOC == type)) { - if (USB_DIR_IN & desc->bEndpointAddress) - ep = find_ep (gadget, "ep5in"); - else - ep = find_ep (gadget, "ep6out"); - } else if (USB_ENDPOINT_XFER_INT == type) { - if (USB_DIR_IN & desc->bEndpointAddress) - ep = find_ep(gadget, "ep1in"); - else - ep = find_ep(gadget, "ep2out"); - } else - ep = NULL; - if (ep && ep_matches(gadget, ep, desc, ep_comp)) - goto found_ep; -#endif } /* Second, look at endpoints until an unclaimed one looks usable */ diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index cba59b1585..42a2b03ad2 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -104,7 +104,7 @@ acm_iad_descriptor = { .bInterfaceCount = 2, // control + data .bFunctionClass = USB_CLASS_COMM, .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, - .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + .bFunctionProtocol = USB_CDC_PROTO_NONE, /* .iFunction = DYNAMIC */ }; @@ -116,7 +116,7 @@ static struct usb_interface_descriptor acm_control_interface_desc = { .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + .bInterfaceProtocol = USB_CDC_PROTO_NONE, /* .iInterface = DYNAMIC */ }; diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index e357456098..096f05ed48 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -108,6 +108,21 @@ void usb_gadget_set_state(struct usb_gadget *gadget, } EXPORT_SYMBOL_GPL(usb_gadget_set_state); +/** + * usb_gadget_udc_reset - notifies the udc core that bus reset occurs + * @gadget: The gadget which bus reset occurs + * @driver: The gadget driver we want to notify + * + * If the udc driver has bus reset handler, it needs to call this when the bus + * reset occurs, it notifies the gadget driver that the bus reset occurs as + * well as updates gadget state. + */ +void usb_gadget_udc_reset(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + usb_gadget_set_state(gadget, USB_STATE_DEFAULT); +} +EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); /* ------------------------------------------------------------------------- */ /** diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 84a05c4f96..4c11e6580c 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -121,7 +121,6 @@ MODULE_ALIAS("platform:" MUSB_DRIVER_NAME); /*-------------------------------------------------------------------------*/ -#ifndef CONFIG_BLACKFIN static int musb_ulpi_read(struct usb_phy *phy, u32 offset) { void __iomem *addr = phy->io_priv; @@ -195,10 +194,6 @@ static int musb_ulpi_write(struct usb_phy *phy, u32 offset, u32 data) out: return ret; } -#else -#define musb_ulpi_read NULL -#define musb_ulpi_write NULL -#endif struct usb_phy_io_ops musb_ulpi_access = { .read = musb_ulpi_read, @@ -207,7 +202,7 @@ struct usb_phy_io_ops musb_ulpi_access = { /*-------------------------------------------------------------------------*/ -#if !defined(CONFIG_USB_MUSB_TUSB6010) && !defined(CONFIG_USB_MUSB_BLACKFIN) +#if !defined(CONFIG_USB_MUSB_TUSB6010) /* * Load an endpoint's FIFO @@ -824,11 +819,9 @@ static int musb_core_init(u16 musb_type, struct musb *musb) } else { musb->is_multipoint = 0; type = ""; -#ifndef CONFIG_USB_OTG_BLACKLIST_HUB printk(KERN_ERR "%s: kernel must blacklist external hubs\n", musb_driver_name); -#endif } /* log release info */ diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index a21ddd5df2..d9402fcc4a 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -122,7 +122,7 @@ enum musb_g_ep0_state { */ #if defined(CONFIG_ARCH_DAVINCI) || defined(CONFIG_SOC_OMAP2430) \ - || defined(CONFIG_SOC_OMAP3430) || defined(CONFIG_BLACKFIN) \ + || defined(CONFIG_SOC_OMAP3430) \ || defined(CONFIG_ARCH_OMAP4) /* REVISIT indexed access seemed to * misbehave (on DaVinci) for at least peripheral IN ... @@ -437,34 +437,6 @@ static inline struct musb *gadget_to_musb(struct usb_gadget *g) return container_of(g, struct musb, g); } -#ifdef CONFIG_BLACKFIN -static inline int musb_read_fifosize(struct musb *musb, - struct musb_hw_ep *hw_ep, u8 epnum) -{ - musb->nr_endpoints++; - musb->epmask |= (1 << epnum); - - if (epnum < 5) { - hw_ep->max_packet_sz_tx = 128; - hw_ep->max_packet_sz_rx = 128; - } else { - hw_ep->max_packet_sz_tx = 1024; - hw_ep->max_packet_sz_rx = 1024; - } - hw_ep->is_shared_fifo = false; - - return 0; -} - -static inline void musb_configure_ep0(struct musb *musb) -{ - musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE; - musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE; - musb->endpoints[0].is_shared_fifo = true; -} - -#else - static inline int musb_read_fifosize(struct musb *musb, struct musb_hw_ep *hw_ep, u8 epnum) { @@ -501,8 +473,6 @@ static inline void musb_configure_ep0(struct musb *musb) musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE; musb->endpoints[0].is_shared_fifo = true; } -#endif /* CONFIG_BLACKFIN */ - /***************************** Glue it together *****************************/ diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 1345a4ff04..97a03cbcf4 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -80,17 +80,6 @@ struct musb_hw_ep; #define tusb_dma_omap() 0 #endif -/* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1 - * Only allow DMA mode 1 to be used when the USB will actually generate the - * interrupts we expect. - */ -#ifdef CONFIG_BLACKFIN -# undef USE_MODE1 -# if !ANOMALY_05000456 -# define USE_MODE1 -# endif -#endif - /* * DMA channel status ... updated by the dma controller driver whenever that * status changes, and protected by the overall controller spinlock. diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index a4be339355..ec474477a1 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -37,8 +37,6 @@ #include <io.h> -#ifndef CONFIG_BLACKFIN - /* NOTE: these offsets are all in bytes */ static inline u16 musb_readw(const void __iomem *addr, unsigned offset) @@ -97,26 +95,4 @@ static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data) #endif /* CONFIG_USB_MUSB_TUSB6010 */ -#else - -static inline u8 musb_readb(const void __iomem *addr, unsigned offset) - { return (u8) (bfin_read16(addr + offset)); } - -static inline u16 musb_readw(const void __iomem *addr, unsigned offset) - { return bfin_read16(addr + offset); } - -static inline u32 musb_readl(const void __iomem *addr, unsigned offset) - { return (u32) (bfin_read16(addr + offset)); } - -static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data) - { bfin_write16(addr + offset, (u16) data); } - -static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data) - { bfin_write16(addr + offset, data); } - -static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data) - { bfin_write16(addr + offset, (u16) data); } - -#endif /* CONFIG_BLACKFIN */ - #endif diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index b9bcda5e39..2cb749140b 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -220,8 +220,6 @@ #define MUSB_HUBADDR_MULTI_TT 0x80 -#ifndef CONFIG_BLACKFIN - /* * Common USB registers */ @@ -460,193 +458,4 @@ static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum) return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT)); } -#else /* CONFIG_BLACKFIN */ - -#define USB_BASE USB_FADDR -#define USB_OFFSET(reg) (reg - USB_BASE) - -/* - * Common USB registers - */ -#define MUSB_FADDR USB_OFFSET(USB_FADDR) /* 8-bit */ -#define MUSB_POWER USB_OFFSET(USB_POWER) /* 8-bit */ -#define MUSB_INTRTX USB_OFFSET(USB_INTRTX) /* 16-bit */ -#define MUSB_INTRRX USB_OFFSET(USB_INTRRX) -#define MUSB_INTRTXE USB_OFFSET(USB_INTRTXE) -#define MUSB_INTRRXE USB_OFFSET(USB_INTRRXE) -#define MUSB_INTRUSB USB_OFFSET(USB_INTRUSB) /* 8 bit */ -#define MUSB_INTRUSBE USB_OFFSET(USB_INTRUSBE)/* 8 bit */ -#define MUSB_FRAME USB_OFFSET(USB_FRAME) -#define MUSB_INDEX USB_OFFSET(USB_INDEX) /* 8 bit */ -#define MUSB_TESTMODE USB_OFFSET(USB_TESTMODE)/* 8 bit */ - -/* Get offset for a given FIFO from musb->mregs */ -#define MUSB_FIFO_OFFSET(epnum) \ - (USB_OFFSET(USB_EP0_FIFO) + ((epnum) * 8)) - -/* - * Additional Control Registers - */ - -#define MUSB_DEVCTL USB_OFFSET(USB_OTG_DEV_CTL) /* 8 bit */ - -#define MUSB_LINKINFO USB_OFFSET(USB_LINKINFO)/* 8 bit */ -#define MUSB_VPLEN USB_OFFSET(USB_VPLEN) /* 8 bit */ -#define MUSB_HS_EOF1 USB_OFFSET(USB_HS_EOF1) /* 8 bit */ -#define MUSB_FS_EOF1 USB_OFFSET(USB_FS_EOF1) /* 8 bit */ -#define MUSB_LS_EOF1 USB_OFFSET(USB_LS_EOF1) /* 8 bit */ - -/* Offsets to endpoint registers */ -#define MUSB_TXMAXP 0x00 -#define MUSB_TXCSR 0x04 -#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */ -#define MUSB_RXMAXP 0x08 -#define MUSB_RXCSR 0x0C -#define MUSB_RXCOUNT 0x10 -#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */ -#define MUSB_TXTYPE 0x14 -#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */ -#define MUSB_TXINTERVAL 0x18 -#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */ -#define MUSB_RXTYPE 0x1C -#define MUSB_RXINTERVAL 0x20 -#define MUSB_TXCOUNT 0x28 - -/* Offsets to endpoint registers in indexed model (using INDEX register) */ -#define MUSB_INDEXED_OFFSET(_epnum, _offset) \ - (0x40 + (_offset)) - -/* Offsets to endpoint registers in flat models */ -#define MUSB_FLAT_OFFSET(_epnum, _offset) \ - (USB_OFFSET(USB_EP_NI0_TXMAXP) + (0x40 * (_epnum)) + (_offset)) - -/* Not implemented - HW has separate Tx/Rx FIFO */ -#define MUSB_TXCSR_MODE 0x0000 - -static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) -{ -} - -static inline void musb_write_txfifoadd(void __iomem *mbase, u16 c_off) -{ -} - -static inline void musb_write_rxfifosz(void __iomem *mbase, u8 c_size) -{ -} - -static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off) -{ -} - -static inline void musb_write_ulpi_buscontrol(void __iomem *mbase, u8 val) -{ -} - -static inline u8 musb_read_txfifosz(void __iomem *mbase) -{ - return 0; -} - -static inline u16 musb_read_txfifoadd(void __iomem *mbase) -{ - return 0; -} - -static inline u8 musb_read_rxfifosz(void __iomem *mbase) -{ - return 0; -} - -static inline u16 musb_read_rxfifoadd(void __iomem *mbase) -{ - return 0; -} - -static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase) -{ - return 0; -} - -static inline u8 musb_read_configdata(void __iomem *mbase) -{ - return 0; -} - -static inline u16 musb_read_hwvers(void __iomem *mbase) -{ - /* - * This register is invisible on Blackfin, actually the MUSB - * RTL version of Blackfin is 1.9, so just harcode its value. - */ - return MUSB_HWVERS_1900; -} - -static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) -{ - return NULL; -} - -static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, - u8 qh_addr_req) -{ -} - -static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, - u8 qh_h_addr_reg) -{ -} - -static inline void musb_write_rxhubport(void __iomem *ep_target_regs, - u8 qh_h_port_reg) -{ -} - -static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum, - u8 qh_addr_reg) -{ -} - -static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum, - u8 qh_addr_reg) -{ -} - -static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum, - u8 qh_h_port_reg) -{ -} - -static inline u8 musb_read_rxfunaddr(void __iomem *mbase, u8 epnum) -{ - return 0; -} - -static inline u8 musb_read_rxhubaddr(void __iomem *mbase, u8 epnum) -{ - return 0; -} - -static inline u8 musb_read_rxhubport(void __iomem *mbase, u8 epnum) -{ - return 0; -} - -static inline u8 musb_read_txfunaddr(void __iomem *mbase, u8 epnum) -{ - return 0; -} - -static inline u8 musb_read_txhubaddr(void __iomem *mbase, u8 epnum) -{ - return 0; -} - -static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum) -{ - return 0; -} - -#endif /* CONFIG_BLACKFIN */ - #endif /* __MUSB_REGS_H__ */ diff --git a/drivers/video/imx.c b/drivers/video/imx.c index 947f8d5349..d15c2d88fb 100644 --- a/drivers/video/imx.c +++ b/drivers/video/imx.c @@ -152,8 +152,6 @@ struct imxfb_info { cmap_static:1, unused:30; - struct imx_fb_videomode *mode; - struct fb_info info; struct device_d *dev; @@ -293,14 +291,6 @@ static int imxfb_activate_var(struct fb_info *info) unsigned long long tmp; struct imxfb_info *fbi = info->priv; u32 pcr; - int i; - - for (i = 0; i < info->modes.num_modes; i++) { - if (!strcmp(fbi->mode[i].mode.name, mode->name)) { - fbi->pcr = fbi->mode[i].pcr; - break; - } - } /* physical screen start address */ writel(VPW_VPW(mode->xres * info->bits_per_pixel / 8 / 4), @@ -556,7 +546,7 @@ static int imxfb_probe(struct device_d *dev) mode_list = xzalloc(sizeof(*mode_list) * pdata->num_modes); for (i = 0; i < pdata->num_modes; i++) - mode_list[i] = pdata->mode[i].mode; + mode_list[i] = pdata->mode[i]; fbi = xzalloc(sizeof(*fbi)); info = &fbi->info; @@ -573,13 +563,12 @@ static int imxfb_probe(struct device_d *dev) if (IS_ERR(fbi->ipg_clk)) return PTR_ERR(fbi->ipg_clk); - fbi->mode = pdata->mode; iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); fbi->regs = IOMEM(iores->start); - fbi->pcr = pdata->mode->pcr; + fbi->pcr = pdata->pcr; fbi->pwmr = pdata->pwmr; fbi->lscr1 = pdata->lscr1; fbi->dmacr = pdata->dmacr; @@ -588,10 +577,10 @@ static int imxfb_probe(struct device_d *dev) info->priv = fbi; info->modes.modes = mode_list; info->modes.num_modes = pdata->num_modes; - info->mode = &pdata->mode->mode; - info->xres = pdata->mode->mode.xres; - info->yres = pdata->mode->mode.yres; - info->bits_per_pixel = pdata->mode->bpp; + info->mode = pdata->mode; + info->xres = pdata->mode->xres; + info->yres = pdata->mode->yres; + info->bits_per_pixel = pdata->bpp; info->fbops = &imxfb_ops; dev_info(dev, "i.MX Framebuffer driver\n"); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index fbaab896d4..45dd41a2a2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -96,4 +96,13 @@ config STPMIC1_WATCHDOG help Enable to support configuration of the stpmic1's built-in watchdog. +config F71808E_WDT + bool "Fintek F718xx, F818xx Super I/O Watchdog" + depends on X86 + depends on FINTEK_SUPERIO + help + This is the driver for the hardware watchdog on the Fintek F71808E, + F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866 + Super I/O controllers. + endif diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 1fbd780885..63efc2a87e 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_ARCH_BCM283X) += bcm2835_wdt.o obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o obj-$(CONFIG_STM32_IWDG_WATCHDOG) += stm32_iwdg.o obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o +obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c new file mode 100644 index 0000000000..4f881a1d02 --- /dev/null +++ b/drivers/watchdog/f71808e_wdt.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*************************************************************************** + * Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> * + * Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com> * + * Copyright (C) 2010 Giel van Schijndel <me@mortis.eu> * + * Copyright (C) 2019 Ahmad Fatoum <a.fatoum@pengutronix.de> * + * * + ***************************************************************************/ + +#define pr_fmt(fmt) "f71808e_wdt: " fmt + +#include <init.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include <driver.h> +#include <watchdog.h> +#include <printk.h> +#include <reset_source.h> +#include <superio.h> +#include <common.h> + +#define SIO_F71808FG_LD_WDT 0x07 /* Watchdog timer logical device */ +#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ +#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVREV 0x22 /* Device revision */ +#define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */ +#define SIO_F81866_REG_PORT_SEL 0x27 /* F81866 Multi-Function Register */ +#define SIO_REG_MFUNCT1 0x29 /* Multi function select 1 */ +#define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */ +#define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */ +#define SIO_F81866_REG_GPIO1 0x2c /* F81866 GPIO1 Enable Register */ +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ +#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ + +#define F71808FG_REG_WDO_CONF 0xf0 +#define F71808FG_REG_WDT_CONF 0xf5 +#define F71808FG_REG_WD_TIME 0xf6 + +#define F71808FG_FLAG_WDOUT_EN 7 + +#define F71808FG_FLAG_WDTMOUT_STS 6 +#define F71808FG_FLAG_WD_EN 5 +#define F71808FG_FLAG_WD_PULSE 4 +#define F71808FG_FLAG_WD_UNIT 3 + +#define F81865_REG_WDO_CONF 0xfa +#define F81865_FLAG_WDOUT_EN 0 + +/* Default values */ +#define WATCHDOG_MAX_TIMEOUT (60 * 255) + +enum pulse_width { + PULSE_WIDTH_LEVEL, PULSE_WIDTH_1MS, + PULSE_WIDTH_LOW, PULSE_WIDTH_MID, PULSE_WIDTH_HIGH +}; + +const char *pulse_width_names[] = { "level", "1", "25", "125", "5000" }; +const char *pulse_width_names_f71868[] = { "level", "1", "30", "150", "6000" }; + +enum wdtrst_pin { + WDTRST_PIN_56, WDTRST_PIN_63, +}; + +const char *f71862fg_pin_names[] = { "56", "63" }; + +enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg, + f81865, f81866}; + +struct f71808e_wdt; + +struct f71808e_variant_data { + enum chips type; + void (*pinconf)(struct f71808e_wdt *wd); +}; + +struct f71808e_wdt { + struct watchdog wdd; + u16 sioaddr; + const struct f71808e_variant_data *variant; + unsigned int timeout; + u8 timer_val; /* content for the wd_time register */ + char minutes_mode; + int pulse_width; + int f71862fg_pin; +}; + +static inline struct f71808e_wdt *to_f71808e_wdt(struct watchdog *wdd) +{ + return container_of(wdd, struct f71808e_wdt, wdd); +} + +static inline bool has_f81865_wdo_conf(struct f71808e_wdt *wd) +{ + return wd->variant->type == f81865 || wd->variant->type == f81866; +} + +static inline void superio_enter(u16 base) +{ + /* according to the datasheet the key must be sent twice! */ + outb(SIO_UNLOCK_KEY, base); + outb(SIO_UNLOCK_KEY, base); +} + +static inline void superio_select(u16 base, int ld) +{ + outb(SIO_REG_LDSEL, base); + outb(ld, base + 1); +} + +static inline void superio_exit(u16 base) +{ + outb(SIO_LOCK_KEY, base); +} + +static void f71808e_wdt_keepalive(struct f71808e_wdt *wd) +{ + superio_enter(wd->sioaddr); + + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + + if (wd->minutes_mode) + /* select minutes for timer units */ + superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, + F71808FG_FLAG_WD_UNIT); + else + /* select seconds for timer units */ + superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, + F71808FG_FLAG_WD_UNIT); + + /* Set timer value */ + superio_outb(wd->sioaddr, F71808FG_REG_WD_TIME, + wd->timer_val); + + superio_exit(wd->sioaddr); +} + +static void f71808e_wdt_start(struct f71808e_wdt *wd) +{ + /* Make sure we don't die as soon as the watchdog is enabled below */ + f71808e_wdt_keepalive(wd); + + superio_enter(wd->sioaddr); + + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + + /* Watchdog pin configuration */ + wd->variant->pinconf(wd); + + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + superio_set_bit(wd->sioaddr, SIO_REG_ENABLE, 0); + + if (has_f81865_wdo_conf(wd)) + superio_set_bit(wd->sioaddr, F81865_REG_WDO_CONF, + F81865_FLAG_WDOUT_EN); + else + superio_set_bit(wd->sioaddr, F71808FG_REG_WDO_CONF, + F71808FG_FLAG_WDOUT_EN); + + superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, + F71808FG_FLAG_WD_EN); + + if (wd->pulse_width > 0) { + /* Select "pulse" output mode with given duration */ + u8 wdt_conf = superio_inb(wd->sioaddr, F71808FG_REG_WDT_CONF); + + /* Set WD_PSWIDTH bits (1:0) */ + wdt_conf = (wdt_conf & 0xfc) | (wd->pulse_width & 0x03); + /* Set WD_PULSE to "pulse" mode */ + wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE); + + superio_outb(wd->sioaddr, F71808FG_REG_WDT_CONF, wdt_conf); + } else { + /* Select "level" output mode */ + superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, + F71808FG_FLAG_WD_PULSE); + } + + superio_exit(wd->sioaddr); +} + +static void f71808e_wdt_stop(struct f71808e_wdt *wd) +{ + superio_enter(wd->sioaddr); + + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + + superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, + F71808FG_FLAG_WD_EN); + + superio_exit(wd->sioaddr); +} + +static int f71808e_wdt_set_timeout(struct watchdog *wdd, unsigned int new_timeout) +{ + struct f71808e_wdt *wd = to_f71808e_wdt(wdd); + + if (!new_timeout) { + f71808e_wdt_stop(wd); + return 0; + } + + if (wd->timeout != new_timeout) { + if (new_timeout > 0xff) { + wd->timer_val = DIV_ROUND_UP(new_timeout, 60); + wd->minutes_mode = true; + } else { + wd->timer_val = new_timeout; + wd->minutes_mode = false; + } + + f71808e_wdt_start(wd); + wd->timeout = new_timeout; + } + + f71808e_wdt_keepalive(wd); + return 0; +} + +static int f71808e_wdt_init(struct f71808e_wdt *wd, struct device_d *dev) +{ + struct watchdog *wdd = &wd->wdd; + const char * const *names = pulse_width_names; + int wdt_conf; + int ret; + + superio_enter(wd->sioaddr); + + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + + wdt_conf = superio_inb(wd->sioaddr, F71808FG_REG_WDT_CONF); + + superio_exit(wd->sioaddr); + + if (wd->variant->type == f71868) + names = pulse_width_names_f71868; + + wd->pulse_width = PULSE_WIDTH_MID; /* either 125ms or 150ms */ + + dev_add_param_enum(dev, "pulse_width_ms", NULL, NULL, + &wd->pulse_width, names, + ARRAY_SIZE(pulse_width_names), + wd); + + if (wd->variant->type == f71862fg) { + wd->f71862fg_pin = WDTRST_PIN_63; + + dev_add_param_enum(dev, "wdtrst_pin", NULL, NULL, + &wd->f71862fg_pin, f71862fg_pin_names, + ARRAY_SIZE(f71862fg_pin_names), + wd); + } + + wdd->hwdev = dev; + wdd->set_timeout = &f71808e_wdt_set_timeout; + wdd->timeout_max = WATCHDOG_MAX_TIMEOUT; + + if (wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS)) + reset_source_set_priority(RESET_WDG, + RESET_SOURCE_DEFAULT_PRIORITY); + + dev_info(dev, "reset reason: %s\n", reset_source_name()); + + ret = watchdog_register(wdd); + if (ret) + return ret; + + superio_enter(wd->sioaddr); + dev_info(dev, "revision %d probed.\n", + superio_inb(wd->sioaddr, SIO_REG_DEVREV)); + superio_exit(wd->sioaddr); + + return 0; +} + +static void f71808fg_pinconf(struct f71808e_wdt *wd) +{ + /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */ + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT2, 3); + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 3); +} +static void f71862fg_pinconf(struct f71808e_wdt *wd) +{ + u16 ioaddr = wd->sioaddr; + + if (wd->f71862fg_pin == WDTRST_PIN_63) { + /* SPI must be disabled first to use this pin! */ + superio_clear_bit(ioaddr, SIO_REG_ROM_ADDR_SEL, 6); + superio_set_bit(ioaddr, SIO_REG_MFUNCT3, 4); + } else if (wd->f71862fg_pin == WDTRST_PIN_56) { + superio_set_bit(ioaddr, SIO_REG_MFUNCT1, 1); + } +} +static void f71868_pinconf(struct f71808e_wdt *wd) +{ + /* GPIO14 --> WDTRST# */ + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT1, 4); +} +static void f71882fg_pinconf(struct f71808e_wdt *wd) +{ + /* Set pin 56 to WDTRST# */ + superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1); +} +static void f71889fg_pinconf(struct f71808e_wdt *wd) +{ + /* set pin 40 to WDTRST# */ + superio_outb(wd->sioaddr, SIO_REG_MFUNCT3, + superio_inb(wd->sioaddr, SIO_REG_MFUNCT3) & 0xcf); +} +static void f81865_pinconf(struct f71808e_wdt *wd) +{ + /* Set pin 70 to WDTRST# */ + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 5); +} +static void f81866_pinconf(struct f71808e_wdt *wd) +{ + /* + * GPIO1 Control Register when 27h BIT3:2 = 01 & BIT0 = 0. + * The PIN 70(GPIO15/WDTRST) is controlled by 2Ch: + * BIT5: 0 -> WDTRST# + * 1 -> GPIO15 + */ + u8 tmp = superio_inb(wd->sioaddr, SIO_F81866_REG_PORT_SEL); + tmp &= ~(BIT(3) | BIT(0)); + tmp |= BIT(2); + superio_outb(wd->sioaddr, SIO_F81866_REG_PORT_SEL, tmp); + + superio_clear_bit(wd->sioaddr, SIO_F81866_REG_GPIO1, 5); +} + +static struct f71808e_variant_data f71808fg_data = { .type = f71808fg, .pinconf = f71808fg_pinconf }; +static struct f71808e_variant_data f71862fg_data = { .type = f71862fg, .pinconf = f71862fg_pinconf }; +static struct f71808e_variant_data f71868_data = { .type = f71868, .pinconf = f71868_pinconf }; +static struct f71808e_variant_data f71869_data = { .type = f71869, .pinconf = f71868_pinconf }; +static struct f71808e_variant_data f71882fg_data = { .type = f71882fg, .pinconf = f71882fg_pinconf }; +static struct f71808e_variant_data f71889fg_data = { .type = f71889fg, .pinconf = f71889fg_pinconf }; +static struct f71808e_variant_data f81865_data = { .type = f81865, .pinconf = f81865_pinconf }; +static struct f71808e_variant_data f81866_data = { .type = f81866, .pinconf = f81866_pinconf }; + +static struct platform_device_id f71808e_wdt_ids[] = { + { .name = "f71808fg_wdt", .driver_data = (unsigned long)&f71808fg_data }, + { .name = "f71862fg_wdt", .driver_data = (unsigned long)&f71862fg_data }, + { .name = "f71868_wdt", .driver_data = (unsigned long)&f71868_data }, + { .name = "f71869_wdt", .driver_data = (unsigned long)&f71869_data }, + { .name = "f71882fg_wdt", .driver_data = (unsigned long)&f71882fg_data }, + { .name = "f71889fg_wdt", .driver_data = (unsigned long)&f71889fg_data }, + { .name = "f81865_wdt", .driver_data = (unsigned long)&f81865_data }, + { .name = "f81866_wdt", .driver_data = (unsigned long)&f81866_data }, + { /* sentinel */ }, +}; + +static int f71808e_probe(struct device_d *dev) +{ + struct f71808e_wdt *wd; + struct resource *res; + int ret; + + wd = xzalloc(sizeof(*wd)); + + ret = dev_get_drvdata(dev, (const void **)&wd->variant); + if (ret) + return ret; + + res = dev_get_resource(dev->parent, IORESOURCE_IO, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + wd->sioaddr = res->start; + + return f71808e_wdt_init(wd, dev); +} + +static struct driver_d f71808e_wdt_driver = { + .probe = f71808e_probe, + .name = "f71808e_wdt", + .id_table = f71808e_wdt_ids, +}; + +device_platform_driver(f71808e_wdt_driver); |