* [PATCH v4 2/3] Broadcom USB DRD Phy driver for Northstar2
From: Raviteja Garimella @ 2017-03-28 12:27 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Kishon Vijay Abraham I, Ray Jui,
Scott Branden, Jon Mason, Catalin Marinas, Will Deacon
Cc: devicetree, linux-kernel, bcm-kernel-feedback-list,
linux-arm-kernel
In-Reply-To: <1490704052-11314-1-git-send-email-raviteja.garimella@broadcom.com>
This is driver for USB DRD Phy used in Broadcom's Northstar2
SoC. The phy can be configured to be in Device mode or Host
mode based on the type of cable connected to the port. The
driver registers to extcon framework to get appropriate
connect events for Host/Device cables connect/disconnect
states based on VBUS and ID interrupts.
Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
---
drivers/phy/Kconfig | 13 +
drivers/phy/Makefile | 1 +
drivers/phy/phy-bcm-ns2-usbdrd.c | 567 +++++++++++++++++++++++++++++++++++++++
3 files changed, 581 insertions(+)
create mode 100644 drivers/phy/phy-bcm-ns2-usbdrd.c
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index dc5277a..001bc33 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -487,6 +487,19 @@ config PHY_CYGNUS_PCIE
Enable this to support the Broadcom Cygnus PCIe PHY.
If unsure, say N.
+config PHY_NS2_USB_DRD
+ tristate "Broadcom Northstar2 USB DRD PHY support"
+ depends on OF && (ARCH_BCM_IPROC || COMPILE_TEST)
+ select GENERIC_PHY
+ select EXTCON
+ default ARCH_BCM_IPROC
+ help
+ Enable this to support the Broadcom Northstar2 USB DRD PHY.
+ This driver initializes the PHY in either HOST or DEVICE mode.
+ The host or device configuration is read from device tree.
+
+ If unsure, say N.
+
source "drivers/phy/tegra/Kconfig"
config PHY_NS2_PCIE
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index e7b0feb..122eee6 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o
+obj-$(CONFIG_PHY_NS2_USB_DRD) += phy-bcm-ns2-usbdrd.o
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o
obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o
diff --git a/drivers/phy/phy-bcm-ns2-usbdrd.c b/drivers/phy/phy-bcm-ns2-usbdrd.c
new file mode 100644
index 0000000..f41763f
--- /dev/null
+++ b/drivers/phy/phy-bcm-ns2-usbdrd.c
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 2016 Broadcom
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/extcon.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#define ICFG_DRD_AFE 0x0
+#define ICFG_MISC_STAT 0x18
+#define ICFG_DRD_P0CTL 0x1C
+#define ICFG_STRAP_CTRL 0x20
+#define ICFG_FSM_CTRL 0x24
+
+#define IDM_RST_BIT BIT(0)
+#define AFE_CORERDY_VDDC BIT(18)
+#define PHY_PLL_RESETB BIT(15)
+#define PHY_RESETB BIT(14)
+#define PHY_PLL_LOCK BIT(0)
+
+#define DRD_DEV_MODE BIT(20)
+#define OHCI_OVRCUR_POL BIT(11)
+#define ICFG_OFF_MODE BIT(6)
+#define PLL_LOCK_RETRY 1000
+
+#define EVT_DEVICE 0
+#define EVT_HOST 1
+#define EVT_IDLE 2
+
+#define DRD_HOST_MODE (BIT(2) | BIT(3))
+#define DRD_DEVICE_MODE (BIT(4) | BIT(5))
+#define DRD_HOST_VAL 0x803
+#define DRD_DEV_VAL 0x807
+#define GPIO_DELAY 20
+#define PHY_WQ_DELAY msecs_to_jiffies(600)
+
+struct ns2_phy_data;
+struct ns2_phy_driver {
+ void __iomem *icfgdrd_regs;
+ void __iomem *idmdrd_rst_ctrl;
+ void __iomem *crmu_usb2_ctrl;
+ void __iomem *usb2h_strap_reg;
+ spinlock_t lock; /* spin lock for phy driver */
+ struct ns2_phy_data *data;
+ struct extcon_specific_cable_nb extcon_dev;
+ struct extcon_specific_cable_nb extcon_host;
+ struct notifier_block host_nb;
+ struct notifier_block dev_nb;
+ struct delayed_work conn_work;
+ struct extcon_dev *edev;
+ struct gpio_desc *vbus_gpiod;
+ struct gpio_desc *id_gpiod;
+ int id_irq;
+ int vbus_irq;
+ unsigned long debounce_jiffies;
+ struct delayed_work wq_extcon;
+};
+
+struct ns2_phy_data {
+ struct ns2_phy_driver *driver;
+ struct phy *phy;
+ int new_state;
+};
+
+static const unsigned int usb_extcon_cable[] = {
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+ EXTCON_NONE,
+};
+
+static inline int pll_lock_stat(u32 usb_reg, int reg_mask,
+ struct ns2_phy_driver *driver)
+{
+ int retry = PLL_LOCK_RETRY;
+ u32 val;
+
+ do {
+ udelay(1);
+ val = readl(driver->icfgdrd_regs + usb_reg);
+ if (val & reg_mask)
+ return 0;
+ } while (--retry > 0);
+
+ return -EBUSY;
+}
+
+static int ns2_drd_phy_init(struct phy *phy)
+{
+ struct ns2_phy_data *data = phy_get_drvdata(phy);
+ struct ns2_phy_driver *driver = data->driver;
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&driver->lock, flags);
+
+ val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
+
+ if (data->new_state == EVT_HOST) {
+ val &= ~DRD_DEVICE_MODE;
+ val |= DRD_HOST_MODE;
+ } else {
+ val &= ~DRD_HOST_MODE;
+ val |= DRD_DEVICE_MODE;
+ }
+ writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
+
+ spin_unlock_irqrestore(&driver->lock, flags);
+ return 0;
+}
+
+static int ns2_drd_phy_shutdown(struct phy *phy)
+{
+ struct ns2_phy_data *data = phy_get_drvdata(phy);
+ struct ns2_phy_driver *driver = data->driver;
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&driver->lock, flags);
+
+ val = readl(driver->crmu_usb2_ctrl);
+ val &= ~AFE_CORERDY_VDDC;
+ writel(val, driver->crmu_usb2_ctrl);
+
+ val = readl(driver->crmu_usb2_ctrl);
+ val &= ~DRD_DEV_MODE;
+ writel(val, driver->crmu_usb2_ctrl);
+
+ /* Disable Host and Device Mode */
+ val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
+ val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE | ICFG_OFF_MODE);
+ writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
+
+ spin_unlock_irqrestore(&driver->lock, flags);
+ return 0;
+}
+
+static int ns2_drd_phy_poweron(struct phy *phy)
+{
+ struct ns2_phy_data *data = phy_get_drvdata(phy);
+ struct ns2_phy_driver *driver = data->driver;
+ u32 extcon_event = data->new_state;
+ unsigned long flags;
+ int ret;
+ u32 val;
+
+ spin_lock_irqsave(&driver->lock, flags);
+ if (extcon_event == EVT_DEVICE) {
+ writel(DRD_DEV_VAL, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
+
+ val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
+ val &= ~(DRD_HOST_MODE | ICFG_OFF_MODE);
+ val |= DRD_DEVICE_MODE;
+ writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
+
+ val = readl(driver->idmdrd_rst_ctrl);
+ val &= ~IDM_RST_BIT;
+ writel(val, driver->idmdrd_rst_ctrl);
+
+ val = readl(driver->crmu_usb2_ctrl);
+ val |= (AFE_CORERDY_VDDC | DRD_DEV_MODE);
+ writel(val, driver->crmu_usb2_ctrl);
+
+ /* Bring PHY and PHY_PLL out of Reset */
+ val = readl(driver->crmu_usb2_ctrl);
+ val |= (PHY_PLL_RESETB | PHY_RESETB);
+ writel(val, driver->crmu_usb2_ctrl);
+
+ ret = pll_lock_stat(ICFG_MISC_STAT, PHY_PLL_LOCK, driver);
+ if (ret < 0) {
+ dev_err(&phy->dev, "Phy PLL lock failed\n");
+ goto err_shutdown;
+ }
+ } else {
+ writel(DRD_HOST_VAL, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
+
+ val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
+ val &= ~(DRD_DEVICE_MODE | ICFG_OFF_MODE);
+ val |= DRD_HOST_MODE;
+ writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
+
+ val = readl(driver->crmu_usb2_ctrl);
+ val |= AFE_CORERDY_VDDC;
+ writel(val, driver->crmu_usb2_ctrl);
+
+ ret = pll_lock_stat(ICFG_MISC_STAT, PHY_PLL_LOCK, driver);
+ if (ret < 0) {
+ dev_err(&phy->dev, "Phy PLL lock failed\n");
+ goto err_shutdown;
+ }
+
+ val = readl(driver->idmdrd_rst_ctrl);
+ val &= ~IDM_RST_BIT;
+ writel(val, driver->idmdrd_rst_ctrl);
+
+ /* port over current Polarity */
+ val = readl(driver->usb2h_strap_reg);
+ val |= OHCI_OVRCUR_POL;
+ writel(val, driver->usb2h_strap_reg);
+ }
+ spin_unlock_irqrestore(&driver->lock, flags);
+ return 0;
+
+err_shutdown:
+ spin_unlock_irqrestore(&driver->lock, flags);
+ ns2_drd_phy_shutdown(phy);
+ return ret;
+}
+
+static void connect_work(struct work_struct *work)
+{
+ struct ns2_phy_driver *driver;
+ struct ns2_phy_data *data;
+ u32 extcon_event;
+
+ driver = container_of(to_delayed_work(work),
+ struct ns2_phy_driver, conn_work);
+ data = driver->data;
+ extcon_event = data->new_state;
+
+ if (extcon_event == EVT_DEVICE || extcon_event == EVT_HOST) {
+ ns2_drd_phy_init(data->phy);
+ ns2_drd_phy_poweron(data->phy);
+ } else if (extcon_event == EVT_IDLE) {
+ ns2_drd_phy_shutdown(data->phy);
+ }
+}
+
+static int drd_device_notify(struct notifier_block *self,
+ unsigned long event, void *ptr)
+{
+ struct ns2_phy_driver *driver = container_of(self,
+ struct ns2_phy_driver, dev_nb);
+
+ if (event) {
+ pr_debug("Device connected\n");
+ driver->data->new_state = EVT_DEVICE;
+ schedule_delayed_work(&driver->conn_work, 0);
+ } else {
+ pr_debug("Device disconnected\n");
+ driver->data->new_state = EVT_IDLE;
+ schedule_delayed_work(&driver->conn_work, PHY_WQ_DELAY);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int drd_host_notify(struct notifier_block *self,
+ unsigned long event, void *ptr)
+{
+ struct ns2_phy_driver *driver = container_of(self,
+ struct ns2_phy_driver, host_nb);
+
+ if (event) {
+ pr_debug("Host connected\n");
+ driver->data->new_state = EVT_HOST;
+ schedule_delayed_work(&driver->conn_work, 0);
+ } else {
+ pr_debug("Host disconnected\n");
+ driver->data->new_state = EVT_IDLE;
+ schedule_delayed_work(&driver->conn_work, PHY_WQ_DELAY);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static void extcon_work(struct work_struct *work)
+{
+ struct ns2_phy_driver *driver;
+ int vbus;
+ int id;
+
+ driver = container_of(to_delayed_work(work),
+ struct ns2_phy_driver, wq_extcon);
+
+ id = gpiod_get_value_cansleep(driver->id_gpiod);
+ vbus = gpiod_get_value_cansleep(driver->vbus_gpiod);
+
+ if (!id && vbus) {
+ extcon_set_cable_state_(driver->edev, EXTCON_USB_HOST, true);
+ } else if (id && !vbus) {
+ extcon_set_cable_state_(driver->edev, EXTCON_USB_HOST, false);
+ extcon_set_cable_state_(driver->edev, EXTCON_USB, false);
+ } else if (id && vbus) {
+ extcon_set_cable_state_(driver->edev, EXTCON_USB, true);
+ }
+}
+
+static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
+{
+ struct ns2_phy_driver *driver = dev_id;
+
+ queue_delayed_work(system_power_efficient_wq, &driver->wq_extcon,
+ driver->debounce_jiffies);
+
+ return IRQ_HANDLED;
+}
+
+static int register_extcon_notifier(struct ns2_phy_driver *phy_driver,
+ struct device *dev)
+{
+ struct extcon_dev *edev;
+ int ret;
+
+ phy_driver->host_nb.notifier_call = drd_host_notify;
+ phy_driver->dev_nb.notifier_call = drd_device_notify;
+
+ edev = phy_driver->edev;
+
+ /* Register for device change notification */
+ ret = extcon_register_notifier(edev, EXTCON_USB,
+ &phy_driver->dev_nb);
+ if (ret < 0) {
+ dev_err(dev, "can't register extcon_dev\n");
+ return ret;
+ }
+
+ /* Register for host change notification */
+ ret = extcon_register_notifier(edev, EXTCON_USB_HOST,
+ &phy_driver->host_nb);
+ if (ret < 0) {
+ dev_err(dev, "can't register extcon_dev\n");
+ goto err_dev;
+ }
+
+ /* Check the device cable connect state */
+ ret = extcon_get_cable_state_(edev, EXTCON_USB);
+ if (ret < 0) {
+ dev_err(dev, "can't get extcon_dev state\n");
+ goto err_host;
+ } else if (ret) {
+ phy_driver->data->new_state = EVT_DEVICE;
+ }
+
+ /* Check the host cable connect state */
+ ret = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
+ if (ret < 0) {
+ dev_err(dev, "can't get extcon_dev state\n");
+ goto err_host;
+ } else if (ret) {
+ phy_driver->data->new_state = EVT_HOST;
+ }
+
+ return 0;
+
+err_host:
+ ret = extcon_unregister_notifier(edev, EXTCON_USB_HOST,
+ &phy_driver->host_nb);
+err_dev:
+ ret = extcon_unregister_notifier(edev, EXTCON_USB,
+ &phy_driver->dev_nb);
+ return ret;
+}
+
+static struct phy_ops ops = {
+ .init = ns2_drd_phy_init,
+ .power_on = ns2_drd_phy_poweron,
+ .power_off = ns2_drd_phy_shutdown,
+ .owner = THIS_MODULE,
+};
+
+static const struct of_device_id ns2_drd_phy_dt_ids[] = {
+ { .compatible = "brcm,ns2-drd-phy", },
+ { }
+};
+
+static int ns2_drd_phy_remove(struct platform_device *pdev)
+{
+ struct ns2_phy_driver *driver = dev_get_drvdata(&pdev->dev);
+
+ if (driver->edev) {
+ extcon_unregister_notifier(driver->edev, EXTCON_USB_HOST,
+ &driver->host_nb);
+ extcon_unregister_notifier(driver->edev, EXTCON_USB,
+ &driver->dev_nb);
+ }
+
+ return 0;
+}
+static int ns2_drd_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct ns2_phy_driver *driver;
+ struct ns2_phy_data *data;
+ struct resource *res;
+ int ret;
+ u32 val;
+
+ driver = devm_kzalloc(dev, sizeof(struct ns2_phy_driver),
+ GFP_KERNEL);
+ if (!driver)
+ return -ENOMEM;
+
+ driver->data = devm_kzalloc(dev, sizeof(struct ns2_phy_data),
+ GFP_KERNEL);
+ if (!driver->data)
+ return -ENOMEM;
+
+ spin_lock_init(&driver->lock);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "icfg");
+ driver->icfgdrd_regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(driver->icfgdrd_regs))
+ return PTR_ERR(driver->icfgdrd_regs);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rst-ctrl");
+ driver->idmdrd_rst_ctrl = devm_ioremap_resource(dev, res);
+ if (IS_ERR(driver->idmdrd_rst_ctrl))
+ return PTR_ERR(driver->idmdrd_rst_ctrl);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "crmu-ctrl");
+ driver->crmu_usb2_ctrl = devm_ioremap_resource(dev, res);
+ if (IS_ERR(driver->crmu_usb2_ctrl))
+ return PTR_ERR(driver->crmu_usb2_ctrl);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2-strap");
+ driver->usb2h_strap_reg = devm_ioremap_resource(dev, res);
+ if (IS_ERR(driver->usb2h_strap_reg))
+ return PTR_ERR(driver->usb2h_strap_reg);
+
+ /* create extcon */
+ driver->id_gpiod = devm_gpiod_get(&pdev->dev, "id", GPIOD_IN);
+ if (IS_ERR(driver->id_gpiod)) {
+ dev_err(dev, "failed to get ID GPIO\n");
+ return PTR_ERR(driver->id_gpiod);
+ }
+ driver->vbus_gpiod = devm_gpiod_get(&pdev->dev, "vbus", GPIOD_IN);
+ if (IS_ERR(driver->vbus_gpiod)) {
+ dev_err(dev, "failed to get VBUS GPIO\n");
+ return PTR_ERR(driver->vbus_gpiod);
+ }
+
+ driver->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
+ if (IS_ERR(driver->edev)) {
+ dev_err(dev, "failed to allocate extcon device\n");
+ return -ENOMEM;
+ }
+
+ ret = devm_extcon_dev_register(dev, driver->edev);
+ if (ret < 0) {
+ dev_err(dev, "failed to register extcon device\n");
+ goto extcon_free;
+ }
+
+ ret = gpiod_set_debounce(driver->id_gpiod, GPIO_DELAY * 1000);
+ if (ret < 0)
+ driver->debounce_jiffies = msecs_to_jiffies(GPIO_DELAY);
+
+ INIT_DELAYED_WORK(&driver->wq_extcon, extcon_work);
+
+ driver->id_irq = gpiod_to_irq(driver->id_gpiod);
+ if (driver->id_irq < 0) {
+ dev_err(dev, "failed to get ID IRQ\n");
+ ret = driver->id_irq;
+ goto extcon_unregister;
+ }
+ driver->vbus_irq = gpiod_to_irq(driver->vbus_gpiod);
+ if (driver->vbus_irq < 0) {
+ dev_err(dev, "failed to get ID IRQ\n");
+ ret = driver->vbus_irq;
+ goto extcon_unregister;
+ }
+
+ ret = devm_request_threaded_irq(dev, driver->id_irq, NULL,
+ gpio_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "usb_id", driver);
+ if (ret < 0) {
+ dev_err(dev, "failed to request handler for ID IRQ\n");
+ goto extcon_unregister;
+ }
+ ret = devm_request_threaded_irq(dev, driver->vbus_irq, NULL,
+ gpio_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "usb_vbus", driver);
+ if (ret < 0) {
+ dev_err(dev, "failed to request handler for VBUS IRQ\n");
+ goto extcon_unregister;
+ }
+
+ dev_set_drvdata(dev, driver);
+
+ /* Shutdown all ports. They can be powered up as required */
+ val = readl(driver->crmu_usb2_ctrl);
+ val &= ~(AFE_CORERDY_VDDC | PHY_RESETB);
+ writel(val, driver->crmu_usb2_ctrl);
+
+ data = driver->data;
+ data->phy = devm_phy_create(dev, dev->of_node, &ops);
+ if (IS_ERR(data->phy)) {
+ dev_err(dev, "Failed to create usb drd phy\n");
+ ret = PTR_ERR(data->phy);
+ goto extcon_unregister;
+ }
+
+ data->driver = driver;
+ phy_set_drvdata(data->phy, data);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider)) {
+ dev_err(dev, "Failed to register as phy provider\n");
+ ret = PTR_ERR(phy_provider);
+ goto extcon_unregister;
+ }
+
+ INIT_DELAYED_WORK(&driver->conn_work, connect_work);
+ platform_set_drvdata(pdev, driver);
+
+ ret = register_extcon_notifier(driver, dev);
+ if (ret < 0) {
+ dev_err(dev, "register extcon notifier failed (%d)\n", ret);
+ goto extcon_unregister;
+ }
+ dev_info(dev, "Registered extcon device\n");
+ queue_delayed_work(system_power_efficient_wq, &driver->wq_extcon,
+ driver->debounce_jiffies);
+
+ return 0;
+
+extcon_unregister:
+ devm_extcon_dev_unregister(dev, driver->edev);
+extcon_free:
+ devm_extcon_dev_free(dev, driver->edev);
+ return ret;
+}
+
+MODULE_DEVICE_TABLE(of, ns2_drd_phy_dt_ids);
+
+static struct platform_driver ns2_drd_phy_driver = {
+ .probe = ns2_drd_phy_probe,
+ .remove = ns2_drd_phy_remove,
+ .driver = {
+ .name = "bcm-ns2-usbphy",
+ .of_match_table = of_match_ptr(ns2_drd_phy_dt_ids),
+ },
+};
+module_platform_driver(ns2_drd_phy_driver);
+
+MODULE_ALIAS("platform:bcm-ns2-drd-phy");
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Broadcom NS2 USB2 PHY driver");
+MODULE_LICENSE("GPL v2");
--
2.1.0
^ permalink raw reply related
* [PATCH v4 1/3] Add DT bindings documentation for NS2 USB DRD phy
From: Raviteja Garimella @ 2017-03-28 12:27 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Kishon Vijay Abraham I, Ray Jui,
Scott Branden, Jon Mason, Catalin Marinas, Will Deacon
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1490704052-11314-1-git-send-email-raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
This patch adds documentation for NS2 DRD Phy driver DT bindings
Signed-off-by: Raviteja Garimella <raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
.../devicetree/bindings/phy/brcm,ns2-drd-phy.txt | 30 ++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/brcm,ns2-drd-phy.txt
diff --git a/Documentation/devicetree/bindings/phy/brcm,ns2-drd-phy.txt b/Documentation/devicetree/bindings/phy/brcm,ns2-drd-phy.txt
new file mode 100644
index 0000000..04f063a
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/brcm,ns2-drd-phy.txt
@@ -0,0 +1,30 @@
+BROADCOM NORTHSTAR2 USB2 (DUAL ROLE DEVICE) PHY
+
+Required properties:
+ - compatible: brcm,ns2-drd-phy
+ - reg: offset and length of the NS2 PHY related registers.
+ - reg-names
+ The below registers must be provided.
+ icfg - for DRD ICFG configurations
+ rst-ctrl - for DRD IDM reset
+ crmu-ctrl - for CRMU core vdd, PHY and PHY PLL reset
+ usb2-strap - for port over current polarity reversal
+ - #phy-cells: Must be 0. No args required.
+ - vbus-gpios: vbus gpio binding
+ - id-gpios: id gpio binding
+
+Refer to phy/phy-bindings.txt for the generic PHY binding properties
+
+Example:
+ usbdrd_phy: phy@66000960 {
+ #phy-cells = <0>;
+ compatible = "brcm,ns2-drd-phy";
+ reg = <0x66000960 0x24>,
+ <0x67012800 0x4>,
+ <0x6501d148 0x4>,
+ <0x664d0700 0x4>;
+ reg-names = "icfg", "rst-ctrl",
+ "crmu-ctrl", "usb2-strap";
+ id-gpios = <&gpio_g 30 0>;
+ vbus-gpios = <&gpio_g 31 0>;
+ };
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 0/3] Support for USB DRD Phy driver for NS2
From: Raviteja Garimella @ 2017-03-28 12:27 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Kishon Vijay Abraham I, Ray Jui,
Scott Branden, Jon Mason, Catalin Marinas, Will Deacon
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Changes in v4:
=============
Remove references to edev->name in debug prints.
<clip: as before>
Changes in v3:
=============
Remove unnecessary checks for poweron as suggested in review.
Changes in v2:
=============
1. Initialize file operations .owner field with THIS_MODULE.
2. Remove unnecessary gpio example in DT bindings documentation.
This is previously acked by Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Introduction for PATCH v1:
This patch adds support for USB Dual Role Device Phy for Broadcom
Northstar2 SoC. Apart from the new phy driver, this patchset contains
changes to Kconfig, Makefile, and Device tree files.
This patchset is tested on Broadcom NS2 BCM958712K reference board.
Repo: https://github.com/Broadcom/arm64-linux.git
Branch: ns2_drdphy_v4
Raviteja Garimella (3):
Add DT bindings documentation for NS2 USB DRD phy
Broadcom USB DRD Phy driver for Northstar2
DT nodes for Broadcom Northstar2 USB DRD Phy
.../devicetree/bindings/phy/brcm,ns2-drd-phy.txt | 30 ++
arch/arm64/boot/dts/broadcom/ns2.dtsi | 14 +
drivers/phy/Kconfig | 13 +
drivers/phy/Makefile | 1 +
drivers/phy/phy-bcm-ns2-usbdrd.c | 567 +++++++++++++++++++++
5 files changed, 625 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/brcm,ns2-drd-phy.txt
create mode 100644 drivers/phy/phy-bcm-ns2-usbdrd.c
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v2 1/1] device property: Read strings using string array reading functions
From: Sakari Ailus @ 2017-03-28 12:26 UTC (permalink / raw)
To: linux-acpi-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, rafael-DgEjT+Ai2ygdnm+yROfE0A
Cc: sudeep.holla-5wv7dgnIgG8, lorenzo.pieralisi-5wv7dgnIgG8,
mika.westerberg-VuQAYsv1563Yd54FQh9/CA, mark.rutland-5wv7dgnIgG8,
broonie-DgEjT+Ai2ygdnm+yROfE0A, robh-DgEjT+Ai2ygdnm+yROfE0A,
ahs3-H+wXaHxf7aLQT0dZR+AlfA
Always read strings using of_property_read_string_array() instead of
of_property_read_string(). This allows using a single operation struct
callback for accessing strings.
Same for pset_prop_read_string_array() and pset_prop_read_string().
Signed-off-by: Sakari Ailus <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
Depends-on: ("device property: Make dev_fwnode() public")
Depends-on: ("device property: fwnode_property_read_string_array() returns nr of strings")
Reviewed-by: Mika Westerberg <mika.westerberg-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
---
since v1:
- Rebase on most recent version of "device property:
fwnode_property_read_string_array() returns nr of strings"; there was a
trivial conflict.
- Add dependency information to the commit message, i.e. this patch
applies on top of the two patchsets but not on either alone: "ACPI graph
support" and "Fwnode property API fixes for OF, pset and ACPI".
drivers/base/property.c | 45 +--------------------------------------------
1 file changed, 1 insertion(+), 44 deletions(-)
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 7152045..149de31 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -180,31 +180,6 @@ static int pset_prop_read_string_array(struct property_set *pset,
return array_len;
}
-static int pset_prop_read_string(struct property_set *pset,
- const char *propname, const char **strings)
-{
- const struct property_entry *prop;
- const char * const *pointer;
-
- prop = pset_prop_get(pset, propname);
- if (!prop)
- return -EINVAL;
- if (!prop->is_string)
- return -EILSEQ;
- if (prop->is_array) {
- pointer = prop->pointer.str;
- if (!pointer)
- return -ENODATA;
- } else {
- pointer = &prop->value.str;
- if (*pointer && strnlen(*pointer, prop->length) >= prop->length)
- return -EILSEQ;
- }
-
- *strings = *pointer;
- return 0;
-}
-
struct fwnode_handle *dev_fwnode(struct device *dev)
{
return IS_ENABLED(CONFIG_OF) && dev->of_node ?
@@ -582,19 +557,6 @@ static int __fwnode_property_read_string_array(struct fwnode_handle *fwnode,
return -ENXIO;
}
-static int __fwnode_property_read_string(struct fwnode_handle *fwnode,
- const char *propname, const char **val)
-{
- if (is_of_node(fwnode))
- return of_property_read_string(to_of_node(fwnode), propname, val);
- else if (is_acpi_node(fwnode))
- return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
- val, 1);
- else if (is_pset_node(fwnode))
- return pset_prop_read_string(to_pset_node(fwnode), propname, val);
- return -ENXIO;
-}
-
/**
* fwnode_property_read_string_array - return string array property of a node
* @fwnode: Firmware node to get the property of
@@ -646,13 +608,8 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);
int fwnode_property_read_string(struct fwnode_handle *fwnode,
const char *propname, const char **val)
{
- int ret;
+ int ret = fwnode_property_read_string_array(fwnode, propname, val, 1);
- ret = __fwnode_property_read_string(fwnode, propname, val);
- if (ret == -EINVAL && !IS_ERR_OR_NULL(fwnode) &&
- !IS_ERR_OR_NULL(fwnode->secondary))
- ret = __fwnode_property_read_string(fwnode->secondary,
- propname, val);
return ret < 0 ? ret : 0;
}
EXPORT_SYMBOL_GPL(fwnode_property_read_string);
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 3/3] device property: fwnode_property_read_string_array() returns nr of strings
From: Sakari Ailus @ 2017-03-28 12:22 UTC (permalink / raw)
To: linux-acpi-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, rafael-DgEjT+Ai2ygdnm+yROfE0A
Cc: sudeep.holla-5wv7dgnIgG8, lorenzo.pieralisi-5wv7dgnIgG8,
mika.westerberg-VuQAYsv1563Yd54FQh9/CA, mark.rutland-5wv7dgnIgG8,
broonie-DgEjT+Ai2ygdnm+yROfE0A, robh-DgEjT+Ai2ygdnm+yROfE0A,
ahs3-H+wXaHxf7aLQT0dZR+AlfA
In-Reply-To: <1490703739-28270-1-git-send-email-sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
Functionally fwnode_property_read_string_array() should match
of_property_read_string_array() and work as a drop-in substitute for the
latter. of_property_read_string_array() returns the number of strings read
if the target string pointer array is non-NULL. Make
fwnode_property_read_string_array() do the same.
Signed-off-by: Sakari Ailus <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
---
drivers/acpi/property.c | 22 +++++++++++++++++-----
drivers/base/property.c | 12 ++++++------
2 files changed, 23 insertions(+), 11 deletions(-)
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index 3afddcd..06f8fda 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -699,6 +699,8 @@ static int acpi_data_prop_read_single(struct acpi_device_data *data,
return ret;
*(char **)val = obj->string.pointer;
+
+ return 1;
} else {
ret = -EINVAL;
}
@@ -708,7 +710,15 @@ static int acpi_data_prop_read_single(struct acpi_device_data *data,
int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname,
enum dev_prop_type proptype, void *val)
{
- return adev ? acpi_data_prop_read_single(&adev->data, propname, proptype, val) : -EINVAL;
+ int ret;
+
+ if (!adev)
+ return -EINVAL;
+
+ ret = acpi_data_prop_read_single(&adev->data, propname, proptype, val);
+ if (ret < 0 || proptype != ACPI_TYPE_STRING)
+ return ret;
+ return 0;
}
static int acpi_copy_property_array_u8(const union acpi_object *items, u8 *val,
@@ -784,7 +794,7 @@ static int acpi_copy_property_array_string(const union acpi_object *items,
val[i] = items[i].string.pointer;
}
- return 0;
+ return nval;
}
static int acpi_data_prop_read(struct acpi_device_data *data,
@@ -798,7 +808,7 @@ static int acpi_data_prop_read(struct acpi_device_data *data,
if (val && nval == 1) {
ret = acpi_data_prop_read_single(data, propname, proptype, val);
- if (!ret)
+ if (ret >= 0)
return ret;
}
@@ -809,7 +819,7 @@ static int acpi_data_prop_read(struct acpi_device_data *data,
if (!val)
return obj->package.count;
- if (nval > obj->package.count)
+ if (proptype != DEV_PROP_STRING && nval > obj->package.count)
return -EOVERFLOW;
else if (nval <= 0)
return -EINVAL;
@@ -830,7 +840,9 @@ static int acpi_data_prop_read(struct acpi_device_data *data,
ret = acpi_copy_property_array_u64(items, (u64 *)val, nval);
break;
case DEV_PROP_STRING:
- ret = acpi_copy_property_array_string(items, (char **)val, nval);
+ ret = acpi_copy_property_array_string(
+ items, (char **)val,
+ min_t(u32, nval, obj->package.count));
break;
default:
ret = -EINVAL;
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 686e985..b517485 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -176,7 +176,7 @@ static int pset_prop_read_string_array(struct property_set *pset,
memcpy(strings, pointer, length);
- return 0;
+ return array_len;
}
static int pset_prop_read_string(struct property_set *pset,
@@ -362,8 +362,8 @@ EXPORT_SYMBOL_GPL(device_property_read_u64_array);
* Function reads an array of string properties with @propname from the device
* firmware description and stores them to @val if found.
*
- * Return: number of values if @val was %NULL,
- * %0 if the property was found (success),
+ * Return: number of values read on success if @val is non-NULL,
+ * number of values available on success if @val is NULL,
* %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value,
* %-EPROTO or %-EILSEQ if the property is not an array of strings,
@@ -603,8 +603,8 @@ static int __fwnode_property_read_string(struct fwnode_handle *fwnode,
* Read an string list property @propname from the given firmware node and store
* them to @val if found.
*
- * Return: number of values if @val was %NULL,
- * %0 if the property was found (success),
+ * Return: number of values read on success if @val is non-NULL,
+ * number of values available on success if @val is NULL,
* %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value,
* %-EPROTO or %-EILSEQ if the property is not an array of strings,
@@ -651,7 +651,7 @@ int fwnode_property_read_string(struct fwnode_handle *fwnode,
!IS_ERR_OR_NULL(fwnode->secondary))
ret = __fwnode_property_read_string(fwnode->secondary,
propname, val);
- return ret;
+ return ret < 0 ? ret : 0;
}
EXPORT_SYMBOL_GPL(fwnode_property_read_string);
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 2/3] device property: Fix reading pset strings using array access functions
From: Sakari Ailus @ 2017-03-28 12:22 UTC (permalink / raw)
To: linux-acpi, devicetree, rafael
Cc: sudeep.holla, lorenzo.pieralisi, mika.westerberg, mark.rutland,
broonie, robh, ahs3
In-Reply-To: <1490703739-28270-1-git-send-email-sakari.ailus@linux.intel.com>
The length field value of non-array string properties is the length of the
string itself. Non-array string properties thus require specific handling.
Fix this.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
drivers/base/property.c | 32 +++++++++++++++++++++++++-------
1 file changed, 25 insertions(+), 7 deletions(-)
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 19a3dc5..686e985 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -146,14 +146,36 @@ static int pset_prop_read_string_array(struct property_set *pset,
const char *propname,
const char **strings, size_t nval)
{
+ const struct property_entry *prop;
const void *pointer;
- size_t length = nval * sizeof(*strings);
+ size_t array_len, length;
+
+ /* Find out the array length. */
+ prop = pset_prop_get(pset, propname);
+ if (!prop)
+ return -EINVAL;
+
+ if (!prop->is_array)
+ /* The array length for a non-array string property is 1. */
+ array_len = 1;
+ else
+ /* Find the length of an array. */
+ array_len = pset_prop_count_elems_of_size(pset, propname,
+ sizeof(const char *));
+
+ /* Return how many there are if strings is NULL. */
+ if (!strings)
+ return array_len;
+
+ array_len = min(nval, array_len);
+ length = array_len * sizeof(*strings);
pointer = pset_prop_find(pset, propname, length);
if (IS_ERR(pointer))
return PTR_ERR(pointer);
memcpy(strings, pointer, length);
+
return 0;
}
@@ -553,12 +575,8 @@ static int __fwnode_property_read_string_array(struct fwnode_handle *fwnode,
return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
val, nval);
else if (is_pset_node(fwnode))
- return val ?
- pset_prop_read_string_array(to_pset_node(fwnode),
- propname, val, nval) :
- pset_prop_count_elems_of_size(to_pset_node(fwnode),
- propname,
- sizeof(const char *));
+ return pset_prop_read_string_array(to_pset_node(fwnode),
+ propname, val, nval);
return -ENXIO;
}
--
2.7.4
^ permalink raw reply related
* [PATCH v2 1/3] device property: fwnode_property_read_string_array() may return -EILSEQ
From: Sakari Ailus @ 2017-03-28 12:22 UTC (permalink / raw)
To: linux-acpi, devicetree, rafael
Cc: sudeep.holla, lorenzo.pieralisi, mika.westerberg, mark.rutland,
broonie, robh, ahs3
In-Reply-To: <1490703739-28270-1-git-send-email-sakari.ailus@linux.intel.com>
fwnode_property_read_string_array() may return -EILSEQ through
of_property_read_string_array(). Document this.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
drivers/base/property.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/base/property.c b/drivers/base/property.c
index c458c63..19a3dc5 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -589,7 +589,7 @@ static int __fwnode_property_read_string(struct fwnode_handle *fwnode,
* %0 if the property was found (success),
* %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value,
- * %-EPROTO if the property is not an array of strings,
+ * %-EPROTO or %-EILSEQ if the property is not an array of strings,
* %-EOVERFLOW if the size of the property is not as expected,
* %-ENXIO if no suitable firmware interface is present.
*/
--
2.7.4
^ permalink raw reply related
* [PATCH v2 0/3] Fwnode property API fixes for OF, pset and ACPI
From: Sakari Ailus @ 2017-03-28 12:22 UTC (permalink / raw)
To: linux-acpi, devicetree, rafael
Cc: sudeep.holla, lorenzo.pieralisi, mika.westerberg, mark.rutland,
broonie, robh, ahs3
Hi folks,
Here are three patches that fix a few issues in the fwnode property API
implementation. They were formerly a part of a larger patchset "Move
firmware specific code to firmware specific locations" here:
<URL:http://www.spinics.net/lists/linux-acpi/msg72174.html>
The v1 (previous version) of the set is also available here:
<URL:http://www.spinics.net/lists/linux-acpi/msg72327.html>
These patches apply cleanly on v4.11-rc1 and linux-next with or without
the ACPI graph support patchset I've sent a moment ago.
changes since v1.1:
- Move fixes to fwnode_property_read_string_array() return value fixes to
respective implementations of those functions, rather than working
around them in the fwnode property interface functions.
- fwnode_property_read_string_array() now may return fewer strings than
requested also on ACPI in order to make behaviour of
fwnode_property_read_string_array() fully consistent.
changes since v1:
- Make prop const. pset_prop_get() returns const struct property_entry *
now in __fwnode_property_read_string_array().
- Make fwnode_property_read_string_array() behave as
of_property_read_string_array() does in terms of the return value.
Sakari Ailus (3):
device property: fwnode_property_read_string_array() may return
-EILSEQ
device property: Fix reading pset strings using array access functions
device property: fwnode_property_read_string_array() returns nr of
strings
drivers/acpi/property.c | 22 +++++++++++++++++-----
drivers/base/property.c | 46 ++++++++++++++++++++++++++++++++--------------
2 files changed, 49 insertions(+), 19 deletions(-)
--
Kind regards,
Sakari
^ permalink raw reply
* Re: [RFC 5/8] regulator: anatop: fix min dropout for bypass mode
From: Leonard Crestez @ 2017-03-28 11:52 UTC (permalink / raw)
To: Mark Brown
Cc: Mark Rutland, devicetree, Anson Huang, Irina Tirdea, Viresh Kumar,
linux-pm, Rafael J. Wysocki, Liam Girdwood, linux-kernel,
Rob Herring, Octavian Purdila, Sascha Hauer, Fabio Estevam,
Robin Gong, Shawn Guo, linux-arm-kernel
In-Reply-To: <20170324125438.5wy3r2mr3g5eaxvy@sirena.org.uk>
On Fri, 2017-03-24 at 12:54 +0000, Mark Brown wrote:
> On Wed, Mar 22, 2017 at 06:53:07PM +0200, Leonard Crestez wrote:
> > + if (anatop_reg->bypass)
> > + anatop_reg->rdesc.min_dropout_uV = 0;
> > + else
> > + anatop_reg->rdesc.min_dropout_uV = LDO_MIN_DROPOUT_UV;
> No, this is completely broken - you can't expect to randomly change hthe
> regulator description at runtime behind the back of the framework and
> expect things to work. If there is a need to do this we need an
> interface for getting the current value and a way to notify of changes.
>
> That said I would not expect the dropout voltage to be considered at
> all when the regulator is bypassed, since the regulator is not
> regulating it doesn't need any headroom.
It's a more complex solution but this could be handled in the core instead.
Basically the core would treat min_dropout_uV as zero if the regulator is
currently in bypass mode.
In theory a function could be added in regulator_ops to ask a regulator driver
what requirements it has for its supply but this does not seem necessary.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH] arm64: tegra: Add CPU and PSCI nodes for NVIDIA Tegra210 platforms
From: Jon Hunter @ 2017-03-28 11:48 UTC (permalink / raw)
To: Thierry Reding, Stephen Warren
Cc: devicetree, Vagrant Cascadian, Jon Hunter, linux-tegra,
Martin Michlmayr, linux-arm-kernel
Add the CPU and PSCI nodes for the NVIDIA Tegra210 platforms so that
all CPUs can be enabled on boot. This assumes that the PSCI firmware
has been loaded during the initial bootstrap on the device before the
kernel starts (which is typically the case for these platforms). The
PSCI firmware version is set to v0.2 which aligns with the current
shipping version for Tegra.
Reported-by: Martin Michlmayr <tbm@cyrius.com>
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
---
arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi | 23 +++++++++++++++++++++++
arch/arm64/boot/dts/nvidia/tegra210-p2530.dtsi | 24 ++++++++++++++++++++++++
2 files changed, 47 insertions(+)
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
index 906fb836d241..de1696c28140 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
@@ -296,6 +296,29 @@
};
};
+ cpus {
+ cpu@0 {
+ enable-method = "psci";
+ };
+
+ cpu@1 {
+ enable-method = "psci";
+ };
+
+ cpu@2 {
+ enable-method = "psci";
+ };
+
+ cpu@3 {
+ enable-method = "psci";
+ };
+ };
+
+ psci {
+ compatible = "arm,psci-0.2";
+ method = "smc";
+ };
+
regulators {
vdd_gpu: regulator@100 {
compatible = "pwm-regulator";
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2530.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2530.dtsi
index 0ec92578cacb..67cb039965fd 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2530.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2530.dtsi
@@ -51,4 +51,28 @@
clock-frequency = <32768>;
};
};
+
+ cpus {
+ cpu@0 {
+ enable-method = "psci";
+ };
+
+ cpu@1 {
+ enable-method = "psci";
+ };
+
+ cpu@2 {
+ enable-method = "psci";
+ };
+
+ cpu@3 {
+ enable-method = "psci";
+ };
+ };
+
+ psci {
+ compatible = "arm,psci-0.2";
+ method = "smc";
+ };
+
};
--
2.7.4
^ permalink raw reply related
* [PATCH v2] arm64: dts: ls1012a: add crypto node
From: Horia Geantă @ 2017-03-28 11:46 UTC (permalink / raw)
To: Shawn Guo, Rob Herring, Mark Rutland
Cc: devicetree, Herbert Xu, Harninder Rai, Catalin Marinas,
Bhaskar Upadhaya, Will Deacon, Dan Douglass, linux-crypto,
David S. Miller, linux-arm-kernel
In-Reply-To: <20170328104115.GT30608@dragon>
LS1012A has a SEC v5.4 security engine.
Signed-off-by: Horia Geantă <horia.geanta@nxp.com>
---
v2: move aliases from board specific files into the shared dtsi.
arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi | 100 ++++++++++++++++++++++++-
1 file changed, 99 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi
index cffebb4b3df1..1c3493606cca 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi
@@ -42,7 +42,7 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
-#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
/ {
compatible = "fsl,ls1012a";
@@ -50,6 +50,15 @@
#address-cells = <2>;
#size-cells = <2>;
+ aliases {
+ crypto = &crypto;
+ rtic_a = &rtic_a;
+ rtic_b = &rtic_b;
+ rtic_c = &rtic_c;
+ rtic_d = &rtic_d;
+ sec_mon = &sec_mon;
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -113,6 +122,95 @@
big-endian;
};
+ crypto: crypto@1700000 {
+ compatible = "fsl,sec-v5.4", "fsl,sec-v5.0",
+ "fsl,sec-v4.0";
+ fsl,sec-era = <8>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x00 0x1700000 0x100000>;
+ reg = <0x00 0x1700000 0x0 0x100000>;
+ interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
+
+ sec_jr0: jr@10000 {
+ compatible = "fsl,sec-v5.4-job-ring",
+ "fsl,sec-v5.0-job-ring",
+ "fsl,sec-v4.0-job-ring";
+ reg = <0x10000 0x10000>;
+ interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ sec_jr1: jr@20000 {
+ compatible = "fsl,sec-v5.4-job-ring",
+ "fsl,sec-v5.0-job-ring",
+ "fsl,sec-v4.0-job-ring";
+ reg = <0x20000 0x10000>;
+ interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ sec_jr2: jr@30000 {
+ compatible = "fsl,sec-v5.4-job-ring",
+ "fsl,sec-v5.0-job-ring",
+ "fsl,sec-v4.0-job-ring";
+ reg = <0x30000 0x10000>;
+ interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ sec_jr3: jr@40000 {
+ compatible = "fsl,sec-v5.4-job-ring",
+ "fsl,sec-v5.0-job-ring",
+ "fsl,sec-v4.0-job-ring";
+ reg = <0x40000 0x10000>;
+ interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ rtic@60000 {
+ compatible = "fsl,sec-v5.4-rtic",
+ "fsl,sec-v5.0-rtic",
+ "fsl,sec-v4.0-rtic";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x60000 0x100 0x60e00 0x18>;
+ ranges = <0x0 0x60100 0x500>;
+
+ rtic_a: rtic-a@0 {
+ compatible = "fsl,sec-v5.4-rtic-memory",
+ "fsl,sec-v5.0-rtic-memory",
+ "fsl,sec-v4.0-rtic-memory";
+ reg = <0x00 0x20 0x100 0x100>;
+ };
+
+ rtic_b: rtic-b@20 {
+ compatible = "fsl,sec-v5.4-rtic-memory",
+ "fsl,sec-v5.0-rtic-memory",
+ "fsl,sec-v4.0-rtic-memory";
+ reg = <0x20 0x20 0x200 0x100>;
+ };
+
+ rtic_c: rtic-c@40 {
+ compatible = "fsl,sec-v5.4-rtic-memory",
+ "fsl,sec-v5.0-rtic-memory",
+ "fsl,sec-v4.0-rtic-memory";
+ reg = <0x40 0x20 0x300 0x100>;
+ };
+
+ rtic_d: rtic-d@60 {
+ compatible = "fsl,sec-v5.4-rtic-memory",
+ "fsl,sec-v5.0-rtic-memory",
+ "fsl,sec-v4.0-rtic-memory";
+ reg = <0x60 0x20 0x400 0x100>;
+ };
+ };
+ };
+
+ sec_mon: sec_mon@1e90000 {
+ compatible = "fsl,sec-v5.4-mon", "fsl,sec-v5.0-mon",
+ "fsl,sec-v4.0-mon";
+ reg = <0x0 0x1e90000 0x0 0x10000>;
+ interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
dcfg: dcfg@1ee0000 {
compatible = "fsl,ls1012a-dcfg",
"syscon";
--
2.12.0.264.gd6db3f216544
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH] ATM: dts: at91: sama5d2_xplained: add pwm controller
From: Alexandre Belloni @ 2017-03-28 11:34 UTC (permalink / raw)
To: m18063
Cc: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org,
boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
pawel.moll-5wv7dgnIgG8@public.gmane.org,
mark.rutland-5wv7dgnIgG8@public.gmane.org,
ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg@public.gmane.org,
galak-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org,
ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org,
cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <60c37963-509f-7764-9554-520694294909-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
Hi,
On 28/03/2017 at 14:04:25 +0300, m18063 wrote:
> >> diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
> >> index cd459e8..e88785e 100644
> >> --- a/arch/arm/boot/dts/sama5d2.dtsi
> >> +++ b/arch/arm/boot/dts/sama5d2.dtsi
> >> @@ -1098,6 +1098,14 @@
> >> status = "disabled";
> >> };
> >>
> >> + pwm0: pwm@f802c000 {
> >> + compatible = "atmel,sama5d2-pwm";
> > This compatible is not documented and is not used in any driver. Is that
> > really working?
>
> At the moment I send this patch another driver patch was send
> to used this device tree bindings and also to document it.
> Meanwhile you reviewed also that other patch.
>
Yes, I received that patch first, without the other ones. You should
have put everything in the same series.
> Please let me know if you want to send again this DT patch since
> I didn't yet receive any inputs its v2.
>
I'll take it once the pwm patch are taken by Thierry because it is not
useful as-is, unless you use:
compatible = "atmel,sama5d2-pwm", "atmel,sama5d3-pwm";
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* RE: [PATCH V6 4/7] mfd: da9061: MFD core support
From: Steve Twiss @ 2017-03-28 11:24 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Lee Jones, kbuild test robot, kbuild-all@01.org, LINUX-KERNEL,
DEVICETREE, Dmitry Torokhov, Eduardo Valentin, Guenter Roeck,
LINUX-INPUT, LINUX-PM, LINUX-WATCHDOG, Liam Girdwood, Mark Brown,
Mark Rutland, Rob Herring, Support Opensource, Wim Van Sebroeck,
Zhang Rui
In-Reply-To: <CAMuHMdU-kaAZ-Reji2v3jSypoDSiwYEWqD0vyYWwJpr6+rAPOA@mail.gmail.com>
On 28 March 2017 11:51, Geert Uytterhoeven wrote:
> Hi Steve,
>
> On Tue, Mar 28, 2017 at 12:42 PM, Steve Twiss wrote:
> > On 28 March 2017 09:37, Geert Uytterhoeven wrote:
> >> Subject: Re: [PATCH V6 4/7] mfd: da9061: MFD core support
> >> On Tue, Mar 28, 2017 at 10:21 AM, Lee Jones <lee.jones@linaro.org> wrote:
> >> >> [auto build test WARNING on ljones-mfd/for-mfd-next]
> >> >> [also build test WARNING on v4.11-rc4 next-20170327]
> >> >> base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
> >> >> config: x86_64-randconfig-x009-201713 (attached as .config)
> >> >> compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
> >> >> reproduce:
> >> >> # save the attached .config to linux build tree
> >> >> make ARCH=x86_64
> >> >>
> >> >> All warnings (new ones prefixed by >>):
> >> >>
> >> >> drivers//mfd/da9062-core.c: In function 'da9062_i2c_probe':
> >> >> >> drivers//mfd/da9062-core.c:845:21: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
> >> >> chip->chip_type = (int)match->data;
> >> >> ^
> >> >
> >> > Please use longs or enums.
> >>
> >> Enums would still give a warning on 64-bit.
> >> The simple fix is change the cast from (int) to (uintptr_t).
> >
> > Hi Lee and Geert,
> >
> > How about this? Fix by redefining the enum chip_type to be an int.
> > Then, just use substitution:
> > #define COMPAT_TYPE_DA9061 1
> > #define COMPAT_TYPE_DA9062 2
> >
> > That would be simple.
> > Are there any reasons this would not be acceptable?
>
> I don't see how that can help.
> The warning is caused by casting the "void *" (which is either 32-bit or
> 64-bit) in of_device_if.data to an integer or enum (which is always 32-bit).
>
> The right fix is to cast it to uintptr_t intead of int, like other drivers do.
Thanks Geert,
Um. Of course. Thanks, and my apologies. It would have helped if I had compile tested the right file :(
I'll follow the convention and cast (uintptr_t).
Thanks,
Steve
^ permalink raw reply
* Re: [PATCH] ATM: dts: at91: sama5d2_xplained: add pwm controller
From: m18063 @ 2017-03-28 11:04 UTC (permalink / raw)
To: Alexandre Belloni
Cc: mark.rutland@arm.com, boris.brezillon@free-electrons.com,
pawel.moll@arm.com, ijc+devicetree@hellion.org.uk,
ludovic.desroches@atmel.com, nicolas.ferre@atmel.com,
linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
robh+dt@kernel.org, galak@codeaurora.org,
cyrille.pitchen@atmel.com, linux-arm-kernel@lists.infradead.org
In-Reply-To: <20170223084241.6eqm5jxe4t7fpn6a@piout.net>
Hi Alexandre,
Sorry for the late response, I am seeing just now your question bellow.
Thank you,
Claudiu Beznea
On 23.02.2017 10:42, Alexandre Belloni wrote:
> Hi,
>
> There is a typo in your subject, please fix.
>
> On 23/02/2017 at 10:32:58 +0200, Claudiu Beznea wrote:
>> Add pwm controller bindings for sam5d2_xplained
>> and enable it.
>>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
>> ---
>> arch/arm/boot/dts/at91-sama5d2_xplained_common.dtsi | 4 ++++
>> arch/arm/boot/dts/sama5d2.dtsi | 8 ++++++++
>> 2 files changed, 12 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained_common.dtsi b/arch/arm/boot/dts/at91-sama5d2_xplained_common.dtsi
>> index 9fc270f..493ea29 100644
>> --- a/arch/arm/boot/dts/at91-sama5d2_xplained_common.dtsi
>> +++ b/arch/arm/boot/dts/at91-sama5d2_xplained_common.dtsi
>> @@ -318,6 +318,10 @@
>> };
>> };
>>
>> + pwm0: pwm@f802c000 {
>> + status = "okay";
>> + };
>> +
>> flx0: flexcom@f8034000 {
>> atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_USART>;
>> status = "disabled"; /* conflict with ISC_D2 & ISC_D3 data pins */
>> diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
>> index cd459e8..e88785e 100644
>> --- a/arch/arm/boot/dts/sama5d2.dtsi
>> +++ b/arch/arm/boot/dts/sama5d2.dtsi
>> @@ -1098,6 +1098,14 @@
>> status = "disabled";
>> };
>>
>> + pwm0: pwm@f802c000 {
>> + compatible = "atmel,sama5d2-pwm";
> This compatible is not documented and is not used in any driver. Is that
> really working?
At the moment I send this patch another driver patch was send
to used this device tree bindings and also to document it.
Meanwhile you reviewed also that other patch.
Please let me know if you want to send again this DT patch since
I didn't yet receive any inputs its v2.
>
>> + reg = <0xf802c000 0x4000>;
>> + interrupts = <38 IRQ_TYPE_LEVEL_HIGH 7>;
>> + #pwm-cells = <3>;
>> + clocks = <&pwm_clk>;
>> + };
>> +
>> sfr: sfr@f8030000 {
>> compatible = "atmel,sama5d2-sfr", "syscon";
>> reg = <0xf8030000 0x98>;
>> --
>> 2.7.4
>>
^ permalink raw reply
* [PATCH v3 7/7] UDC: Add Synopsys UDC Platform driver
From: Raviteja Garimella @ 2017-03-28 11:02 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree, linux-kernel, bcm-kernel-feedback-list, linux-usb
In-Reply-To: <1490698929-10915-1-git-send-email-raviteja.garimella@broadcom.com>
This patch adds platform driver support for Synopsys UDC.
A new driver file (snps_udc_plat.c) is created for this purpose
where the platform driver registration is done based on OF
node.
Currently, UDC integrated into Broadcom's iProc SoCs (Northstar2
and Cygnus) work with this driver.
New members are added to the UDC data structure for having platform
device support along with extcon and phy support.
Kconfig and Makefiles are modified to select platform driver for
compilation.
Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
---
drivers/usb/gadget/udc/Kconfig | 16 +-
drivers/usb/gadget/udc/Makefile | 1 +
drivers/usb/gadget/udc/amd5536udc.h | 14 ++
drivers/usb/gadget/udc/snps_udc_core.c | 54 ++++--
drivers/usb/gadget/udc/snps_udc_plat.c | 344 +++++++++++++++++++++++++++++++++
5 files changed, 409 insertions(+), 20 deletions(-)
create mode 100644 drivers/usb/gadget/udc/snps_udc_plat.c
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 2cee076..fe87523 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -254,7 +254,7 @@ config USB_MV_U3D
controller, which support super speed USB peripheral.
config USB_SNP_CORE
- depends on USB_AMD5536UDC
+ depends on (USB_AMD5536UDC || USB_SNP_UDC_PLAT)
tristate
help
This enables core driver support for Synopsys USB 2.0 Device
@@ -267,6 +267,20 @@ config USB_SNP_CORE
This IP is different to the High Speed OTG IP that can be enabled
by selecting USB_DWC2 or USB_DWC3 options.
+config USB_SNP_UDC_PLAT
+ tristate "Synopsys USB 2.0 Device controller"
+ depends on (USB_GADGET && OF)
+ select USB_GADGET_DUALSPEED
+ select USB_SNP_CORE
+ default ARCH_BCM_IPROC
+ help
+ This adds Platform Device support for Synopsys Designware core
+ AHB subsystem USB2.0 Device Controller (UDC).
+
+ This driver works with UDCs integrated into Broadcom's Northstar2
+ and Cygnus SoCs.
+
+ If unsure, say N.
#
# Controllers available in both integrated and discrete versions
#
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index 4f4fd62..ea9e1c7 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -37,4 +37,5 @@ obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o
+obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o
obj-$(CONFIG_USB_BDC_UDC) += bdc/
diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
index 91aae23..4fe22d4 100644
--- a/drivers/usb/gadget/udc/amd5536udc.h
+++ b/drivers/usb/gadget/udc/amd5536udc.h
@@ -16,6 +16,7 @@
/* debug control */
/* #define UDC_VERBOSE */
+#include <linux/extcon.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -28,6 +29,9 @@
#define UDC_HSA0_REV 1
#define UDC_HSB1_REV 2
+/* Broadcom chip rev. */
+#define UDC_BCM_REV 10
+
/*
* SETUP usb commands
* needed, because some SETUP's are handled in hw, but must be passed to
@@ -112,6 +116,7 @@
#define UDC_DEVCTL_BRLEN_MASK 0x00ff0000
#define UDC_DEVCTL_BRLEN_OFS 16
+#define UDC_DEVCTL_SRX_FLUSH 14
#define UDC_DEVCTL_CSR_DONE 13
#define UDC_DEVCTL_DEVNAK 12
#define UDC_DEVCTL_SD 10
@@ -564,7 +569,15 @@ struct udc {
u16 cur_intf;
u16 cur_alt;
+ /* for platform device and extcon support */
struct device *dev;
+ struct phy *udc_phy;
+ struct extcon_dev *edev;
+ struct extcon_specific_cable_nb extcon_nb;
+ struct notifier_block nb;
+ struct delayed_work drd_work;
+ struct workqueue_struct *drd_wq;
+ u32 conn_type;
};
#define to_amd5536_udc(g) (container_of((g), struct udc, gadget))
@@ -580,6 +593,7 @@ int udc_enable_dev_setup_interrupts(struct udc *dev);
int udc_mask_unused_interrupts(struct udc *dev);
irqreturn_t udc_irq(int irq, void *pdev);
void gadget_release(struct device *pdev);
+void empty_req_queue(struct udc_ep *ep);
void udc_basic_init(struct udc *dev);
void free_dma_pools(struct udc *dev);
int init_dma_pools(struct udc *dev);
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 9d3861b..b5f772e 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -41,7 +41,6 @@
#include "amd5536udc.h"
static void udc_tasklet_disconnect(unsigned long);
-static void empty_req_queue(struct udc_ep *);
static void udc_setup_endpoints(struct udc *dev);
static void udc_soft_reset(struct udc *dev);
static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep);
@@ -1248,7 +1247,7 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp)
}
/* Empty request queue of an endpoint; caller holds spinlock */
-static void empty_req_queue(struct udc_ep *ep)
+void empty_req_queue(struct udc_ep *ep)
{
struct udc_request *req;
@@ -1260,6 +1259,7 @@ static void empty_req_queue(struct udc_ep *ep)
complete_req(ep, req, -ESHUTDOWN);
}
}
+EXPORT_SYMBOL_GPL(empty_req_queue);
/* Dequeues a request packet, called by gadget driver */
static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
@@ -1627,6 +1627,9 @@ static void udc_setup_endpoints(struct udc *dev)
/* Bringup after Connect event, initial bringup to be ready for ep0 events */
static void usb_connect(struct udc *dev)
{
+ /* Return if already connected */
+ if (dev->connected)
+ return;
dev_info(dev->dev, "USB Connect\n");
@@ -1645,6 +1648,9 @@ static void usb_connect(struct udc *dev)
*/
static void usb_disconnect(struct udc *dev)
{
+ /* Return if already disconnected */
+ if (!dev->connected)
+ return;
dev_info(dev->dev, "USB Disconnect\n");
@@ -1719,11 +1725,15 @@ static void udc_soft_reset(struct udc *dev)
/* device int. status reset */
writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts);
- spin_lock_irqsave(&udc_irq_spinlock, flags);
- writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
- readl(&dev->regs->cfg);
- spin_unlock_irqrestore(&udc_irq_spinlock, flags);
-
+ /* Don't do this for Broadcom UDC since this is a reserved
+ * bit.
+ */
+ if (dev->chiprev != UDC_BCM_REV) {
+ spin_lock_irqsave(&udc_irq_spinlock, flags);
+ writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
+ readl(&dev->regs->cfg);
+ spin_unlock_irqrestore(&udc_irq_spinlock, flags);
+ }
}
/* RDE timer callback to set RDE bit */
@@ -3175,21 +3185,27 @@ int udc_probe(struct udc *dev)
dev_info(dev->dev, "%s\n", mod_desc);
snprintf(tmp, sizeof(tmp), "%d", dev->irq);
- dev_info(dev->dev,
- "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
- tmp, dev->phys_addr, dev->chiprev,
- (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1");
- strcpy(tmp, UDC_DRIVER_VERSION_STRING);
- if (dev->chiprev == UDC_HSA0_REV) {
- dev_err(dev->dev, "chip revision is A0; too old\n");
- retval = -ENODEV;
- goto finished;
+
+ /* Print this device info for AMD chips only*/
+ if (dev->chiprev == UDC_HSA0_REV ||
+ dev->chiprev == UDC_HSB1_REV) {
+ dev_info(dev->dev, "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
+ tmp, dev->phys_addr, dev->chiprev,
+ (dev->chiprev == UDC_HSA0_REV) ?
+ "A0" : "B1");
+ strcpy(tmp, UDC_DRIVER_VERSION_STRING);
+ if (dev->chiprev == UDC_HSA0_REV) {
+ dev_err(dev->dev, "chip revision is A0; too old\n");
+ retval = -ENODEV;
+ goto finished;
+ }
+ dev_info(dev->dev,
+ "driver version: %s(for Geode5536 B1)\n", tmp);
}
- dev_info(dev->dev,
- "driver version: %s(for Geode5536 B1)\n", tmp);
+
udc = dev;
- retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget,
+ retval = usb_add_gadget_udc_release(udc->dev, &dev->gadget,
gadget_release);
if (retval)
goto finished;
diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c
new file mode 100644
index 0000000..2e11f19
--- /dev/null
+++ b/drivers/usb/gadget/udc/snps_udc_plat.c
@@ -0,0 +1,344 @@
+/*
+ * snps_udc_plat.c - Synopsys UDC Platform Driver
+ *
+ * Copyright (C) 2016 Broadcom
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/extcon.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/module.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include "amd5536udc.h"
+
+/* description */
+#define UDC_MOD_DESCRIPTION "Synopsys UDC platform driver"
+
+void start_udc(struct udc *udc)
+{
+ if (udc->driver) {
+ dev_info(udc->dev, "Connecting...\n");
+ udc_enable_dev_setup_interrupts(udc);
+ udc_basic_init(udc);
+ udc->connected = 1;
+ }
+}
+
+void stop_udc(struct udc *udc)
+{
+ int tmp;
+ u32 reg;
+
+ spin_lock(&udc->lock);
+
+ /* Flush the receieve fifo */
+ reg = readl(&udc->regs->ctl);
+ reg |= AMD_BIT(UDC_DEVCTL_SRX_FLUSH);
+ writel(reg, &udc->regs->ctl);
+
+ reg = readl(&udc->regs->ctl);
+ reg &= ~(AMD_BIT(UDC_DEVCTL_SRX_FLUSH));
+ writel(reg, &udc->regs->ctl);
+ dev_dbg(udc->dev, "ep rx queue flushed\n");
+
+ /* Mask interrupts. Required more so when the
+ * UDC is connected to a DRD phy.
+ */
+ udc_mask_unused_interrupts(udc);
+
+ /* Disconnect gadget driver */
+ if (udc->driver) {
+ spin_unlock(&udc->lock);
+ udc->driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
+
+ /* empty queues */
+ for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
+ empty_req_queue(&udc->ep[tmp]);
+ }
+ udc->connected = 0;
+
+ spin_unlock(&udc->lock);
+ dev_info(udc->dev, "Device disconnected\n");
+}
+
+void udc_drd_work(struct work_struct *work)
+{
+ struct udc *udc;
+
+ udc = container_of(to_delayed_work(work),
+ struct udc, drd_work);
+
+ if (udc->conn_type) {
+ dev_dbg(udc->dev, "idle -> device\n");
+ start_udc(udc);
+ } else {
+ dev_dbg(udc->dev, "device -> idle\n");
+ stop_udc(udc);
+ }
+}
+
+static int usbd_connect_notify(struct notifier_block *self,
+ unsigned long event, void *ptr)
+{
+ struct udc *udc = container_of(self, struct udc, nb);
+
+ dev_dbg(udc->dev, "%s: event: %lu\n", __func__, event);
+
+ udc->conn_type = event;
+
+ schedule_delayed_work(&udc->drd_work, 0);
+
+ return NOTIFY_OK;
+}
+
+static int udc_plat_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct udc *udc;
+ int ret;
+
+ udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ return -ENOMEM;
+
+ spin_lock_init(&udc->lock);
+ udc->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ udc->virt_addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(udc->regs))
+ return PTR_ERR(udc->regs);
+
+ /* udc csr registers base */
+ udc->csr = udc->virt_addr + UDC_CSR_ADDR;
+
+ /* dev registers base */
+ udc->regs = udc->virt_addr + UDC_DEVCFG_ADDR;
+
+ /* ep registers base */
+ udc->ep_regs = udc->virt_addr + UDC_EPREGS_ADDR;
+
+ /* fifo's base */
+ udc->rxfifo = (u32 __iomem *)(udc->virt_addr + UDC_RXFIFO_ADDR);
+ udc->txfifo = (u32 __iomem *)(udc->virt_addr + UDC_TXFIFO_ADDR);
+
+ udc->phys_addr = (unsigned long)res->start;
+
+ udc->irq = irq_of_parse_and_map(dev->of_node, 0);
+ if (udc->irq <= 0) {
+ dev_err(dev, "Can't parse and map interrupt\n");
+ return -EINVAL;
+ }
+
+ udc->udc_phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
+ if (IS_ERR(udc->udc_phy)) {
+ dev_err(dev, "Failed to obtain phy from device tree\n");
+ return PTR_ERR(udc->udc_phy);
+ }
+
+ ret = phy_init(udc->udc_phy);
+ if (ret) {
+ dev_err(dev, "UDC phy init failed");
+ return ret;
+ }
+
+ ret = phy_power_on(udc->udc_phy);
+ if (ret) {
+ dev_err(dev, "UDC phy power on failed");
+ phy_exit(udc->udc_phy);
+ return ret;
+ }
+
+ /* Register for extcon if supported */
+ if (of_get_property(dev->of_node, "extcon", NULL)) {
+ udc->edev = extcon_get_edev_by_phandle(dev, 0);
+ if (IS_ERR(udc->edev)) {
+ if (PTR_ERR(udc->edev) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_err(dev, "Invalid or missing extcon\n");
+ ret = PTR_ERR(udc->edev);
+ goto exit_phy;
+ }
+
+ udc->nb.notifier_call = usbd_connect_notify;
+ ret = extcon_register_notifier(udc->edev, EXTCON_USB,
+ &udc->nb);
+ if (ret < 0) {
+ dev_err(dev, "Can't register extcon device\n");
+ goto exit_phy;
+ }
+
+ ret = extcon_get_cable_state_(udc->edev, EXTCON_USB);
+ if (ret < 0) {
+ dev_err(dev, "Can't get cable state\n");
+ goto exit_extcon;
+ } else if (ret) {
+ udc->conn_type = ret;
+ }
+ INIT_DELAYED_WORK(&udc->drd_work, udc_drd_work);
+ }
+
+ /* init dma pools */
+ if (use_dma) {
+ ret = init_dma_pools(udc);
+ if (ret != 0)
+ goto exit_extcon;
+ }
+
+ ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED,
+ "snps-udc", udc);
+ if (ret < 0) {
+ dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
+ goto exit_dma;
+ }
+
+ platform_set_drvdata(pdev, udc);
+ udc->chiprev = UDC_BCM_REV;
+
+ if (udc_probe(udc)) {
+ ret = -ENODEV;
+ goto exit_dma;
+ }
+ dev_info(dev, "Synopsys UDC platform driver probe successful\n");
+
+ return 0;
+
+exit_dma:
+ if (use_dma)
+ free_dma_pools(udc);
+exit_extcon:
+ if (udc->edev)
+ extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
+exit_phy:
+ if (udc->udc_phy) {
+ phy_power_off(udc->udc_phy);
+ phy_exit(udc->udc_phy);
+ }
+ return ret;
+}
+
+static int udc_plat_remove(struct platform_device *pdev)
+{
+ struct udc *dev;
+
+ dev = platform_get_drvdata(pdev);
+
+ usb_del_gadget_udc(&dev->gadget);
+ /* gadget driver must not be registered */
+ if (WARN_ON(dev->driver))
+ return 0;
+
+ /* dma pool cleanup */
+ free_dma_pools(dev);
+
+ udc_remove(dev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (dev->drd_wq) {
+ flush_workqueue(dev->drd_wq);
+ destroy_workqueue(dev->drd_wq);
+ }
+
+ phy_power_off(dev->udc_phy);
+ phy_exit(dev->udc_phy);
+ extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);
+
+ dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int udc_plat_suspend(struct device *dev)
+{
+ struct udc *udc;
+
+ udc = dev_get_drvdata(dev);
+ stop_udc(udc);
+
+ if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
+ dev_dbg(udc->dev, "device -> idle\n");
+ stop_udc(udc);
+ }
+ phy_power_off(udc->udc_phy);
+ phy_exit(udc->udc_phy);
+
+ return 0;
+}
+
+static int udc_plat_resume(struct device *dev)
+{
+ struct udc *udc;
+ int ret;
+
+ udc = dev_get_drvdata(dev);
+
+ ret = phy_init(udc->udc_phy);
+ if (ret) {
+ dev_err(udc->dev, "UDC phy init failure");
+ return ret;
+ }
+
+ ret = phy_power_on(udc->udc_phy);
+ if (ret) {
+ dev_err(udc->dev, "UDC phy power on failure");
+ phy_exit(udc->udc_phy);
+ return ret;
+ }
+
+ if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
+ dev_dbg(udc->dev, "idle -> device\n");
+ start_udc(udc);
+ }
+
+ return 0;
+}
+static const struct dev_pm_ops udc_plat_pm_ops = {
+ .suspend = udc_plat_suspend,
+ .resume = udc_plat_resume,
+};
+#endif
+
+#if defined(CONFIG_OF)
+static const struct of_device_id of_udc_match[] = {
+ { .compatible = "brcm,ns2-udc", },
+ { .compatible = "brcm,cygnus-udc", },
+ { .compatible = "brcm,iproc-udc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, of_udc_match);
+#endif
+
+static struct platform_driver udc_plat_driver = {
+ .probe = udc_plat_probe,
+ .remove = udc_plat_remove,
+ .driver = {
+ .name = "snps-udc-plat",
+ .of_match_table = of_match_ptr(of_udc_match),
+#ifdef CONFIG_PM_SLEEP
+ .pm = &udc_plat_pm_ops,
+#endif
+ },
+};
+module_platform_driver(udc_plat_driver);
+
+MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
--
2.1.0
^ permalink raw reply related
* [PATCH v3 6/7] DT bindings documentation for Broadcom IPROC USB Device controller.
From: Raviteja Garimella @ 2017-03-28 11:02 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1490698929-10915-1-git-send-email-raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
The device node is used for UDCs integrated into Broadcom's
iProc family of SoCs'. The UDC is based on Synopsys Designware
Cores AHB Subsystem USB Device Controller IP.
Signed-off-by: Raviteja Garimella <raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
---
Documentation/devicetree/bindings/usb/iproc-udc.txt | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/iproc-udc.txt
diff --git a/Documentation/devicetree/bindings/usb/iproc-udc.txt b/Documentation/devicetree/bindings/usb/iproc-udc.txt
new file mode 100644
index 0000000..272d7fa
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/iproc-udc.txt
@@ -0,0 +1,21 @@
+Broadcom IPROC USB Device controller.
+
+The device node is used for UDCs integrated into Broadcom's
+iProc family (Northstar2, Cygnus) of SoCs'. The UDC is based
+on Synopsys Designware Cores AHB Subsystem Device Controller
+IP.
+
+Required properties:
+ - compatible: Add the compatibility strings for supported platforms.
+ For Broadcom NS2 platform, add "brcm,ns2-udc","brcm,iproc-udc".
+ For Broadcom Cygnus platform, add "brcm,cygnus-udc", "brcm,iproc-udc".
+ - reg: Offset and length of UDC register set
+ - interrupts: description of interrupt line
+ - phys: phandle to phy node.
+
+Example:
+ udc_dwc: usb@664e0000 {
+ compatible = "brcm,ns2-udc", "brcm,iproc-udc";
+ reg = <0x664e0000 0x2000>;
+ interrupts = <GIC_SPI 424 IRQ_TYPE_LEVEL_HIGH>;
+ phys = <&usbdrd_phy>;
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 5/7] UDC: Use struct dma_pool instead of pci_pool
From: Raviteja Garimella @ 2017-03-28 11:02 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1490698929-10915-1-git-send-email-raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
Using dma_pool instead of pci_pool will make snps_udc_core driver
to be compatible with non-pci platforms.
Signed-off-by: Raviteja Garimella <raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
---
drivers/usb/gadget/udc/amd5536udc.h | 4 ++--
drivers/usb/gadget/udc/snps_udc_core.c | 8 ++++----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
index c252457..91aae23 100644
--- a/drivers/usb/gadget/udc/amd5536udc.h
+++ b/drivers/usb/gadget/udc/amd5536udc.h
@@ -551,8 +551,8 @@ struct udc {
u32 __iomem *txfifo;
/* DMA desc pools */
- struct pci_pool *data_requests;
- struct pci_pool *stp_requests;
+ struct dma_pool *data_requests;
+ struct dma_pool *stp_requests;
/* device data */
unsigned long phys_addr;
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 5f95a65..9d3861b 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -543,7 +543,7 @@ udc_alloc_request(struct usb_ep *usbep, gfp_t gfp)
if (ep->dma) {
/* ep0 in requests are allocated from data pool here */
- dma_desc = pci_pool_alloc(ep->dev->data_requests, gfp,
+ dma_desc = dma_pool_alloc(ep->dev->data_requests, gfp,
&req->td_phys);
if (!dma_desc) {
kfree(req);
@@ -582,7 +582,7 @@ static int udc_free_dma_chain(struct udc *dev, struct udc_request *req)
td = phys_to_virt(td_last->next);
for (i = 1; i < req->chain_len; i++) {
- pci_pool_free(dev->data_requests, td,
+ dma_pool_free(dev->data_requests, td,
(dma_addr_t)td_last->next);
td_last = td;
td = phys_to_virt(td_last->next);
@@ -612,7 +612,7 @@ udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq)
if (req->chain_len > 1)
udc_free_dma_chain(ep->dev, req);
- pci_pool_free(ep->dev->data_requests, req->td_data,
+ dma_pool_free(ep->dev->data_requests, req->td_data,
req->td_phys);
}
kfree(req);
@@ -807,7 +807,7 @@ static int udc_create_dma_chain(
for (i = buf_len; i < bytes; i += buf_len) {
/* create or determine next desc. */
if (create_new_chain) {
- td = pci_pool_alloc(ep->dev->data_requests,
+ td = dma_pool_alloc(ep->dev->data_requests,
gfp_flags, &dma_addr);
if (!td)
return -ENOMEM;
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 4/7] UDC: Provide correct arguments for 'dma_pool_create'
From: Raviteja Garimella @ 2017-03-28 11:02 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree, linux-kernel, bcm-kernel-feedback-list, linux-usb
In-Reply-To: <1490698929-10915-1-git-send-email-raviteja.garimella@broadcom.com>
Change the argument from NULL to a struct device for the
dma_pool_create call during dma init.
Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
---
drivers/usb/gadget/udc/snps_udc_core.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index c778d51..5f95a65 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -3101,7 +3101,7 @@ int init_dma_pools(struct udc *dev)
}
/* DMA setup */
- dev->data_requests = dma_pool_create("data_requests", NULL,
+ dev->data_requests = dma_pool_create("data_requests", dev->dev,
sizeof(struct udc_data_dma), 0, 0);
if (!dev->data_requests) {
DBG(dev, "can't get request data pool\n");
@@ -3112,7 +3112,7 @@ int init_dma_pools(struct udc *dev)
dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl;
/* dma desc for setup data */
- dev->stp_requests = dma_pool_create("setup requests", NULL,
+ dev->stp_requests = dma_pool_create("setup requests", dev->dev,
sizeof(struct udc_stp_dma), 0, 0);
if (!dev->stp_requests) {
DBG(dev, "can't get stp request pool\n");
--
2.1.0
^ permalink raw reply related
* [PATCH v3 3/7] UDC: make debug prints compatible with both pci and platform devices
From: Raviteja Garimella @ 2017-03-28 11:02 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1490698929-10915-1-git-send-email-raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
This patch adds a struct device member to UDC data structure and
makes changes to the arguments of dev_err and dev_dbg calls so that
the debug prints work for both pci and platform devices.
Signed-off-by: Raviteja Garimella <raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
---
drivers/usb/gadget/udc/amd5536udc.h | 4 +++-
drivers/usb/gadget/udc/amd5536udc_pci.c | 1 +
drivers/usb/gadget/udc/snps_udc_core.c | 28 ++++++++++++++--------------
3 files changed, 18 insertions(+), 15 deletions(-)
diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
index bd2a18e..c252457 100644
--- a/drivers/usb/gadget/udc/amd5536udc.h
+++ b/drivers/usb/gadget/udc/amd5536udc.h
@@ -563,6 +563,8 @@ struct udc {
u16 cur_config;
u16 cur_intf;
u16 cur_alt;
+
+ struct device *dev;
};
#define to_amd5536_udc(g) (container_of((g), struct udc, gadget))
@@ -639,7 +641,7 @@ MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
/* debug macros ------------------------------------------------------------*/
-#define DBG(udc , args...) dev_dbg(&(udc)->pdev->dev, args)
+#define DBG(udc , args...) dev_dbg(udc->dev, args)
#ifdef UDC_VERBOSE
#define VDBG DBG
diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c
index 2a2d0a9..57a13f0 100644
--- a/drivers/usb/gadget/udc/amd5536udc_pci.c
+++ b/drivers/usb/gadget/udc/amd5536udc_pci.c
@@ -168,6 +168,7 @@ static int udc_pci_probe(
dev->phys_addr = resource;
dev->irq = pdev->irq;
dev->pdev = pdev;
+ dev->dev = &pdev->dev;
/* general probing */
if (udc_probe(dev)) {
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 72f3c8f..c778d51 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -209,18 +209,18 @@ static void print_regs(struct udc *dev)
if (use_dma && use_dma_ppb && !use_dma_ppb_du) {
DBG(dev, "DMA mode = PPBNDU (packet per buffer "
"WITHOUT desc. update)\n");
- dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU");
+ dev_info(dev->dev, "DMA mode (%s)\n", "PPBNDU");
} else if (use_dma && use_dma_ppb && use_dma_ppb_du) {
DBG(dev, "DMA mode = PPBDU (packet per buffer "
"WITH desc. update)\n");
- dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU");
+ dev_info(dev->dev, "DMA mode (%s)\n", "PPBDU");
}
if (use_dma && use_dma_bufferfill_mode) {
DBG(dev, "DMA mode = BF (buffer fill mode)\n");
- dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF");
+ dev_info(dev->dev, "DMA mode (%s)\n", "BF");
}
if (!use_dma)
- dev_info(&dev->pdev->dev, "FIFO mode\n");
+ dev_info(dev->dev, "FIFO mode\n");
DBG(dev, "-------------------------------------------------------\n");
}
@@ -1628,7 +1628,7 @@ static void udc_setup_endpoints(struct udc *dev)
static void usb_connect(struct udc *dev)
{
- dev_info(&dev->pdev->dev, "USB Connect\n");
+ dev_info(dev->dev, "USB Connect\n");
dev->connected = 1;
@@ -1646,7 +1646,7 @@ static void usb_connect(struct udc *dev)
static void usb_disconnect(struct udc *dev)
{
- dev_info(&dev->pdev->dev, "USB Disconnect\n");
+ dev_info(dev->dev, "USB Disconnect\n");
dev->connected = 0;
@@ -2110,7 +2110,7 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix)
}
/* HE event ? */
if (tmp & AMD_BIT(UDC_EPSTS_HE)) {
- dev_err(&dev->pdev->dev, "HE ep%dout occurred\n", ep->num);
+ dev_err(dev->dev, "HE ep%dout occurred\n", ep->num);
/* clear HE */
writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
@@ -2309,7 +2309,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
if (use_dma) {
/* BNA ? */
if (epsts & AMD_BIT(UDC_EPSTS_BNA)) {
- dev_err(&dev->pdev->dev,
+ dev_err(dev->dev,
"BNA ep%din occurred - DESPTR = %08lx\n",
ep->num,
(unsigned long) readl(&ep->regs->desptr));
@@ -2322,7 +2322,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
}
/* HE event ? */
if (epsts & AMD_BIT(UDC_EPSTS_HE)) {
- dev_err(&dev->pdev->dev,
+ dev_err(dev->dev,
"HE ep%dn occurred - DESPTR = %08lx\n",
ep->num, (unsigned long) readl(&ep->regs->desptr));
@@ -2960,7 +2960,7 @@ __acquires(dev->lock)
/* link up all endpoints */
udc_setup_endpoints(dev);
- dev_info(&dev->pdev->dev, "Connect: %s\n",
+ dev_info(dev->dev, "Connect: %s\n",
usb_speed_string(dev->gadget.speed));
/* init ep 0 */
@@ -3172,20 +3172,20 @@ int udc_probe(struct udc *dev)
/* init registers, interrupts, ... */
startup_registers(dev);
- dev_info(&dev->pdev->dev, "%s\n", mod_desc);
+ dev_info(dev->dev, "%s\n", mod_desc);
snprintf(tmp, sizeof(tmp), "%d", dev->irq);
- dev_info(&dev->pdev->dev,
+ dev_info(dev->dev,
"irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
tmp, dev->phys_addr, dev->chiprev,
(dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1");
strcpy(tmp, UDC_DRIVER_VERSION_STRING);
if (dev->chiprev == UDC_HSA0_REV) {
- dev_err(&dev->pdev->dev, "chip revision is A0; too old\n");
+ dev_err(dev->dev, "chip revision is A0; too old\n");
retval = -ENODEV;
goto finished;
}
- dev_info(&dev->pdev->dev,
+ dev_info(dev->dev,
"driver version: %s(for Geode5536 B1)\n", tmp);
udc = dev;
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 2/7] UDC: Rename amd5536udc driver file based on IP
From: Raviteja Garimella @ 2017-03-28 11:02 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree, linux-kernel, bcm-kernel-feedback-list, linux-usb
In-Reply-To: <1490698929-10915-1-git-send-email-raviteja.garimella@broadcom.com>
This patch renames the amd5536udc.c that has the core driver
functionality of Synopsys UDC to snps_udc_core.c
The symbols exported here can be used by any UDC driver that uses
the same Synopsys IP.
Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
---
drivers/usb/gadget/udc/Makefile | 2 +-
drivers/usb/gadget/udc/amd5536udc.c | 3223 --------------------------------
drivers/usb/gadget/udc/snps_udc_core.c | 3223 ++++++++++++++++++++++++++++++++
3 files changed, 3224 insertions(+), 3224 deletions(-)
delete mode 100644 drivers/usb/gadget/udc/amd5536udc.c
create mode 100644 drivers/usb/gadget/udc/snps_udc_core.c
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index 626e1f1..4f4fd62 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_USB_GADGET) += udc-core.o
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2272) += net2272.o
obj-$(CONFIG_USB_NET2280) += net2280.o
-obj-$(CONFIG_USB_SNP_CORE) += amd5536udc.o
+obj-$(CONFIG_USB_SNP_CORE) += snps_udc_core.o
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc_pci.o
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
diff --git a/drivers/usb/gadget/udc/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c
deleted file mode 100644
index 72f3c8f..0000000
--- a/drivers/usb/gadget/udc/amd5536udc.c
+++ /dev/null
@@ -1,3223 +0,0 @@
-/*
- * amd5536.c -- AMD 5536 UDC high/full speed USB device controller
- *
- * Copyright (C) 2005-2007 AMD (http://www.amd.com)
- * Author: Thomas Dahlmann
- *
- * 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 file does the core driver implementation for the UDC that is based
- * on Synopsys device controller IP (different than HS OTG IP) that is either
- * connected through PCI bus or integrated to SoC platforms.
- */
-
-/* Driver strings */
-#define UDC_MOD_DESCRIPTION "Synopsys USB Device Controller"
-#define UDC_DRIVER_VERSION_STRING "01.00.0206"
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/timer.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-#include <linux/ioctl.h>
-#include <linux/fs.h>
-#include <linux/dmapool.h>
-#include <linux/prefetch.h>
-#include <linux/moduleparam.h>
-#include <asm/byteorder.h>
-#include <asm/unaligned.h>
-#include "amd5536udc.h"
-
-static void udc_tasklet_disconnect(unsigned long);
-static void empty_req_queue(struct udc_ep *);
-static void udc_setup_endpoints(struct udc *dev);
-static void udc_soft_reset(struct udc *dev);
-static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep);
-static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq);
-
-/* description */
-static const char mod_desc[] = UDC_MOD_DESCRIPTION;
-static const char name[] = "udc";
-
-/* structure to hold endpoint function pointers */
-static const struct usb_ep_ops udc_ep_ops;
-
-/* received setup data */
-static union udc_setup_data setup_data;
-
-/* pointer to device object */
-static struct udc *udc;
-
-/* irq spin lock for soft reset */
-static DEFINE_SPINLOCK(udc_irq_spinlock);
-/* stall spin lock */
-static DEFINE_SPINLOCK(udc_stall_spinlock);
-
-/*
-* slave mode: pending bytes in rx fifo after nyet,
-* used if EPIN irq came but no req was available
-*/
-static unsigned int udc_rxfifo_pending;
-
-/* count soft resets after suspend to avoid loop */
-static int soft_reset_occured;
-static int soft_reset_after_usbreset_occured;
-
-/* timer */
-static struct timer_list udc_timer;
-static int stop_timer;
-
-/* set_rde -- Is used to control enabling of RX DMA. Problem is
- * that UDC has only one bit (RDE) to enable/disable RX DMA for
- * all OUT endpoints. So we have to handle race conditions like
- * when OUT data reaches the fifo but no request was queued yet.
- * This cannot be solved by letting the RX DMA disabled until a
- * request gets queued because there may be other OUT packets
- * in the FIFO (important for not blocking control traffic).
- * The value of set_rde controls the correspondig timer.
- *
- * set_rde -1 == not used, means it is alloed to be set to 0 or 1
- * set_rde 0 == do not touch RDE, do no start the RDE timer
- * set_rde 1 == timer function will look whether FIFO has data
- * set_rde 2 == set by timer function to enable RX DMA on next call
- */
-static int set_rde = -1;
-
-static DECLARE_COMPLETION(on_exit);
-static struct timer_list udc_pollstall_timer;
-static int stop_pollstall_timer;
-static DECLARE_COMPLETION(on_pollstall_exit);
-
-/* tasklet for usb disconnect */
-static DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect,
- (unsigned long) &udc);
-
-
-/* endpoint names used for print */
-static const char ep0_string[] = "ep0in";
-static const struct {
- const char *name;
- const struct usb_ep_caps caps;
-} ep_info[] = {
-#define EP_INFO(_name, _caps) \
- { \
- .name = _name, \
- .caps = _caps, \
- }
-
- EP_INFO(ep0_string,
- USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep1in-int",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep2in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep3in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep4in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep5in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep6in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep7in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep8in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep9in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep10in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep11in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep12in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep13in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep14in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep15in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep0out",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep1out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep2out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep3out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep4out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep5out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep6out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep7out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep8out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep9out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep10out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep11out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep12out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep13out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep14out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep15out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
-
-#undef EP_INFO
-};
-
-/* buffer fill mode */
-static int use_dma_bufferfill_mode;
-/* tx buffer size for high speed */
-static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE;
-
-/*---------------------------------------------------------------------------*/
-/* Prints UDC device registers and endpoint irq registers */
-static void print_regs(struct udc *dev)
-{
- DBG(dev, "------- Device registers -------\n");
- DBG(dev, "dev config = %08x\n", readl(&dev->regs->cfg));
- DBG(dev, "dev control = %08x\n", readl(&dev->regs->ctl));
- DBG(dev, "dev status = %08x\n", readl(&dev->regs->sts));
- DBG(dev, "\n");
- DBG(dev, "dev int's = %08x\n", readl(&dev->regs->irqsts));
- DBG(dev, "dev intmask = %08x\n", readl(&dev->regs->irqmsk));
- DBG(dev, "\n");
- DBG(dev, "dev ep int's = %08x\n", readl(&dev->regs->ep_irqsts));
- DBG(dev, "dev ep intmask = %08x\n", readl(&dev->regs->ep_irqmsk));
- DBG(dev, "\n");
- DBG(dev, "USE DMA = %d\n", use_dma);
- if (use_dma && use_dma_ppb && !use_dma_ppb_du) {
- DBG(dev, "DMA mode = PPBNDU (packet per buffer "
- "WITHOUT desc. update)\n");
- dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU");
- } else if (use_dma && use_dma_ppb && use_dma_ppb_du) {
- DBG(dev, "DMA mode = PPBDU (packet per buffer "
- "WITH desc. update)\n");
- dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU");
- }
- if (use_dma && use_dma_bufferfill_mode) {
- DBG(dev, "DMA mode = BF (buffer fill mode)\n");
- dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF");
- }
- if (!use_dma)
- dev_info(&dev->pdev->dev, "FIFO mode\n");
- DBG(dev, "-------------------------------------------------------\n");
-}
-
-/* Masks unused interrupts */
-int udc_mask_unused_interrupts(struct udc *dev)
-{
- u32 tmp;
-
- /* mask all dev interrupts */
- tmp = AMD_BIT(UDC_DEVINT_SVC) |
- AMD_BIT(UDC_DEVINT_ENUM) |
- AMD_BIT(UDC_DEVINT_US) |
- AMD_BIT(UDC_DEVINT_UR) |
- AMD_BIT(UDC_DEVINT_ES) |
- AMD_BIT(UDC_DEVINT_SI) |
- AMD_BIT(UDC_DEVINT_SOF)|
- AMD_BIT(UDC_DEVINT_SC);
- writel(tmp, &dev->regs->irqmsk);
-
- /* mask all ep interrupts */
- writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqmsk);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(udc_mask_unused_interrupts);
-
-/* Enables endpoint 0 interrupts */
-static int udc_enable_ep0_interrupts(struct udc *dev)
-{
- u32 tmp;
-
- DBG(dev, "udc_enable_ep0_interrupts()\n");
-
- /* read irq mask */
- tmp = readl(&dev->regs->ep_irqmsk);
- /* enable ep0 irq's */
- tmp &= AMD_UNMASK_BIT(UDC_EPINT_IN_EP0)
- & AMD_UNMASK_BIT(UDC_EPINT_OUT_EP0);
- writel(tmp, &dev->regs->ep_irqmsk);
-
- return 0;
-}
-
-/* Enables device interrupts for SET_INTF and SET_CONFIG */
-int udc_enable_dev_setup_interrupts(struct udc *dev)
-{
- u32 tmp;
-
- DBG(dev, "enable device interrupts for setup data\n");
-
- /* read irq mask */
- tmp = readl(&dev->regs->irqmsk);
-
- /* enable SET_INTERFACE, SET_CONFIG and other needed irq's */
- tmp &= AMD_UNMASK_BIT(UDC_DEVINT_SI)
- & AMD_UNMASK_BIT(UDC_DEVINT_SC)
- & AMD_UNMASK_BIT(UDC_DEVINT_UR)
- & AMD_UNMASK_BIT(UDC_DEVINT_SVC)
- & AMD_UNMASK_BIT(UDC_DEVINT_ENUM);
- writel(tmp, &dev->regs->irqmsk);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(udc_enable_dev_setup_interrupts);
-
-/* Calculates fifo start of endpoint based on preceding endpoints */
-static int udc_set_txfifo_addr(struct udc_ep *ep)
-{
- struct udc *dev;
- u32 tmp;
- int i;
-
- if (!ep || !(ep->in))
- return -EINVAL;
-
- dev = ep->dev;
- ep->txfifo = dev->txfifo;
-
- /* traverse ep's */
- for (i = 0; i < ep->num; i++) {
- if (dev->ep[i].regs) {
- /* read fifo size */
- tmp = readl(&dev->ep[i].regs->bufin_framenum);
- tmp = AMD_GETBITS(tmp, UDC_EPIN_BUFF_SIZE);
- ep->txfifo += tmp;
- }
- }
- return 0;
-}
-
-/* CNAK pending field: bit0 = ep0in, bit16 = ep0out */
-static u32 cnak_pending;
-
-static void UDC_QUEUE_CNAK(struct udc_ep *ep, unsigned num)
-{
- if (readl(&ep->regs->ctl) & AMD_BIT(UDC_EPCTL_NAK)) {
- DBG(ep->dev, "NAK could not be cleared for ep%d\n", num);
- cnak_pending |= 1 << (num);
- ep->naking = 1;
- } else
- cnak_pending = cnak_pending & (~(1 << (num)));
-}
-
-
-/* Enables endpoint, is called by gadget driver */
-static int
-udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc)
-{
- struct udc_ep *ep;
- struct udc *dev;
- u32 tmp;
- unsigned long iflags;
- u8 udc_csr_epix;
- unsigned maxpacket;
-
- if (!usbep
- || usbep->name == ep0_string
- || !desc
- || desc->bDescriptorType != USB_DT_ENDPOINT)
- return -EINVAL;
-
- ep = container_of(usbep, struct udc_ep, ep);
- dev = ep->dev;
-
- DBG(dev, "udc_ep_enable() ep %d\n", ep->num);
-
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- spin_lock_irqsave(&dev->lock, iflags);
- ep->ep.desc = desc;
-
- ep->halted = 0;
-
- /* set traffic type */
- tmp = readl(&dev->ep[ep->num].regs->ctl);
- tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_EPCTL_ET);
- writel(tmp, &dev->ep[ep->num].regs->ctl);
-
- /* set max packet size */
- maxpacket = usb_endpoint_maxp(desc);
- tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt);
- tmp = AMD_ADDBITS(tmp, maxpacket, UDC_EP_MAX_PKT_SIZE);
- ep->ep.maxpacket = maxpacket;
- writel(tmp, &dev->ep[ep->num].regs->bufout_maxpkt);
-
- /* IN ep */
- if (ep->in) {
-
- /* ep ix in UDC CSR register space */
- udc_csr_epix = ep->num;
-
- /* set buffer size (tx fifo entries) */
- tmp = readl(&dev->ep[ep->num].regs->bufin_framenum);
- /* double buffering: fifo size = 2 x max packet size */
- tmp = AMD_ADDBITS(
- tmp,
- maxpacket * UDC_EPIN_BUFF_SIZE_MULT
- / UDC_DWORD_BYTES,
- UDC_EPIN_BUFF_SIZE);
- writel(tmp, &dev->ep[ep->num].regs->bufin_framenum);
-
- /* calc. tx fifo base addr */
- udc_set_txfifo_addr(ep);
-
- /* flush fifo */
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_F);
- writel(tmp, &ep->regs->ctl);
-
- /* OUT ep */
- } else {
- /* ep ix in UDC CSR register space */
- udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
-
- /* set max packet size UDC CSR */
- tmp = readl(&dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);
- tmp = AMD_ADDBITS(tmp, maxpacket,
- UDC_CSR_NE_MAX_PKT);
- writel(tmp, &dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);
-
- if (use_dma && !ep->in) {
- /* alloc and init BNA dummy request */
- ep->bna_dummy_req = udc_alloc_bna_dummy(ep);
- ep->bna_occurred = 0;
- }
-
- if (ep->num != UDC_EP0OUT_IX)
- dev->data_ep_enabled = 1;
- }
-
- /* set ep values */
- tmp = readl(&dev->csr->ne[udc_csr_epix]);
- /* max packet */
- tmp = AMD_ADDBITS(tmp, maxpacket, UDC_CSR_NE_MAX_PKT);
- /* ep number */
- tmp = AMD_ADDBITS(tmp, desc->bEndpointAddress, UDC_CSR_NE_NUM);
- /* ep direction */
- tmp = AMD_ADDBITS(tmp, ep->in, UDC_CSR_NE_DIR);
- /* ep type */
- tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_CSR_NE_TYPE);
- /* ep config */
- tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, UDC_CSR_NE_CFG);
- /* ep interface */
- tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, UDC_CSR_NE_INTF);
- /* ep alt */
- tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, UDC_CSR_NE_ALT);
- /* write reg */
- writel(tmp, &dev->csr->ne[udc_csr_epix]);
-
- /* enable ep irq */
- tmp = readl(&dev->regs->ep_irqmsk);
- tmp &= AMD_UNMASK_BIT(ep->num);
- writel(tmp, &dev->regs->ep_irqmsk);
-
- /*
- * clear NAK by writing CNAK
- * avoid BNA for OUT DMA, don't clear NAK until DMA desc. written
- */
- if (!use_dma || ep->in) {
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &ep->regs->ctl);
- ep->naking = 0;
- UDC_QUEUE_CNAK(ep, ep->num);
- }
- tmp = desc->bEndpointAddress;
- DBG(dev, "%s enabled\n", usbep->name);
-
- spin_unlock_irqrestore(&dev->lock, iflags);
- return 0;
-}
-
-/* Resets endpoint */
-static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep)
-{
- u32 tmp;
-
- VDBG(ep->dev, "ep-%d reset\n", ep->num);
- ep->ep.desc = NULL;
- ep->ep.ops = &udc_ep_ops;
- INIT_LIST_HEAD(&ep->queue);
-
- usb_ep_set_maxpacket_limit(&ep->ep,(u16) ~0);
- /* set NAK */
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_SNAK);
- writel(tmp, &ep->regs->ctl);
- ep->naking = 1;
-
- /* disable interrupt */
- tmp = readl(®s->ep_irqmsk);
- tmp |= AMD_BIT(ep->num);
- writel(tmp, ®s->ep_irqmsk);
-
- if (ep->in) {
- /* unset P and IN bit of potential former DMA */
- tmp = readl(&ep->regs->ctl);
- tmp &= AMD_UNMASK_BIT(UDC_EPCTL_P);
- writel(tmp, &ep->regs->ctl);
-
- tmp = readl(&ep->regs->sts);
- tmp |= AMD_BIT(UDC_EPSTS_IN);
- writel(tmp, &ep->regs->sts);
-
- /* flush the fifo */
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_F);
- writel(tmp, &ep->regs->ctl);
-
- }
- /* reset desc pointer */
- writel(0, &ep->regs->desptr);
-}
-
-/* Disables endpoint, is called by gadget driver */
-static int udc_ep_disable(struct usb_ep *usbep)
-{
- struct udc_ep *ep = NULL;
- unsigned long iflags;
-
- if (!usbep)
- return -EINVAL;
-
- ep = container_of(usbep, struct udc_ep, ep);
- if (usbep->name == ep0_string || !ep->ep.desc)
- return -EINVAL;
-
- DBG(ep->dev, "Disable ep-%d\n", ep->num);
-
- spin_lock_irqsave(&ep->dev->lock, iflags);
- udc_free_request(&ep->ep, &ep->bna_dummy_req->req);
- empty_req_queue(ep);
- ep_init(ep->dev->regs, ep);
- spin_unlock_irqrestore(&ep->dev->lock, iflags);
-
- return 0;
-}
-
-/* Allocates request packet, called by gadget driver */
-static struct usb_request *
-udc_alloc_request(struct usb_ep *usbep, gfp_t gfp)
-{
- struct udc_request *req;
- struct udc_data_dma *dma_desc;
- struct udc_ep *ep;
-
- if (!usbep)
- return NULL;
-
- ep = container_of(usbep, struct udc_ep, ep);
-
- VDBG(ep->dev, "udc_alloc_req(): ep%d\n", ep->num);
- req = kzalloc(sizeof(struct udc_request), gfp);
- if (!req)
- return NULL;
-
- req->req.dma = DMA_DONT_USE;
- INIT_LIST_HEAD(&req->queue);
-
- if (ep->dma) {
- /* ep0 in requests are allocated from data pool here */
- dma_desc = pci_pool_alloc(ep->dev->data_requests, gfp,
- &req->td_phys);
- if (!dma_desc) {
- kfree(req);
- return NULL;
- }
-
- VDBG(ep->dev, "udc_alloc_req: req = %p dma_desc = %p, "
- "td_phys = %lx\n",
- req, dma_desc,
- (unsigned long)req->td_phys);
- /* prevent from using desc. - set HOST BUSY */
- dma_desc->status = AMD_ADDBITS(dma_desc->status,
- UDC_DMA_STP_STS_BS_HOST_BUSY,
- UDC_DMA_STP_STS_BS);
- dma_desc->bufptr = cpu_to_le32(DMA_DONT_USE);
- req->td_data = dma_desc;
- req->td_data_last = NULL;
- req->chain_len = 1;
- }
-
- return &req->req;
-}
-
-/* frees pci pool descriptors of a DMA chain */
-static int udc_free_dma_chain(struct udc *dev, struct udc_request *req)
-{
- int ret_val = 0;
- struct udc_data_dma *td;
- struct udc_data_dma *td_last = NULL;
- unsigned int i;
-
- DBG(dev, "free chain req = %p\n", req);
-
- /* do not free first desc., will be done by free for request */
- td_last = req->td_data;
- td = phys_to_virt(td_last->next);
-
- for (i = 1; i < req->chain_len; i++) {
- pci_pool_free(dev->data_requests, td,
- (dma_addr_t)td_last->next);
- td_last = td;
- td = phys_to_virt(td_last->next);
- }
-
- return ret_val;
-}
-
-/* Frees request packet, called by gadget driver */
-static void
-udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq)
-{
- struct udc_ep *ep;
- struct udc_request *req;
-
- if (!usbep || !usbreq)
- return;
-
- ep = container_of(usbep, struct udc_ep, ep);
- req = container_of(usbreq, struct udc_request, req);
- VDBG(ep->dev, "free_req req=%p\n", req);
- BUG_ON(!list_empty(&req->queue));
- if (req->td_data) {
- VDBG(ep->dev, "req->td_data=%p\n", req->td_data);
-
- /* free dma chain if created */
- if (req->chain_len > 1)
- udc_free_dma_chain(ep->dev, req);
-
- pci_pool_free(ep->dev->data_requests, req->td_data,
- req->td_phys);
- }
- kfree(req);
-}
-
-/* Init BNA dummy descriptor for HOST BUSY and pointing to itself */
-static void udc_init_bna_dummy(struct udc_request *req)
-{
- if (req) {
- /* set last bit */
- req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L);
- /* set next pointer to itself */
- req->td_data->next = req->td_phys;
- /* set HOST BUSY */
- req->td_data->status
- = AMD_ADDBITS(req->td_data->status,
- UDC_DMA_STP_STS_BS_DMA_DONE,
- UDC_DMA_STP_STS_BS);
-#ifdef UDC_VERBOSE
- pr_debug("bna desc = %p, sts = %08x\n",
- req->td_data, req->td_data->status);
-#endif
- }
-}
-
-/* Allocate BNA dummy descriptor */
-static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep)
-{
- struct udc_request *req = NULL;
- struct usb_request *_req = NULL;
-
- /* alloc the dummy request */
- _req = udc_alloc_request(&ep->ep, GFP_ATOMIC);
- if (_req) {
- req = container_of(_req, struct udc_request, req);
- ep->bna_dummy_req = req;
- udc_init_bna_dummy(req);
- }
- return req;
-}
-
-/* Write data to TX fifo for IN packets */
-static void
-udc_txfifo_write(struct udc_ep *ep, struct usb_request *req)
-{
- u8 *req_buf;
- u32 *buf;
- int i, j;
- unsigned bytes = 0;
- unsigned remaining = 0;
-
- if (!req || !ep)
- return;
-
- req_buf = req->buf + req->actual;
- prefetch(req_buf);
- remaining = req->length - req->actual;
-
- buf = (u32 *) req_buf;
-
- bytes = ep->ep.maxpacket;
- if (bytes > remaining)
- bytes = remaining;
-
- /* dwords first */
- for (i = 0; i < bytes / UDC_DWORD_BYTES; i++)
- writel(*(buf + i), ep->txfifo);
-
- /* remaining bytes must be written by byte access */
- for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
- writeb((u8)(*(buf + i) >> (j << UDC_BITS_PER_BYTE_SHIFT)),
- ep->txfifo);
- }
-
- /* dummy write confirm */
- writel(0, &ep->regs->confirm);
-}
-
-/* Read dwords from RX fifo for OUT transfers */
-static int udc_rxfifo_read_dwords(struct udc *dev, u32 *buf, int dwords)
-{
- int i;
-
- VDBG(dev, "udc_read_dwords(): %d dwords\n", dwords);
-
- for (i = 0; i < dwords; i++)
- *(buf + i) = readl(dev->rxfifo);
- return 0;
-}
-
-/* Read bytes from RX fifo for OUT transfers */
-static int udc_rxfifo_read_bytes(struct udc *dev, u8 *buf, int bytes)
-{
- int i, j;
- u32 tmp;
-
- VDBG(dev, "udc_read_bytes(): %d bytes\n", bytes);
-
- /* dwords first */
- for (i = 0; i < bytes / UDC_DWORD_BYTES; i++)
- *((u32 *)(buf + (i<<2))) = readl(dev->rxfifo);
-
- /* remaining bytes must be read by byte access */
- if (bytes % UDC_DWORD_BYTES) {
- tmp = readl(dev->rxfifo);
- for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
- *(buf + (i<<2) + j) = (u8)(tmp & UDC_BYTE_MASK);
- tmp = tmp >> UDC_BITS_PER_BYTE;
- }
- }
-
- return 0;
-}
-
-/* Read data from RX fifo for OUT transfers */
-static int
-udc_rxfifo_read(struct udc_ep *ep, struct udc_request *req)
-{
- u8 *buf;
- unsigned buf_space;
- unsigned bytes = 0;
- unsigned finished = 0;
-
- /* received number bytes */
- bytes = readl(&ep->regs->sts);
- bytes = AMD_GETBITS(bytes, UDC_EPSTS_RX_PKT_SIZE);
-
- buf_space = req->req.length - req->req.actual;
- buf = req->req.buf + req->req.actual;
- if (bytes > buf_space) {
- if ((buf_space % ep->ep.maxpacket) != 0) {
- DBG(ep->dev,
- "%s: rx %d bytes, rx-buf space = %d bytesn\n",
- ep->ep.name, bytes, buf_space);
- req->req.status = -EOVERFLOW;
- }
- bytes = buf_space;
- }
- req->req.actual += bytes;
-
- /* last packet ? */
- if (((bytes % ep->ep.maxpacket) != 0) || (!bytes)
- || ((req->req.actual == req->req.length) && !req->req.zero))
- finished = 1;
-
- /* read rx fifo bytes */
- VDBG(ep->dev, "ep %s: rxfifo read %d bytes\n", ep->ep.name, bytes);
- udc_rxfifo_read_bytes(ep->dev, buf, bytes);
-
- return finished;
-}
-
-/* Creates or re-inits a DMA chain */
-static int udc_create_dma_chain(
- struct udc_ep *ep,
- struct udc_request *req,
- unsigned long buf_len, gfp_t gfp_flags
-)
-{
- unsigned long bytes = req->req.length;
- unsigned int i;
- dma_addr_t dma_addr;
- struct udc_data_dma *td = NULL;
- struct udc_data_dma *last = NULL;
- unsigned long txbytes;
- unsigned create_new_chain = 0;
- unsigned len;
-
- VDBG(ep->dev, "udc_create_dma_chain: bytes=%ld buf_len=%ld\n",
- bytes, buf_len);
- dma_addr = DMA_DONT_USE;
-
- /* unset L bit in first desc for OUT */
- if (!ep->in)
- req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L);
-
- /* alloc only new desc's if not already available */
- len = req->req.length / ep->ep.maxpacket;
- if (req->req.length % ep->ep.maxpacket)
- len++;
-
- if (len > req->chain_len) {
- /* shorter chain already allocated before */
- if (req->chain_len > 1)
- udc_free_dma_chain(ep->dev, req);
- req->chain_len = len;
- create_new_chain = 1;
- }
-
- td = req->td_data;
- /* gen. required number of descriptors and buffers */
- for (i = buf_len; i < bytes; i += buf_len) {
- /* create or determine next desc. */
- if (create_new_chain) {
- td = pci_pool_alloc(ep->dev->data_requests,
- gfp_flags, &dma_addr);
- if (!td)
- return -ENOMEM;
-
- td->status = 0;
- } else if (i == buf_len) {
- /* first td */
- td = (struct udc_data_dma *)phys_to_virt(
- req->td_data->next);
- td->status = 0;
- } else {
- td = (struct udc_data_dma *)phys_to_virt(last->next);
- td->status = 0;
- }
-
- if (td)
- td->bufptr = req->req.dma + i; /* assign buffer */
- else
- break;
-
- /* short packet ? */
- if ((bytes - i) >= buf_len) {
- txbytes = buf_len;
- } else {
- /* short packet */
- txbytes = bytes - i;
- }
-
- /* link td and assign tx bytes */
- if (i == buf_len) {
- if (create_new_chain)
- req->td_data->next = dma_addr;
- /*
- * else
- * req->td_data->next = virt_to_phys(td);
- */
- /* write tx bytes */
- if (ep->in) {
- /* first desc */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- ep->ep.maxpacket,
- UDC_DMA_IN_STS_TXBYTES);
- /* second desc */
- td->status = AMD_ADDBITS(td->status,
- txbytes,
- UDC_DMA_IN_STS_TXBYTES);
- }
- } else {
- if (create_new_chain)
- last->next = dma_addr;
- /*
- * else
- * last->next = virt_to_phys(td);
- */
- if (ep->in) {
- /* write tx bytes */
- td->status = AMD_ADDBITS(td->status,
- txbytes,
- UDC_DMA_IN_STS_TXBYTES);
- }
- }
- last = td;
- }
- /* set last bit */
- if (td) {
- td->status |= AMD_BIT(UDC_DMA_IN_STS_L);
- /* last desc. points to itself */
- req->td_data_last = td;
- }
-
- return 0;
-}
-
-/* create/re-init a DMA descriptor or a DMA descriptor chain */
-static int prep_dma(struct udc_ep *ep, struct udc_request *req, gfp_t gfp)
-{
- int retval = 0;
- u32 tmp;
-
- VDBG(ep->dev, "prep_dma\n");
- VDBG(ep->dev, "prep_dma ep%d req->td_data=%p\n",
- ep->num, req->td_data);
-
- /* set buffer pointer */
- req->td_data->bufptr = req->req.dma;
-
- /* set last bit */
- req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L);
-
- /* build/re-init dma chain if maxpkt scatter mode, not for EP0 */
- if (use_dma_ppb) {
-
- retval = udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp);
- if (retval != 0) {
- if (retval == -ENOMEM)
- DBG(ep->dev, "Out of DMA memory\n");
- return retval;
- }
- if (ep->in) {
- if (req->req.length == ep->ep.maxpacket) {
- /* write tx bytes */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- ep->ep.maxpacket,
- UDC_DMA_IN_STS_TXBYTES);
-
- }
- }
-
- }
-
- if (ep->in) {
- VDBG(ep->dev, "IN: use_dma_ppb=%d req->req.len=%d "
- "maxpacket=%d ep%d\n",
- use_dma_ppb, req->req.length,
- ep->ep.maxpacket, ep->num);
- /*
- * if bytes < max packet then tx bytes must
- * be written in packet per buffer mode
- */
- if (!use_dma_ppb || req->req.length < ep->ep.maxpacket
- || ep->num == UDC_EP0OUT_IX
- || ep->num == UDC_EP0IN_IX) {
- /* write tx bytes */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- req->req.length,
- UDC_DMA_IN_STS_TXBYTES);
- /* reset frame num */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- 0,
- UDC_DMA_IN_STS_FRAMENUM);
- }
- /* set HOST BUSY */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- UDC_DMA_STP_STS_BS_HOST_BUSY,
- UDC_DMA_STP_STS_BS);
- } else {
- VDBG(ep->dev, "OUT set host ready\n");
- /* set HOST READY */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- UDC_DMA_STP_STS_BS_HOST_READY,
- UDC_DMA_STP_STS_BS);
-
-
- /* clear NAK by writing CNAK */
- if (ep->naking) {
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &ep->regs->ctl);
- ep->naking = 0;
- UDC_QUEUE_CNAK(ep, ep->num);
- }
-
- }
-
- return retval;
-}
-
-/* Completes request packet ... caller MUST hold lock */
-static void
-complete_req(struct udc_ep *ep, struct udc_request *req, int sts)
-__releases(ep->dev->lock)
-__acquires(ep->dev->lock)
-{
- struct udc *dev;
- unsigned halted;
-
- VDBG(ep->dev, "complete_req(): ep%d\n", ep->num);
-
- dev = ep->dev;
- /* unmap DMA */
- if (ep->dma)
- usb_gadget_unmap_request(&dev->gadget, &req->req, ep->in);
-
- halted = ep->halted;
- ep->halted = 1;
-
- /* set new status if pending */
- if (req->req.status == -EINPROGRESS)
- req->req.status = sts;
-
- /* remove from ep queue */
- list_del_init(&req->queue);
-
- VDBG(ep->dev, "req %p => complete %d bytes at %s with sts %d\n",
- &req->req, req->req.length, ep->ep.name, sts);
-
- spin_unlock(&dev->lock);
- usb_gadget_giveback_request(&ep->ep, &req->req);
- spin_lock(&dev->lock);
- ep->halted = halted;
-}
-
-/* Iterates to the end of a DMA chain and returns last descriptor */
-static struct udc_data_dma *udc_get_last_dma_desc(struct udc_request *req)
-{
- struct udc_data_dma *td;
-
- td = req->td_data;
- while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L)))
- td = phys_to_virt(td->next);
-
- return td;
-
-}
-
-/* Iterates to the end of a DMA chain and counts bytes received */
-static u32 udc_get_ppbdu_rxbytes(struct udc_request *req)
-{
- struct udc_data_dma *td;
- u32 count;
-
- td = req->td_data;
- /* received number bytes */
- count = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_RXBYTES);
-
- while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) {
- td = phys_to_virt(td->next);
- /* received number bytes */
- if (td) {
- count += AMD_GETBITS(td->status,
- UDC_DMA_OUT_STS_RXBYTES);
- }
- }
-
- return count;
-
-}
-
-/* Enabling RX DMA */
-static void udc_set_rde(struct udc *dev)
-{
- u32 tmp;
-
- VDBG(dev, "udc_set_rde()\n");
- /* stop RDE timer */
- if (timer_pending(&udc_timer)) {
- set_rde = 0;
- mod_timer(&udc_timer, jiffies - 1);
- }
- /* set RDE */
- tmp = readl(&dev->regs->ctl);
- tmp |= AMD_BIT(UDC_DEVCTL_RDE);
- writel(tmp, &dev->regs->ctl);
-}
-
-/* Queues a request packet, called by gadget driver */
-static int
-udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp)
-{
- int retval = 0;
- u8 open_rxfifo = 0;
- unsigned long iflags;
- struct udc_ep *ep;
- struct udc_request *req;
- struct udc *dev;
- u32 tmp;
-
- /* check the inputs */
- req = container_of(usbreq, struct udc_request, req);
-
- if (!usbep || !usbreq || !usbreq->complete || !usbreq->buf
- || !list_empty(&req->queue))
- return -EINVAL;
-
- ep = container_of(usbep, struct udc_ep, ep);
- if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
- return -EINVAL;
-
- VDBG(ep->dev, "udc_queue(): ep%d-in=%d\n", ep->num, ep->in);
- dev = ep->dev;
-
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- /* map dma (usually done before) */
- if (ep->dma) {
- VDBG(dev, "DMA map req %p\n", req);
- retval = usb_gadget_map_request(&udc->gadget, usbreq, ep->in);
- if (retval)
- return retval;
- }
-
- VDBG(dev, "%s queue req %p, len %d req->td_data=%p buf %p\n",
- usbep->name, usbreq, usbreq->length,
- req->td_data, usbreq->buf);
-
- spin_lock_irqsave(&dev->lock, iflags);
- usbreq->actual = 0;
- usbreq->status = -EINPROGRESS;
- req->dma_done = 0;
-
- /* on empty queue just do first transfer */
- if (list_empty(&ep->queue)) {
- /* zlp */
- if (usbreq->length == 0) {
- /* IN zlp's are handled by hardware */
- complete_req(ep, req, 0);
- VDBG(dev, "%s: zlp\n", ep->ep.name);
- /*
- * if set_config or set_intf is waiting for ack by zlp
- * then set CSR_DONE
- */
- if (dev->set_cfg_not_acked) {
- tmp = readl(&dev->regs->ctl);
- tmp |= AMD_BIT(UDC_DEVCTL_CSR_DONE);
- writel(tmp, &dev->regs->ctl);
- dev->set_cfg_not_acked = 0;
- }
- /* setup command is ACK'ed now by zlp */
- if (dev->waiting_zlp_ack_ep0in) {
- /* clear NAK by writing CNAK in EP0_IN */
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
- dev->ep[UDC_EP0IN_IX].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX],
- UDC_EP0IN_IX);
- dev->waiting_zlp_ack_ep0in = 0;
- }
- goto finished;
- }
- if (ep->dma) {
- retval = prep_dma(ep, req, GFP_ATOMIC);
- if (retval != 0)
- goto finished;
- /* write desc pointer to enable DMA */
- if (ep->in) {
- /* set HOST READY */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- UDC_DMA_IN_STS_BS_HOST_READY,
- UDC_DMA_IN_STS_BS);
- }
-
- /* disabled rx dma while descriptor update */
- if (!ep->in) {
- /* stop RDE timer */
- if (timer_pending(&udc_timer)) {
- set_rde = 0;
- mod_timer(&udc_timer, jiffies - 1);
- }
- /* clear RDE */
- tmp = readl(&dev->regs->ctl);
- tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE);
- writel(tmp, &dev->regs->ctl);
- open_rxfifo = 1;
-
- /*
- * if BNA occurred then let BNA dummy desc.
- * point to current desc.
- */
- if (ep->bna_occurred) {
- VDBG(dev, "copy to BNA dummy desc.\n");
- memcpy(ep->bna_dummy_req->td_data,
- req->td_data,
- sizeof(struct udc_data_dma));
- }
- }
- /* write desc pointer */
- writel(req->td_phys, &ep->regs->desptr);
-
- /* clear NAK by writing CNAK */
- if (ep->naking) {
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &ep->regs->ctl);
- ep->naking = 0;
- UDC_QUEUE_CNAK(ep, ep->num);
- }
-
- if (ep->in) {
- /* enable ep irq */
- tmp = readl(&dev->regs->ep_irqmsk);
- tmp &= AMD_UNMASK_BIT(ep->num);
- writel(tmp, &dev->regs->ep_irqmsk);
- }
- } else if (ep->in) {
- /* enable ep irq */
- tmp = readl(&dev->regs->ep_irqmsk);
- tmp &= AMD_UNMASK_BIT(ep->num);
- writel(tmp, &dev->regs->ep_irqmsk);
- }
-
- } else if (ep->dma) {
-
- /*
- * prep_dma not used for OUT ep's, this is not possible
- * for PPB modes, because of chain creation reasons
- */
- if (ep->in) {
- retval = prep_dma(ep, req, GFP_ATOMIC);
- if (retval != 0)
- goto finished;
- }
- }
- VDBG(dev, "list_add\n");
- /* add request to ep queue */
- if (req) {
-
- list_add_tail(&req->queue, &ep->queue);
-
- /* open rxfifo if out data queued */
- if (open_rxfifo) {
- /* enable DMA */
- req->dma_going = 1;
- udc_set_rde(dev);
- if (ep->num != UDC_EP0OUT_IX)
- dev->data_ep_queued = 1;
- }
- /* stop OUT naking */
- if (!ep->in) {
- if (!use_dma && udc_rxfifo_pending) {
- DBG(dev, "udc_queue(): pending bytes in "
- "rxfifo after nyet\n");
- /*
- * read pending bytes afer nyet:
- * referring to isr
- */
- if (udc_rxfifo_read(ep, req)) {
- /* finish */
- complete_req(ep, req, 0);
- }
- udc_rxfifo_pending = 0;
-
- }
- }
- }
-
-finished:
- spin_unlock_irqrestore(&dev->lock, iflags);
- return retval;
-}
-
-/* Empty request queue of an endpoint; caller holds spinlock */
-static void empty_req_queue(struct udc_ep *ep)
-{
- struct udc_request *req;
-
- ep->halted = 1;
- while (!list_empty(&ep->queue)) {
- req = list_entry(ep->queue.next,
- struct udc_request,
- queue);
- complete_req(ep, req, -ESHUTDOWN);
- }
-}
-
-/* Dequeues a request packet, called by gadget driver */
-static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
-{
- struct udc_ep *ep;
- struct udc_request *req;
- unsigned halted;
- unsigned long iflags;
-
- ep = container_of(usbep, struct udc_ep, ep);
- if (!usbep || !usbreq || (!ep->ep.desc && (ep->num != 0
- && ep->num != UDC_EP0OUT_IX)))
- return -EINVAL;
-
- req = container_of(usbreq, struct udc_request, req);
-
- spin_lock_irqsave(&ep->dev->lock, iflags);
- halted = ep->halted;
- ep->halted = 1;
- /* request in processing or next one */
- if (ep->queue.next == &req->queue) {
- if (ep->dma && req->dma_going) {
- if (ep->in)
- ep->cancel_transfer = 1;
- else {
- u32 tmp;
- u32 dma_sts;
- /* stop potential receive DMA */
- tmp = readl(&udc->regs->ctl);
- writel(tmp & AMD_UNMASK_BIT(UDC_DEVCTL_RDE),
- &udc->regs->ctl);
- /*
- * Cancel transfer later in ISR
- * if descriptor was touched.
- */
- dma_sts = AMD_GETBITS(req->td_data->status,
- UDC_DMA_OUT_STS_BS);
- if (dma_sts != UDC_DMA_OUT_STS_BS_HOST_READY)
- ep->cancel_transfer = 1;
- else {
- udc_init_bna_dummy(ep->req);
- writel(ep->bna_dummy_req->td_phys,
- &ep->regs->desptr);
- }
- writel(tmp, &udc->regs->ctl);
- }
- }
- }
- complete_req(ep, req, -ECONNRESET);
- ep->halted = halted;
-
- spin_unlock_irqrestore(&ep->dev->lock, iflags);
- return 0;
-}
-
-/* Halt or clear halt of endpoint */
-static int
-udc_set_halt(struct usb_ep *usbep, int halt)
-{
- struct udc_ep *ep;
- u32 tmp;
- unsigned long iflags;
- int retval = 0;
-
- if (!usbep)
- return -EINVAL;
-
- pr_debug("set_halt %s: halt=%d\n", usbep->name, halt);
-
- ep = container_of(usbep, struct udc_ep, ep);
- if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
- return -EINVAL;
- if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- spin_lock_irqsave(&udc_stall_spinlock, iflags);
- /* halt or clear halt */
- if (halt) {
- if (ep->num == 0)
- ep->dev->stall_ep0in = 1;
- else {
- /*
- * set STALL
- * rxfifo empty not taken into acount
- */
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_S);
- writel(tmp, &ep->regs->ctl);
- ep->halted = 1;
-
- /* setup poll timer */
- if (!timer_pending(&udc_pollstall_timer)) {
- udc_pollstall_timer.expires = jiffies +
- HZ * UDC_POLLSTALL_TIMER_USECONDS
- / (1000 * 1000);
- if (!stop_pollstall_timer) {
- DBG(ep->dev, "start polltimer\n");
- add_timer(&udc_pollstall_timer);
- }
- }
- }
- } else {
- /* ep is halted by set_halt() before */
- if (ep->halted) {
- tmp = readl(&ep->regs->ctl);
- /* clear stall bit */
- tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
- /* clear NAK by writing CNAK */
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &ep->regs->ctl);
- ep->halted = 0;
- UDC_QUEUE_CNAK(ep, ep->num);
- }
- }
- spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
- return retval;
-}
-
-/* gadget interface */
-static const struct usb_ep_ops udc_ep_ops = {
- .enable = udc_ep_enable,
- .disable = udc_ep_disable,
-
- .alloc_request = udc_alloc_request,
- .free_request = udc_free_request,
-
- .queue = udc_queue,
- .dequeue = udc_dequeue,
-
- .set_halt = udc_set_halt,
- /* fifo ops not implemented */
-};
-
-/*-------------------------------------------------------------------------*/
-
-/* Get frame counter (not implemented) */
-static int udc_get_frame(struct usb_gadget *gadget)
-{
- return -EOPNOTSUPP;
-}
-
-/* Initiates a remote wakeup */
-static int udc_remote_wakeup(struct udc *dev)
-{
- unsigned long flags;
- u32 tmp;
-
- DBG(dev, "UDC initiates remote wakeup\n");
-
- spin_lock_irqsave(&dev->lock, flags);
-
- tmp = readl(&dev->regs->ctl);
- tmp |= AMD_BIT(UDC_DEVCTL_RES);
- writel(tmp, &dev->regs->ctl);
- tmp &= AMD_CLEAR_BIT(UDC_DEVCTL_RES);
- writel(tmp, &dev->regs->ctl);
-
- spin_unlock_irqrestore(&dev->lock, flags);
- return 0;
-}
-
-/* Remote wakeup gadget interface */
-static int udc_wakeup(struct usb_gadget *gadget)
-{
- struct udc *dev;
-
- if (!gadget)
- return -EINVAL;
- dev = container_of(gadget, struct udc, gadget);
- udc_remote_wakeup(dev);
-
- return 0;
-}
-
-static int amd5536_udc_start(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
-static int amd5536_udc_stop(struct usb_gadget *g);
-
-static const struct usb_gadget_ops udc_ops = {
- .wakeup = udc_wakeup,
- .get_frame = udc_get_frame,
- .udc_start = amd5536_udc_start,
- .udc_stop = amd5536_udc_stop,
-};
-
-/* Setups endpoint parameters, adds endpoints to linked list */
-static void make_ep_lists(struct udc *dev)
-{
- /* make gadget ep lists */
- INIT_LIST_HEAD(&dev->gadget.ep_list);
- list_add_tail(&dev->ep[UDC_EPIN_STATUS_IX].ep.ep_list,
- &dev->gadget.ep_list);
- list_add_tail(&dev->ep[UDC_EPIN_IX].ep.ep_list,
- &dev->gadget.ep_list);
- list_add_tail(&dev->ep[UDC_EPOUT_IX].ep.ep_list,
- &dev->gadget.ep_list);
-
- /* fifo config */
- dev->ep[UDC_EPIN_STATUS_IX].fifo_depth = UDC_EPIN_SMALLINT_BUFF_SIZE;
- if (dev->gadget.speed == USB_SPEED_FULL)
- dev->ep[UDC_EPIN_IX].fifo_depth = UDC_FS_EPIN_BUFF_SIZE;
- else if (dev->gadget.speed == USB_SPEED_HIGH)
- dev->ep[UDC_EPIN_IX].fifo_depth = hs_tx_buf;
- dev->ep[UDC_EPOUT_IX].fifo_depth = UDC_RXFIFO_SIZE;
-}
-
-/* Inits UDC context */
-void udc_basic_init(struct udc *dev)
-{
- u32 tmp;
-
- DBG(dev, "udc_basic_init()\n");
-
- dev->gadget.speed = USB_SPEED_UNKNOWN;
-
- /* stop RDE timer */
- if (timer_pending(&udc_timer)) {
- set_rde = 0;
- mod_timer(&udc_timer, jiffies - 1);
- }
- /* stop poll stall timer */
- if (timer_pending(&udc_pollstall_timer))
- mod_timer(&udc_pollstall_timer, jiffies - 1);
- /* disable DMA */
- tmp = readl(&dev->regs->ctl);
- tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE);
- tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_TDE);
- writel(tmp, &dev->regs->ctl);
-
- /* enable dynamic CSR programming */
- tmp = readl(&dev->regs->cfg);
- tmp |= AMD_BIT(UDC_DEVCFG_CSR_PRG);
- /* set self powered */
- tmp |= AMD_BIT(UDC_DEVCFG_SP);
- /* set remote wakeupable */
- tmp |= AMD_BIT(UDC_DEVCFG_RWKP);
- writel(tmp, &dev->regs->cfg);
-
- make_ep_lists(dev);
-
- dev->data_ep_enabled = 0;
- dev->data_ep_queued = 0;
-}
-EXPORT_SYMBOL_GPL(udc_basic_init);
-
-/* init registers at driver load time */
-static int startup_registers(struct udc *dev)
-{
- u32 tmp;
-
- /* init controller by soft reset */
- udc_soft_reset(dev);
-
- /* mask not needed interrupts */
- udc_mask_unused_interrupts(dev);
-
- /* put into initial config */
- udc_basic_init(dev);
- /* link up all endpoints */
- udc_setup_endpoints(dev);
-
- /* program speed */
- tmp = readl(&dev->regs->cfg);
- if (use_fullspeed)
- tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
- else
- tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD);
- writel(tmp, &dev->regs->cfg);
-
- return 0;
-}
-
-/* Sets initial endpoint parameters */
-static void udc_setup_endpoints(struct udc *dev)
-{
- struct udc_ep *ep;
- u32 tmp;
- u32 reg;
-
- DBG(dev, "udc_setup_endpoints()\n");
-
- /* read enum speed */
- tmp = readl(&dev->regs->sts);
- tmp = AMD_GETBITS(tmp, UDC_DEVSTS_ENUM_SPEED);
- if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH)
- dev->gadget.speed = USB_SPEED_HIGH;
- else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL)
- dev->gadget.speed = USB_SPEED_FULL;
-
- /* set basic ep parameters */
- for (tmp = 0; tmp < UDC_EP_NUM; tmp++) {
- ep = &dev->ep[tmp];
- ep->dev = dev;
- ep->ep.name = ep_info[tmp].name;
- ep->ep.caps = ep_info[tmp].caps;
- ep->num = tmp;
- /* txfifo size is calculated at enable time */
- ep->txfifo = dev->txfifo;
-
- /* fifo size */
- if (tmp < UDC_EPIN_NUM) {
- ep->fifo_depth = UDC_TXFIFO_SIZE;
- ep->in = 1;
- } else {
- ep->fifo_depth = UDC_RXFIFO_SIZE;
- ep->in = 0;
-
- }
- ep->regs = &dev->ep_regs[tmp];
- /*
- * ep will be reset only if ep was not enabled before to avoid
- * disabling ep interrupts when ENUM interrupt occurs but ep is
- * not enabled by gadget driver
- */
- if (!ep->ep.desc)
- ep_init(dev->regs, ep);
-
- if (use_dma) {
- /*
- * ep->dma is not really used, just to indicate that
- * DMA is active: remove this
- * dma regs = dev control regs
- */
- ep->dma = &dev->regs->ctl;
-
- /* nak OUT endpoints until enable - not for ep0 */
- if (tmp != UDC_EP0IN_IX && tmp != UDC_EP0OUT_IX
- && tmp > UDC_EPIN_NUM) {
- /* set NAK */
- reg = readl(&dev->ep[tmp].regs->ctl);
- reg |= AMD_BIT(UDC_EPCTL_SNAK);
- writel(reg, &dev->ep[tmp].regs->ctl);
- dev->ep[tmp].naking = 1;
-
- }
- }
- }
- /* EP0 max packet */
- if (dev->gadget.speed == USB_SPEED_FULL) {
- usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep,
- UDC_FS_EP0IN_MAX_PKT_SIZE);
- usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep,
- UDC_FS_EP0OUT_MAX_PKT_SIZE);
- } else if (dev->gadget.speed == USB_SPEED_HIGH) {
- usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep,
- UDC_EP0IN_MAX_PKT_SIZE);
- usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep,
- UDC_EP0OUT_MAX_PKT_SIZE);
- }
-
- /*
- * with suspend bug workaround, ep0 params for gadget driver
- * are set at gadget driver bind() call
- */
- dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
- dev->ep[UDC_EP0IN_IX].halted = 0;
- INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
-
- /* init cfg/alt/int */
- dev->cur_config = 0;
- dev->cur_intf = 0;
- dev->cur_alt = 0;
-}
-
-/* Bringup after Connect event, initial bringup to be ready for ep0 events */
-static void usb_connect(struct udc *dev)
-{
-
- dev_info(&dev->pdev->dev, "USB Connect\n");
-
- dev->connected = 1;
-
- /* put into initial config */
- udc_basic_init(dev);
-
- /* enable device setup interrupts */
- udc_enable_dev_setup_interrupts(dev);
-}
-
-/*
- * Calls gadget with disconnect event and resets the UDC and makes
- * initial bringup to be ready for ep0 events
- */
-static void usb_disconnect(struct udc *dev)
-{
-
- dev_info(&dev->pdev->dev, "USB Disconnect\n");
-
- dev->connected = 0;
-
- /* mask interrupts */
- udc_mask_unused_interrupts(dev);
-
- /* REVISIT there doesn't seem to be a point to having this
- * talk to a tasklet ... do it directly, we already hold
- * the spinlock needed to process the disconnect.
- */
-
- tasklet_schedule(&disconnect_tasklet);
-}
-
-/* Tasklet for disconnect to be outside of interrupt context */
-static void udc_tasklet_disconnect(unsigned long par)
-{
- struct udc *dev = (struct udc *)(*((struct udc **) par));
- u32 tmp;
-
- DBG(dev, "Tasklet disconnect\n");
- spin_lock_irq(&dev->lock);
-
- if (dev->driver) {
- spin_unlock(&dev->lock);
- dev->driver->disconnect(&dev->gadget);
- spin_lock(&dev->lock);
-
- /* empty queues */
- for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
- empty_req_queue(&dev->ep[tmp]);
-
- }
-
- /* disable ep0 */
- ep_init(dev->regs,
- &dev->ep[UDC_EP0IN_IX]);
-
-
- if (!soft_reset_occured) {
- /* init controller by soft reset */
- udc_soft_reset(dev);
- soft_reset_occured++;
- }
-
- /* re-enable dev interrupts */
- udc_enable_dev_setup_interrupts(dev);
- /* back to full speed ? */
- if (use_fullspeed) {
- tmp = readl(&dev->regs->cfg);
- tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
- writel(tmp, &dev->regs->cfg);
- }
-
- spin_unlock_irq(&dev->lock);
-}
-
-/* Reset the UDC core */
-static void udc_soft_reset(struct udc *dev)
-{
- unsigned long flags;
-
- DBG(dev, "Soft reset\n");
- /*
- * reset possible waiting interrupts, because int.
- * status is lost after soft reset,
- * ep int. status reset
- */
- writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqsts);
- /* device int. status reset */
- writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts);
-
- spin_lock_irqsave(&udc_irq_spinlock, flags);
- writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
- readl(&dev->regs->cfg);
- spin_unlock_irqrestore(&udc_irq_spinlock, flags);
-
-}
-
-/* RDE timer callback to set RDE bit */
-static void udc_timer_function(unsigned long v)
-{
- u32 tmp;
-
- spin_lock_irq(&udc_irq_spinlock);
-
- if (set_rde > 0) {
- /*
- * open the fifo if fifo was filled on last timer call
- * conditionally
- */
- if (set_rde > 1) {
- /* set RDE to receive setup data */
- tmp = readl(&udc->regs->ctl);
- tmp |= AMD_BIT(UDC_DEVCTL_RDE);
- writel(tmp, &udc->regs->ctl);
- set_rde = -1;
- } else if (readl(&udc->regs->sts)
- & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) {
- /*
- * if fifo empty setup polling, do not just
- * open the fifo
- */
- udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV;
- if (!stop_timer)
- add_timer(&udc_timer);
- } else {
- /*
- * fifo contains data now, setup timer for opening
- * the fifo when timer expires to be able to receive
- * setup packets, when data packets gets queued by
- * gadget layer then timer will forced to expire with
- * set_rde=0 (RDE is set in udc_queue())
- */
- set_rde++;
- /* debug: lhadmot_timer_start = 221070 */
- udc_timer.expires = jiffies + HZ*UDC_RDE_TIMER_SECONDS;
- if (!stop_timer)
- add_timer(&udc_timer);
- }
-
- } else
- set_rde = -1; /* RDE was set by udc_queue() */
- spin_unlock_irq(&udc_irq_spinlock);
- if (stop_timer)
- complete(&on_exit);
-
-}
-
-/* Handle halt state, used in stall poll timer */
-static void udc_handle_halt_state(struct udc_ep *ep)
-{
- u32 tmp;
- /* set stall as long not halted */
- if (ep->halted == 1) {
- tmp = readl(&ep->regs->ctl);
- /* STALL cleared ? */
- if (!(tmp & AMD_BIT(UDC_EPCTL_S))) {
- /*
- * FIXME: MSC spec requires that stall remains
- * even on receivng of CLEAR_FEATURE HALT. So
- * we would set STALL again here to be compliant.
- * But with current mass storage drivers this does
- * not work (would produce endless host retries).
- * So we clear halt on CLEAR_FEATURE.
- *
- DBG(ep->dev, "ep %d: set STALL again\n", ep->num);
- tmp |= AMD_BIT(UDC_EPCTL_S);
- writel(tmp, &ep->regs->ctl);*/
-
- /* clear NAK by writing CNAK */
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &ep->regs->ctl);
- ep->halted = 0;
- UDC_QUEUE_CNAK(ep, ep->num);
- }
- }
-}
-
-/* Stall timer callback to poll S bit and set it again after */
-static void udc_pollstall_timer_function(unsigned long v)
-{
- struct udc_ep *ep;
- int halted = 0;
-
- spin_lock_irq(&udc_stall_spinlock);
- /*
- * only one IN and OUT endpoints are handled
- * IN poll stall
- */
- ep = &udc->ep[UDC_EPIN_IX];
- udc_handle_halt_state(ep);
- if (ep->halted)
- halted = 1;
- /* OUT poll stall */
- ep = &udc->ep[UDC_EPOUT_IX];
- udc_handle_halt_state(ep);
- if (ep->halted)
- halted = 1;
-
- /* setup timer again when still halted */
- if (!stop_pollstall_timer && halted) {
- udc_pollstall_timer.expires = jiffies +
- HZ * UDC_POLLSTALL_TIMER_USECONDS
- / (1000 * 1000);
- add_timer(&udc_pollstall_timer);
- }
- spin_unlock_irq(&udc_stall_spinlock);
-
- if (stop_pollstall_timer)
- complete(&on_pollstall_exit);
-}
-
-/* Inits endpoint 0 so that SETUP packets are processed */
-static void activate_control_endpoints(struct udc *dev)
-{
- u32 tmp;
-
- DBG(dev, "activate_control_endpoints\n");
-
- /* flush fifo */
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_F);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
-
- /* set ep0 directions */
- dev->ep[UDC_EP0IN_IX].in = 1;
- dev->ep[UDC_EP0OUT_IX].in = 0;
-
- /* set buffer size (tx fifo entries) of EP0_IN */
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufin_framenum);
- if (dev->gadget.speed == USB_SPEED_FULL)
- tmp = AMD_ADDBITS(tmp, UDC_FS_EPIN0_BUFF_SIZE,
- UDC_EPIN_BUFF_SIZE);
- else if (dev->gadget.speed == USB_SPEED_HIGH)
- tmp = AMD_ADDBITS(tmp, UDC_EPIN0_BUFF_SIZE,
- UDC_EPIN_BUFF_SIZE);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufin_framenum);
-
- /* set max packet size of EP0_IN */
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt);
- if (dev->gadget.speed == USB_SPEED_FULL)
- tmp = AMD_ADDBITS(tmp, UDC_FS_EP0IN_MAX_PKT_SIZE,
- UDC_EP_MAX_PKT_SIZE);
- else if (dev->gadget.speed == USB_SPEED_HIGH)
- tmp = AMD_ADDBITS(tmp, UDC_EP0IN_MAX_PKT_SIZE,
- UDC_EP_MAX_PKT_SIZE);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt);
-
- /* set max packet size of EP0_OUT */
- tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt);
- if (dev->gadget.speed == USB_SPEED_FULL)
- tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE,
- UDC_EP_MAX_PKT_SIZE);
- else if (dev->gadget.speed == USB_SPEED_HIGH)
- tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE,
- UDC_EP_MAX_PKT_SIZE);
- writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt);
-
- /* set max packet size of EP0 in UDC CSR */
- tmp = readl(&dev->csr->ne[0]);
- if (dev->gadget.speed == USB_SPEED_FULL)
- tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE,
- UDC_CSR_NE_MAX_PKT);
- else if (dev->gadget.speed == USB_SPEED_HIGH)
- tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE,
- UDC_CSR_NE_MAX_PKT);
- writel(tmp, &dev->csr->ne[0]);
-
- if (use_dma) {
- dev->ep[UDC_EP0OUT_IX].td->status |=
- AMD_BIT(UDC_DMA_OUT_STS_L);
- /* write dma desc address */
- writel(dev->ep[UDC_EP0OUT_IX].td_stp_dma,
- &dev->ep[UDC_EP0OUT_IX].regs->subptr);
- writel(dev->ep[UDC_EP0OUT_IX].td_phys,
- &dev->ep[UDC_EP0OUT_IX].regs->desptr);
- /* stop RDE timer */
- if (timer_pending(&udc_timer)) {
- set_rde = 0;
- mod_timer(&udc_timer, jiffies - 1);
- }
- /* stop pollstall timer */
- if (timer_pending(&udc_pollstall_timer))
- mod_timer(&udc_pollstall_timer, jiffies - 1);
- /* enable DMA */
- tmp = readl(&dev->regs->ctl);
- tmp |= AMD_BIT(UDC_DEVCTL_MODE)
- | AMD_BIT(UDC_DEVCTL_RDE)
- | AMD_BIT(UDC_DEVCTL_TDE);
- if (use_dma_bufferfill_mode)
- tmp |= AMD_BIT(UDC_DEVCTL_BF);
- else if (use_dma_ppb_du)
- tmp |= AMD_BIT(UDC_DEVCTL_DU);
- writel(tmp, &dev->regs->ctl);
- }
-
- /* clear NAK by writing CNAK for EP0IN */
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
- dev->ep[UDC_EP0IN_IX].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX);
-
- /* clear NAK by writing CNAK for EP0OUT */
- tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
- dev->ep[UDC_EP0OUT_IX].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX);
-}
-
-/* Make endpoint 0 ready for control traffic */
-static int setup_ep0(struct udc *dev)
-{
- activate_control_endpoints(dev);
- /* enable ep0 interrupts */
- udc_enable_ep0_interrupts(dev);
- /* enable device setup interrupts */
- udc_enable_dev_setup_interrupts(dev);
-
- return 0;
-}
-
-/* Called by gadget driver to register itself */
-static int amd5536_udc_start(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
-{
- struct udc *dev = to_amd5536_udc(g);
- u32 tmp;
-
- driver->driver.bus = NULL;
- dev->driver = driver;
-
- /* Some gadget drivers use both ep0 directions.
- * NOTE: to gadget driver, ep0 is just one endpoint...
- */
- dev->ep[UDC_EP0OUT_IX].ep.driver_data =
- dev->ep[UDC_EP0IN_IX].ep.driver_data;
-
- /* get ready for ep0 traffic */
- setup_ep0(dev);
-
- /* clear SD */
- tmp = readl(&dev->regs->ctl);
- tmp = tmp & AMD_CLEAR_BIT(UDC_DEVCTL_SD);
- writel(tmp, &dev->regs->ctl);
-
- usb_connect(dev);
-
- return 0;
-}
-
-/* shutdown requests and disconnect from gadget */
-static void
-shutdown(struct udc *dev, struct usb_gadget_driver *driver)
-__releases(dev->lock)
-__acquires(dev->lock)
-{
- int tmp;
-
- /* empty queues and init hardware */
- udc_basic_init(dev);
-
- for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
- empty_req_queue(&dev->ep[tmp]);
-
- udc_setup_endpoints(dev);
-}
-
-/* Called by gadget driver to unregister itself */
-static int amd5536_udc_stop(struct usb_gadget *g)
-{
- struct udc *dev = to_amd5536_udc(g);
- unsigned long flags;
- u32 tmp;
-
- spin_lock_irqsave(&dev->lock, flags);
- udc_mask_unused_interrupts(dev);
- shutdown(dev, NULL);
- spin_unlock_irqrestore(&dev->lock, flags);
-
- dev->driver = NULL;
-
- /* set SD */
- tmp = readl(&dev->regs->ctl);
- tmp |= AMD_BIT(UDC_DEVCTL_SD);
- writel(tmp, &dev->regs->ctl);
-
- return 0;
-}
-
-/* Clear pending NAK bits */
-static void udc_process_cnak_queue(struct udc *dev)
-{
- u32 tmp;
- u32 reg;
-
- /* check epin's */
- DBG(dev, "CNAK pending queue processing\n");
- for (tmp = 0; tmp < UDC_EPIN_NUM_USED; tmp++) {
- if (cnak_pending & (1 << tmp)) {
- DBG(dev, "CNAK pending for ep%d\n", tmp);
- /* clear NAK by writing CNAK */
- reg = readl(&dev->ep[tmp].regs->ctl);
- reg |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(reg, &dev->ep[tmp].regs->ctl);
- dev->ep[tmp].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[tmp], dev->ep[tmp].num);
- }
- }
- /* ... and ep0out */
- if (cnak_pending & (1 << UDC_EP0OUT_IX)) {
- DBG(dev, "CNAK pending for ep%d\n", UDC_EP0OUT_IX);
- /* clear NAK by writing CNAK */
- reg = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
- reg |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(reg, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
- dev->ep[UDC_EP0OUT_IX].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX],
- dev->ep[UDC_EP0OUT_IX].num);
- }
-}
-
-/* Enabling RX DMA after setup packet */
-static void udc_ep0_set_rde(struct udc *dev)
-{
- if (use_dma) {
- /*
- * only enable RXDMA when no data endpoint enabled
- * or data is queued
- */
- if (!dev->data_ep_enabled || dev->data_ep_queued) {
- udc_set_rde(dev);
- } else {
- /*
- * setup timer for enabling RDE (to not enable
- * RXFIFO DMA for data endpoints to early)
- */
- if (set_rde != 0 && !timer_pending(&udc_timer)) {
- udc_timer.expires =
- jiffies + HZ/UDC_RDE_TIMER_DIV;
- set_rde = 1;
- if (!stop_timer)
- add_timer(&udc_timer);
- }
- }
- }
-}
-
-
-/* Interrupt handler for data OUT traffic */
-static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix)
-{
- irqreturn_t ret_val = IRQ_NONE;
- u32 tmp;
- struct udc_ep *ep;
- struct udc_request *req;
- unsigned int count;
- struct udc_data_dma *td = NULL;
- unsigned dma_done;
-
- VDBG(dev, "ep%d irq\n", ep_ix);
- ep = &dev->ep[ep_ix];
-
- tmp = readl(&ep->regs->sts);
- if (use_dma) {
- /* BNA event ? */
- if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
- DBG(dev, "BNA ep%dout occurred - DESPTR = %x\n",
- ep->num, readl(&ep->regs->desptr));
- /* clear BNA */
- writel(tmp | AMD_BIT(UDC_EPSTS_BNA), &ep->regs->sts);
- if (!ep->cancel_transfer)
- ep->bna_occurred = 1;
- else
- ep->cancel_transfer = 0;
- ret_val = IRQ_HANDLED;
- goto finished;
- }
- }
- /* HE event ? */
- if (tmp & AMD_BIT(UDC_EPSTS_HE)) {
- dev_err(&dev->pdev->dev, "HE ep%dout occurred\n", ep->num);
-
- /* clear HE */
- writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
- ret_val = IRQ_HANDLED;
- goto finished;
- }
-
- if (!list_empty(&ep->queue)) {
-
- /* next request */
- req = list_entry(ep->queue.next,
- struct udc_request, queue);
- } else {
- req = NULL;
- udc_rxfifo_pending = 1;
- }
- VDBG(dev, "req = %p\n", req);
- /* fifo mode */
- if (!use_dma) {
-
- /* read fifo */
- if (req && udc_rxfifo_read(ep, req)) {
- ret_val = IRQ_HANDLED;
-
- /* finish */
- complete_req(ep, req, 0);
- /* next request */
- if (!list_empty(&ep->queue) && !ep->halted) {
- req = list_entry(ep->queue.next,
- struct udc_request, queue);
- } else
- req = NULL;
- }
-
- /* DMA */
- } else if (!ep->cancel_transfer && req) {
- ret_val = IRQ_HANDLED;
-
- /* check for DMA done */
- if (!use_dma_ppb) {
- dma_done = AMD_GETBITS(req->td_data->status,
- UDC_DMA_OUT_STS_BS);
- /* packet per buffer mode - rx bytes */
- } else {
- /*
- * if BNA occurred then recover desc. from
- * BNA dummy desc.
- */
- if (ep->bna_occurred) {
- VDBG(dev, "Recover desc. from BNA dummy\n");
- memcpy(req->td_data, ep->bna_dummy_req->td_data,
- sizeof(struct udc_data_dma));
- ep->bna_occurred = 0;
- udc_init_bna_dummy(ep->req);
- }
- td = udc_get_last_dma_desc(req);
- dma_done = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_BS);
- }
- if (dma_done == UDC_DMA_OUT_STS_BS_DMA_DONE) {
- /* buffer fill mode - rx bytes */
- if (!use_dma_ppb) {
- /* received number bytes */
- count = AMD_GETBITS(req->td_data->status,
- UDC_DMA_OUT_STS_RXBYTES);
- VDBG(dev, "rx bytes=%u\n", count);
- /* packet per buffer mode - rx bytes */
- } else {
- VDBG(dev, "req->td_data=%p\n", req->td_data);
- VDBG(dev, "last desc = %p\n", td);
- /* received number bytes */
- if (use_dma_ppb_du) {
- /* every desc. counts bytes */
- count = udc_get_ppbdu_rxbytes(req);
- } else {
- /* last desc. counts bytes */
- count = AMD_GETBITS(td->status,
- UDC_DMA_OUT_STS_RXBYTES);
- if (!count && req->req.length
- == UDC_DMA_MAXPACKET) {
- /*
- * on 64k packets the RXBYTES
- * field is zero
- */
- count = UDC_DMA_MAXPACKET;
- }
- }
- VDBG(dev, "last desc rx bytes=%u\n", count);
- }
-
- tmp = req->req.length - req->req.actual;
- if (count > tmp) {
- if ((tmp % ep->ep.maxpacket) != 0) {
- DBG(dev, "%s: rx %db, space=%db\n",
- ep->ep.name, count, tmp);
- req->req.status = -EOVERFLOW;
- }
- count = tmp;
- }
- req->req.actual += count;
- req->dma_going = 0;
- /* complete request */
- complete_req(ep, req, 0);
-
- /* next request */
- if (!list_empty(&ep->queue) && !ep->halted) {
- req = list_entry(ep->queue.next,
- struct udc_request,
- queue);
- /*
- * DMA may be already started by udc_queue()
- * called by gadget drivers completion
- * routine. This happens when queue
- * holds one request only.
- */
- if (req->dma_going == 0) {
- /* next dma */
- if (prep_dma(ep, req, GFP_ATOMIC) != 0)
- goto finished;
- /* write desc pointer */
- writel(req->td_phys,
- &ep->regs->desptr);
- req->dma_going = 1;
- /* enable DMA */
- udc_set_rde(dev);
- }
- } else {
- /*
- * implant BNA dummy descriptor to allow
- * RXFIFO opening by RDE
- */
- if (ep->bna_dummy_req) {
- /* write desc pointer */
- writel(ep->bna_dummy_req->td_phys,
- &ep->regs->desptr);
- ep->bna_occurred = 0;
- }
-
- /*
- * schedule timer for setting RDE if queue
- * remains empty to allow ep0 packets pass
- * through
- */
- if (set_rde != 0
- && !timer_pending(&udc_timer)) {
- udc_timer.expires =
- jiffies
- + HZ*UDC_RDE_TIMER_SECONDS;
- set_rde = 1;
- if (!stop_timer)
- add_timer(&udc_timer);
- }
- if (ep->num != UDC_EP0OUT_IX)
- dev->data_ep_queued = 0;
- }
-
- } else {
- /*
- * RX DMA must be reenabled for each desc in PPBDU mode
- * and must be enabled for PPBNDU mode in case of BNA
- */
- udc_set_rde(dev);
- }
-
- } else if (ep->cancel_transfer) {
- ret_val = IRQ_HANDLED;
- ep->cancel_transfer = 0;
- }
-
- /* check pending CNAKS */
- if (cnak_pending) {
- /* CNAk processing when rxfifo empty only */
- if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
- udc_process_cnak_queue(dev);
- }
-
- /* clear OUT bits in ep status */
- writel(UDC_EPSTS_OUT_CLEAR, &ep->regs->sts);
-finished:
- return ret_val;
-}
-
-/* Interrupt handler for data IN traffic */
-static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
-{
- irqreturn_t ret_val = IRQ_NONE;
- u32 tmp;
- u32 epsts;
- struct udc_ep *ep;
- struct udc_request *req;
- struct udc_data_dma *td;
- unsigned len;
-
- ep = &dev->ep[ep_ix];
-
- epsts = readl(&ep->regs->sts);
- if (use_dma) {
- /* BNA ? */
- if (epsts & AMD_BIT(UDC_EPSTS_BNA)) {
- dev_err(&dev->pdev->dev,
- "BNA ep%din occurred - DESPTR = %08lx\n",
- ep->num,
- (unsigned long) readl(&ep->regs->desptr));
-
- /* clear BNA */
- writel(epsts, &ep->regs->sts);
- ret_val = IRQ_HANDLED;
- goto finished;
- }
- }
- /* HE event ? */
- if (epsts & AMD_BIT(UDC_EPSTS_HE)) {
- dev_err(&dev->pdev->dev,
- "HE ep%dn occurred - DESPTR = %08lx\n",
- ep->num, (unsigned long) readl(&ep->regs->desptr));
-
- /* clear HE */
- writel(epsts | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
- ret_val = IRQ_HANDLED;
- goto finished;
- }
-
- /* DMA completion */
- if (epsts & AMD_BIT(UDC_EPSTS_TDC)) {
- VDBG(dev, "TDC set- completion\n");
- ret_val = IRQ_HANDLED;
- if (!ep->cancel_transfer && !list_empty(&ep->queue)) {
- req = list_entry(ep->queue.next,
- struct udc_request, queue);
- /*
- * length bytes transferred
- * check dma done of last desc. in PPBDU mode
- */
- if (use_dma_ppb_du) {
- td = udc_get_last_dma_desc(req);
- if (td)
- req->req.actual = req->req.length;
- } else {
- /* assume all bytes transferred */
- req->req.actual = req->req.length;
- }
-
- if (req->req.actual == req->req.length) {
- /* complete req */
- complete_req(ep, req, 0);
- req->dma_going = 0;
- /* further request available ? */
- if (list_empty(&ep->queue)) {
- /* disable interrupt */
- tmp = readl(&dev->regs->ep_irqmsk);
- tmp |= AMD_BIT(ep->num);
- writel(tmp, &dev->regs->ep_irqmsk);
- }
- }
- }
- ep->cancel_transfer = 0;
-
- }
- /*
- * status reg has IN bit set and TDC not set (if TDC was handled,
- * IN must not be handled (UDC defect) ?
- */
- if ((epsts & AMD_BIT(UDC_EPSTS_IN))
- && !(epsts & AMD_BIT(UDC_EPSTS_TDC))) {
- ret_val = IRQ_HANDLED;
- if (!list_empty(&ep->queue)) {
- /* next request */
- req = list_entry(ep->queue.next,
- struct udc_request, queue);
- /* FIFO mode */
- if (!use_dma) {
- /* write fifo */
- udc_txfifo_write(ep, &req->req);
- len = req->req.length - req->req.actual;
- if (len > ep->ep.maxpacket)
- len = ep->ep.maxpacket;
- req->req.actual += len;
- if (req->req.actual == req->req.length
- || (len != ep->ep.maxpacket)) {
- /* complete req */
- complete_req(ep, req, 0);
- }
- /* DMA */
- } else if (req && !req->dma_going) {
- VDBG(dev, "IN DMA : req=%p req->td_data=%p\n",
- req, req->td_data);
- if (req->td_data) {
-
- req->dma_going = 1;
-
- /*
- * unset L bit of first desc.
- * for chain
- */
- if (use_dma_ppb && req->req.length >
- ep->ep.maxpacket) {
- req->td_data->status &=
- AMD_CLEAR_BIT(
- UDC_DMA_IN_STS_L);
- }
-
- /* write desc pointer */
- writel(req->td_phys, &ep->regs->desptr);
-
- /* set HOST READY */
- req->td_data->status =
- AMD_ADDBITS(
- req->td_data->status,
- UDC_DMA_IN_STS_BS_HOST_READY,
- UDC_DMA_IN_STS_BS);
-
- /* set poll demand bit */
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_P);
- writel(tmp, &ep->regs->ctl);
- }
- }
-
- } else if (!use_dma && ep->in) {
- /* disable interrupt */
- tmp = readl(
- &dev->regs->ep_irqmsk);
- tmp |= AMD_BIT(ep->num);
- writel(tmp,
- &dev->regs->ep_irqmsk);
- }
- }
- /* clear status bits */
- writel(epsts, &ep->regs->sts);
-
-finished:
- return ret_val;
-
-}
-
-/* Interrupt handler for Control OUT traffic */
-static irqreturn_t udc_control_out_isr(struct udc *dev)
-__releases(dev->lock)
-__acquires(dev->lock)
-{
- irqreturn_t ret_val = IRQ_NONE;
- u32 tmp;
- int setup_supported;
- u32 count;
- int set = 0;
- struct udc_ep *ep;
- struct udc_ep *ep_tmp;
-
- ep = &dev->ep[UDC_EP0OUT_IX];
-
- /* clear irq */
- writel(AMD_BIT(UDC_EPINT_OUT_EP0), &dev->regs->ep_irqsts);
-
- tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts);
- /* check BNA and clear if set */
- if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
- VDBG(dev, "ep0: BNA set\n");
- writel(AMD_BIT(UDC_EPSTS_BNA),
- &dev->ep[UDC_EP0OUT_IX].regs->sts);
- ep->bna_occurred = 1;
- ret_val = IRQ_HANDLED;
- goto finished;
- }
-
- /* type of data: SETUP or DATA 0 bytes */
- tmp = AMD_GETBITS(tmp, UDC_EPSTS_OUT);
- VDBG(dev, "data_typ = %x\n", tmp);
-
- /* setup data */
- if (tmp == UDC_EPSTS_OUT_SETUP) {
- ret_val = IRQ_HANDLED;
-
- ep->dev->stall_ep0in = 0;
- dev->waiting_zlp_ack_ep0in = 0;
-
- /* set NAK for EP0_IN */
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_SNAK);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
- dev->ep[UDC_EP0IN_IX].naking = 1;
- /* get setup data */
- if (use_dma) {
-
- /* clear OUT bits in ep status */
- writel(UDC_EPSTS_OUT_CLEAR,
- &dev->ep[UDC_EP0OUT_IX].regs->sts);
-
- setup_data.data[0] =
- dev->ep[UDC_EP0OUT_IX].td_stp->data12;
- setup_data.data[1] =
- dev->ep[UDC_EP0OUT_IX].td_stp->data34;
- /* set HOST READY */
- dev->ep[UDC_EP0OUT_IX].td_stp->status =
- UDC_DMA_STP_STS_BS_HOST_READY;
- } else {
- /* read fifo */
- udc_rxfifo_read_dwords(dev, setup_data.data, 2);
- }
-
- /* determine direction of control data */
- if ((setup_data.request.bRequestType & USB_DIR_IN) != 0) {
- dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
- /* enable RDE */
- udc_ep0_set_rde(dev);
- set = 0;
- } else {
- dev->gadget.ep0 = &dev->ep[UDC_EP0OUT_IX].ep;
- /*
- * implant BNA dummy descriptor to allow RXFIFO opening
- * by RDE
- */
- if (ep->bna_dummy_req) {
- /* write desc pointer */
- writel(ep->bna_dummy_req->td_phys,
- &dev->ep[UDC_EP0OUT_IX].regs->desptr);
- ep->bna_occurred = 0;
- }
-
- set = 1;
- dev->ep[UDC_EP0OUT_IX].naking = 1;
- /*
- * setup timer for enabling RDE (to not enable
- * RXFIFO DMA for data to early)
- */
- set_rde = 1;
- if (!timer_pending(&udc_timer)) {
- udc_timer.expires = jiffies +
- HZ/UDC_RDE_TIMER_DIV;
- if (!stop_timer)
- add_timer(&udc_timer);
- }
- }
-
- /*
- * mass storage reset must be processed here because
- * next packet may be a CLEAR_FEATURE HALT which would not
- * clear the stall bit when no STALL handshake was received
- * before (autostall can cause this)
- */
- if (setup_data.data[0] == UDC_MSCRES_DWORD0
- && setup_data.data[1] == UDC_MSCRES_DWORD1) {
- DBG(dev, "MSC Reset\n");
- /*
- * clear stall bits
- * only one IN and OUT endpoints are handled
- */
- ep_tmp = &udc->ep[UDC_EPIN_IX];
- udc_set_halt(&ep_tmp->ep, 0);
- ep_tmp = &udc->ep[UDC_EPOUT_IX];
- udc_set_halt(&ep_tmp->ep, 0);
- }
-
- /* call gadget with setup data received */
- spin_unlock(&dev->lock);
- setup_supported = dev->driver->setup(&dev->gadget,
- &setup_data.request);
- spin_lock(&dev->lock);
-
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
- /* ep0 in returns data (not zlp) on IN phase */
- if (setup_supported >= 0 && setup_supported <
- UDC_EP0IN_MAXPACKET) {
- /* clear NAK by writing CNAK in EP0_IN */
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
- dev->ep[UDC_EP0IN_IX].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX);
-
- /* if unsupported request then stall */
- } else if (setup_supported < 0) {
- tmp |= AMD_BIT(UDC_EPCTL_S);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
- } else
- dev->waiting_zlp_ack_ep0in = 1;
-
-
- /* clear NAK by writing CNAK in EP0_OUT */
- if (!set) {
- tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
- dev->ep[UDC_EP0OUT_IX].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX);
- }
-
- if (!use_dma) {
- /* clear OUT bits in ep status */
- writel(UDC_EPSTS_OUT_CLEAR,
- &dev->ep[UDC_EP0OUT_IX].regs->sts);
- }
-
- /* data packet 0 bytes */
- } else if (tmp == UDC_EPSTS_OUT_DATA) {
- /* clear OUT bits in ep status */
- writel(UDC_EPSTS_OUT_CLEAR, &dev->ep[UDC_EP0OUT_IX].regs->sts);
-
- /* get setup data: only 0 packet */
- if (use_dma) {
- /* no req if 0 packet, just reactivate */
- if (list_empty(&dev->ep[UDC_EP0OUT_IX].queue)) {
- VDBG(dev, "ZLP\n");
-
- /* set HOST READY */
- dev->ep[UDC_EP0OUT_IX].td->status =
- AMD_ADDBITS(
- dev->ep[UDC_EP0OUT_IX].td->status,
- UDC_DMA_OUT_STS_BS_HOST_READY,
- UDC_DMA_OUT_STS_BS);
- /* enable RDE */
- udc_ep0_set_rde(dev);
- ret_val = IRQ_HANDLED;
-
- } else {
- /* control write */
- ret_val |= udc_data_out_isr(dev, UDC_EP0OUT_IX);
- /* re-program desc. pointer for possible ZLPs */
- writel(dev->ep[UDC_EP0OUT_IX].td_phys,
- &dev->ep[UDC_EP0OUT_IX].regs->desptr);
- /* enable RDE */
- udc_ep0_set_rde(dev);
- }
- } else {
-
- /* received number bytes */
- count = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts);
- count = AMD_GETBITS(count, UDC_EPSTS_RX_PKT_SIZE);
- /* out data for fifo mode not working */
- count = 0;
-
- /* 0 packet or real data ? */
- if (count != 0) {
- ret_val |= udc_data_out_isr(dev, UDC_EP0OUT_IX);
- } else {
- /* dummy read confirm */
- readl(&dev->ep[UDC_EP0OUT_IX].regs->confirm);
- ret_val = IRQ_HANDLED;
- }
- }
- }
-
- /* check pending CNAKS */
- if (cnak_pending) {
- /* CNAk processing when rxfifo empty only */
- if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
- udc_process_cnak_queue(dev);
- }
-
-finished:
- return ret_val;
-}
-
-/* Interrupt handler for Control IN traffic */
-static irqreturn_t udc_control_in_isr(struct udc *dev)
-{
- irqreturn_t ret_val = IRQ_NONE;
- u32 tmp;
- struct udc_ep *ep;
- struct udc_request *req;
- unsigned len;
-
- ep = &dev->ep[UDC_EP0IN_IX];
-
- /* clear irq */
- writel(AMD_BIT(UDC_EPINT_IN_EP0), &dev->regs->ep_irqsts);
-
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->sts);
- /* DMA completion */
- if (tmp & AMD_BIT(UDC_EPSTS_TDC)) {
- VDBG(dev, "isr: TDC clear\n");
- ret_val = IRQ_HANDLED;
-
- /* clear TDC bit */
- writel(AMD_BIT(UDC_EPSTS_TDC),
- &dev->ep[UDC_EP0IN_IX].regs->sts);
-
- /* status reg has IN bit set ? */
- } else if (tmp & AMD_BIT(UDC_EPSTS_IN)) {
- ret_val = IRQ_HANDLED;
-
- if (ep->dma) {
- /* clear IN bit */
- writel(AMD_BIT(UDC_EPSTS_IN),
- &dev->ep[UDC_EP0IN_IX].regs->sts);
- }
- if (dev->stall_ep0in) {
- DBG(dev, "stall ep0in\n");
- /* halt ep0in */
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_S);
- writel(tmp, &ep->regs->ctl);
- } else {
- if (!list_empty(&ep->queue)) {
- /* next request */
- req = list_entry(ep->queue.next,
- struct udc_request, queue);
-
- if (ep->dma) {
- /* write desc pointer */
- writel(req->td_phys, &ep->regs->desptr);
- /* set HOST READY */
- req->td_data->status =
- AMD_ADDBITS(
- req->td_data->status,
- UDC_DMA_STP_STS_BS_HOST_READY,
- UDC_DMA_STP_STS_BS);
-
- /* set poll demand bit */
- tmp =
- readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_P);
- writel(tmp,
- &dev->ep[UDC_EP0IN_IX].regs->ctl);
-
- /* all bytes will be transferred */
- req->req.actual = req->req.length;
-
- /* complete req */
- complete_req(ep, req, 0);
-
- } else {
- /* write fifo */
- udc_txfifo_write(ep, &req->req);
-
- /* lengh bytes transferred */
- len = req->req.length - req->req.actual;
- if (len > ep->ep.maxpacket)
- len = ep->ep.maxpacket;
-
- req->req.actual += len;
- if (req->req.actual == req->req.length
- || (len != ep->ep.maxpacket)) {
- /* complete req */
- complete_req(ep, req, 0);
- }
- }
-
- }
- }
- ep->halted = 0;
- dev->stall_ep0in = 0;
- if (!ep->dma) {
- /* clear IN bit */
- writel(AMD_BIT(UDC_EPSTS_IN),
- &dev->ep[UDC_EP0IN_IX].regs->sts);
- }
- }
-
- return ret_val;
-}
-
-
-/* Interrupt handler for global device events */
-static irqreturn_t udc_dev_isr(struct udc *dev, u32 dev_irq)
-__releases(dev->lock)
-__acquires(dev->lock)
-{
- irqreturn_t ret_val = IRQ_NONE;
- u32 tmp;
- u32 cfg;
- struct udc_ep *ep;
- u16 i;
- u8 udc_csr_epix;
-
- /* SET_CONFIG irq ? */
- if (dev_irq & AMD_BIT(UDC_DEVINT_SC)) {
- ret_val = IRQ_HANDLED;
-
- /* read config value */
- tmp = readl(&dev->regs->sts);
- cfg = AMD_GETBITS(tmp, UDC_DEVSTS_CFG);
- DBG(dev, "SET_CONFIG interrupt: config=%d\n", cfg);
- dev->cur_config = cfg;
- dev->set_cfg_not_acked = 1;
-
- /* make usb request for gadget driver */
- memset(&setup_data, 0 , sizeof(union udc_setup_data));
- setup_data.request.bRequest = USB_REQ_SET_CONFIGURATION;
- setup_data.request.wValue = cpu_to_le16(dev->cur_config);
-
- /* programm the NE registers */
- for (i = 0; i < UDC_EP_NUM; i++) {
- ep = &dev->ep[i];
- if (ep->in) {
-
- /* ep ix in UDC CSR register space */
- udc_csr_epix = ep->num;
-
-
- /* OUT ep */
- } else {
- /* ep ix in UDC CSR register space */
- udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
- }
-
- tmp = readl(&dev->csr->ne[udc_csr_epix]);
- /* ep cfg */
- tmp = AMD_ADDBITS(tmp, ep->dev->cur_config,
- UDC_CSR_NE_CFG);
- /* write reg */
- writel(tmp, &dev->csr->ne[udc_csr_epix]);
-
- /* clear stall bits */
- ep->halted = 0;
- tmp = readl(&ep->regs->ctl);
- tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
- writel(tmp, &ep->regs->ctl);
- }
- /* call gadget zero with setup data received */
- spin_unlock(&dev->lock);
- tmp = dev->driver->setup(&dev->gadget, &setup_data.request);
- spin_lock(&dev->lock);
-
- } /* SET_INTERFACE ? */
- if (dev_irq & AMD_BIT(UDC_DEVINT_SI)) {
- ret_val = IRQ_HANDLED;
-
- dev->set_cfg_not_acked = 1;
- /* read interface and alt setting values */
- tmp = readl(&dev->regs->sts);
- dev->cur_alt = AMD_GETBITS(tmp, UDC_DEVSTS_ALT);
- dev->cur_intf = AMD_GETBITS(tmp, UDC_DEVSTS_INTF);
-
- /* make usb request for gadget driver */
- memset(&setup_data, 0 , sizeof(union udc_setup_data));
- setup_data.request.bRequest = USB_REQ_SET_INTERFACE;
- setup_data.request.bRequestType = USB_RECIP_INTERFACE;
- setup_data.request.wValue = cpu_to_le16(dev->cur_alt);
- setup_data.request.wIndex = cpu_to_le16(dev->cur_intf);
-
- DBG(dev, "SET_INTERFACE interrupt: alt=%d intf=%d\n",
- dev->cur_alt, dev->cur_intf);
-
- /* programm the NE registers */
- for (i = 0; i < UDC_EP_NUM; i++) {
- ep = &dev->ep[i];
- if (ep->in) {
-
- /* ep ix in UDC CSR register space */
- udc_csr_epix = ep->num;
-
-
- /* OUT ep */
- } else {
- /* ep ix in UDC CSR register space */
- udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
- }
-
- /* UDC CSR reg */
- /* set ep values */
- tmp = readl(&dev->csr->ne[udc_csr_epix]);
- /* ep interface */
- tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf,
- UDC_CSR_NE_INTF);
- /* tmp = AMD_ADDBITS(tmp, 2, UDC_CSR_NE_INTF); */
- /* ep alt */
- tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt,
- UDC_CSR_NE_ALT);
- /* write reg */
- writel(tmp, &dev->csr->ne[udc_csr_epix]);
-
- /* clear stall bits */
- ep->halted = 0;
- tmp = readl(&ep->regs->ctl);
- tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
- writel(tmp, &ep->regs->ctl);
- }
-
- /* call gadget zero with setup data received */
- spin_unlock(&dev->lock);
- tmp = dev->driver->setup(&dev->gadget, &setup_data.request);
- spin_lock(&dev->lock);
-
- } /* USB reset */
- if (dev_irq & AMD_BIT(UDC_DEVINT_UR)) {
- DBG(dev, "USB Reset interrupt\n");
- ret_val = IRQ_HANDLED;
-
- /* allow soft reset when suspend occurs */
- soft_reset_occured = 0;
-
- dev->waiting_zlp_ack_ep0in = 0;
- dev->set_cfg_not_acked = 0;
-
- /* mask not needed interrupts */
- udc_mask_unused_interrupts(dev);
-
- /* call gadget to resume and reset configs etc. */
- spin_unlock(&dev->lock);
- if (dev->sys_suspended && dev->driver->resume) {
- dev->driver->resume(&dev->gadget);
- dev->sys_suspended = 0;
- }
- usb_gadget_udc_reset(&dev->gadget, dev->driver);
- spin_lock(&dev->lock);
-
- /* disable ep0 to empty req queue */
- empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
- ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]);
-
- /* soft reset when rxfifo not empty */
- tmp = readl(&dev->regs->sts);
- if (!(tmp & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
- && !soft_reset_after_usbreset_occured) {
- udc_soft_reset(dev);
- soft_reset_after_usbreset_occured++;
- }
-
- /*
- * DMA reset to kill potential old DMA hw hang,
- * POLL bit is already reset by ep_init() through
- * disconnect()
- */
- DBG(dev, "DMA machine reset\n");
- tmp = readl(&dev->regs->cfg);
- writel(tmp | AMD_BIT(UDC_DEVCFG_DMARST), &dev->regs->cfg);
- writel(tmp, &dev->regs->cfg);
-
- /* put into initial config */
- udc_basic_init(dev);
-
- /* enable device setup interrupts */
- udc_enable_dev_setup_interrupts(dev);
-
- /* enable suspend interrupt */
- tmp = readl(&dev->regs->irqmsk);
- tmp &= AMD_UNMASK_BIT(UDC_DEVINT_US);
- writel(tmp, &dev->regs->irqmsk);
-
- } /* USB suspend */
- if (dev_irq & AMD_BIT(UDC_DEVINT_US)) {
- DBG(dev, "USB Suspend interrupt\n");
- ret_val = IRQ_HANDLED;
- if (dev->driver->suspend) {
- spin_unlock(&dev->lock);
- dev->sys_suspended = 1;
- dev->driver->suspend(&dev->gadget);
- spin_lock(&dev->lock);
- }
- } /* new speed ? */
- if (dev_irq & AMD_BIT(UDC_DEVINT_ENUM)) {
- DBG(dev, "ENUM interrupt\n");
- ret_val = IRQ_HANDLED;
- soft_reset_after_usbreset_occured = 0;
-
- /* disable ep0 to empty req queue */
- empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
- ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]);
-
- /* link up all endpoints */
- udc_setup_endpoints(dev);
- dev_info(&dev->pdev->dev, "Connect: %s\n",
- usb_speed_string(dev->gadget.speed));
-
- /* init ep 0 */
- activate_control_endpoints(dev);
-
- /* enable ep0 interrupts */
- udc_enable_ep0_interrupts(dev);
- }
- /* session valid change interrupt */
- if (dev_irq & AMD_BIT(UDC_DEVINT_SVC)) {
- DBG(dev, "USB SVC interrupt\n");
- ret_val = IRQ_HANDLED;
-
- /* check that session is not valid to detect disconnect */
- tmp = readl(&dev->regs->sts);
- if (!(tmp & AMD_BIT(UDC_DEVSTS_SESSVLD))) {
- /* disable suspend interrupt */
- tmp = readl(&dev->regs->irqmsk);
- tmp |= AMD_BIT(UDC_DEVINT_US);
- writel(tmp, &dev->regs->irqmsk);
- DBG(dev, "USB Disconnect (session valid low)\n");
- /* cleanup on disconnect */
- usb_disconnect(udc);
- }
-
- }
-
- return ret_val;
-}
-
-/* Interrupt Service Routine, see Linux Kernel Doc for parameters */
-irqreturn_t udc_irq(int irq, void *pdev)
-{
- struct udc *dev = pdev;
- u32 reg;
- u16 i;
- u32 ep_irq;
- irqreturn_t ret_val = IRQ_NONE;
-
- spin_lock(&dev->lock);
-
- /* check for ep irq */
- reg = readl(&dev->regs->ep_irqsts);
- if (reg) {
- if (reg & AMD_BIT(UDC_EPINT_OUT_EP0))
- ret_val |= udc_control_out_isr(dev);
- if (reg & AMD_BIT(UDC_EPINT_IN_EP0))
- ret_val |= udc_control_in_isr(dev);
-
- /*
- * data endpoint
- * iterate ep's
- */
- for (i = 1; i < UDC_EP_NUM; i++) {
- ep_irq = 1 << i;
- if (!(reg & ep_irq) || i == UDC_EPINT_OUT_EP0)
- continue;
-
- /* clear irq status */
- writel(ep_irq, &dev->regs->ep_irqsts);
-
- /* irq for out ep ? */
- if (i > UDC_EPIN_NUM)
- ret_val |= udc_data_out_isr(dev, i);
- else
- ret_val |= udc_data_in_isr(dev, i);
- }
-
- }
-
-
- /* check for dev irq */
- reg = readl(&dev->regs->irqsts);
- if (reg) {
- /* clear irq */
- writel(reg, &dev->regs->irqsts);
- ret_val |= udc_dev_isr(dev, reg);
- }
-
-
- spin_unlock(&dev->lock);
- return ret_val;
-}
-EXPORT_SYMBOL_GPL(udc_irq);
-
-/* Tears down device */
-void gadget_release(struct device *pdev)
-{
- struct amd5536udc *dev = dev_get_drvdata(pdev);
- kfree(dev);
-}
-EXPORT_SYMBOL_GPL(gadget_release);
-
-/* Cleanup on device remove */
-void udc_remove(struct udc *dev)
-{
- /* remove timer */
- stop_timer++;
- if (timer_pending(&udc_timer))
- wait_for_completion(&on_exit);
- if (udc_timer.data)
- del_timer_sync(&udc_timer);
- /* remove pollstall timer */
- stop_pollstall_timer++;
- if (timer_pending(&udc_pollstall_timer))
- wait_for_completion(&on_pollstall_exit);
- if (udc_pollstall_timer.data)
- del_timer_sync(&udc_pollstall_timer);
- udc = NULL;
-}
-EXPORT_SYMBOL_GPL(udc_remove);
-
-/* free all the dma pools */
-void free_dma_pools(struct udc *dev)
-{
- dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td,
- dev->ep[UDC_EP0OUT_IX].td_phys);
- dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td_stp,
- dev->ep[UDC_EP0OUT_IX].td_stp_dma);
- dma_pool_destroy(dev->stp_requests);
- dma_pool_destroy(dev->data_requests);
-}
-EXPORT_SYMBOL_GPL(free_dma_pools);
-
-/* create dma pools on init */
-int init_dma_pools(struct udc *dev)
-{
- struct udc_stp_dma *td_stp;
- struct udc_data_dma *td_data;
- int retval;
-
- /* consistent DMA mode setting ? */
- if (use_dma_ppb) {
- use_dma_bufferfill_mode = 0;
- } else {
- use_dma_ppb_du = 0;
- use_dma_bufferfill_mode = 1;
- }
-
- /* DMA setup */
- dev->data_requests = dma_pool_create("data_requests", NULL,
- sizeof(struct udc_data_dma), 0, 0);
- if (!dev->data_requests) {
- DBG(dev, "can't get request data pool\n");
- return -ENOMEM;
- }
-
- /* EP0 in dma regs = dev control regs */
- dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl;
-
- /* dma desc for setup data */
- dev->stp_requests = dma_pool_create("setup requests", NULL,
- sizeof(struct udc_stp_dma), 0, 0);
- if (!dev->stp_requests) {
- DBG(dev, "can't get stp request pool\n");
- retval = -ENOMEM;
- goto err_create_dma_pool;
- }
- /* setup */
- td_stp = dma_pool_alloc(dev->stp_requests, GFP_KERNEL,
- &dev->ep[UDC_EP0OUT_IX].td_stp_dma);
- if (!td_stp) {
- retval = -ENOMEM;
- goto err_alloc_dma;
- }
- dev->ep[UDC_EP0OUT_IX].td_stp = td_stp;
-
- /* data: 0 packets !? */
- td_data = dma_pool_alloc(dev->stp_requests, GFP_KERNEL,
- &dev->ep[UDC_EP0OUT_IX].td_phys);
- if (!td_data) {
- retval = -ENOMEM;
- goto err_alloc_phys;
- }
- dev->ep[UDC_EP0OUT_IX].td = td_data;
- return 0;
-
-err_alloc_phys:
- dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td_stp,
- dev->ep[UDC_EP0OUT_IX].td_stp_dma);
-err_alloc_dma:
- dma_pool_destroy(dev->stp_requests);
- dev->stp_requests = NULL;
-err_create_dma_pool:
- dma_pool_destroy(dev->data_requests);
- dev->data_requests = NULL;
- return retval;
-}
-EXPORT_SYMBOL_GPL(init_dma_pools);
-
-/* general probe */
-int udc_probe(struct udc *dev)
-{
- char tmp[128];
- u32 reg;
- int retval;
-
- /* mark timer as not initialized */
- udc_timer.data = 0;
- udc_pollstall_timer.data = 0;
-
- /* device struct setup */
- dev->gadget.ops = &udc_ops;
-
- dev_set_name(&dev->gadget.dev, "gadget");
- dev->gadget.name = name;
- dev->gadget.max_speed = USB_SPEED_HIGH;
-
- /* init registers, interrupts, ... */
- startup_registers(dev);
-
- dev_info(&dev->pdev->dev, "%s\n", mod_desc);
-
- snprintf(tmp, sizeof(tmp), "%d", dev->irq);
- dev_info(&dev->pdev->dev,
- "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
- tmp, dev->phys_addr, dev->chiprev,
- (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1");
- strcpy(tmp, UDC_DRIVER_VERSION_STRING);
- if (dev->chiprev == UDC_HSA0_REV) {
- dev_err(&dev->pdev->dev, "chip revision is A0; too old\n");
- retval = -ENODEV;
- goto finished;
- }
- dev_info(&dev->pdev->dev,
- "driver version: %s(for Geode5536 B1)\n", tmp);
- udc = dev;
-
- retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget,
- gadget_release);
- if (retval)
- goto finished;
-
- /* timer init */
- init_timer(&udc_timer);
- udc_timer.function = udc_timer_function;
- udc_timer.data = 1;
- /* timer pollstall init */
- init_timer(&udc_pollstall_timer);
- udc_pollstall_timer.function = udc_pollstall_timer_function;
- udc_pollstall_timer.data = 1;
-
- /* set SD */
- reg = readl(&dev->regs->ctl);
- reg |= AMD_BIT(UDC_DEVCTL_SD);
- writel(reg, &dev->regs->ctl);
-
- /* print dev register info */
- print_regs(dev);
-
- return 0;
-
-finished:
- return retval;
-}
-EXPORT_SYMBOL_GPL(udc_probe);
-
-MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
-MODULE_AUTHOR("Thomas Dahlmann");
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
new file mode 100644
index 0000000..72f3c8f
--- /dev/null
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -0,0 +1,3223 @@
+/*
+ * amd5536.c -- AMD 5536 UDC high/full speed USB device controller
+ *
+ * Copyright (C) 2005-2007 AMD (http://www.amd.com)
+ * Author: Thomas Dahlmann
+ *
+ * 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 file does the core driver implementation for the UDC that is based
+ * on Synopsys device controller IP (different than HS OTG IP) that is either
+ * connected through PCI bus or integrated to SoC platforms.
+ */
+
+/* Driver strings */
+#define UDC_MOD_DESCRIPTION "Synopsys USB Device Controller"
+#define UDC_DRIVER_VERSION_STRING "01.00.0206"
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/dmapool.h>
+#include <linux/prefetch.h>
+#include <linux/moduleparam.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include "amd5536udc.h"
+
+static void udc_tasklet_disconnect(unsigned long);
+static void empty_req_queue(struct udc_ep *);
+static void udc_setup_endpoints(struct udc *dev);
+static void udc_soft_reset(struct udc *dev);
+static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep);
+static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq);
+
+/* description */
+static const char mod_desc[] = UDC_MOD_DESCRIPTION;
+static const char name[] = "udc";
+
+/* structure to hold endpoint function pointers */
+static const struct usb_ep_ops udc_ep_ops;
+
+/* received setup data */
+static union udc_setup_data setup_data;
+
+/* pointer to device object */
+static struct udc *udc;
+
+/* irq spin lock for soft reset */
+static DEFINE_SPINLOCK(udc_irq_spinlock);
+/* stall spin lock */
+static DEFINE_SPINLOCK(udc_stall_spinlock);
+
+/*
+* slave mode: pending bytes in rx fifo after nyet,
+* used if EPIN irq came but no req was available
+*/
+static unsigned int udc_rxfifo_pending;
+
+/* count soft resets after suspend to avoid loop */
+static int soft_reset_occured;
+static int soft_reset_after_usbreset_occured;
+
+/* timer */
+static struct timer_list udc_timer;
+static int stop_timer;
+
+/* set_rde -- Is used to control enabling of RX DMA. Problem is
+ * that UDC has only one bit (RDE) to enable/disable RX DMA for
+ * all OUT endpoints. So we have to handle race conditions like
+ * when OUT data reaches the fifo but no request was queued yet.
+ * This cannot be solved by letting the RX DMA disabled until a
+ * request gets queued because there may be other OUT packets
+ * in the FIFO (important for not blocking control traffic).
+ * The value of set_rde controls the correspondig timer.
+ *
+ * set_rde -1 == not used, means it is alloed to be set to 0 or 1
+ * set_rde 0 == do not touch RDE, do no start the RDE timer
+ * set_rde 1 == timer function will look whether FIFO has data
+ * set_rde 2 == set by timer function to enable RX DMA on next call
+ */
+static int set_rde = -1;
+
+static DECLARE_COMPLETION(on_exit);
+static struct timer_list udc_pollstall_timer;
+static int stop_pollstall_timer;
+static DECLARE_COMPLETION(on_pollstall_exit);
+
+/* tasklet for usb disconnect */
+static DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect,
+ (unsigned long) &udc);
+
+
+/* endpoint names used for print */
+static const char ep0_string[] = "ep0in";
+static const struct {
+ const char *name;
+ const struct usb_ep_caps caps;
+} ep_info[] = {
+#define EP_INFO(_name, _caps) \
+ { \
+ .name = _name, \
+ .caps = _caps, \
+ }
+
+ EP_INFO(ep0_string,
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep1in-int",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep2in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep3in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep4in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep5in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep6in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep7in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep8in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep9in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep10in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep11in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep12in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep13in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep14in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep15in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep0out",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep1out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep2out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep3out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep4out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep5out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep6out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep7out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep8out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep9out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep10out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep11out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep12out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep13out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep14out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep15out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+
+#undef EP_INFO
+};
+
+/* buffer fill mode */
+static int use_dma_bufferfill_mode;
+/* tx buffer size for high speed */
+static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE;
+
+/*---------------------------------------------------------------------------*/
+/* Prints UDC device registers and endpoint irq registers */
+static void print_regs(struct udc *dev)
+{
+ DBG(dev, "------- Device registers -------\n");
+ DBG(dev, "dev config = %08x\n", readl(&dev->regs->cfg));
+ DBG(dev, "dev control = %08x\n", readl(&dev->regs->ctl));
+ DBG(dev, "dev status = %08x\n", readl(&dev->regs->sts));
+ DBG(dev, "\n");
+ DBG(dev, "dev int's = %08x\n", readl(&dev->regs->irqsts));
+ DBG(dev, "dev intmask = %08x\n", readl(&dev->regs->irqmsk));
+ DBG(dev, "\n");
+ DBG(dev, "dev ep int's = %08x\n", readl(&dev->regs->ep_irqsts));
+ DBG(dev, "dev ep intmask = %08x\n", readl(&dev->regs->ep_irqmsk));
+ DBG(dev, "\n");
+ DBG(dev, "USE DMA = %d\n", use_dma);
+ if (use_dma && use_dma_ppb && !use_dma_ppb_du) {
+ DBG(dev, "DMA mode = PPBNDU (packet per buffer "
+ "WITHOUT desc. update)\n");
+ dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU");
+ } else if (use_dma && use_dma_ppb && use_dma_ppb_du) {
+ DBG(dev, "DMA mode = PPBDU (packet per buffer "
+ "WITH desc. update)\n");
+ dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU");
+ }
+ if (use_dma && use_dma_bufferfill_mode) {
+ DBG(dev, "DMA mode = BF (buffer fill mode)\n");
+ dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF");
+ }
+ if (!use_dma)
+ dev_info(&dev->pdev->dev, "FIFO mode\n");
+ DBG(dev, "-------------------------------------------------------\n");
+}
+
+/* Masks unused interrupts */
+int udc_mask_unused_interrupts(struct udc *dev)
+{
+ u32 tmp;
+
+ /* mask all dev interrupts */
+ tmp = AMD_BIT(UDC_DEVINT_SVC) |
+ AMD_BIT(UDC_DEVINT_ENUM) |
+ AMD_BIT(UDC_DEVINT_US) |
+ AMD_BIT(UDC_DEVINT_UR) |
+ AMD_BIT(UDC_DEVINT_ES) |
+ AMD_BIT(UDC_DEVINT_SI) |
+ AMD_BIT(UDC_DEVINT_SOF)|
+ AMD_BIT(UDC_DEVINT_SC);
+ writel(tmp, &dev->regs->irqmsk);
+
+ /* mask all ep interrupts */
+ writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqmsk);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udc_mask_unused_interrupts);
+
+/* Enables endpoint 0 interrupts */
+static int udc_enable_ep0_interrupts(struct udc *dev)
+{
+ u32 tmp;
+
+ DBG(dev, "udc_enable_ep0_interrupts()\n");
+
+ /* read irq mask */
+ tmp = readl(&dev->regs->ep_irqmsk);
+ /* enable ep0 irq's */
+ tmp &= AMD_UNMASK_BIT(UDC_EPINT_IN_EP0)
+ & AMD_UNMASK_BIT(UDC_EPINT_OUT_EP0);
+ writel(tmp, &dev->regs->ep_irqmsk);
+
+ return 0;
+}
+
+/* Enables device interrupts for SET_INTF and SET_CONFIG */
+int udc_enable_dev_setup_interrupts(struct udc *dev)
+{
+ u32 tmp;
+
+ DBG(dev, "enable device interrupts for setup data\n");
+
+ /* read irq mask */
+ tmp = readl(&dev->regs->irqmsk);
+
+ /* enable SET_INTERFACE, SET_CONFIG and other needed irq's */
+ tmp &= AMD_UNMASK_BIT(UDC_DEVINT_SI)
+ & AMD_UNMASK_BIT(UDC_DEVINT_SC)
+ & AMD_UNMASK_BIT(UDC_DEVINT_UR)
+ & AMD_UNMASK_BIT(UDC_DEVINT_SVC)
+ & AMD_UNMASK_BIT(UDC_DEVINT_ENUM);
+ writel(tmp, &dev->regs->irqmsk);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udc_enable_dev_setup_interrupts);
+
+/* Calculates fifo start of endpoint based on preceding endpoints */
+static int udc_set_txfifo_addr(struct udc_ep *ep)
+{
+ struct udc *dev;
+ u32 tmp;
+ int i;
+
+ if (!ep || !(ep->in))
+ return -EINVAL;
+
+ dev = ep->dev;
+ ep->txfifo = dev->txfifo;
+
+ /* traverse ep's */
+ for (i = 0; i < ep->num; i++) {
+ if (dev->ep[i].regs) {
+ /* read fifo size */
+ tmp = readl(&dev->ep[i].regs->bufin_framenum);
+ tmp = AMD_GETBITS(tmp, UDC_EPIN_BUFF_SIZE);
+ ep->txfifo += tmp;
+ }
+ }
+ return 0;
+}
+
+/* CNAK pending field: bit0 = ep0in, bit16 = ep0out */
+static u32 cnak_pending;
+
+static void UDC_QUEUE_CNAK(struct udc_ep *ep, unsigned num)
+{
+ if (readl(&ep->regs->ctl) & AMD_BIT(UDC_EPCTL_NAK)) {
+ DBG(ep->dev, "NAK could not be cleared for ep%d\n", num);
+ cnak_pending |= 1 << (num);
+ ep->naking = 1;
+ } else
+ cnak_pending = cnak_pending & (~(1 << (num)));
+}
+
+
+/* Enables endpoint, is called by gadget driver */
+static int
+udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc)
+{
+ struct udc_ep *ep;
+ struct udc *dev;
+ u32 tmp;
+ unsigned long iflags;
+ u8 udc_csr_epix;
+ unsigned maxpacket;
+
+ if (!usbep
+ || usbep->name == ep0_string
+ || !desc
+ || desc->bDescriptorType != USB_DT_ENDPOINT)
+ return -EINVAL;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ dev = ep->dev;
+
+ DBG(dev, "udc_ep_enable() ep %d\n", ep->num);
+
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&dev->lock, iflags);
+ ep->ep.desc = desc;
+
+ ep->halted = 0;
+
+ /* set traffic type */
+ tmp = readl(&dev->ep[ep->num].regs->ctl);
+ tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_EPCTL_ET);
+ writel(tmp, &dev->ep[ep->num].regs->ctl);
+
+ /* set max packet size */
+ maxpacket = usb_endpoint_maxp(desc);
+ tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt);
+ tmp = AMD_ADDBITS(tmp, maxpacket, UDC_EP_MAX_PKT_SIZE);
+ ep->ep.maxpacket = maxpacket;
+ writel(tmp, &dev->ep[ep->num].regs->bufout_maxpkt);
+
+ /* IN ep */
+ if (ep->in) {
+
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num;
+
+ /* set buffer size (tx fifo entries) */
+ tmp = readl(&dev->ep[ep->num].regs->bufin_framenum);
+ /* double buffering: fifo size = 2 x max packet size */
+ tmp = AMD_ADDBITS(
+ tmp,
+ maxpacket * UDC_EPIN_BUFF_SIZE_MULT
+ / UDC_DWORD_BYTES,
+ UDC_EPIN_BUFF_SIZE);
+ writel(tmp, &dev->ep[ep->num].regs->bufin_framenum);
+
+ /* calc. tx fifo base addr */
+ udc_set_txfifo_addr(ep);
+
+ /* flush fifo */
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_F);
+ writel(tmp, &ep->regs->ctl);
+
+ /* OUT ep */
+ } else {
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+
+ /* set max packet size UDC CSR */
+ tmp = readl(&dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);
+ tmp = AMD_ADDBITS(tmp, maxpacket,
+ UDC_CSR_NE_MAX_PKT);
+ writel(tmp, &dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);
+
+ if (use_dma && !ep->in) {
+ /* alloc and init BNA dummy request */
+ ep->bna_dummy_req = udc_alloc_bna_dummy(ep);
+ ep->bna_occurred = 0;
+ }
+
+ if (ep->num != UDC_EP0OUT_IX)
+ dev->data_ep_enabled = 1;
+ }
+
+ /* set ep values */
+ tmp = readl(&dev->csr->ne[udc_csr_epix]);
+ /* max packet */
+ tmp = AMD_ADDBITS(tmp, maxpacket, UDC_CSR_NE_MAX_PKT);
+ /* ep number */
+ tmp = AMD_ADDBITS(tmp, desc->bEndpointAddress, UDC_CSR_NE_NUM);
+ /* ep direction */
+ tmp = AMD_ADDBITS(tmp, ep->in, UDC_CSR_NE_DIR);
+ /* ep type */
+ tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_CSR_NE_TYPE);
+ /* ep config */
+ tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, UDC_CSR_NE_CFG);
+ /* ep interface */
+ tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, UDC_CSR_NE_INTF);
+ /* ep alt */
+ tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, UDC_CSR_NE_ALT);
+ /* write reg */
+ writel(tmp, &dev->csr->ne[udc_csr_epix]);
+
+ /* enable ep irq */
+ tmp = readl(&dev->regs->ep_irqmsk);
+ tmp &= AMD_UNMASK_BIT(ep->num);
+ writel(tmp, &dev->regs->ep_irqmsk);
+
+ /*
+ * clear NAK by writing CNAK
+ * avoid BNA for OUT DMA, don't clear NAK until DMA desc. written
+ */
+ if (!use_dma || ep->in) {
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &ep->regs->ctl);
+ ep->naking = 0;
+ UDC_QUEUE_CNAK(ep, ep->num);
+ }
+ tmp = desc->bEndpointAddress;
+ DBG(dev, "%s enabled\n", usbep->name);
+
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ return 0;
+}
+
+/* Resets endpoint */
+static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep)
+{
+ u32 tmp;
+
+ VDBG(ep->dev, "ep-%d reset\n", ep->num);
+ ep->ep.desc = NULL;
+ ep->ep.ops = &udc_ep_ops;
+ INIT_LIST_HEAD(&ep->queue);
+
+ usb_ep_set_maxpacket_limit(&ep->ep,(u16) ~0);
+ /* set NAK */
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_SNAK);
+ writel(tmp, &ep->regs->ctl);
+ ep->naking = 1;
+
+ /* disable interrupt */
+ tmp = readl(®s->ep_irqmsk);
+ tmp |= AMD_BIT(ep->num);
+ writel(tmp, ®s->ep_irqmsk);
+
+ if (ep->in) {
+ /* unset P and IN bit of potential former DMA */
+ tmp = readl(&ep->regs->ctl);
+ tmp &= AMD_UNMASK_BIT(UDC_EPCTL_P);
+ writel(tmp, &ep->regs->ctl);
+
+ tmp = readl(&ep->regs->sts);
+ tmp |= AMD_BIT(UDC_EPSTS_IN);
+ writel(tmp, &ep->regs->sts);
+
+ /* flush the fifo */
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_F);
+ writel(tmp, &ep->regs->ctl);
+
+ }
+ /* reset desc pointer */
+ writel(0, &ep->regs->desptr);
+}
+
+/* Disables endpoint, is called by gadget driver */
+static int udc_ep_disable(struct usb_ep *usbep)
+{
+ struct udc_ep *ep = NULL;
+ unsigned long iflags;
+
+ if (!usbep)
+ return -EINVAL;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ if (usbep->name == ep0_string || !ep->ep.desc)
+ return -EINVAL;
+
+ DBG(ep->dev, "Disable ep-%d\n", ep->num);
+
+ spin_lock_irqsave(&ep->dev->lock, iflags);
+ udc_free_request(&ep->ep, &ep->bna_dummy_req->req);
+ empty_req_queue(ep);
+ ep_init(ep->dev->regs, ep);
+ spin_unlock_irqrestore(&ep->dev->lock, iflags);
+
+ return 0;
+}
+
+/* Allocates request packet, called by gadget driver */
+static struct usb_request *
+udc_alloc_request(struct usb_ep *usbep, gfp_t gfp)
+{
+ struct udc_request *req;
+ struct udc_data_dma *dma_desc;
+ struct udc_ep *ep;
+
+ if (!usbep)
+ return NULL;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+
+ VDBG(ep->dev, "udc_alloc_req(): ep%d\n", ep->num);
+ req = kzalloc(sizeof(struct udc_request), gfp);
+ if (!req)
+ return NULL;
+
+ req->req.dma = DMA_DONT_USE;
+ INIT_LIST_HEAD(&req->queue);
+
+ if (ep->dma) {
+ /* ep0 in requests are allocated from data pool here */
+ dma_desc = pci_pool_alloc(ep->dev->data_requests, gfp,
+ &req->td_phys);
+ if (!dma_desc) {
+ kfree(req);
+ return NULL;
+ }
+
+ VDBG(ep->dev, "udc_alloc_req: req = %p dma_desc = %p, "
+ "td_phys = %lx\n",
+ req, dma_desc,
+ (unsigned long)req->td_phys);
+ /* prevent from using desc. - set HOST BUSY */
+ dma_desc->status = AMD_ADDBITS(dma_desc->status,
+ UDC_DMA_STP_STS_BS_HOST_BUSY,
+ UDC_DMA_STP_STS_BS);
+ dma_desc->bufptr = cpu_to_le32(DMA_DONT_USE);
+ req->td_data = dma_desc;
+ req->td_data_last = NULL;
+ req->chain_len = 1;
+ }
+
+ return &req->req;
+}
+
+/* frees pci pool descriptors of a DMA chain */
+static int udc_free_dma_chain(struct udc *dev, struct udc_request *req)
+{
+ int ret_val = 0;
+ struct udc_data_dma *td;
+ struct udc_data_dma *td_last = NULL;
+ unsigned int i;
+
+ DBG(dev, "free chain req = %p\n", req);
+
+ /* do not free first desc., will be done by free for request */
+ td_last = req->td_data;
+ td = phys_to_virt(td_last->next);
+
+ for (i = 1; i < req->chain_len; i++) {
+ pci_pool_free(dev->data_requests, td,
+ (dma_addr_t)td_last->next);
+ td_last = td;
+ td = phys_to_virt(td_last->next);
+ }
+
+ return ret_val;
+}
+
+/* Frees request packet, called by gadget driver */
+static void
+udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq)
+{
+ struct udc_ep *ep;
+ struct udc_request *req;
+
+ if (!usbep || !usbreq)
+ return;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ req = container_of(usbreq, struct udc_request, req);
+ VDBG(ep->dev, "free_req req=%p\n", req);
+ BUG_ON(!list_empty(&req->queue));
+ if (req->td_data) {
+ VDBG(ep->dev, "req->td_data=%p\n", req->td_data);
+
+ /* free dma chain if created */
+ if (req->chain_len > 1)
+ udc_free_dma_chain(ep->dev, req);
+
+ pci_pool_free(ep->dev->data_requests, req->td_data,
+ req->td_phys);
+ }
+ kfree(req);
+}
+
+/* Init BNA dummy descriptor for HOST BUSY and pointing to itself */
+static void udc_init_bna_dummy(struct udc_request *req)
+{
+ if (req) {
+ /* set last bit */
+ req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L);
+ /* set next pointer to itself */
+ req->td_data->next = req->td_phys;
+ /* set HOST BUSY */
+ req->td_data->status
+ = AMD_ADDBITS(req->td_data->status,
+ UDC_DMA_STP_STS_BS_DMA_DONE,
+ UDC_DMA_STP_STS_BS);
+#ifdef UDC_VERBOSE
+ pr_debug("bna desc = %p, sts = %08x\n",
+ req->td_data, req->td_data->status);
+#endif
+ }
+}
+
+/* Allocate BNA dummy descriptor */
+static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep)
+{
+ struct udc_request *req = NULL;
+ struct usb_request *_req = NULL;
+
+ /* alloc the dummy request */
+ _req = udc_alloc_request(&ep->ep, GFP_ATOMIC);
+ if (_req) {
+ req = container_of(_req, struct udc_request, req);
+ ep->bna_dummy_req = req;
+ udc_init_bna_dummy(req);
+ }
+ return req;
+}
+
+/* Write data to TX fifo for IN packets */
+static void
+udc_txfifo_write(struct udc_ep *ep, struct usb_request *req)
+{
+ u8 *req_buf;
+ u32 *buf;
+ int i, j;
+ unsigned bytes = 0;
+ unsigned remaining = 0;
+
+ if (!req || !ep)
+ return;
+
+ req_buf = req->buf + req->actual;
+ prefetch(req_buf);
+ remaining = req->length - req->actual;
+
+ buf = (u32 *) req_buf;
+
+ bytes = ep->ep.maxpacket;
+ if (bytes > remaining)
+ bytes = remaining;
+
+ /* dwords first */
+ for (i = 0; i < bytes / UDC_DWORD_BYTES; i++)
+ writel(*(buf + i), ep->txfifo);
+
+ /* remaining bytes must be written by byte access */
+ for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
+ writeb((u8)(*(buf + i) >> (j << UDC_BITS_PER_BYTE_SHIFT)),
+ ep->txfifo);
+ }
+
+ /* dummy write confirm */
+ writel(0, &ep->regs->confirm);
+}
+
+/* Read dwords from RX fifo for OUT transfers */
+static int udc_rxfifo_read_dwords(struct udc *dev, u32 *buf, int dwords)
+{
+ int i;
+
+ VDBG(dev, "udc_read_dwords(): %d dwords\n", dwords);
+
+ for (i = 0; i < dwords; i++)
+ *(buf + i) = readl(dev->rxfifo);
+ return 0;
+}
+
+/* Read bytes from RX fifo for OUT transfers */
+static int udc_rxfifo_read_bytes(struct udc *dev, u8 *buf, int bytes)
+{
+ int i, j;
+ u32 tmp;
+
+ VDBG(dev, "udc_read_bytes(): %d bytes\n", bytes);
+
+ /* dwords first */
+ for (i = 0; i < bytes / UDC_DWORD_BYTES; i++)
+ *((u32 *)(buf + (i<<2))) = readl(dev->rxfifo);
+
+ /* remaining bytes must be read by byte access */
+ if (bytes % UDC_DWORD_BYTES) {
+ tmp = readl(dev->rxfifo);
+ for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
+ *(buf + (i<<2) + j) = (u8)(tmp & UDC_BYTE_MASK);
+ tmp = tmp >> UDC_BITS_PER_BYTE;
+ }
+ }
+
+ return 0;
+}
+
+/* Read data from RX fifo for OUT transfers */
+static int
+udc_rxfifo_read(struct udc_ep *ep, struct udc_request *req)
+{
+ u8 *buf;
+ unsigned buf_space;
+ unsigned bytes = 0;
+ unsigned finished = 0;
+
+ /* received number bytes */
+ bytes = readl(&ep->regs->sts);
+ bytes = AMD_GETBITS(bytes, UDC_EPSTS_RX_PKT_SIZE);
+
+ buf_space = req->req.length - req->req.actual;
+ buf = req->req.buf + req->req.actual;
+ if (bytes > buf_space) {
+ if ((buf_space % ep->ep.maxpacket) != 0) {
+ DBG(ep->dev,
+ "%s: rx %d bytes, rx-buf space = %d bytesn\n",
+ ep->ep.name, bytes, buf_space);
+ req->req.status = -EOVERFLOW;
+ }
+ bytes = buf_space;
+ }
+ req->req.actual += bytes;
+
+ /* last packet ? */
+ if (((bytes % ep->ep.maxpacket) != 0) || (!bytes)
+ || ((req->req.actual == req->req.length) && !req->req.zero))
+ finished = 1;
+
+ /* read rx fifo bytes */
+ VDBG(ep->dev, "ep %s: rxfifo read %d bytes\n", ep->ep.name, bytes);
+ udc_rxfifo_read_bytes(ep->dev, buf, bytes);
+
+ return finished;
+}
+
+/* Creates or re-inits a DMA chain */
+static int udc_create_dma_chain(
+ struct udc_ep *ep,
+ struct udc_request *req,
+ unsigned long buf_len, gfp_t gfp_flags
+)
+{
+ unsigned long bytes = req->req.length;
+ unsigned int i;
+ dma_addr_t dma_addr;
+ struct udc_data_dma *td = NULL;
+ struct udc_data_dma *last = NULL;
+ unsigned long txbytes;
+ unsigned create_new_chain = 0;
+ unsigned len;
+
+ VDBG(ep->dev, "udc_create_dma_chain: bytes=%ld buf_len=%ld\n",
+ bytes, buf_len);
+ dma_addr = DMA_DONT_USE;
+
+ /* unset L bit in first desc for OUT */
+ if (!ep->in)
+ req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L);
+
+ /* alloc only new desc's if not already available */
+ len = req->req.length / ep->ep.maxpacket;
+ if (req->req.length % ep->ep.maxpacket)
+ len++;
+
+ if (len > req->chain_len) {
+ /* shorter chain already allocated before */
+ if (req->chain_len > 1)
+ udc_free_dma_chain(ep->dev, req);
+ req->chain_len = len;
+ create_new_chain = 1;
+ }
+
+ td = req->td_data;
+ /* gen. required number of descriptors and buffers */
+ for (i = buf_len; i < bytes; i += buf_len) {
+ /* create or determine next desc. */
+ if (create_new_chain) {
+ td = pci_pool_alloc(ep->dev->data_requests,
+ gfp_flags, &dma_addr);
+ if (!td)
+ return -ENOMEM;
+
+ td->status = 0;
+ } else if (i == buf_len) {
+ /* first td */
+ td = (struct udc_data_dma *)phys_to_virt(
+ req->td_data->next);
+ td->status = 0;
+ } else {
+ td = (struct udc_data_dma *)phys_to_virt(last->next);
+ td->status = 0;
+ }
+
+ if (td)
+ td->bufptr = req->req.dma + i; /* assign buffer */
+ else
+ break;
+
+ /* short packet ? */
+ if ((bytes - i) >= buf_len) {
+ txbytes = buf_len;
+ } else {
+ /* short packet */
+ txbytes = bytes - i;
+ }
+
+ /* link td and assign tx bytes */
+ if (i == buf_len) {
+ if (create_new_chain)
+ req->td_data->next = dma_addr;
+ /*
+ * else
+ * req->td_data->next = virt_to_phys(td);
+ */
+ /* write tx bytes */
+ if (ep->in) {
+ /* first desc */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ ep->ep.maxpacket,
+ UDC_DMA_IN_STS_TXBYTES);
+ /* second desc */
+ td->status = AMD_ADDBITS(td->status,
+ txbytes,
+ UDC_DMA_IN_STS_TXBYTES);
+ }
+ } else {
+ if (create_new_chain)
+ last->next = dma_addr;
+ /*
+ * else
+ * last->next = virt_to_phys(td);
+ */
+ if (ep->in) {
+ /* write tx bytes */
+ td->status = AMD_ADDBITS(td->status,
+ txbytes,
+ UDC_DMA_IN_STS_TXBYTES);
+ }
+ }
+ last = td;
+ }
+ /* set last bit */
+ if (td) {
+ td->status |= AMD_BIT(UDC_DMA_IN_STS_L);
+ /* last desc. points to itself */
+ req->td_data_last = td;
+ }
+
+ return 0;
+}
+
+/* create/re-init a DMA descriptor or a DMA descriptor chain */
+static int prep_dma(struct udc_ep *ep, struct udc_request *req, gfp_t gfp)
+{
+ int retval = 0;
+ u32 tmp;
+
+ VDBG(ep->dev, "prep_dma\n");
+ VDBG(ep->dev, "prep_dma ep%d req->td_data=%p\n",
+ ep->num, req->td_data);
+
+ /* set buffer pointer */
+ req->td_data->bufptr = req->req.dma;
+
+ /* set last bit */
+ req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L);
+
+ /* build/re-init dma chain if maxpkt scatter mode, not for EP0 */
+ if (use_dma_ppb) {
+
+ retval = udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp);
+ if (retval != 0) {
+ if (retval == -ENOMEM)
+ DBG(ep->dev, "Out of DMA memory\n");
+ return retval;
+ }
+ if (ep->in) {
+ if (req->req.length == ep->ep.maxpacket) {
+ /* write tx bytes */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ ep->ep.maxpacket,
+ UDC_DMA_IN_STS_TXBYTES);
+
+ }
+ }
+
+ }
+
+ if (ep->in) {
+ VDBG(ep->dev, "IN: use_dma_ppb=%d req->req.len=%d "
+ "maxpacket=%d ep%d\n",
+ use_dma_ppb, req->req.length,
+ ep->ep.maxpacket, ep->num);
+ /*
+ * if bytes < max packet then tx bytes must
+ * be written in packet per buffer mode
+ */
+ if (!use_dma_ppb || req->req.length < ep->ep.maxpacket
+ || ep->num == UDC_EP0OUT_IX
+ || ep->num == UDC_EP0IN_IX) {
+ /* write tx bytes */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ req->req.length,
+ UDC_DMA_IN_STS_TXBYTES);
+ /* reset frame num */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ 0,
+ UDC_DMA_IN_STS_FRAMENUM);
+ }
+ /* set HOST BUSY */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ UDC_DMA_STP_STS_BS_HOST_BUSY,
+ UDC_DMA_STP_STS_BS);
+ } else {
+ VDBG(ep->dev, "OUT set host ready\n");
+ /* set HOST READY */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ UDC_DMA_STP_STS_BS_HOST_READY,
+ UDC_DMA_STP_STS_BS);
+
+
+ /* clear NAK by writing CNAK */
+ if (ep->naking) {
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &ep->regs->ctl);
+ ep->naking = 0;
+ UDC_QUEUE_CNAK(ep, ep->num);
+ }
+
+ }
+
+ return retval;
+}
+
+/* Completes request packet ... caller MUST hold lock */
+static void
+complete_req(struct udc_ep *ep, struct udc_request *req, int sts)
+__releases(ep->dev->lock)
+__acquires(ep->dev->lock)
+{
+ struct udc *dev;
+ unsigned halted;
+
+ VDBG(ep->dev, "complete_req(): ep%d\n", ep->num);
+
+ dev = ep->dev;
+ /* unmap DMA */
+ if (ep->dma)
+ usb_gadget_unmap_request(&dev->gadget, &req->req, ep->in);
+
+ halted = ep->halted;
+ ep->halted = 1;
+
+ /* set new status if pending */
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = sts;
+
+ /* remove from ep queue */
+ list_del_init(&req->queue);
+
+ VDBG(ep->dev, "req %p => complete %d bytes at %s with sts %d\n",
+ &req->req, req->req.length, ep->ep.name, sts);
+
+ spin_unlock(&dev->lock);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
+ spin_lock(&dev->lock);
+ ep->halted = halted;
+}
+
+/* Iterates to the end of a DMA chain and returns last descriptor */
+static struct udc_data_dma *udc_get_last_dma_desc(struct udc_request *req)
+{
+ struct udc_data_dma *td;
+
+ td = req->td_data;
+ while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L)))
+ td = phys_to_virt(td->next);
+
+ return td;
+
+}
+
+/* Iterates to the end of a DMA chain and counts bytes received */
+static u32 udc_get_ppbdu_rxbytes(struct udc_request *req)
+{
+ struct udc_data_dma *td;
+ u32 count;
+
+ td = req->td_data;
+ /* received number bytes */
+ count = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_RXBYTES);
+
+ while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) {
+ td = phys_to_virt(td->next);
+ /* received number bytes */
+ if (td) {
+ count += AMD_GETBITS(td->status,
+ UDC_DMA_OUT_STS_RXBYTES);
+ }
+ }
+
+ return count;
+
+}
+
+/* Enabling RX DMA */
+static void udc_set_rde(struct udc *dev)
+{
+ u32 tmp;
+
+ VDBG(dev, "udc_set_rde()\n");
+ /* stop RDE timer */
+ if (timer_pending(&udc_timer)) {
+ set_rde = 0;
+ mod_timer(&udc_timer, jiffies - 1);
+ }
+ /* set RDE */
+ tmp = readl(&dev->regs->ctl);
+ tmp |= AMD_BIT(UDC_DEVCTL_RDE);
+ writel(tmp, &dev->regs->ctl);
+}
+
+/* Queues a request packet, called by gadget driver */
+static int
+udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp)
+{
+ int retval = 0;
+ u8 open_rxfifo = 0;
+ unsigned long iflags;
+ struct udc_ep *ep;
+ struct udc_request *req;
+ struct udc *dev;
+ u32 tmp;
+
+ /* check the inputs */
+ req = container_of(usbreq, struct udc_request, req);
+
+ if (!usbep || !usbreq || !usbreq->complete || !usbreq->buf
+ || !list_empty(&req->queue))
+ return -EINVAL;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
+ return -EINVAL;
+
+ VDBG(ep->dev, "udc_queue(): ep%d-in=%d\n", ep->num, ep->in);
+ dev = ep->dev;
+
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ /* map dma (usually done before) */
+ if (ep->dma) {
+ VDBG(dev, "DMA map req %p\n", req);
+ retval = usb_gadget_map_request(&udc->gadget, usbreq, ep->in);
+ if (retval)
+ return retval;
+ }
+
+ VDBG(dev, "%s queue req %p, len %d req->td_data=%p buf %p\n",
+ usbep->name, usbreq, usbreq->length,
+ req->td_data, usbreq->buf);
+
+ spin_lock_irqsave(&dev->lock, iflags);
+ usbreq->actual = 0;
+ usbreq->status = -EINPROGRESS;
+ req->dma_done = 0;
+
+ /* on empty queue just do first transfer */
+ if (list_empty(&ep->queue)) {
+ /* zlp */
+ if (usbreq->length == 0) {
+ /* IN zlp's are handled by hardware */
+ complete_req(ep, req, 0);
+ VDBG(dev, "%s: zlp\n", ep->ep.name);
+ /*
+ * if set_config or set_intf is waiting for ack by zlp
+ * then set CSR_DONE
+ */
+ if (dev->set_cfg_not_acked) {
+ tmp = readl(&dev->regs->ctl);
+ tmp |= AMD_BIT(UDC_DEVCTL_CSR_DONE);
+ writel(tmp, &dev->regs->ctl);
+ dev->set_cfg_not_acked = 0;
+ }
+ /* setup command is ACK'ed now by zlp */
+ if (dev->waiting_zlp_ack_ep0in) {
+ /* clear NAK by writing CNAK in EP0_IN */
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+ dev->ep[UDC_EP0IN_IX].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX],
+ UDC_EP0IN_IX);
+ dev->waiting_zlp_ack_ep0in = 0;
+ }
+ goto finished;
+ }
+ if (ep->dma) {
+ retval = prep_dma(ep, req, GFP_ATOMIC);
+ if (retval != 0)
+ goto finished;
+ /* write desc pointer to enable DMA */
+ if (ep->in) {
+ /* set HOST READY */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ UDC_DMA_IN_STS_BS_HOST_READY,
+ UDC_DMA_IN_STS_BS);
+ }
+
+ /* disabled rx dma while descriptor update */
+ if (!ep->in) {
+ /* stop RDE timer */
+ if (timer_pending(&udc_timer)) {
+ set_rde = 0;
+ mod_timer(&udc_timer, jiffies - 1);
+ }
+ /* clear RDE */
+ tmp = readl(&dev->regs->ctl);
+ tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE);
+ writel(tmp, &dev->regs->ctl);
+ open_rxfifo = 1;
+
+ /*
+ * if BNA occurred then let BNA dummy desc.
+ * point to current desc.
+ */
+ if (ep->bna_occurred) {
+ VDBG(dev, "copy to BNA dummy desc.\n");
+ memcpy(ep->bna_dummy_req->td_data,
+ req->td_data,
+ sizeof(struct udc_data_dma));
+ }
+ }
+ /* write desc pointer */
+ writel(req->td_phys, &ep->regs->desptr);
+
+ /* clear NAK by writing CNAK */
+ if (ep->naking) {
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &ep->regs->ctl);
+ ep->naking = 0;
+ UDC_QUEUE_CNAK(ep, ep->num);
+ }
+
+ if (ep->in) {
+ /* enable ep irq */
+ tmp = readl(&dev->regs->ep_irqmsk);
+ tmp &= AMD_UNMASK_BIT(ep->num);
+ writel(tmp, &dev->regs->ep_irqmsk);
+ }
+ } else if (ep->in) {
+ /* enable ep irq */
+ tmp = readl(&dev->regs->ep_irqmsk);
+ tmp &= AMD_UNMASK_BIT(ep->num);
+ writel(tmp, &dev->regs->ep_irqmsk);
+ }
+
+ } else if (ep->dma) {
+
+ /*
+ * prep_dma not used for OUT ep's, this is not possible
+ * for PPB modes, because of chain creation reasons
+ */
+ if (ep->in) {
+ retval = prep_dma(ep, req, GFP_ATOMIC);
+ if (retval != 0)
+ goto finished;
+ }
+ }
+ VDBG(dev, "list_add\n");
+ /* add request to ep queue */
+ if (req) {
+
+ list_add_tail(&req->queue, &ep->queue);
+
+ /* open rxfifo if out data queued */
+ if (open_rxfifo) {
+ /* enable DMA */
+ req->dma_going = 1;
+ udc_set_rde(dev);
+ if (ep->num != UDC_EP0OUT_IX)
+ dev->data_ep_queued = 1;
+ }
+ /* stop OUT naking */
+ if (!ep->in) {
+ if (!use_dma && udc_rxfifo_pending) {
+ DBG(dev, "udc_queue(): pending bytes in "
+ "rxfifo after nyet\n");
+ /*
+ * read pending bytes afer nyet:
+ * referring to isr
+ */
+ if (udc_rxfifo_read(ep, req)) {
+ /* finish */
+ complete_req(ep, req, 0);
+ }
+ udc_rxfifo_pending = 0;
+
+ }
+ }
+ }
+
+finished:
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ return retval;
+}
+
+/* Empty request queue of an endpoint; caller holds spinlock */
+static void empty_req_queue(struct udc_ep *ep)
+{
+ struct udc_request *req;
+
+ ep->halted = 1;
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct udc_request,
+ queue);
+ complete_req(ep, req, -ESHUTDOWN);
+ }
+}
+
+/* Dequeues a request packet, called by gadget driver */
+static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
+{
+ struct udc_ep *ep;
+ struct udc_request *req;
+ unsigned halted;
+ unsigned long iflags;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ if (!usbep || !usbreq || (!ep->ep.desc && (ep->num != 0
+ && ep->num != UDC_EP0OUT_IX)))
+ return -EINVAL;
+
+ req = container_of(usbreq, struct udc_request, req);
+
+ spin_lock_irqsave(&ep->dev->lock, iflags);
+ halted = ep->halted;
+ ep->halted = 1;
+ /* request in processing or next one */
+ if (ep->queue.next == &req->queue) {
+ if (ep->dma && req->dma_going) {
+ if (ep->in)
+ ep->cancel_transfer = 1;
+ else {
+ u32 tmp;
+ u32 dma_sts;
+ /* stop potential receive DMA */
+ tmp = readl(&udc->regs->ctl);
+ writel(tmp & AMD_UNMASK_BIT(UDC_DEVCTL_RDE),
+ &udc->regs->ctl);
+ /*
+ * Cancel transfer later in ISR
+ * if descriptor was touched.
+ */
+ dma_sts = AMD_GETBITS(req->td_data->status,
+ UDC_DMA_OUT_STS_BS);
+ if (dma_sts != UDC_DMA_OUT_STS_BS_HOST_READY)
+ ep->cancel_transfer = 1;
+ else {
+ udc_init_bna_dummy(ep->req);
+ writel(ep->bna_dummy_req->td_phys,
+ &ep->regs->desptr);
+ }
+ writel(tmp, &udc->regs->ctl);
+ }
+ }
+ }
+ complete_req(ep, req, -ECONNRESET);
+ ep->halted = halted;
+
+ spin_unlock_irqrestore(&ep->dev->lock, iflags);
+ return 0;
+}
+
+/* Halt or clear halt of endpoint */
+static int
+udc_set_halt(struct usb_ep *usbep, int halt)
+{
+ struct udc_ep *ep;
+ u32 tmp;
+ unsigned long iflags;
+ int retval = 0;
+
+ if (!usbep)
+ return -EINVAL;
+
+ pr_debug("set_halt %s: halt=%d\n", usbep->name, halt);
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
+ return -EINVAL;
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&udc_stall_spinlock, iflags);
+ /* halt or clear halt */
+ if (halt) {
+ if (ep->num == 0)
+ ep->dev->stall_ep0in = 1;
+ else {
+ /*
+ * set STALL
+ * rxfifo empty not taken into acount
+ */
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_S);
+ writel(tmp, &ep->regs->ctl);
+ ep->halted = 1;
+
+ /* setup poll timer */
+ if (!timer_pending(&udc_pollstall_timer)) {
+ udc_pollstall_timer.expires = jiffies +
+ HZ * UDC_POLLSTALL_TIMER_USECONDS
+ / (1000 * 1000);
+ if (!stop_pollstall_timer) {
+ DBG(ep->dev, "start polltimer\n");
+ add_timer(&udc_pollstall_timer);
+ }
+ }
+ }
+ } else {
+ /* ep is halted by set_halt() before */
+ if (ep->halted) {
+ tmp = readl(&ep->regs->ctl);
+ /* clear stall bit */
+ tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
+ /* clear NAK by writing CNAK */
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &ep->regs->ctl);
+ ep->halted = 0;
+ UDC_QUEUE_CNAK(ep, ep->num);
+ }
+ }
+ spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
+ return retval;
+}
+
+/* gadget interface */
+static const struct usb_ep_ops udc_ep_ops = {
+ .enable = udc_ep_enable,
+ .disable = udc_ep_disable,
+
+ .alloc_request = udc_alloc_request,
+ .free_request = udc_free_request,
+
+ .queue = udc_queue,
+ .dequeue = udc_dequeue,
+
+ .set_halt = udc_set_halt,
+ /* fifo ops not implemented */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Get frame counter (not implemented) */
+static int udc_get_frame(struct usb_gadget *gadget)
+{
+ return -EOPNOTSUPP;
+}
+
+/* Initiates a remote wakeup */
+static int udc_remote_wakeup(struct udc *dev)
+{
+ unsigned long flags;
+ u32 tmp;
+
+ DBG(dev, "UDC initiates remote wakeup\n");
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ tmp = readl(&dev->regs->ctl);
+ tmp |= AMD_BIT(UDC_DEVCTL_RES);
+ writel(tmp, &dev->regs->ctl);
+ tmp &= AMD_CLEAR_BIT(UDC_DEVCTL_RES);
+ writel(tmp, &dev->regs->ctl);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+}
+
+/* Remote wakeup gadget interface */
+static int udc_wakeup(struct usb_gadget *gadget)
+{
+ struct udc *dev;
+
+ if (!gadget)
+ return -EINVAL;
+ dev = container_of(gadget, struct udc, gadget);
+ udc_remote_wakeup(dev);
+
+ return 0;
+}
+
+static int amd5536_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int amd5536_udc_stop(struct usb_gadget *g);
+
+static const struct usb_gadget_ops udc_ops = {
+ .wakeup = udc_wakeup,
+ .get_frame = udc_get_frame,
+ .udc_start = amd5536_udc_start,
+ .udc_stop = amd5536_udc_stop,
+};
+
+/* Setups endpoint parameters, adds endpoints to linked list */
+static void make_ep_lists(struct udc *dev)
+{
+ /* make gadget ep lists */
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ list_add_tail(&dev->ep[UDC_EPIN_STATUS_IX].ep.ep_list,
+ &dev->gadget.ep_list);
+ list_add_tail(&dev->ep[UDC_EPIN_IX].ep.ep_list,
+ &dev->gadget.ep_list);
+ list_add_tail(&dev->ep[UDC_EPOUT_IX].ep.ep_list,
+ &dev->gadget.ep_list);
+
+ /* fifo config */
+ dev->ep[UDC_EPIN_STATUS_IX].fifo_depth = UDC_EPIN_SMALLINT_BUFF_SIZE;
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ dev->ep[UDC_EPIN_IX].fifo_depth = UDC_FS_EPIN_BUFF_SIZE;
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ dev->ep[UDC_EPIN_IX].fifo_depth = hs_tx_buf;
+ dev->ep[UDC_EPOUT_IX].fifo_depth = UDC_RXFIFO_SIZE;
+}
+
+/* Inits UDC context */
+void udc_basic_init(struct udc *dev)
+{
+ u32 tmp;
+
+ DBG(dev, "udc_basic_init()\n");
+
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* stop RDE timer */
+ if (timer_pending(&udc_timer)) {
+ set_rde = 0;
+ mod_timer(&udc_timer, jiffies - 1);
+ }
+ /* stop poll stall timer */
+ if (timer_pending(&udc_pollstall_timer))
+ mod_timer(&udc_pollstall_timer, jiffies - 1);
+ /* disable DMA */
+ tmp = readl(&dev->regs->ctl);
+ tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE);
+ tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_TDE);
+ writel(tmp, &dev->regs->ctl);
+
+ /* enable dynamic CSR programming */
+ tmp = readl(&dev->regs->cfg);
+ tmp |= AMD_BIT(UDC_DEVCFG_CSR_PRG);
+ /* set self powered */
+ tmp |= AMD_BIT(UDC_DEVCFG_SP);
+ /* set remote wakeupable */
+ tmp |= AMD_BIT(UDC_DEVCFG_RWKP);
+ writel(tmp, &dev->regs->cfg);
+
+ make_ep_lists(dev);
+
+ dev->data_ep_enabled = 0;
+ dev->data_ep_queued = 0;
+}
+EXPORT_SYMBOL_GPL(udc_basic_init);
+
+/* init registers at driver load time */
+static int startup_registers(struct udc *dev)
+{
+ u32 tmp;
+
+ /* init controller by soft reset */
+ udc_soft_reset(dev);
+
+ /* mask not needed interrupts */
+ udc_mask_unused_interrupts(dev);
+
+ /* put into initial config */
+ udc_basic_init(dev);
+ /* link up all endpoints */
+ udc_setup_endpoints(dev);
+
+ /* program speed */
+ tmp = readl(&dev->regs->cfg);
+ if (use_fullspeed)
+ tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
+ else
+ tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD);
+ writel(tmp, &dev->regs->cfg);
+
+ return 0;
+}
+
+/* Sets initial endpoint parameters */
+static void udc_setup_endpoints(struct udc *dev)
+{
+ struct udc_ep *ep;
+ u32 tmp;
+ u32 reg;
+
+ DBG(dev, "udc_setup_endpoints()\n");
+
+ /* read enum speed */
+ tmp = readl(&dev->regs->sts);
+ tmp = AMD_GETBITS(tmp, UDC_DEVSTS_ENUM_SPEED);
+ if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH)
+ dev->gadget.speed = USB_SPEED_HIGH;
+ else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL)
+ dev->gadget.speed = USB_SPEED_FULL;
+
+ /* set basic ep parameters */
+ for (tmp = 0; tmp < UDC_EP_NUM; tmp++) {
+ ep = &dev->ep[tmp];
+ ep->dev = dev;
+ ep->ep.name = ep_info[tmp].name;
+ ep->ep.caps = ep_info[tmp].caps;
+ ep->num = tmp;
+ /* txfifo size is calculated at enable time */
+ ep->txfifo = dev->txfifo;
+
+ /* fifo size */
+ if (tmp < UDC_EPIN_NUM) {
+ ep->fifo_depth = UDC_TXFIFO_SIZE;
+ ep->in = 1;
+ } else {
+ ep->fifo_depth = UDC_RXFIFO_SIZE;
+ ep->in = 0;
+
+ }
+ ep->regs = &dev->ep_regs[tmp];
+ /*
+ * ep will be reset only if ep was not enabled before to avoid
+ * disabling ep interrupts when ENUM interrupt occurs but ep is
+ * not enabled by gadget driver
+ */
+ if (!ep->ep.desc)
+ ep_init(dev->regs, ep);
+
+ if (use_dma) {
+ /*
+ * ep->dma is not really used, just to indicate that
+ * DMA is active: remove this
+ * dma regs = dev control regs
+ */
+ ep->dma = &dev->regs->ctl;
+
+ /* nak OUT endpoints until enable - not for ep0 */
+ if (tmp != UDC_EP0IN_IX && tmp != UDC_EP0OUT_IX
+ && tmp > UDC_EPIN_NUM) {
+ /* set NAK */
+ reg = readl(&dev->ep[tmp].regs->ctl);
+ reg |= AMD_BIT(UDC_EPCTL_SNAK);
+ writel(reg, &dev->ep[tmp].regs->ctl);
+ dev->ep[tmp].naking = 1;
+
+ }
+ }
+ }
+ /* EP0 max packet */
+ if (dev->gadget.speed == USB_SPEED_FULL) {
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep,
+ UDC_FS_EP0IN_MAX_PKT_SIZE);
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep,
+ UDC_FS_EP0OUT_MAX_PKT_SIZE);
+ } else if (dev->gadget.speed == USB_SPEED_HIGH) {
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep,
+ UDC_EP0IN_MAX_PKT_SIZE);
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep,
+ UDC_EP0OUT_MAX_PKT_SIZE);
+ }
+
+ /*
+ * with suspend bug workaround, ep0 params for gadget driver
+ * are set at gadget driver bind() call
+ */
+ dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
+ dev->ep[UDC_EP0IN_IX].halted = 0;
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+ /* init cfg/alt/int */
+ dev->cur_config = 0;
+ dev->cur_intf = 0;
+ dev->cur_alt = 0;
+}
+
+/* Bringup after Connect event, initial bringup to be ready for ep0 events */
+static void usb_connect(struct udc *dev)
+{
+
+ dev_info(&dev->pdev->dev, "USB Connect\n");
+
+ dev->connected = 1;
+
+ /* put into initial config */
+ udc_basic_init(dev);
+
+ /* enable device setup interrupts */
+ udc_enable_dev_setup_interrupts(dev);
+}
+
+/*
+ * Calls gadget with disconnect event and resets the UDC and makes
+ * initial bringup to be ready for ep0 events
+ */
+static void usb_disconnect(struct udc *dev)
+{
+
+ dev_info(&dev->pdev->dev, "USB Disconnect\n");
+
+ dev->connected = 0;
+
+ /* mask interrupts */
+ udc_mask_unused_interrupts(dev);
+
+ /* REVISIT there doesn't seem to be a point to having this
+ * talk to a tasklet ... do it directly, we already hold
+ * the spinlock needed to process the disconnect.
+ */
+
+ tasklet_schedule(&disconnect_tasklet);
+}
+
+/* Tasklet for disconnect to be outside of interrupt context */
+static void udc_tasklet_disconnect(unsigned long par)
+{
+ struct udc *dev = (struct udc *)(*((struct udc **) par));
+ u32 tmp;
+
+ DBG(dev, "Tasklet disconnect\n");
+ spin_lock_irq(&dev->lock);
+
+ if (dev->driver) {
+ spin_unlock(&dev->lock);
+ dev->driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+
+ /* empty queues */
+ for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
+ empty_req_queue(&dev->ep[tmp]);
+
+ }
+
+ /* disable ep0 */
+ ep_init(dev->regs,
+ &dev->ep[UDC_EP0IN_IX]);
+
+
+ if (!soft_reset_occured) {
+ /* init controller by soft reset */
+ udc_soft_reset(dev);
+ soft_reset_occured++;
+ }
+
+ /* re-enable dev interrupts */
+ udc_enable_dev_setup_interrupts(dev);
+ /* back to full speed ? */
+ if (use_fullspeed) {
+ tmp = readl(&dev->regs->cfg);
+ tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
+ writel(tmp, &dev->regs->cfg);
+ }
+
+ spin_unlock_irq(&dev->lock);
+}
+
+/* Reset the UDC core */
+static void udc_soft_reset(struct udc *dev)
+{
+ unsigned long flags;
+
+ DBG(dev, "Soft reset\n");
+ /*
+ * reset possible waiting interrupts, because int.
+ * status is lost after soft reset,
+ * ep int. status reset
+ */
+ writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqsts);
+ /* device int. status reset */
+ writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts);
+
+ spin_lock_irqsave(&udc_irq_spinlock, flags);
+ writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
+ readl(&dev->regs->cfg);
+ spin_unlock_irqrestore(&udc_irq_spinlock, flags);
+
+}
+
+/* RDE timer callback to set RDE bit */
+static void udc_timer_function(unsigned long v)
+{
+ u32 tmp;
+
+ spin_lock_irq(&udc_irq_spinlock);
+
+ if (set_rde > 0) {
+ /*
+ * open the fifo if fifo was filled on last timer call
+ * conditionally
+ */
+ if (set_rde > 1) {
+ /* set RDE to receive setup data */
+ tmp = readl(&udc->regs->ctl);
+ tmp |= AMD_BIT(UDC_DEVCTL_RDE);
+ writel(tmp, &udc->regs->ctl);
+ set_rde = -1;
+ } else if (readl(&udc->regs->sts)
+ & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) {
+ /*
+ * if fifo empty setup polling, do not just
+ * open the fifo
+ */
+ udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV;
+ if (!stop_timer)
+ add_timer(&udc_timer);
+ } else {
+ /*
+ * fifo contains data now, setup timer for opening
+ * the fifo when timer expires to be able to receive
+ * setup packets, when data packets gets queued by
+ * gadget layer then timer will forced to expire with
+ * set_rde=0 (RDE is set in udc_queue())
+ */
+ set_rde++;
+ /* debug: lhadmot_timer_start = 221070 */
+ udc_timer.expires = jiffies + HZ*UDC_RDE_TIMER_SECONDS;
+ if (!stop_timer)
+ add_timer(&udc_timer);
+ }
+
+ } else
+ set_rde = -1; /* RDE was set by udc_queue() */
+ spin_unlock_irq(&udc_irq_spinlock);
+ if (stop_timer)
+ complete(&on_exit);
+
+}
+
+/* Handle halt state, used in stall poll timer */
+static void udc_handle_halt_state(struct udc_ep *ep)
+{
+ u32 tmp;
+ /* set stall as long not halted */
+ if (ep->halted == 1) {
+ tmp = readl(&ep->regs->ctl);
+ /* STALL cleared ? */
+ if (!(tmp & AMD_BIT(UDC_EPCTL_S))) {
+ /*
+ * FIXME: MSC spec requires that stall remains
+ * even on receivng of CLEAR_FEATURE HALT. So
+ * we would set STALL again here to be compliant.
+ * But with current mass storage drivers this does
+ * not work (would produce endless host retries).
+ * So we clear halt on CLEAR_FEATURE.
+ *
+ DBG(ep->dev, "ep %d: set STALL again\n", ep->num);
+ tmp |= AMD_BIT(UDC_EPCTL_S);
+ writel(tmp, &ep->regs->ctl);*/
+
+ /* clear NAK by writing CNAK */
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &ep->regs->ctl);
+ ep->halted = 0;
+ UDC_QUEUE_CNAK(ep, ep->num);
+ }
+ }
+}
+
+/* Stall timer callback to poll S bit and set it again after */
+static void udc_pollstall_timer_function(unsigned long v)
+{
+ struct udc_ep *ep;
+ int halted = 0;
+
+ spin_lock_irq(&udc_stall_spinlock);
+ /*
+ * only one IN and OUT endpoints are handled
+ * IN poll stall
+ */
+ ep = &udc->ep[UDC_EPIN_IX];
+ udc_handle_halt_state(ep);
+ if (ep->halted)
+ halted = 1;
+ /* OUT poll stall */
+ ep = &udc->ep[UDC_EPOUT_IX];
+ udc_handle_halt_state(ep);
+ if (ep->halted)
+ halted = 1;
+
+ /* setup timer again when still halted */
+ if (!stop_pollstall_timer && halted) {
+ udc_pollstall_timer.expires = jiffies +
+ HZ * UDC_POLLSTALL_TIMER_USECONDS
+ / (1000 * 1000);
+ add_timer(&udc_pollstall_timer);
+ }
+ spin_unlock_irq(&udc_stall_spinlock);
+
+ if (stop_pollstall_timer)
+ complete(&on_pollstall_exit);
+}
+
+/* Inits endpoint 0 so that SETUP packets are processed */
+static void activate_control_endpoints(struct udc *dev)
+{
+ u32 tmp;
+
+ DBG(dev, "activate_control_endpoints\n");
+
+ /* flush fifo */
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_F);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+
+ /* set ep0 directions */
+ dev->ep[UDC_EP0IN_IX].in = 1;
+ dev->ep[UDC_EP0OUT_IX].in = 0;
+
+ /* set buffer size (tx fifo entries) of EP0_IN */
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufin_framenum);
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ tmp = AMD_ADDBITS(tmp, UDC_FS_EPIN0_BUFF_SIZE,
+ UDC_EPIN_BUFF_SIZE);
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ tmp = AMD_ADDBITS(tmp, UDC_EPIN0_BUFF_SIZE,
+ UDC_EPIN_BUFF_SIZE);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufin_framenum);
+
+ /* set max packet size of EP0_IN */
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt);
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ tmp = AMD_ADDBITS(tmp, UDC_FS_EP0IN_MAX_PKT_SIZE,
+ UDC_EP_MAX_PKT_SIZE);
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ tmp = AMD_ADDBITS(tmp, UDC_EP0IN_MAX_PKT_SIZE,
+ UDC_EP_MAX_PKT_SIZE);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt);
+
+ /* set max packet size of EP0_OUT */
+ tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt);
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE,
+ UDC_EP_MAX_PKT_SIZE);
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE,
+ UDC_EP_MAX_PKT_SIZE);
+ writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt);
+
+ /* set max packet size of EP0 in UDC CSR */
+ tmp = readl(&dev->csr->ne[0]);
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE,
+ UDC_CSR_NE_MAX_PKT);
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE,
+ UDC_CSR_NE_MAX_PKT);
+ writel(tmp, &dev->csr->ne[0]);
+
+ if (use_dma) {
+ dev->ep[UDC_EP0OUT_IX].td->status |=
+ AMD_BIT(UDC_DMA_OUT_STS_L);
+ /* write dma desc address */
+ writel(dev->ep[UDC_EP0OUT_IX].td_stp_dma,
+ &dev->ep[UDC_EP0OUT_IX].regs->subptr);
+ writel(dev->ep[UDC_EP0OUT_IX].td_phys,
+ &dev->ep[UDC_EP0OUT_IX].regs->desptr);
+ /* stop RDE timer */
+ if (timer_pending(&udc_timer)) {
+ set_rde = 0;
+ mod_timer(&udc_timer, jiffies - 1);
+ }
+ /* stop pollstall timer */
+ if (timer_pending(&udc_pollstall_timer))
+ mod_timer(&udc_pollstall_timer, jiffies - 1);
+ /* enable DMA */
+ tmp = readl(&dev->regs->ctl);
+ tmp |= AMD_BIT(UDC_DEVCTL_MODE)
+ | AMD_BIT(UDC_DEVCTL_RDE)
+ | AMD_BIT(UDC_DEVCTL_TDE);
+ if (use_dma_bufferfill_mode)
+ tmp |= AMD_BIT(UDC_DEVCTL_BF);
+ else if (use_dma_ppb_du)
+ tmp |= AMD_BIT(UDC_DEVCTL_DU);
+ writel(tmp, &dev->regs->ctl);
+ }
+
+ /* clear NAK by writing CNAK for EP0IN */
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+ dev->ep[UDC_EP0IN_IX].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX);
+
+ /* clear NAK by writing CNAK for EP0OUT */
+ tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
+ dev->ep[UDC_EP0OUT_IX].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX);
+}
+
+/* Make endpoint 0 ready for control traffic */
+static int setup_ep0(struct udc *dev)
+{
+ activate_control_endpoints(dev);
+ /* enable ep0 interrupts */
+ udc_enable_ep0_interrupts(dev);
+ /* enable device setup interrupts */
+ udc_enable_dev_setup_interrupts(dev);
+
+ return 0;
+}
+
+/* Called by gadget driver to register itself */
+static int amd5536_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
+{
+ struct udc *dev = to_amd5536_udc(g);
+ u32 tmp;
+
+ driver->driver.bus = NULL;
+ dev->driver = driver;
+
+ /* Some gadget drivers use both ep0 directions.
+ * NOTE: to gadget driver, ep0 is just one endpoint...
+ */
+ dev->ep[UDC_EP0OUT_IX].ep.driver_data =
+ dev->ep[UDC_EP0IN_IX].ep.driver_data;
+
+ /* get ready for ep0 traffic */
+ setup_ep0(dev);
+
+ /* clear SD */
+ tmp = readl(&dev->regs->ctl);
+ tmp = tmp & AMD_CLEAR_BIT(UDC_DEVCTL_SD);
+ writel(tmp, &dev->regs->ctl);
+
+ usb_connect(dev);
+
+ return 0;
+}
+
+/* shutdown requests and disconnect from gadget */
+static void
+shutdown(struct udc *dev, struct usb_gadget_driver *driver)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+ int tmp;
+
+ /* empty queues and init hardware */
+ udc_basic_init(dev);
+
+ for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
+ empty_req_queue(&dev->ep[tmp]);
+
+ udc_setup_endpoints(dev);
+}
+
+/* Called by gadget driver to unregister itself */
+static int amd5536_udc_stop(struct usb_gadget *g)
+{
+ struct udc *dev = to_amd5536_udc(g);
+ unsigned long flags;
+ u32 tmp;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ udc_mask_unused_interrupts(dev);
+ shutdown(dev, NULL);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ dev->driver = NULL;
+
+ /* set SD */
+ tmp = readl(&dev->regs->ctl);
+ tmp |= AMD_BIT(UDC_DEVCTL_SD);
+ writel(tmp, &dev->regs->ctl);
+
+ return 0;
+}
+
+/* Clear pending NAK bits */
+static void udc_process_cnak_queue(struct udc *dev)
+{
+ u32 tmp;
+ u32 reg;
+
+ /* check epin's */
+ DBG(dev, "CNAK pending queue processing\n");
+ for (tmp = 0; tmp < UDC_EPIN_NUM_USED; tmp++) {
+ if (cnak_pending & (1 << tmp)) {
+ DBG(dev, "CNAK pending for ep%d\n", tmp);
+ /* clear NAK by writing CNAK */
+ reg = readl(&dev->ep[tmp].regs->ctl);
+ reg |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(reg, &dev->ep[tmp].regs->ctl);
+ dev->ep[tmp].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[tmp], dev->ep[tmp].num);
+ }
+ }
+ /* ... and ep0out */
+ if (cnak_pending & (1 << UDC_EP0OUT_IX)) {
+ DBG(dev, "CNAK pending for ep%d\n", UDC_EP0OUT_IX);
+ /* clear NAK by writing CNAK */
+ reg = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
+ reg |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(reg, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
+ dev->ep[UDC_EP0OUT_IX].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX],
+ dev->ep[UDC_EP0OUT_IX].num);
+ }
+}
+
+/* Enabling RX DMA after setup packet */
+static void udc_ep0_set_rde(struct udc *dev)
+{
+ if (use_dma) {
+ /*
+ * only enable RXDMA when no data endpoint enabled
+ * or data is queued
+ */
+ if (!dev->data_ep_enabled || dev->data_ep_queued) {
+ udc_set_rde(dev);
+ } else {
+ /*
+ * setup timer for enabling RDE (to not enable
+ * RXFIFO DMA for data endpoints to early)
+ */
+ if (set_rde != 0 && !timer_pending(&udc_timer)) {
+ udc_timer.expires =
+ jiffies + HZ/UDC_RDE_TIMER_DIV;
+ set_rde = 1;
+ if (!stop_timer)
+ add_timer(&udc_timer);
+ }
+ }
+ }
+}
+
+
+/* Interrupt handler for data OUT traffic */
+static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix)
+{
+ irqreturn_t ret_val = IRQ_NONE;
+ u32 tmp;
+ struct udc_ep *ep;
+ struct udc_request *req;
+ unsigned int count;
+ struct udc_data_dma *td = NULL;
+ unsigned dma_done;
+
+ VDBG(dev, "ep%d irq\n", ep_ix);
+ ep = &dev->ep[ep_ix];
+
+ tmp = readl(&ep->regs->sts);
+ if (use_dma) {
+ /* BNA event ? */
+ if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
+ DBG(dev, "BNA ep%dout occurred - DESPTR = %x\n",
+ ep->num, readl(&ep->regs->desptr));
+ /* clear BNA */
+ writel(tmp | AMD_BIT(UDC_EPSTS_BNA), &ep->regs->sts);
+ if (!ep->cancel_transfer)
+ ep->bna_occurred = 1;
+ else
+ ep->cancel_transfer = 0;
+ ret_val = IRQ_HANDLED;
+ goto finished;
+ }
+ }
+ /* HE event ? */
+ if (tmp & AMD_BIT(UDC_EPSTS_HE)) {
+ dev_err(&dev->pdev->dev, "HE ep%dout occurred\n", ep->num);
+
+ /* clear HE */
+ writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
+ ret_val = IRQ_HANDLED;
+ goto finished;
+ }
+
+ if (!list_empty(&ep->queue)) {
+
+ /* next request */
+ req = list_entry(ep->queue.next,
+ struct udc_request, queue);
+ } else {
+ req = NULL;
+ udc_rxfifo_pending = 1;
+ }
+ VDBG(dev, "req = %p\n", req);
+ /* fifo mode */
+ if (!use_dma) {
+
+ /* read fifo */
+ if (req && udc_rxfifo_read(ep, req)) {
+ ret_val = IRQ_HANDLED;
+
+ /* finish */
+ complete_req(ep, req, 0);
+ /* next request */
+ if (!list_empty(&ep->queue) && !ep->halted) {
+ req = list_entry(ep->queue.next,
+ struct udc_request, queue);
+ } else
+ req = NULL;
+ }
+
+ /* DMA */
+ } else if (!ep->cancel_transfer && req) {
+ ret_val = IRQ_HANDLED;
+
+ /* check for DMA done */
+ if (!use_dma_ppb) {
+ dma_done = AMD_GETBITS(req->td_data->status,
+ UDC_DMA_OUT_STS_BS);
+ /* packet per buffer mode - rx bytes */
+ } else {
+ /*
+ * if BNA occurred then recover desc. from
+ * BNA dummy desc.
+ */
+ if (ep->bna_occurred) {
+ VDBG(dev, "Recover desc. from BNA dummy\n");
+ memcpy(req->td_data, ep->bna_dummy_req->td_data,
+ sizeof(struct udc_data_dma));
+ ep->bna_occurred = 0;
+ udc_init_bna_dummy(ep->req);
+ }
+ td = udc_get_last_dma_desc(req);
+ dma_done = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_BS);
+ }
+ if (dma_done == UDC_DMA_OUT_STS_BS_DMA_DONE) {
+ /* buffer fill mode - rx bytes */
+ if (!use_dma_ppb) {
+ /* received number bytes */
+ count = AMD_GETBITS(req->td_data->status,
+ UDC_DMA_OUT_STS_RXBYTES);
+ VDBG(dev, "rx bytes=%u\n", count);
+ /* packet per buffer mode - rx bytes */
+ } else {
+ VDBG(dev, "req->td_data=%p\n", req->td_data);
+ VDBG(dev, "last desc = %p\n", td);
+ /* received number bytes */
+ if (use_dma_ppb_du) {
+ /* every desc. counts bytes */
+ count = udc_get_ppbdu_rxbytes(req);
+ } else {
+ /* last desc. counts bytes */
+ count = AMD_GETBITS(td->status,
+ UDC_DMA_OUT_STS_RXBYTES);
+ if (!count && req->req.length
+ == UDC_DMA_MAXPACKET) {
+ /*
+ * on 64k packets the RXBYTES
+ * field is zero
+ */
+ count = UDC_DMA_MAXPACKET;
+ }
+ }
+ VDBG(dev, "last desc rx bytes=%u\n", count);
+ }
+
+ tmp = req->req.length - req->req.actual;
+ if (count > tmp) {
+ if ((tmp % ep->ep.maxpacket) != 0) {
+ DBG(dev, "%s: rx %db, space=%db\n",
+ ep->ep.name, count, tmp);
+ req->req.status = -EOVERFLOW;
+ }
+ count = tmp;
+ }
+ req->req.actual += count;
+ req->dma_going = 0;
+ /* complete request */
+ complete_req(ep, req, 0);
+
+ /* next request */
+ if (!list_empty(&ep->queue) && !ep->halted) {
+ req = list_entry(ep->queue.next,
+ struct udc_request,
+ queue);
+ /*
+ * DMA may be already started by udc_queue()
+ * called by gadget drivers completion
+ * routine. This happens when queue
+ * holds one request only.
+ */
+ if (req->dma_going == 0) {
+ /* next dma */
+ if (prep_dma(ep, req, GFP_ATOMIC) != 0)
+ goto finished;
+ /* write desc pointer */
+ writel(req->td_phys,
+ &ep->regs->desptr);
+ req->dma_going = 1;
+ /* enable DMA */
+ udc_set_rde(dev);
+ }
+ } else {
+ /*
+ * implant BNA dummy descriptor to allow
+ * RXFIFO opening by RDE
+ */
+ if (ep->bna_dummy_req) {
+ /* write desc pointer */
+ writel(ep->bna_dummy_req->td_phys,
+ &ep->regs->desptr);
+ ep->bna_occurred = 0;
+ }
+
+ /*
+ * schedule timer for setting RDE if queue
+ * remains empty to allow ep0 packets pass
+ * through
+ */
+ if (set_rde != 0
+ && !timer_pending(&udc_timer)) {
+ udc_timer.expires =
+ jiffies
+ + HZ*UDC_RDE_TIMER_SECONDS;
+ set_rde = 1;
+ if (!stop_timer)
+ add_timer(&udc_timer);
+ }
+ if (ep->num != UDC_EP0OUT_IX)
+ dev->data_ep_queued = 0;
+ }
+
+ } else {
+ /*
+ * RX DMA must be reenabled for each desc in PPBDU mode
+ * and must be enabled for PPBNDU mode in case of BNA
+ */
+ udc_set_rde(dev);
+ }
+
+ } else if (ep->cancel_transfer) {
+ ret_val = IRQ_HANDLED;
+ ep->cancel_transfer = 0;
+ }
+
+ /* check pending CNAKS */
+ if (cnak_pending) {
+ /* CNAk processing when rxfifo empty only */
+ if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
+ udc_process_cnak_queue(dev);
+ }
+
+ /* clear OUT bits in ep status */
+ writel(UDC_EPSTS_OUT_CLEAR, &ep->regs->sts);
+finished:
+ return ret_val;
+}
+
+/* Interrupt handler for data IN traffic */
+static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
+{
+ irqreturn_t ret_val = IRQ_NONE;
+ u32 tmp;
+ u32 epsts;
+ struct udc_ep *ep;
+ struct udc_request *req;
+ struct udc_data_dma *td;
+ unsigned len;
+
+ ep = &dev->ep[ep_ix];
+
+ epsts = readl(&ep->regs->sts);
+ if (use_dma) {
+ /* BNA ? */
+ if (epsts & AMD_BIT(UDC_EPSTS_BNA)) {
+ dev_err(&dev->pdev->dev,
+ "BNA ep%din occurred - DESPTR = %08lx\n",
+ ep->num,
+ (unsigned long) readl(&ep->regs->desptr));
+
+ /* clear BNA */
+ writel(epsts, &ep->regs->sts);
+ ret_val = IRQ_HANDLED;
+ goto finished;
+ }
+ }
+ /* HE event ? */
+ if (epsts & AMD_BIT(UDC_EPSTS_HE)) {
+ dev_err(&dev->pdev->dev,
+ "HE ep%dn occurred - DESPTR = %08lx\n",
+ ep->num, (unsigned long) readl(&ep->regs->desptr));
+
+ /* clear HE */
+ writel(epsts | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
+ ret_val = IRQ_HANDLED;
+ goto finished;
+ }
+
+ /* DMA completion */
+ if (epsts & AMD_BIT(UDC_EPSTS_TDC)) {
+ VDBG(dev, "TDC set- completion\n");
+ ret_val = IRQ_HANDLED;
+ if (!ep->cancel_transfer && !list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct udc_request, queue);
+ /*
+ * length bytes transferred
+ * check dma done of last desc. in PPBDU mode
+ */
+ if (use_dma_ppb_du) {
+ td = udc_get_last_dma_desc(req);
+ if (td)
+ req->req.actual = req->req.length;
+ } else {
+ /* assume all bytes transferred */
+ req->req.actual = req->req.length;
+ }
+
+ if (req->req.actual == req->req.length) {
+ /* complete req */
+ complete_req(ep, req, 0);
+ req->dma_going = 0;
+ /* further request available ? */
+ if (list_empty(&ep->queue)) {
+ /* disable interrupt */
+ tmp = readl(&dev->regs->ep_irqmsk);
+ tmp |= AMD_BIT(ep->num);
+ writel(tmp, &dev->regs->ep_irqmsk);
+ }
+ }
+ }
+ ep->cancel_transfer = 0;
+
+ }
+ /*
+ * status reg has IN bit set and TDC not set (if TDC was handled,
+ * IN must not be handled (UDC defect) ?
+ */
+ if ((epsts & AMD_BIT(UDC_EPSTS_IN))
+ && !(epsts & AMD_BIT(UDC_EPSTS_TDC))) {
+ ret_val = IRQ_HANDLED;
+ if (!list_empty(&ep->queue)) {
+ /* next request */
+ req = list_entry(ep->queue.next,
+ struct udc_request, queue);
+ /* FIFO mode */
+ if (!use_dma) {
+ /* write fifo */
+ udc_txfifo_write(ep, &req->req);
+ len = req->req.length - req->req.actual;
+ if (len > ep->ep.maxpacket)
+ len = ep->ep.maxpacket;
+ req->req.actual += len;
+ if (req->req.actual == req->req.length
+ || (len != ep->ep.maxpacket)) {
+ /* complete req */
+ complete_req(ep, req, 0);
+ }
+ /* DMA */
+ } else if (req && !req->dma_going) {
+ VDBG(dev, "IN DMA : req=%p req->td_data=%p\n",
+ req, req->td_data);
+ if (req->td_data) {
+
+ req->dma_going = 1;
+
+ /*
+ * unset L bit of first desc.
+ * for chain
+ */
+ if (use_dma_ppb && req->req.length >
+ ep->ep.maxpacket) {
+ req->td_data->status &=
+ AMD_CLEAR_BIT(
+ UDC_DMA_IN_STS_L);
+ }
+
+ /* write desc pointer */
+ writel(req->td_phys, &ep->regs->desptr);
+
+ /* set HOST READY */
+ req->td_data->status =
+ AMD_ADDBITS(
+ req->td_data->status,
+ UDC_DMA_IN_STS_BS_HOST_READY,
+ UDC_DMA_IN_STS_BS);
+
+ /* set poll demand bit */
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_P);
+ writel(tmp, &ep->regs->ctl);
+ }
+ }
+
+ } else if (!use_dma && ep->in) {
+ /* disable interrupt */
+ tmp = readl(
+ &dev->regs->ep_irqmsk);
+ tmp |= AMD_BIT(ep->num);
+ writel(tmp,
+ &dev->regs->ep_irqmsk);
+ }
+ }
+ /* clear status bits */
+ writel(epsts, &ep->regs->sts);
+
+finished:
+ return ret_val;
+
+}
+
+/* Interrupt handler for Control OUT traffic */
+static irqreturn_t udc_control_out_isr(struct udc *dev)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+ irqreturn_t ret_val = IRQ_NONE;
+ u32 tmp;
+ int setup_supported;
+ u32 count;
+ int set = 0;
+ struct udc_ep *ep;
+ struct udc_ep *ep_tmp;
+
+ ep = &dev->ep[UDC_EP0OUT_IX];
+
+ /* clear irq */
+ writel(AMD_BIT(UDC_EPINT_OUT_EP0), &dev->regs->ep_irqsts);
+
+ tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts);
+ /* check BNA and clear if set */
+ if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
+ VDBG(dev, "ep0: BNA set\n");
+ writel(AMD_BIT(UDC_EPSTS_BNA),
+ &dev->ep[UDC_EP0OUT_IX].regs->sts);
+ ep->bna_occurred = 1;
+ ret_val = IRQ_HANDLED;
+ goto finished;
+ }
+
+ /* type of data: SETUP or DATA 0 bytes */
+ tmp = AMD_GETBITS(tmp, UDC_EPSTS_OUT);
+ VDBG(dev, "data_typ = %x\n", tmp);
+
+ /* setup data */
+ if (tmp == UDC_EPSTS_OUT_SETUP) {
+ ret_val = IRQ_HANDLED;
+
+ ep->dev->stall_ep0in = 0;
+ dev->waiting_zlp_ack_ep0in = 0;
+
+ /* set NAK for EP0_IN */
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_SNAK);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+ dev->ep[UDC_EP0IN_IX].naking = 1;
+ /* get setup data */
+ if (use_dma) {
+
+ /* clear OUT bits in ep status */
+ writel(UDC_EPSTS_OUT_CLEAR,
+ &dev->ep[UDC_EP0OUT_IX].regs->sts);
+
+ setup_data.data[0] =
+ dev->ep[UDC_EP0OUT_IX].td_stp->data12;
+ setup_data.data[1] =
+ dev->ep[UDC_EP0OUT_IX].td_stp->data34;
+ /* set HOST READY */
+ dev->ep[UDC_EP0OUT_IX].td_stp->status =
+ UDC_DMA_STP_STS_BS_HOST_READY;
+ } else {
+ /* read fifo */
+ udc_rxfifo_read_dwords(dev, setup_data.data, 2);
+ }
+
+ /* determine direction of control data */
+ if ((setup_data.request.bRequestType & USB_DIR_IN) != 0) {
+ dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
+ /* enable RDE */
+ udc_ep0_set_rde(dev);
+ set = 0;
+ } else {
+ dev->gadget.ep0 = &dev->ep[UDC_EP0OUT_IX].ep;
+ /*
+ * implant BNA dummy descriptor to allow RXFIFO opening
+ * by RDE
+ */
+ if (ep->bna_dummy_req) {
+ /* write desc pointer */
+ writel(ep->bna_dummy_req->td_phys,
+ &dev->ep[UDC_EP0OUT_IX].regs->desptr);
+ ep->bna_occurred = 0;
+ }
+
+ set = 1;
+ dev->ep[UDC_EP0OUT_IX].naking = 1;
+ /*
+ * setup timer for enabling RDE (to not enable
+ * RXFIFO DMA for data to early)
+ */
+ set_rde = 1;
+ if (!timer_pending(&udc_timer)) {
+ udc_timer.expires = jiffies +
+ HZ/UDC_RDE_TIMER_DIV;
+ if (!stop_timer)
+ add_timer(&udc_timer);
+ }
+ }
+
+ /*
+ * mass storage reset must be processed here because
+ * next packet may be a CLEAR_FEATURE HALT which would not
+ * clear the stall bit when no STALL handshake was received
+ * before (autostall can cause this)
+ */
+ if (setup_data.data[0] == UDC_MSCRES_DWORD0
+ && setup_data.data[1] == UDC_MSCRES_DWORD1) {
+ DBG(dev, "MSC Reset\n");
+ /*
+ * clear stall bits
+ * only one IN and OUT endpoints are handled
+ */
+ ep_tmp = &udc->ep[UDC_EPIN_IX];
+ udc_set_halt(&ep_tmp->ep, 0);
+ ep_tmp = &udc->ep[UDC_EPOUT_IX];
+ udc_set_halt(&ep_tmp->ep, 0);
+ }
+
+ /* call gadget with setup data received */
+ spin_unlock(&dev->lock);
+ setup_supported = dev->driver->setup(&dev->gadget,
+ &setup_data.request);
+ spin_lock(&dev->lock);
+
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+ /* ep0 in returns data (not zlp) on IN phase */
+ if (setup_supported >= 0 && setup_supported <
+ UDC_EP0IN_MAXPACKET) {
+ /* clear NAK by writing CNAK in EP0_IN */
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+ dev->ep[UDC_EP0IN_IX].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX);
+
+ /* if unsupported request then stall */
+ } else if (setup_supported < 0) {
+ tmp |= AMD_BIT(UDC_EPCTL_S);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+ } else
+ dev->waiting_zlp_ack_ep0in = 1;
+
+
+ /* clear NAK by writing CNAK in EP0_OUT */
+ if (!set) {
+ tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
+ dev->ep[UDC_EP0OUT_IX].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX);
+ }
+
+ if (!use_dma) {
+ /* clear OUT bits in ep status */
+ writel(UDC_EPSTS_OUT_CLEAR,
+ &dev->ep[UDC_EP0OUT_IX].regs->sts);
+ }
+
+ /* data packet 0 bytes */
+ } else if (tmp == UDC_EPSTS_OUT_DATA) {
+ /* clear OUT bits in ep status */
+ writel(UDC_EPSTS_OUT_CLEAR, &dev->ep[UDC_EP0OUT_IX].regs->sts);
+
+ /* get setup data: only 0 packet */
+ if (use_dma) {
+ /* no req if 0 packet, just reactivate */
+ if (list_empty(&dev->ep[UDC_EP0OUT_IX].queue)) {
+ VDBG(dev, "ZLP\n");
+
+ /* set HOST READY */
+ dev->ep[UDC_EP0OUT_IX].td->status =
+ AMD_ADDBITS(
+ dev->ep[UDC_EP0OUT_IX].td->status,
+ UDC_DMA_OUT_STS_BS_HOST_READY,
+ UDC_DMA_OUT_STS_BS);
+ /* enable RDE */
+ udc_ep0_set_rde(dev);
+ ret_val = IRQ_HANDLED;
+
+ } else {
+ /* control write */
+ ret_val |= udc_data_out_isr(dev, UDC_EP0OUT_IX);
+ /* re-program desc. pointer for possible ZLPs */
+ writel(dev->ep[UDC_EP0OUT_IX].td_phys,
+ &dev->ep[UDC_EP0OUT_IX].regs->desptr);
+ /* enable RDE */
+ udc_ep0_set_rde(dev);
+ }
+ } else {
+
+ /* received number bytes */
+ count = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts);
+ count = AMD_GETBITS(count, UDC_EPSTS_RX_PKT_SIZE);
+ /* out data for fifo mode not working */
+ count = 0;
+
+ /* 0 packet or real data ? */
+ if (count != 0) {
+ ret_val |= udc_data_out_isr(dev, UDC_EP0OUT_IX);
+ } else {
+ /* dummy read confirm */
+ readl(&dev->ep[UDC_EP0OUT_IX].regs->confirm);
+ ret_val = IRQ_HANDLED;
+ }
+ }
+ }
+
+ /* check pending CNAKS */
+ if (cnak_pending) {
+ /* CNAk processing when rxfifo empty only */
+ if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
+ udc_process_cnak_queue(dev);
+ }
+
+finished:
+ return ret_val;
+}
+
+/* Interrupt handler for Control IN traffic */
+static irqreturn_t udc_control_in_isr(struct udc *dev)
+{
+ irqreturn_t ret_val = IRQ_NONE;
+ u32 tmp;
+ struct udc_ep *ep;
+ struct udc_request *req;
+ unsigned len;
+
+ ep = &dev->ep[UDC_EP0IN_IX];
+
+ /* clear irq */
+ writel(AMD_BIT(UDC_EPINT_IN_EP0), &dev->regs->ep_irqsts);
+
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->sts);
+ /* DMA completion */
+ if (tmp & AMD_BIT(UDC_EPSTS_TDC)) {
+ VDBG(dev, "isr: TDC clear\n");
+ ret_val = IRQ_HANDLED;
+
+ /* clear TDC bit */
+ writel(AMD_BIT(UDC_EPSTS_TDC),
+ &dev->ep[UDC_EP0IN_IX].regs->sts);
+
+ /* status reg has IN bit set ? */
+ } else if (tmp & AMD_BIT(UDC_EPSTS_IN)) {
+ ret_val = IRQ_HANDLED;
+
+ if (ep->dma) {
+ /* clear IN bit */
+ writel(AMD_BIT(UDC_EPSTS_IN),
+ &dev->ep[UDC_EP0IN_IX].regs->sts);
+ }
+ if (dev->stall_ep0in) {
+ DBG(dev, "stall ep0in\n");
+ /* halt ep0in */
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_S);
+ writel(tmp, &ep->regs->ctl);
+ } else {
+ if (!list_empty(&ep->queue)) {
+ /* next request */
+ req = list_entry(ep->queue.next,
+ struct udc_request, queue);
+
+ if (ep->dma) {
+ /* write desc pointer */
+ writel(req->td_phys, &ep->regs->desptr);
+ /* set HOST READY */
+ req->td_data->status =
+ AMD_ADDBITS(
+ req->td_data->status,
+ UDC_DMA_STP_STS_BS_HOST_READY,
+ UDC_DMA_STP_STS_BS);
+
+ /* set poll demand bit */
+ tmp =
+ readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_P);
+ writel(tmp,
+ &dev->ep[UDC_EP0IN_IX].regs->ctl);
+
+ /* all bytes will be transferred */
+ req->req.actual = req->req.length;
+
+ /* complete req */
+ complete_req(ep, req, 0);
+
+ } else {
+ /* write fifo */
+ udc_txfifo_write(ep, &req->req);
+
+ /* lengh bytes transferred */
+ len = req->req.length - req->req.actual;
+ if (len > ep->ep.maxpacket)
+ len = ep->ep.maxpacket;
+
+ req->req.actual += len;
+ if (req->req.actual == req->req.length
+ || (len != ep->ep.maxpacket)) {
+ /* complete req */
+ complete_req(ep, req, 0);
+ }
+ }
+
+ }
+ }
+ ep->halted = 0;
+ dev->stall_ep0in = 0;
+ if (!ep->dma) {
+ /* clear IN bit */
+ writel(AMD_BIT(UDC_EPSTS_IN),
+ &dev->ep[UDC_EP0IN_IX].regs->sts);
+ }
+ }
+
+ return ret_val;
+}
+
+
+/* Interrupt handler for global device events */
+static irqreturn_t udc_dev_isr(struct udc *dev, u32 dev_irq)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+ irqreturn_t ret_val = IRQ_NONE;
+ u32 tmp;
+ u32 cfg;
+ struct udc_ep *ep;
+ u16 i;
+ u8 udc_csr_epix;
+
+ /* SET_CONFIG irq ? */
+ if (dev_irq & AMD_BIT(UDC_DEVINT_SC)) {
+ ret_val = IRQ_HANDLED;
+
+ /* read config value */
+ tmp = readl(&dev->regs->sts);
+ cfg = AMD_GETBITS(tmp, UDC_DEVSTS_CFG);
+ DBG(dev, "SET_CONFIG interrupt: config=%d\n", cfg);
+ dev->cur_config = cfg;
+ dev->set_cfg_not_acked = 1;
+
+ /* make usb request for gadget driver */
+ memset(&setup_data, 0 , sizeof(union udc_setup_data));
+ setup_data.request.bRequest = USB_REQ_SET_CONFIGURATION;
+ setup_data.request.wValue = cpu_to_le16(dev->cur_config);
+
+ /* programm the NE registers */
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ ep = &dev->ep[i];
+ if (ep->in) {
+
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num;
+
+
+ /* OUT ep */
+ } else {
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+ }
+
+ tmp = readl(&dev->csr->ne[udc_csr_epix]);
+ /* ep cfg */
+ tmp = AMD_ADDBITS(tmp, ep->dev->cur_config,
+ UDC_CSR_NE_CFG);
+ /* write reg */
+ writel(tmp, &dev->csr->ne[udc_csr_epix]);
+
+ /* clear stall bits */
+ ep->halted = 0;
+ tmp = readl(&ep->regs->ctl);
+ tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
+ writel(tmp, &ep->regs->ctl);
+ }
+ /* call gadget zero with setup data received */
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &setup_data.request);
+ spin_lock(&dev->lock);
+
+ } /* SET_INTERFACE ? */
+ if (dev_irq & AMD_BIT(UDC_DEVINT_SI)) {
+ ret_val = IRQ_HANDLED;
+
+ dev->set_cfg_not_acked = 1;
+ /* read interface and alt setting values */
+ tmp = readl(&dev->regs->sts);
+ dev->cur_alt = AMD_GETBITS(tmp, UDC_DEVSTS_ALT);
+ dev->cur_intf = AMD_GETBITS(tmp, UDC_DEVSTS_INTF);
+
+ /* make usb request for gadget driver */
+ memset(&setup_data, 0 , sizeof(union udc_setup_data));
+ setup_data.request.bRequest = USB_REQ_SET_INTERFACE;
+ setup_data.request.bRequestType = USB_RECIP_INTERFACE;
+ setup_data.request.wValue = cpu_to_le16(dev->cur_alt);
+ setup_data.request.wIndex = cpu_to_le16(dev->cur_intf);
+
+ DBG(dev, "SET_INTERFACE interrupt: alt=%d intf=%d\n",
+ dev->cur_alt, dev->cur_intf);
+
+ /* programm the NE registers */
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ ep = &dev->ep[i];
+ if (ep->in) {
+
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num;
+
+
+ /* OUT ep */
+ } else {
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+ }
+
+ /* UDC CSR reg */
+ /* set ep values */
+ tmp = readl(&dev->csr->ne[udc_csr_epix]);
+ /* ep interface */
+ tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf,
+ UDC_CSR_NE_INTF);
+ /* tmp = AMD_ADDBITS(tmp, 2, UDC_CSR_NE_INTF); */
+ /* ep alt */
+ tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt,
+ UDC_CSR_NE_ALT);
+ /* write reg */
+ writel(tmp, &dev->csr->ne[udc_csr_epix]);
+
+ /* clear stall bits */
+ ep->halted = 0;
+ tmp = readl(&ep->regs->ctl);
+ tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
+ writel(tmp, &ep->regs->ctl);
+ }
+
+ /* call gadget zero with setup data received */
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &setup_data.request);
+ spin_lock(&dev->lock);
+
+ } /* USB reset */
+ if (dev_irq & AMD_BIT(UDC_DEVINT_UR)) {
+ DBG(dev, "USB Reset interrupt\n");
+ ret_val = IRQ_HANDLED;
+
+ /* allow soft reset when suspend occurs */
+ soft_reset_occured = 0;
+
+ dev->waiting_zlp_ack_ep0in = 0;
+ dev->set_cfg_not_acked = 0;
+
+ /* mask not needed interrupts */
+ udc_mask_unused_interrupts(dev);
+
+ /* call gadget to resume and reset configs etc. */
+ spin_unlock(&dev->lock);
+ if (dev->sys_suspended && dev->driver->resume) {
+ dev->driver->resume(&dev->gadget);
+ dev->sys_suspended = 0;
+ }
+ usb_gadget_udc_reset(&dev->gadget, dev->driver);
+ spin_lock(&dev->lock);
+
+ /* disable ep0 to empty req queue */
+ empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
+ ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]);
+
+ /* soft reset when rxfifo not empty */
+ tmp = readl(&dev->regs->sts);
+ if (!(tmp & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
+ && !soft_reset_after_usbreset_occured) {
+ udc_soft_reset(dev);
+ soft_reset_after_usbreset_occured++;
+ }
+
+ /*
+ * DMA reset to kill potential old DMA hw hang,
+ * POLL bit is already reset by ep_init() through
+ * disconnect()
+ */
+ DBG(dev, "DMA machine reset\n");
+ tmp = readl(&dev->regs->cfg);
+ writel(tmp | AMD_BIT(UDC_DEVCFG_DMARST), &dev->regs->cfg);
+ writel(tmp, &dev->regs->cfg);
+
+ /* put into initial config */
+ udc_basic_init(dev);
+
+ /* enable device setup interrupts */
+ udc_enable_dev_setup_interrupts(dev);
+
+ /* enable suspend interrupt */
+ tmp = readl(&dev->regs->irqmsk);
+ tmp &= AMD_UNMASK_BIT(UDC_DEVINT_US);
+ writel(tmp, &dev->regs->irqmsk);
+
+ } /* USB suspend */
+ if (dev_irq & AMD_BIT(UDC_DEVINT_US)) {
+ DBG(dev, "USB Suspend interrupt\n");
+ ret_val = IRQ_HANDLED;
+ if (dev->driver->suspend) {
+ spin_unlock(&dev->lock);
+ dev->sys_suspended = 1;
+ dev->driver->suspend(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
+ } /* new speed ? */
+ if (dev_irq & AMD_BIT(UDC_DEVINT_ENUM)) {
+ DBG(dev, "ENUM interrupt\n");
+ ret_val = IRQ_HANDLED;
+ soft_reset_after_usbreset_occured = 0;
+
+ /* disable ep0 to empty req queue */
+ empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
+ ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]);
+
+ /* link up all endpoints */
+ udc_setup_endpoints(dev);
+ dev_info(&dev->pdev->dev, "Connect: %s\n",
+ usb_speed_string(dev->gadget.speed));
+
+ /* init ep 0 */
+ activate_control_endpoints(dev);
+
+ /* enable ep0 interrupts */
+ udc_enable_ep0_interrupts(dev);
+ }
+ /* session valid change interrupt */
+ if (dev_irq & AMD_BIT(UDC_DEVINT_SVC)) {
+ DBG(dev, "USB SVC interrupt\n");
+ ret_val = IRQ_HANDLED;
+
+ /* check that session is not valid to detect disconnect */
+ tmp = readl(&dev->regs->sts);
+ if (!(tmp & AMD_BIT(UDC_DEVSTS_SESSVLD))) {
+ /* disable suspend interrupt */
+ tmp = readl(&dev->regs->irqmsk);
+ tmp |= AMD_BIT(UDC_DEVINT_US);
+ writel(tmp, &dev->regs->irqmsk);
+ DBG(dev, "USB Disconnect (session valid low)\n");
+ /* cleanup on disconnect */
+ usb_disconnect(udc);
+ }
+
+ }
+
+ return ret_val;
+}
+
+/* Interrupt Service Routine, see Linux Kernel Doc for parameters */
+irqreturn_t udc_irq(int irq, void *pdev)
+{
+ struct udc *dev = pdev;
+ u32 reg;
+ u16 i;
+ u32 ep_irq;
+ irqreturn_t ret_val = IRQ_NONE;
+
+ spin_lock(&dev->lock);
+
+ /* check for ep irq */
+ reg = readl(&dev->regs->ep_irqsts);
+ if (reg) {
+ if (reg & AMD_BIT(UDC_EPINT_OUT_EP0))
+ ret_val |= udc_control_out_isr(dev);
+ if (reg & AMD_BIT(UDC_EPINT_IN_EP0))
+ ret_val |= udc_control_in_isr(dev);
+
+ /*
+ * data endpoint
+ * iterate ep's
+ */
+ for (i = 1; i < UDC_EP_NUM; i++) {
+ ep_irq = 1 << i;
+ if (!(reg & ep_irq) || i == UDC_EPINT_OUT_EP0)
+ continue;
+
+ /* clear irq status */
+ writel(ep_irq, &dev->regs->ep_irqsts);
+
+ /* irq for out ep ? */
+ if (i > UDC_EPIN_NUM)
+ ret_val |= udc_data_out_isr(dev, i);
+ else
+ ret_val |= udc_data_in_isr(dev, i);
+ }
+
+ }
+
+
+ /* check for dev irq */
+ reg = readl(&dev->regs->irqsts);
+ if (reg) {
+ /* clear irq */
+ writel(reg, &dev->regs->irqsts);
+ ret_val |= udc_dev_isr(dev, reg);
+ }
+
+
+ spin_unlock(&dev->lock);
+ return ret_val;
+}
+EXPORT_SYMBOL_GPL(udc_irq);
+
+/* Tears down device */
+void gadget_release(struct device *pdev)
+{
+ struct amd5536udc *dev = dev_get_drvdata(pdev);
+ kfree(dev);
+}
+EXPORT_SYMBOL_GPL(gadget_release);
+
+/* Cleanup on device remove */
+void udc_remove(struct udc *dev)
+{
+ /* remove timer */
+ stop_timer++;
+ if (timer_pending(&udc_timer))
+ wait_for_completion(&on_exit);
+ if (udc_timer.data)
+ del_timer_sync(&udc_timer);
+ /* remove pollstall timer */
+ stop_pollstall_timer++;
+ if (timer_pending(&udc_pollstall_timer))
+ wait_for_completion(&on_pollstall_exit);
+ if (udc_pollstall_timer.data)
+ del_timer_sync(&udc_pollstall_timer);
+ udc = NULL;
+}
+EXPORT_SYMBOL_GPL(udc_remove);
+
+/* free all the dma pools */
+void free_dma_pools(struct udc *dev)
+{
+ dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td,
+ dev->ep[UDC_EP0OUT_IX].td_phys);
+ dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td_stp,
+ dev->ep[UDC_EP0OUT_IX].td_stp_dma);
+ dma_pool_destroy(dev->stp_requests);
+ dma_pool_destroy(dev->data_requests);
+}
+EXPORT_SYMBOL_GPL(free_dma_pools);
+
+/* create dma pools on init */
+int init_dma_pools(struct udc *dev)
+{
+ struct udc_stp_dma *td_stp;
+ struct udc_data_dma *td_data;
+ int retval;
+
+ /* consistent DMA mode setting ? */
+ if (use_dma_ppb) {
+ use_dma_bufferfill_mode = 0;
+ } else {
+ use_dma_ppb_du = 0;
+ use_dma_bufferfill_mode = 1;
+ }
+
+ /* DMA setup */
+ dev->data_requests = dma_pool_create("data_requests", NULL,
+ sizeof(struct udc_data_dma), 0, 0);
+ if (!dev->data_requests) {
+ DBG(dev, "can't get request data pool\n");
+ return -ENOMEM;
+ }
+
+ /* EP0 in dma regs = dev control regs */
+ dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl;
+
+ /* dma desc for setup data */
+ dev->stp_requests = dma_pool_create("setup requests", NULL,
+ sizeof(struct udc_stp_dma), 0, 0);
+ if (!dev->stp_requests) {
+ DBG(dev, "can't get stp request pool\n");
+ retval = -ENOMEM;
+ goto err_create_dma_pool;
+ }
+ /* setup */
+ td_stp = dma_pool_alloc(dev->stp_requests, GFP_KERNEL,
+ &dev->ep[UDC_EP0OUT_IX].td_stp_dma);
+ if (!td_stp) {
+ retval = -ENOMEM;
+ goto err_alloc_dma;
+ }
+ dev->ep[UDC_EP0OUT_IX].td_stp = td_stp;
+
+ /* data: 0 packets !? */
+ td_data = dma_pool_alloc(dev->stp_requests, GFP_KERNEL,
+ &dev->ep[UDC_EP0OUT_IX].td_phys);
+ if (!td_data) {
+ retval = -ENOMEM;
+ goto err_alloc_phys;
+ }
+ dev->ep[UDC_EP0OUT_IX].td = td_data;
+ return 0;
+
+err_alloc_phys:
+ dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td_stp,
+ dev->ep[UDC_EP0OUT_IX].td_stp_dma);
+err_alloc_dma:
+ dma_pool_destroy(dev->stp_requests);
+ dev->stp_requests = NULL;
+err_create_dma_pool:
+ dma_pool_destroy(dev->data_requests);
+ dev->data_requests = NULL;
+ return retval;
+}
+EXPORT_SYMBOL_GPL(init_dma_pools);
+
+/* general probe */
+int udc_probe(struct udc *dev)
+{
+ char tmp[128];
+ u32 reg;
+ int retval;
+
+ /* mark timer as not initialized */
+ udc_timer.data = 0;
+ udc_pollstall_timer.data = 0;
+
+ /* device struct setup */
+ dev->gadget.ops = &udc_ops;
+
+ dev_set_name(&dev->gadget.dev, "gadget");
+ dev->gadget.name = name;
+ dev->gadget.max_speed = USB_SPEED_HIGH;
+
+ /* init registers, interrupts, ... */
+ startup_registers(dev);
+
+ dev_info(&dev->pdev->dev, "%s\n", mod_desc);
+
+ snprintf(tmp, sizeof(tmp), "%d", dev->irq);
+ dev_info(&dev->pdev->dev,
+ "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
+ tmp, dev->phys_addr, dev->chiprev,
+ (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1");
+ strcpy(tmp, UDC_DRIVER_VERSION_STRING);
+ if (dev->chiprev == UDC_HSA0_REV) {
+ dev_err(&dev->pdev->dev, "chip revision is A0; too old\n");
+ retval = -ENODEV;
+ goto finished;
+ }
+ dev_info(&dev->pdev->dev,
+ "driver version: %s(for Geode5536 B1)\n", tmp);
+ udc = dev;
+
+ retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget,
+ gadget_release);
+ if (retval)
+ goto finished;
+
+ /* timer init */
+ init_timer(&udc_timer);
+ udc_timer.function = udc_timer_function;
+ udc_timer.data = 1;
+ /* timer pollstall init */
+ init_timer(&udc_pollstall_timer);
+ udc_pollstall_timer.function = udc_pollstall_timer_function;
+ udc_pollstall_timer.data = 1;
+
+ /* set SD */
+ reg = readl(&dev->regs->ctl);
+ reg |= AMD_BIT(UDC_DEVCTL_SD);
+ writel(reg, &dev->regs->ctl);
+
+ /* print dev register info */
+ print_regs(dev);
+
+ return 0;
+
+finished:
+ return retval;
+}
+EXPORT_SYMBOL_GPL(udc_probe);
+
+MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
+MODULE_AUTHOR("Thomas Dahlmann");
+MODULE_LICENSE("GPL");
--
2.1.0
^ permalink raw reply related
* [PATCH v3 1/7] UDC: Split the driver into amd (pci) and Synopsys core IP driver
From: Raviteja Garimella @ 2017-03-28 11:02 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree, linux-kernel, bcm-kernel-feedback-list, linux-usb
In-Reply-To: <1490698929-10915-1-git-send-email-raviteja.garimella@broadcom.com>
This patch splits the amd5536udc driver into two -- one that does
pci device registration and the other file that does the rest of
the driver tasks like the gadget/ep ops etc for Synopsys UDC.
This way of splitting helps in exporting core driver symbols which
can be used by any other platform/pci driver that is written for
the same Synopsys USB device controller.
The current patch also includes a change in the Kconfig and Makefile.
A new config option USB_SNP_CORE will be selected automatically when
any one of the platform or pci driver for the same UDC is selected.
Main changes:
- amd5536udc_pci.c: PCI device registration is moved to this file.
- amd5536udc.c:
This file does rest of the core UDC fucntionality.
9 symbols are exported so as to be used by amd5536udc_pci.c.
Module parameter definitions are moved to header file.
- amd5536udc.h:
Function declarations, module parameters definitions and few common
header file includes are added to this file
- Kconfig:
New USB_SNP_CORE option is added which will be auto selected when
any pci or platform driver config option for the UDC is chosen.
- Makefile:
Compiles the core and pci files separately.
Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
---
drivers/usb/gadget/udc/Kconfig | 18 +++
drivers/usb/gadget/udc/Makefile | 3 +-
drivers/usb/gadget/udc/amd5536udc.c | 238 ++++----------------------------
drivers/usb/gadget/udc/amd5536udc.h | 36 +++++
drivers/usb/gadget/udc/amd5536udc_pci.c | 217 +++++++++++++++++++++++++++++
5 files changed, 297 insertions(+), 215 deletions(-)
create mode 100644 drivers/usb/gadget/udc/amd5536udc_pci.c
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 4b69f28..2cee076 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -253,6 +253,20 @@ config USB_MV_U3D
MARVELL PXA2128 Processor series include a super speed USB3.0 device
controller, which support super speed USB peripheral.
+config USB_SNP_CORE
+ depends on USB_AMD5536UDC
+ tristate
+ help
+ This enables core driver support for Synopsys USB 2.0 Device
+ controller.
+
+ This will be enabled when PCI or Platform driver for this UDC is
+ selected. Currently, this will be enabled by USB_SNP_UDC_PLAT or
+ USB_AMD5536UDC options.
+
+ This IP is different to the High Speed OTG IP that can be enabled
+ by selecting USB_DWC2 or USB_DWC3 options.
+
#
# Controllers available in both integrated and discrete versions
#
@@ -278,6 +292,7 @@ source "drivers/usb/gadget/udc/bdc/Kconfig"
config USB_AMD5536UDC
tristate "AMD5536 UDC"
depends on PCI
+ select USB_SNP_CORE
help
The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
It is a USB Highspeed DMA capable USB device controller. Beside ep0
@@ -285,6 +300,9 @@ config USB_AMD5536UDC
The UDC port supports OTG operation, and may be used as a host port
if it's not being used to implement peripheral or OTG roles.
+ This UDC is based on Synopsys USB device controller IP and selects
+ CONFIG_USB_SNP_CORE option to build the core driver.
+
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "amd5536udc" and force all
gadget drivers to also be dynamically linked.
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index 98e74ed..626e1f1 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -10,7 +10,8 @@ obj-$(CONFIG_USB_GADGET) += udc-core.o
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2272) += net2272.o
obj-$(CONFIG_USB_NET2280) += net2280.o
-obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
+obj-$(CONFIG_USB_SNP_CORE) += amd5536udc.o
+obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc_pci.o
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
diff --git a/drivers/usb/gadget/udc/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c
index ea03ca7..72f3c8f 100644
--- a/drivers/usb/gadget/udc/amd5536udc.c
+++ b/drivers/usb/gadget/udc/amd5536udc.c
@@ -11,27 +11,15 @@
*/
/*
- * The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536.
- * It is a USB Highspeed DMA capable USB device controller. Beside ep0 it
- * provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
- *
- * Make sure that UDC is assigned to port 4 by BIOS settings (port can also
- * be used as host port) and UOC bits PAD_EN and APU are set (should be done
- * by BIOS init).
- *
- * UDC DMA requires 32-bit aligned buffers so DMA with gadget ether does not
- * work without updating NET_IP_ALIGN. Or PIO mode (module param "use_dma=0")
- * can be used with gadget ether.
+ * This file does the core driver implementation for the UDC that is based
+ * on Synopsys device controller IP (different than HS OTG IP) that is either
+ * connected through PCI bus or integrated to SoC platforms.
*/
-/* debug control */
-/* #define UDC_VERBOSE */
-
/* Driver strings */
-#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller"
+#define UDC_MOD_DESCRIPTION "Synopsys USB Device Controller"
#define UDC_DRIVER_VERSION_STRING "01.00.0206"
-/* system */
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
@@ -46,23 +34,12 @@
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/dmapool.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/irq.h>
#include <linux/prefetch.h>
-
+#include <linux/moduleparam.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
-
-/* gadget stack */
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-
-/* udc specific */
#include "amd5536udc.h"
-
static void udc_tasklet_disconnect(unsigned long);
static void empty_req_queue(struct udc_ep *);
static void udc_setup_endpoints(struct udc *dev);
@@ -72,7 +49,7 @@ static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq);
/* description */
static const char mod_desc[] = UDC_MOD_DESCRIPTION;
-static const char name[] = "amd5536udc";
+static const char name[] = "udc";
/* structure to hold endpoint function pointers */
static const struct usb_ep_ops udc_ep_ops;
@@ -208,30 +185,11 @@ static const struct {
#undef EP_INFO
};
-/* DMA usage flag */
-static bool use_dma = 1;
-/* packet per buffer dma */
-static bool use_dma_ppb = 1;
-/* with per descr. update */
-static bool use_dma_ppb_du;
/* buffer fill mode */
static int use_dma_bufferfill_mode;
-/* full speed only mode */
-static bool use_fullspeed;
/* tx buffer size for high speed */
static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE;
-/* module parameters */
-module_param(use_dma, bool, S_IRUGO);
-MODULE_PARM_DESC(use_dma, "true for DMA");
-module_param(use_dma_ppb, bool, S_IRUGO);
-MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");
-module_param(use_dma_ppb_du, bool, S_IRUGO);
-MODULE_PARM_DESC(use_dma_ppb_du,
- "true for DMA in packet per buffer mode with descriptor update");
-module_param(use_fullspeed, bool, S_IRUGO);
-MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
-
/*---------------------------------------------------------------------------*/
/* Prints UDC device registers and endpoint irq registers */
static void print_regs(struct udc *dev)
@@ -267,7 +225,7 @@ static void print_regs(struct udc *dev)
}
/* Masks unused interrupts */
-static int udc_mask_unused_interrupts(struct udc *dev)
+int udc_mask_unused_interrupts(struct udc *dev)
{
u32 tmp;
@@ -287,6 +245,7 @@ static int udc_mask_unused_interrupts(struct udc *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(udc_mask_unused_interrupts);
/* Enables endpoint 0 interrupts */
static int udc_enable_ep0_interrupts(struct udc *dev)
@@ -306,7 +265,7 @@ static int udc_enable_ep0_interrupts(struct udc *dev)
}
/* Enables device interrupts for SET_INTF and SET_CONFIG */
-static int udc_enable_dev_setup_interrupts(struct udc *dev)
+int udc_enable_dev_setup_interrupts(struct udc *dev)
{
u32 tmp;
@@ -325,6 +284,7 @@ static int udc_enable_dev_setup_interrupts(struct udc *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(udc_enable_dev_setup_interrupts);
/* Calculates fifo start of endpoint based on preceding endpoints */
static int udc_set_txfifo_addr(struct udc_ep *ep)
@@ -1507,7 +1467,7 @@ static void make_ep_lists(struct udc *dev)
}
/* Inits UDC context */
-static void udc_basic_init(struct udc *dev)
+void udc_basic_init(struct udc *dev)
{
u32 tmp;
@@ -1543,6 +1503,7 @@ static void udc_basic_init(struct udc *dev)
dev->data_ep_enabled = 0;
dev->data_ep_queued = 0;
}
+EXPORT_SYMBOL_GPL(udc_basic_init);
/* init registers at driver load time */
static int startup_registers(struct udc *dev)
@@ -3031,7 +2992,7 @@ __acquires(dev->lock)
}
/* Interrupt Service Routine, see Linux Kernel Doc for parameters */
-static irqreturn_t udc_irq(int irq, void *pdev)
+irqreturn_t udc_irq(int irq, void *pdev)
{
struct udc *dev = pdev;
u32 reg;
@@ -3083,16 +3044,18 @@ static irqreturn_t udc_irq(int irq, void *pdev)
spin_unlock(&dev->lock);
return ret_val;
}
+EXPORT_SYMBOL_GPL(udc_irq);
/* Tears down device */
-static void gadget_release(struct device *pdev)
+void gadget_release(struct device *pdev)
{
struct amd5536udc *dev = dev_get_drvdata(pdev);
kfree(dev);
}
+EXPORT_SYMBOL_GPL(gadget_release);
/* Cleanup on device remove */
-static void udc_remove(struct udc *dev)
+void udc_remove(struct udc *dev)
{
/* remove timer */
stop_timer++;
@@ -3108,9 +3071,10 @@ static void udc_remove(struct udc *dev)
del_timer_sync(&udc_pollstall_timer);
udc = NULL;
}
+EXPORT_SYMBOL_GPL(udc_remove);
/* free all the dma pools */
-static void free_dma_pools(struct udc *dev)
+void free_dma_pools(struct udc *dev)
{
dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td,
dev->ep[UDC_EP0OUT_IX].td_phys);
@@ -3119,35 +3083,10 @@ static void free_dma_pools(struct udc *dev)
dma_pool_destroy(dev->stp_requests);
dma_pool_destroy(dev->data_requests);
}
-
-/* Reset all pci context */
-static void udc_pci_remove(struct pci_dev *pdev)
-{
- struct udc *dev;
-
- dev = pci_get_drvdata(pdev);
-
- usb_del_gadget_udc(&udc->gadget);
- /* gadget driver must not be registered */
- if (WARN_ON(dev->driver))
- return;
-
- /* dma pool cleanup */
- free_dma_pools(dev);
-
- /* reset controller */
- writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
- free_irq(pdev->irq, dev);
- iounmap(dev->virt_addr);
- release_mem_region(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
- pci_disable_device(pdev);
-
- udc_remove(dev);
-}
+EXPORT_SYMBOL_GPL(free_dma_pools);
/* create dma pools on init */
-static int init_dma_pools(struct udc *dev)
+int init_dma_pools(struct udc *dev)
{
struct udc_stp_dma *td_stp;
struct udc_data_dma *td_data;
@@ -3210,9 +3149,10 @@ static int init_dma_pools(struct udc *dev)
dev->data_requests = NULL;
return retval;
}
+EXPORT_SYMBOL_GPL(init_dma_pools);
/* general probe */
-static int udc_probe(struct udc *dev)
+int udc_probe(struct udc *dev)
{
char tmp[128];
u32 reg;
@@ -3276,137 +3216,7 @@ static int udc_probe(struct udc *dev)
finished:
return retval;
}
-
-/* Called by pci bus driver to init pci context */
-static int udc_pci_probe(
- struct pci_dev *pdev,
- const struct pci_device_id *id
-)
-{
- struct udc *dev;
- unsigned long resource;
- unsigned long len;
- int retval = 0;
-
- /* one udc only */
- if (udc) {
- dev_dbg(&pdev->dev, "already probed\n");
- return -EBUSY;
- }
-
- /* init */
- dev = kzalloc(sizeof(struct udc), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- /* pci setup */
- if (pci_enable_device(pdev) < 0) {
- retval = -ENODEV;
- goto err_pcidev;
- }
-
- /* PCI resource allocation */
- resource = pci_resource_start(pdev, 0);
- len = pci_resource_len(pdev, 0);
-
- if (!request_mem_region(resource, len, name)) {
- dev_dbg(&pdev->dev, "pci device used already\n");
- retval = -EBUSY;
- goto err_memreg;
- }
-
- dev->virt_addr = ioremap_nocache(resource, len);
- if (!dev->virt_addr) {
- dev_dbg(&pdev->dev, "start address cannot be mapped\n");
- retval = -EFAULT;
- goto err_ioremap;
- }
-
- if (!pdev->irq) {
- dev_err(&pdev->dev, "irq not set\n");
- retval = -ENODEV;
- goto err_irq;
- }
-
- spin_lock_init(&dev->lock);
- /* udc csr registers base */
- dev->csr = dev->virt_addr + UDC_CSR_ADDR;
- /* dev registers base */
- dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR;
- /* ep registers base */
- dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR;
- /* fifo's base */
- dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR);
- dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR);
-
- if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) {
- dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq);
- retval = -EBUSY;
- goto err_irq;
- }
-
- pci_set_drvdata(pdev, dev);
-
- /* chip revision for Hs AMD5536 */
- dev->chiprev = pdev->revision;
-
- pci_set_master(pdev);
- pci_try_set_mwi(pdev);
-
- /* init dma pools */
- if (use_dma) {
- retval = init_dma_pools(dev);
- if (retval != 0)
- goto err_dma;
- }
-
- dev->phys_addr = resource;
- dev->irq = pdev->irq;
- dev->pdev = pdev;
-
- /* general probing */
- if (udc_probe(dev)) {
- retval = -ENODEV;
- goto err_probe;
- }
- return 0;
-
-err_probe:
- if (use_dma)
- free_dma_pools(dev);
-err_dma:
- free_irq(pdev->irq, dev);
-err_irq:
- iounmap(dev->virt_addr);
-err_ioremap:
- release_mem_region(resource, len);
-err_memreg:
- pci_disable_device(pdev);
-err_pcidev:
- kfree(dev);
- return retval;
-}
-
-/* PCI device parameters */
-static const struct pci_device_id pci_id[] = {
- {
- PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096),
- .class = PCI_CLASS_SERIAL_USB_DEVICE,
- .class_mask = 0xffffffff,
- },
- {},
-};
-MODULE_DEVICE_TABLE(pci, pci_id);
-
-/* PCI functions */
-static struct pci_driver udc_pci_driver = {
- .name = (char *) name,
- .id_table = pci_id,
- .probe = udc_pci_probe,
- .remove = udc_pci_remove,
-};
-
-module_pci_driver(udc_pci_driver);
+EXPORT_SYMBOL_GPL(udc_probe);
MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
MODULE_AUTHOR("Thomas Dahlmann");
diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
index 4638d70..bd2a18e 100644
--- a/drivers/usb/gadget/udc/amd5536udc.h
+++ b/drivers/usb/gadget/udc/amd5536udc.h
@@ -13,6 +13,12 @@
#ifndef AMD5536UDC_H
#define AMD5536UDC_H
+/* debug control */
+/* #define UDC_VERBOSE */
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
/* various constants */
#define UDC_RDE_TIMER_SECONDS 1
#define UDC_RDE_TIMER_DIV 10
@@ -567,6 +573,36 @@ union udc_setup_data {
struct usb_ctrlrequest request;
};
+/* Function declarations */
+int udc_enable_dev_setup_interrupts(struct udc *dev);
+int udc_mask_unused_interrupts(struct udc *dev);
+irqreturn_t udc_irq(int irq, void *pdev);
+void gadget_release(struct device *pdev);
+void udc_basic_init(struct udc *dev);
+void free_dma_pools(struct udc *dev);
+int init_dma_pools(struct udc *dev);
+void udc_remove(struct udc *dev);
+int udc_probe(struct udc *dev);
+
+/* DMA usage flag */
+static bool use_dma = 1;
+/* packet per buffer dma */
+static bool use_dma_ppb = 1;
+/* with per descr. update */
+static bool use_dma_ppb_du;
+/* full speed only mode */
+static bool use_fullspeed;
+
+/* module parameters */
+module_param(use_dma, bool, S_IRUGO);
+MODULE_PARM_DESC(use_dma, "true for DMA");
+module_param(use_dma_ppb, bool, S_IRUGO);
+MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");
+module_param(use_dma_ppb_du, bool, S_IRUGO);
+MODULE_PARM_DESC(use_dma_ppb_du,
+ "true for DMA in packet per buffer mode with descriptor update");
+module_param(use_fullspeed, bool, S_IRUGO);
+MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
/*
*---------------------------------------------------------------------------
* SET and GET bitfields in u32 values
diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c
new file mode 100644
index 0000000..2a2d0a9
--- /dev/null
+++ b/drivers/usb/gadget/udc/amd5536udc_pci.c
@@ -0,0 +1,217 @@
+/*
+ * amd5536udc_pci.c -- AMD 5536 UDC high/full speed USB device controller
+ *
+ * Copyright (C) 2005-2007 AMD (http://www.amd.com)
+ * Author: Thomas Dahlmann
+ *
+ * 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.
+ */
+
+/*
+ * The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536.
+ * It is a USB Highspeed DMA capable USB device controller. Beside ep0 it
+ * provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
+ *
+ * Make sure that UDC is assigned to port 4 by BIOS settings (port can also
+ * be used as host port) and UOC bits PAD_EN and APU are set (should be done
+ * by BIOS init).
+ *
+ * UDC DMA requires 32-bit aligned buffers so DMA with gadget ether does not
+ * work without updating NET_IP_ALIGN. Or PIO mode (module param "use_dma=0")
+ * can be used with gadget ether.
+ *
+ * This file does pci device registration, and the core driver implementation
+ * is done in amd5536udc.c
+ *
+ * The driver is split so as to use the core UDC driver which is based on
+ * Synopsys device controller IP (different than HS OTG IP) in UDCs
+ * integrated to SoC platforms.
+ *
+ */
+
+/* Driver strings */
+#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller"
+
+/* system */
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/prefetch.h>
+#include <linux/pci.h>
+
+/* udc specific */
+#include "amd5536udc.h"
+
+/* pointer to device object */
+static struct udc *udc;
+
+/* description */
+static const char mod_desc[] = UDC_MOD_DESCRIPTION;
+static const char name[] = "amd5536udc-pci";
+
+/* Reset all pci context */
+static void udc_pci_remove(struct pci_dev *pdev)
+{
+ struct udc *dev;
+
+ dev = pci_get_drvdata(pdev);
+
+ usb_del_gadget_udc(&udc->gadget);
+ /* gadget driver must not be registered */
+ if (WARN_ON(dev->driver))
+ return;
+
+ /* dma pool cleanup */
+ free_dma_pools(dev);
+
+ /* reset controller */
+ writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
+ free_irq(pdev->irq, dev);
+ iounmap(dev->virt_addr);
+ release_mem_region(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ pci_disable_device(pdev);
+
+ udc_remove(dev);
+}
+
+/* Called by pci bus driver to init pci context */
+static int udc_pci_probe(
+ struct pci_dev *pdev,
+ const struct pci_device_id *id
+)
+{
+ struct udc *dev;
+ unsigned long resource;
+ unsigned long len;
+ int retval = 0;
+
+ /* one udc only */
+ if (udc) {
+ dev_dbg(&pdev->dev, "already probed\n");
+ return -EBUSY;
+ }
+
+ /* init */
+ dev = kzalloc(sizeof(struct udc), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* pci setup */
+ if (pci_enable_device(pdev) < 0) {
+ retval = -ENODEV;
+ goto err_pcidev;
+ }
+
+ /* PCI resource allocation */
+ resource = pci_resource_start(pdev, 0);
+ len = pci_resource_len(pdev, 0);
+
+ if (!request_mem_region(resource, len, name)) {
+ dev_dbg(&pdev->dev, "pci device used already\n");
+ retval = -EBUSY;
+ goto err_memreg;
+ }
+
+ dev->virt_addr = ioremap_nocache(resource, len);
+ if (!dev->virt_addr) {
+ dev_dbg(&pdev->dev, "start address cannot be mapped\n");
+ retval = -EFAULT;
+ goto err_ioremap;
+ }
+
+ if (!pdev->irq) {
+ dev_err(&pdev->dev, "irq not set\n");
+ retval = -ENODEV;
+ goto err_irq;
+ }
+
+ spin_lock_init(&dev->lock);
+ /* udc csr registers base */
+ dev->csr = dev->virt_addr + UDC_CSR_ADDR;
+ /* dev registers base */
+ dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR;
+ /* ep registers base */
+ dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR;
+ /* fifo's base */
+ dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR);
+ dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR);
+
+ if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) {
+ dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq);
+ retval = -EBUSY;
+ goto err_irq;
+ }
+
+ pci_set_drvdata(pdev, dev);
+
+ /* chip revision for Hs AMD5536 */
+ dev->chiprev = pdev->revision;
+
+ pci_set_master(pdev);
+ pci_try_set_mwi(pdev);
+
+ /* init dma pools */
+ if (use_dma) {
+ retval = init_dma_pools(dev);
+ if (retval != 0)
+ goto err_dma;
+ }
+
+ dev->phys_addr = resource;
+ dev->irq = pdev->irq;
+ dev->pdev = pdev;
+
+ /* general probing */
+ if (udc_probe(dev)) {
+ retval = -ENODEV;
+ goto err_probe;
+ }
+ return 0;
+
+err_probe:
+ if (use_dma)
+ free_dma_pools(dev);
+err_dma:
+ free_irq(pdev->irq, dev);
+err_irq:
+ iounmap(dev->virt_addr);
+err_ioremap:
+ release_mem_region(resource, len);
+err_memreg:
+ pci_disable_device(pdev);
+err_pcidev:
+ kfree(dev);
+ return retval;
+}
+
+/* PCI device parameters */
+static const struct pci_device_id pci_id[] = {
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096),
+ .class = PCI_CLASS_SERIAL_USB_DEVICE,
+ .class_mask = 0xffffffff,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(pci, pci_id);
+
+/* PCI functions */
+static struct pci_driver udc_pci_driver = {
+ .name = (char *) name,
+ .id_table = pci_id,
+ .probe = udc_pci_probe,
+ .remove = udc_pci_remove,
+};
+module_pci_driver(udc_pci_driver);
+
+MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
+MODULE_AUTHOR("Thomas Dahlmann");
+MODULE_LICENSE("GPL");
--
2.1.0
^ permalink raw reply related
* [PATCH v3 0/7] Platform driver support for 'amd5536udc' driver
From: Raviteja Garimella @ 2017-03-28 11:02 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree, linux-kernel, bcm-kernel-feedback-list, linux-usb
Changes in PATCH v3:
===================
1. Updated bindings documentation and platform driver to add
"brcm,iproc-udc" compatibility string.
2. Added a patch to replace references of struct pci_pool with
struct dma_pool so that it is compatibile with non-pci drivers.
Changes in PATCH v2:
===================
1. Updated bindings documentation:
-- compatibility string for each supported SoC
-- removed extcon node
-- rename the file name to just iproc-udc.txt
2. Modified comptability strings in platform driver file
to reflect the change made in 1.
3 Kconfig
-- Resolved warnings shown by kbuild-test-robot
The changes are being submitted as PATCH this time. Below are the
details of main changes with respect to previous RFC versions.
Changes in PATCH v1:
===================
Patch 1/6 now splits the driver into amd5536udc_pci_.c (which
contains only the PCI device registration part), and amd5536udc.c
will still be driver that does the rest of UDC driver functionality.
Patch 2/6 renames amd5536udc.c to snps_udc_core.c.
The DT compatibilty string is changed to "brcm,iproc-snps-udc"
as per review comments for RFCv2. The driver and bindings
documentation is modified to reflect this.
This is RFC for the changes made as per the review comments made for
the previous version. I would like to know if this approach (the way
the driver is split and the naming and all)looks good to be submitted.
Changes in RFC v2:
=================
1. Split the driver into platform/pci specific drivers with a core driver
file that handles the common driver routines that are exported.
2. Split the driver into number of patches as suggested in previous
review comments.
3. Added the devicetree bindings documentation for Synopsys platform
driver.
Introduction (RFC v1):
=====================
This patch adds platform device support to the existing 'amd5536udc'
driver.
The UDC is based on Synopsys Designware core USB (2.0) Device controller
IP.
The driver so far supports UDCs that are a part of AMD southbridge
and is connected through PCI bus.
The same driver can be used with UDCs that are integrated into SoCs
like Broadcom's Northstar2/Cygnus platforms by adding platform device
suooprt.
This patch contains all the changes that were required to get the driver
functional on Broadcom's Northstar2 platform.
This is a request for comments from maintainers/others regarding approach
on whether to have 2 different drivers (one each for AMD and Broadcom)
with a common library (3 files in total), or have a single driver like
it's done in this patch and have the driver filename changed to some
common name based on ther underlying IP, like snps_udc.c.
Below are the main changes done:
1. Added OF based platform device registration -- so that the driver gets
probed based on the device tree entry. Like wise, remove routine and
platform PM ops are supported.
2. Modified debug prints to be compatible with both pci and platform
devices.
3. Added members to 'struct udc' in header file for extcon and phy support.
This is required if the UDC is connected to a Dual Role Device Phy
where the Phy can be configured to be in Device mode or Host mode based
on the type of external cable that is connected to the port.
4. Added checks in udc connect/disconnect routines so as to return if the
routine is already called.
5. Modified the arguments passed to dma_pool_create routine -- which
expects struct device, whereas NULL is passed in the existing version.
6. Kconfig changes are done so that the driver now depends on either of
CONFIG_OF or CONFIG_PCI. More description about the Synopsys IP is
provided.
Repo: https://github.com/Broadcom/arm64-linux.git
Branch: udc_snps_v3
Raviteja Garimella (7):
UDC: Split the driver into amd (pci) and Synopsys core IP driver
UDC: Rename amd5536udc driver file based on IP
UDC: make debug prints compatible with both pci and platform devices
UDC: Provide correct arguments for 'dma_pool_create'
UDC: Use struct dma_pool instead of pci_pool
DT bindings documentation for Broadcom IPROC USB Device controller.
UDC: Add Synopsys UDC Platform driver
.../devicetree/bindings/usb/iproc-udc.txt | 21 +
drivers/usb/gadget/udc/Kconfig | 32 +
drivers/usb/gadget/udc/Makefile | 4 +-
drivers/usb/gadget/udc/amd5536udc.c | 3413 --------------------
drivers/usb/gadget/udc/amd5536udc.h | 58 +-
drivers/usb/gadget/udc/amd5536udc_pci.c | 218 ++
drivers/usb/gadget/udc/snps_udc_core.c | 3239 +++++++++++++++++++
drivers/usb/gadget/udc/snps_udc_plat.c | 344 ++
8 files changed, 3912 insertions(+), 3417 deletions(-)
create mode 100644 Documentation/devicetree/bindings/usb/iproc-udc.txt
delete mode 100644 drivers/usb/gadget/udc/amd5536udc.c
create mode 100644 drivers/usb/gadget/udc/amd5536udc_pci.c
create mode 100644 drivers/usb/gadget/udc/snps_udc_core.c
create mode 100644 drivers/usb/gadget/udc/snps_udc_plat.c
--
2.1.0
^ permalink raw reply
* Re: [PATCH V6 4/7] mfd: da9061: MFD core support
From: Geert Uytterhoeven @ 2017-03-28 10:50 UTC (permalink / raw)
To: Steve Twiss
Cc: Lee Jones, kbuild test robot, kbuild-all@01.org, LINUX-KERNEL,
DEVICETREE, Dmitry Torokhov, Eduardo Valentin, Guenter Roeck,
LINUX-INPUT, LINUX-PM, LINUX-WATCHDOG, Liam Girdwood, Mark Brown,
Mark Rutland, Rob Herring, Support Opensource, Wim Van Sebroeck,
Zhang Rui
In-Reply-To: <6ED8E3B22081A4459DAC7699F3695FB7018CD69ECA@SW-EX-MBX02.diasemi.com>
Hi Steve,
On Tue, Mar 28, 2017 at 12:42 PM, Steve Twiss
<stwiss.opensource@diasemi.com> wrote:
> On 28 March 2017 09:37, Geert Uytterhoeven wrote:
>> Subject: Re: [PATCH V6 4/7] mfd: da9061: MFD core support
>> On Tue, Mar 28, 2017 at 10:21 AM, Lee Jones <lee.jones@linaro.org> wrote:
>> >> [auto build test WARNING on ljones-mfd/for-mfd-next]
>> >> [also build test WARNING on v4.11-rc4 next-20170327]
>> >> base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
>> >> config: x86_64-randconfig-x009-201713 (attached as .config)
>> >> compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
>> >> reproduce:
>> >> # save the attached .config to linux build tree
>> >> make ARCH=x86_64
>> >>
>> >> All warnings (new ones prefixed by >>):
>> >>
>> >> drivers//mfd/da9062-core.c: In function 'da9062_i2c_probe':
>> >> >> drivers//mfd/da9062-core.c:845:21: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
>> >> chip->chip_type = (int)match->data;
>> >> ^
>> >
>> > Please use longs or enums.
>>
>> Enums would still give a warning on 64-bit.
>> The simple fix is change the cast from (int) to (uintptr_t).
>
> Hi Lee and Geert,
>
> How about this? Fix by redefining the enum chip_type to be an int.
> Then, just use substitution:
> #define COMPAT_TYPE_DA9061 1
> #define COMPAT_TYPE_DA9062 2
>
> That would be simple.
> Are there any reasons this would not be acceptable?
I don't see how that can help.
The warning is caused by casting the "void *" (which is either 32-bit or
64-bit) in of_device_if.data to an integer or enum (which is always 32-bit).
The right fix is to cast it to uintptr_t intead of int, like other drivers do.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH v2 3/7] pinctrl: armada-37xx: Add pin controller support for Armada 37xx
From: Gregory CLEMENT @ 2017-03-28 10:43 UTC (permalink / raw)
To: Linus Walleij
Cc: Thomas Petazzoni, Andrew Lunn, Jason Cooper,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Nadav Haklai, linux-gpio@vger.kernel.org, Rob Herring,
Neta Zur Hershkovits, Victor Gu, Hua Jing, Marcin Wojtas,
Wilson Ding, linux-arm-kernel@lists.infradead.org,
Sebastian Hesselbarth
In-Reply-To: <CACRpkdYQb9wL+g5T08Mgv+RQYaUZPoS+0RXXfk2XdD+AHSs9gA@mail.gmail.com>
Hi Linus,
On lun., mars 27 2017, Linus Walleij <linus.walleij@linaro.org> wrote:
> On Tue, Mar 21, 2017 at 7:28 PM, Gregory CLEMENT
> <gregory.clement@free-electrons.com> wrote:
>
>> The Armada 37xx SoC come with 2 pin controllers: one on the south
>> bridge (managing 28 pins) and one on the north bridge (managing 36 pins).
>>
>> At the hardware level the controller configure the pins by group and not
>> pin by pin. This constraint is reflected in the design of the driver:
>> only the group related functions are implemented.
>
> Interesting!
>
>> +static int armada_37xx_pmx_direction_input(struct armada_37xx_pinctrl *info,
>> + unsigned int offset)
>> +{
>> + unsigned int reg = OUTPUT_EN;
>> + unsigned int mask;
>> +
>> + if (offset >= GPIO_PER_REG) {
>> + offset -= GPIO_PER_REG;
>> + reg += sizeof(u32);
>> + }
>> + mask = BIT(offset);
>> +
>> + return regmap_update_bits(info->regmap, reg, mask, 0);
>> +}
>> +
>> +
>> +
>
> A bit of excess whitespace, OK nitpicking.
>
As I need to send a v4, I will fix it in the same time.
> Then this stuff:
>
>> +static int armada_37xx_add_function(struct armada_37xx_pmx_func *funcs,
>> + int *funcsize, const char *name)
>> +{
>> + int i = 0;
>> +
>> + if (*funcsize <= 0)
>> + return -EOVERFLOW;
>> +
>> + while (funcs->ngroups) {
>> + /* function already there */
>> + if (strcmp(funcs->name, name) == 0) {
>> + funcs->ngroups++;
>> +
>> + return -EEXIST;
>> + }
>> + funcs++;
>> + i++;
>> + }
>> +
>> + /* append new unique function */
>> + funcs->name = name;
>> + funcs->ngroups = 1;
>> + (*funcsize)--;
>> +
>> + return 0;
>> +}
>> +
>> +static int armada_37xx_fill_group(struct armada_37xx_pinctrl *info, int base)
>> +{
>> + int n, num = 0, funcsize = info->data->nr_pins;
>> +
>> + for (n = 0; n < info->ngroups; n++) {
>> + struct armada_37xx_pin_group *grp = &info->groups[n];
>> + int i, j, f;
>> +
>> + grp->pins = devm_kzalloc(info->dev,
>> + (grp->npins + grp->extra_npins) *
>> + sizeof(*grp->pins), GFP_KERNEL);
>> + if (!grp->pins)
>> + return -ENOMEM;
>> +
>> + for (i = 0; i < grp->npins; i++)
>> + grp->pins[i] = grp->start_pin + base + i;
>> +
>> + for (j = 0; j < grp->extra_npins; j++)
>> + grp->pins[i+j] = grp->extra_pin + base + j;
>> +
>> + for (f = 0; f < NB_FUNCS; f++) {
>> + int ret;
>> + /* check for unique functions and count groups */
>> + ret = armada_37xx_add_function(info->funcs, &funcsize,
>> + grp->funcs[f]);
>> + if (ret == -EOVERFLOW)
>> + dev_err(info->dev,
>> + "More functions than pins(%d)\n",
>> + info->data->nr_pins);
>> + if (ret < 0)
>> + continue;
>> + num++;
>> + }
>> + }
>> +
>> + info->nfuncs = num;
>> +
>> + return 0;
>> +}
>> +
>> +static int armada_37xx_fill_func(struct armada_37xx_pinctrl *info)
>> +{
>> + struct armada_37xx_pmx_func *funcs = info->funcs;
>> + int n;
>> +
>> + for (n = 0; n < info->nfuncs; n++) {
>> + const char *name = funcs[n].name;
>> + const char **groups;
>> + int g;
>> +
>> + funcs[n].groups = devm_kzalloc(info->dev, funcs[n].ngroups *
>> + sizeof(*(funcs[n].groups)),
>> + GFP_KERNEL);
>> + if (!funcs[n].groups)
>> + return -ENOMEM;
>> +
>> + groups = funcs[n].groups;
>> +
>> + for (g = 0; g < info->ngroups; g++) {
>> + struct armada_37xx_pin_group *gp = &info->groups[g];
>> + int f;
>> +
>> + for (f = 0; f < NB_FUNCS; f++) {
>> + if (strcmp(gp->funcs[f], name) == 0) {
>> + *groups = gp->name;
>> + groups++;
>> + }
>> + }
>> + }
>> + }
>> + return 0;
>> +}
>
> I would be happy if you add kerneldoc to these functions and explain
> what they do. Because I don't get it. I guess they are filling in the data
> structures but yeah. Hard to follow.
OK
>
>> + match = of_match_node(armada_37xx_pinctrl_of_match, np);
>> + info->data = (struct armada_37xx_pin_data *)match->data;
>
> Use of_device_get_match_data()
OK
>
>
>> +static struct platform_driver armada_37xx_pinctrl_driver = {
>> + .driver = {
>> + .name = "armada-37xx-pinctrl",
>> + .of_match_table = armada_37xx_pinctrl_of_match,
>> + },
>> + .probe = armada_37xx_pinctrl_probe,
>> +};
>> +
>> +builtin_platform_driver(armada_37xx_pinctrl_driver);
>
> It almost looks like your could use builting_platform_driver_probe() actually,
> and tag the costly initfunctions with __init so they get dicarded
> after probe.
Sure, I will do it
Thanks,
Gregory
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* RE: [PATCH V6 4/7] mfd: da9061: MFD core support
From: Steve Twiss @ 2017-03-28 10:42 UTC (permalink / raw)
To: Geert Uytterhoeven, Lee Jones
Cc: kbuild test robot, Steve Twiss, kbuild-all@01.org, LINUX-KERNEL,
DEVICETREE, Dmitry Torokhov, Eduardo Valentin, Guenter Roeck,
LINUX-INPUT, LINUX-PM, LINUX-WATCHDOG, Liam Girdwood, Mark Brown,
Mark Rutland, Rob Herring, Support Opensource, Wim Van Sebroeck,
Zhang Rui
In-Reply-To: <CAMuHMdX8WH959h50zGQCV7o4E249xBKDn_cVtRbFwYVsVebu9w@mail.gmail.com>
On 28 March 2017 09:37, Geert Uytterhoeven wrote:
> Subject: Re: [PATCH V6 4/7] mfd: da9061: MFD core support
>
> Hi Lee,
>
> On Tue, Mar 28, 2017 at 10:21 AM, Lee Jones <lee.jones@linaro.org> wrote:
> >> [auto build test WARNING on ljones-mfd/for-mfd-next]
> >> [also build test WARNING on v4.11-rc4 next-20170327]
> >> base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
> >> config: x86_64-randconfig-x009-201713 (attached as .config)
> >> compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
> >> reproduce:
> >> # save the attached .config to linux build tree
> >> make ARCH=x86_64
> >>
> >> All warnings (new ones prefixed by >>):
> >>
> >> drivers//mfd/da9062-core.c: In function 'da9062_i2c_probe':
> >> >> drivers//mfd/da9062-core.c:845:21: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
> >> chip->chip_type = (int)match->data;
> >> ^
> >
> > Please use longs or enums.
>
> Enums would still give a warning on 64-bit.
> The simple fix is change the cast from (int) to (uintptr_t).
Hi Lee and Geert,
How about this? Fix by redefining the enum chip_type to be an int.
Then, just use substitution:
#define COMPAT_TYPE_DA9061 1
#define COMPAT_TYPE_DA9062 2
That would be simple.
Are there any reasons this would not be acceptable?
Regards,
Steve
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox