From d281da7ff6f70efca0553c288bb883e8605b3862 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 16 Sep 2010 18:21:24 +0100 Subject: tty: Make tiocgicount a handler Dan Rosenberg noted that various drivers return the struct with uncleared fields. Instead of spending forever trying to stomp all the drivers that get it wrong (and every new driver) do the job in one place. This first patch adds the needed operations and hooks them up, including the needed USB midlayer and serial core plumbing. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/serial.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux/usb') diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 55675b1efb28..16d682f4f7c3 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -271,6 +271,8 @@ struct usb_serial_driver { int (*tiocmget)(struct tty_struct *tty, struct file *file); int (*tiocmset)(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); + int (*get_icount)(struct tty_struct *tty, + struct serial_icounter_struct *icount); /* Called by the tty layer for port level work. There may or may not be an attached tty at this point */ void (*dtr_rts)(struct usb_serial_port *port, int on); -- cgit v1.2.3 From f0ae849df1cd6b597130d890f2107ee31bf02c19 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Thu, 5 Aug 2010 14:17:28 +0100 Subject: usb: Add Intel Langwell USB OTG Transceiver Driver This adds support for the USB transceiver driver in the Langwell chipset used on the Intel MID platforms. It folds up the original patch set which includes basic support for the device, PHY low power mode (Please notice that there is a limitation, after we drive VBus down, 2ms delay is required from SCU FW to sync up OTGSC register with USBCFG register), software timers (the hardware timers do not work in low power mode), HNP, SRP. Signed-off-by: Hao Wu Signed-off-by: Alek Du Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/Kconfig | 14 + drivers/usb/otg/Makefile | 1 + drivers/usb/otg/langwell_otg.c | 2408 ++++++++++++++++++++++++++++++++++++++ include/linux/usb/langwell_otg.h | 139 +++ 4 files changed, 2562 insertions(+) create mode 100644 drivers/usb/otg/langwell_otg.c create mode 100644 include/linux/usb/langwell_otg.h (limited to 'include/linux/usb') diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 3b1289572d72..299dfd2510cb 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -67,4 +67,18 @@ config NOP_USB_XCEIV built-in with usb ip or which are autonomous and doesn't require any phy programming such as ISP1x04 etc. +config USB_LANGWELL_OTG + tristate "Intel Langwell USB OTG dual-role support" + depends on USB && X86_MRST + select USB_OTG + select USB_OTG_UTILS + help + Say Y here if you want to build Intel Langwell USB OTG + transciever driver in kernel. This driver implements role + switch between EHCI host driver and Langwell USB OTG + client driver. + + To compile this driver as a module, choose M here: the + module will be called langwell_otg. + endif # USB || OTG diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index aeb49a8ec412..b6609db3a849 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o +obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o obj-$(CONFIG_USB_ULPI) += ulpi.o diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c new file mode 100644 index 000000000000..879188086daf --- /dev/null +++ b/drivers/usb/otg/langwell_otg.c @@ -0,0 +1,2408 @@ +/* + * Intel Langwell USB OTG transceiver driver + * Copyright (C) 2008 - 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +/* This driver helps to switch Langwell OTG controller function between host + * and peripheral. It works with EHCI driver and Langwell client controller + * driver together. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_DESC "Intel Langwell USB OTG transceiver driver" +#define DRIVER_VERSION "3.0.0.32L.0003" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Henry Yuan , Hao Wu "); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +static const char driver_name[] = "langwell_otg"; + +static int langwell_otg_probe(struct pci_dev *pdev, + const struct pci_device_id *id); +static void langwell_otg_remove(struct pci_dev *pdev); +static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message); +static int langwell_otg_resume(struct pci_dev *pdev); + +static int langwell_otg_set_host(struct otg_transceiver *otg, + struct usb_bus *host); +static int langwell_otg_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget); +static int langwell_otg_start_srp(struct otg_transceiver *otg); + +static const struct pci_device_id pci_ids[] = {{ + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = 0x8086, + .device = 0x0811, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, +}, { /* end: all zeroes */ } +}; + +static struct pci_driver otg_pci_driver = { + .name = (char *) driver_name, + .id_table = pci_ids, + + .probe = langwell_otg_probe, + .remove = langwell_otg_remove, + + .suspend = langwell_otg_suspend, + .resume = langwell_otg_resume, +}; + +static const char *state_string(enum usb_otg_state state) +{ + switch (state) { + case OTG_STATE_A_IDLE: + return "a_idle"; + case OTG_STATE_A_WAIT_VRISE: + return "a_wait_vrise"; + case OTG_STATE_A_WAIT_BCON: + return "a_wait_bcon"; + case OTG_STATE_A_HOST: + return "a_host"; + case OTG_STATE_A_SUSPEND: + return "a_suspend"; + case OTG_STATE_A_PERIPHERAL: + return "a_peripheral"; + case OTG_STATE_A_WAIT_VFALL: + return "a_wait_vfall"; + case OTG_STATE_A_VBUS_ERR: + return "a_vbus_err"; + case OTG_STATE_B_IDLE: + return "b_idle"; + case OTG_STATE_B_SRP_INIT: + return "b_srp_init"; + case OTG_STATE_B_PERIPHERAL: + return "b_peripheral"; + case OTG_STATE_B_WAIT_ACON: + return "b_wait_acon"; + case OTG_STATE_B_HOST: + return "b_host"; + default: + return "UNDEFINED"; + } +} + +/* HSM timers */ +static inline struct langwell_otg_timer *otg_timer_initializer +(void (*function)(unsigned long), unsigned long expires, unsigned long data) +{ + struct langwell_otg_timer *timer; + timer = kmalloc(sizeof(struct langwell_otg_timer), GFP_KERNEL); + if (timer == NULL) + return timer; + + timer->function = function; + timer->expires = expires; + timer->data = data; + return timer; +} + +static struct langwell_otg_timer *a_wait_vrise_tmr, *a_aidl_bdis_tmr, + *b_se0_srp_tmr, *b_srp_init_tmr; + +static struct list_head active_timers; + +static struct langwell_otg *the_transceiver; + +/* host/client notify transceiver when event affects HNP state */ +void langwell_update_transceiver(void) +{ + struct langwell_otg *lnw = the_transceiver; + + dev_dbg(lnw->dev, "transceiver is updated\n"); + + if (!lnw->qwork) + return ; + + queue_work(lnw->qwork, &lnw->work); +} +EXPORT_SYMBOL(langwell_update_transceiver); + +static int langwell_otg_set_host(struct otg_transceiver *otg, + struct usb_bus *host) +{ + otg->host = host; + + return 0; +} + +static int langwell_otg_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget) +{ + otg->gadget = gadget; + + return 0; +} + +static int langwell_otg_set_power(struct otg_transceiver *otg, + unsigned mA) +{ + return 0; +} + +/* A-device drives vbus, controlled through PMIC CHRGCNTL register*/ +static int langwell_otg_set_vbus(struct otg_transceiver *otg, bool enabled) +{ + struct langwell_otg *lnw = the_transceiver; + u8 r; + + dev_dbg(lnw->dev, "%s <--- %s\n", __func__, enabled ? "on" : "off"); + + /* FIXME: surely we should cache this on the first read. If not use + readv to avoid two transactions */ + if (intel_scu_ipc_ioread8(0x00, &r) < 0) { + dev_dbg(lnw->dev, "Failed to read PMIC register 0xD2"); + return -EBUSY; + } + if ((r & 0x03) != 0x02) { + dev_dbg(lnw->dev, "not NEC PMIC attached\n"); + return -EBUSY; + } + + if (intel_scu_ipc_ioread8(0x20, &r) < 0) { + dev_dbg(lnw->dev, "Failed to read PMIC register 0xD2"); + return -EBUSY; + } + + if ((r & 0x20) == 0) { + dev_dbg(lnw->dev, "no battery attached\n"); + return -EBUSY; + } + + /* Workaround for battery attachment issue */ + if (r == 0x34) { + dev_dbg(lnw->dev, "no battery attached on SH\n"); + return -EBUSY; + } + + dev_dbg(lnw->dev, "battery attached. 2 reg = %x\n", r); + + /* workaround: FW detect writing 0x20/0xc0 to d4 event. + * this is only for NEC PMIC. + */ + + if (intel_scu_ipc_iowrite8(0xD4, enabled ? 0x20 : 0xC0)) + dev_dbg(lnw->dev, "Failed to write PMIC.\n"); + + dev_dbg(lnw->dev, "%s --->\n", __func__); + + return 0; +} + +/* charge vbus or discharge vbus through a resistor to ground */ +static void langwell_otg_chrg_vbus(int on) +{ + struct langwell_otg *lnw = the_transceiver; + u32 val; + + val = readl(lnw->iotg.base + CI_OTGSC); + + if (on) + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VC, + lnw->iotg.base + CI_OTGSC); + else + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VD, + lnw->iotg.base + CI_OTGSC); +} + +/* Start SRP */ +static int langwell_otg_start_srp(struct otg_transceiver *otg) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 val; + + dev_dbg(lnw->dev, "%s --->\n", __func__); + + val = readl(iotg->base + CI_OTGSC); + + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP, + iotg->base + CI_OTGSC); + + /* Check if the data plus is finished or not */ + msleep(8); + val = readl(iotg->base + CI_OTGSC); + if (val & (OTGSC_HADP | OTGSC_DP)) + dev_dbg(lnw->dev, "DataLine SRP Error\n"); + + /* Disable interrupt - b_sess_vld */ + val = readl(iotg->base + CI_OTGSC); + val &= (~(OTGSC_BSVIE | OTGSC_BSEIE)); + writel(val, iotg->base + CI_OTGSC); + + /* Start VBus SRP, drive vbus to generate VBus pulse */ + iotg->otg.set_vbus(&iotg->otg, true); + msleep(15); + iotg->otg.set_vbus(&iotg->otg, false); + + /* Enable interrupt - b_sess_vld*/ + val = readl(iotg->base + CI_OTGSC); + dev_dbg(lnw->dev, "after VBUS pulse otgsc = %x\n", val); + + val |= (OTGSC_BSVIE | OTGSC_BSEIE); + writel(val, iotg->base + CI_OTGSC); + + /* If Vbus is valid, then update the hsm */ + if (val & OTGSC_BSV) { + dev_dbg(lnw->dev, "no b_sess_vld interrupt\n"); + + lnw->iotg.hsm.b_sess_vld = 1; + langwell_update_transceiver(); + } + + dev_dbg(lnw->dev, "%s <---\n", __func__); + return 0; +} + +/* stop SOF via bus_suspend */ +static void langwell_otg_loc_sof(int on) +{ + struct langwell_otg *lnw = the_transceiver; + struct usb_hcd *hcd; + int err; + + dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "suspend" : "resume"); + + hcd = bus_to_hcd(lnw->iotg.otg.host); + if (on) + err = hcd->driver->bus_resume(hcd); + else + err = hcd->driver->bus_suspend(hcd); + + if (err) + dev_dbg(lnw->dev, "Fail to resume/suspend USB bus - %d\n", err); + + dev_dbg(lnw->dev, "%s <---\n", __func__); +} + +static int langwell_otg_check_otgsc(void) +{ + struct langwell_otg *lnw = the_transceiver; + u32 otgsc, usbcfg; + + dev_dbg(lnw->dev, "check sync OTGSC and USBCFG registers\n"); + + otgsc = readl(lnw->iotg.base + CI_OTGSC); + usbcfg = readl(lnw->usbcfg); + + dev_dbg(lnw->dev, "OTGSC = %08x, USBCFG = %08x\n", + otgsc, usbcfg); + dev_dbg(lnw->dev, "OTGSC_AVV = %d\n", !!(otgsc & OTGSC_AVV)); + dev_dbg(lnw->dev, "USBCFG.VBUSVAL = %d\n", + !!(usbcfg & USBCFG_VBUSVAL)); + dev_dbg(lnw->dev, "OTGSC_ASV = %d\n", !!(otgsc & OTGSC_ASV)); + dev_dbg(lnw->dev, "USBCFG.AVALID = %d\n", + !!(usbcfg & USBCFG_AVALID)); + dev_dbg(lnw->dev, "OTGSC_BSV = %d\n", !!(otgsc & OTGSC_BSV)); + dev_dbg(lnw->dev, "USBCFG.BVALID = %d\n", + !!(usbcfg & USBCFG_BVALID)); + dev_dbg(lnw->dev, "OTGSC_BSE = %d\n", !!(otgsc & OTGSC_BSE)); + dev_dbg(lnw->dev, "USBCFG.SESEND = %d\n", + !!(usbcfg & USBCFG_SESEND)); + + /* Check USBCFG VBusValid/AValid/BValid/SessEnd */ + if (!!(otgsc & OTGSC_AVV) ^ !!(usbcfg & USBCFG_VBUSVAL)) { + dev_dbg(lnw->dev, "OTGSC.AVV != USBCFG.VBUSVAL\n"); + goto err; + } + if (!!(otgsc & OTGSC_ASV) ^ !!(usbcfg & USBCFG_AVALID)) { + dev_dbg(lnw->dev, "OTGSC.ASV != USBCFG.AVALID\n"); + goto err; + } + if (!!(otgsc & OTGSC_BSV) ^ !!(usbcfg & USBCFG_BVALID)) { + dev_dbg(lnw->dev, "OTGSC.BSV != USBCFG.BVALID\n"); + goto err; + } + if (!!(otgsc & OTGSC_BSE) ^ !!(usbcfg & USBCFG_SESEND)) { + dev_dbg(lnw->dev, "OTGSC.BSE != USBCFG.SESSEN\n"); + goto err; + } + + dev_dbg(lnw->dev, "OTGSC and USBCFG are synced\n"); + + return 0; + +err: + dev_warn(lnw->dev, "OTGSC isn't equal to USBCFG\n"); + return -EPIPE; +} + + +static void langwell_otg_phy_low_power(int on) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u8 val, phcd; + int retval; + + dev_dbg(lnw->dev, "%s ---> %s mode\n", + __func__, on ? "Low power" : "Normal"); + + phcd = 0x40; + + val = readb(iotg->base + CI_HOSTPC1 + 2); + + if (on) { + /* Due to hardware issue, after set PHCD, sync will failed + * between USBCFG and OTGSC, so before set PHCD, check if + * sync is in process now. If the answer is "yes", then do + * not touch PHCD bit */ + retval = langwell_otg_check_otgsc(); + if (retval) { + dev_dbg(lnw->dev, "Skip PHCD programming..\n"); + return ; + } + + writeb(val | phcd, iotg->base + CI_HOSTPC1 + 2); + } else + writeb(val & ~phcd, iotg->base + CI_HOSTPC1 + 2); + + dev_dbg(lnw->dev, "%s <--- done\n", __func__); +} + +/* After drv vbus, add 2 ms delay to set PHCD */ +static void langwell_otg_phy_low_power_wait(int on) +{ + struct langwell_otg *lnw = the_transceiver; + + dev_dbg(lnw->dev, "add 2ms delay before programing PHCD\n"); + + mdelay(2); + langwell_otg_phy_low_power(on); +} + +/* Enable/Disable OTG interrupt */ +static void langwell_otg_intr(int on) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 val; + + dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "on" : "off"); + + val = readl(iotg->base + CI_OTGSC); + + /* OTGSC_INT_MASK doesn't contains 1msInt */ + if (on) { + val = val | (OTGSC_INT_MASK); + writel(val, iotg->base + CI_OTGSC); + } else { + val = val & ~(OTGSC_INT_MASK); + writel(val, iotg->base + CI_OTGSC); + } + + dev_dbg(lnw->dev, "%s <---\n", __func__); +} + +/* set HAAR: Hardware Assist Auto-Reset */ +static void langwell_otg_HAAR(int on) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 val; + + dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "on" : "off"); + + val = readl(iotg->base + CI_OTGSC); + if (on) + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HAAR, + iotg->base + CI_OTGSC); + else + writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HAAR, + iotg->base + CI_OTGSC); + + dev_dbg(lnw->dev, "%s <---\n", __func__); +} + +/* set HABA: Hardware Assist B-Disconnect to A-Connect */ +static void langwell_otg_HABA(int on) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 val; + + dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "on" : "off"); + + val = readl(iotg->base + CI_OTGSC); + if (on) + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA, + iotg->base + CI_OTGSC); + else + writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA, + iotg->base + CI_OTGSC); + + dev_dbg(lnw->dev, "%s <---\n", __func__); +} + +static int langwell_otg_check_se0_srp(int on) +{ + struct langwell_otg *lnw = the_transceiver; + int delay_time = TB_SE0_SRP * 10; + u32 val; + + dev_dbg(lnw->dev, "%s --->\n", __func__); + + do { + udelay(100); + if (!delay_time--) + break; + val = readl(lnw->iotg.base + CI_PORTSC1); + val &= PORTSC_LS; + } while (!val); + + dev_dbg(lnw->dev, "%s <---\n", __func__); + return val; +} + +/* The timeout callback function to set time out bit */ +static void set_tmout(unsigned long indicator) +{ + *(int *)indicator = 1; +} + +void langwell_otg_nsf_msg(unsigned long indicator) +{ + struct langwell_otg *lnw = the_transceiver; + + switch (indicator) { + case 2: + case 4: + case 6: + case 7: + dev_warn(lnw->dev, + "OTG:NSF-%lu - deivce not responding\n", indicator); + break; + case 3: + dev_warn(lnw->dev, + "OTG:NSF-%lu - deivce not supported\n", indicator); + break; + default: + dev_warn(lnw->dev, "Do not have this kind of NSF\n"); + break; + } +} + +/* Initialize timers */ +static int langwell_otg_init_timers(struct otg_hsm *hsm) +{ + /* HSM used timers */ + a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, + (unsigned long)&hsm->a_wait_vrise_tmout); + if (a_wait_vrise_tmr == NULL) + return -ENOMEM; + a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, + (unsigned long)&hsm->a_aidl_bdis_tmout); + if (a_aidl_bdis_tmr == NULL) + return -ENOMEM; + b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, + (unsigned long)&hsm->b_se0_srp); + if (b_se0_srp_tmr == NULL) + return -ENOMEM; + b_srp_init_tmr = otg_timer_initializer(&set_tmout, TB_SRP_INIT, + (unsigned long)&hsm->b_srp_init_tmout); + if (b_srp_init_tmr == NULL) + return -ENOMEM; + + return 0; +} + +/* Free timers */ +static void langwell_otg_free_timers(void) +{ + kfree(a_wait_vrise_tmr); + kfree(a_aidl_bdis_tmr); + kfree(b_se0_srp_tmr); + kfree(b_srp_init_tmr); +} + +/* The timeout callback function to set time out bit */ +static void langwell_otg_timer_fn(unsigned long indicator) +{ + struct langwell_otg *lnw = the_transceiver; + + *(int *)indicator = 1; + + dev_dbg(lnw->dev, "kernel timer - timeout\n"); + + langwell_update_transceiver(); +} + +/* kernel timer used instead of HW based interrupt */ +static void langwell_otg_add_ktimer(enum langwell_otg_timer_type timers) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + unsigned long j = jiffies; + unsigned long data, time; + + switch (timers) { + case TA_WAIT_VRISE_TMR: + iotg->hsm.a_wait_vrise_tmout = 0; + data = (unsigned long)&iotg->hsm.a_wait_vrise_tmout; + time = TA_WAIT_VRISE; + break; + case TA_WAIT_BCON_TMR: + iotg->hsm.a_wait_bcon_tmout = 0; + data = (unsigned long)&iotg->hsm.a_wait_bcon_tmout; + time = TA_WAIT_BCON; + break; + case TA_AIDL_BDIS_TMR: + iotg->hsm.a_aidl_bdis_tmout = 0; + data = (unsigned long)&iotg->hsm.a_aidl_bdis_tmout; + time = TA_AIDL_BDIS; + break; + case TB_ASE0_BRST_TMR: + iotg->hsm.b_ase0_brst_tmout = 0; + data = (unsigned long)&iotg->hsm.b_ase0_brst_tmout; + time = TB_ASE0_BRST; + break; + case TB_SRP_INIT_TMR: + iotg->hsm.b_srp_init_tmout = 0; + data = (unsigned long)&iotg->hsm.b_srp_init_tmout; + time = TB_SRP_INIT; + break; + case TB_SRP_FAIL_TMR: + iotg->hsm.b_srp_fail_tmout = 0; + data = (unsigned long)&iotg->hsm.b_srp_fail_tmout; + time = TB_SRP_FAIL; + break; + case TB_BUS_SUSPEND_TMR: + iotg->hsm.b_bus_suspend_tmout = 0; + data = (unsigned long)&iotg->hsm.b_bus_suspend_tmout; + time = TB_BUS_SUSPEND; + break; + default: + dev_dbg(lnw->dev, "unkown timer, cannot enable it\n"); + return; + } + + lnw->hsm_timer.data = data; + lnw->hsm_timer.function = langwell_otg_timer_fn; + lnw->hsm_timer.expires = j + time * HZ / 1000; /* milliseconds */ + + add_timer(&lnw->hsm_timer); + + dev_dbg(lnw->dev, "add timer successfully\n"); +} + +/* Add timer to timer list */ +static void langwell_otg_add_timer(void *gtimer) +{ + struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; + struct langwell_otg_timer *tmp_timer; + struct intel_mid_otg_xceiv *iotg = &the_transceiver->iotg; + u32 val32; + + /* Check if the timer is already in the active list, + * if so update timer count + */ + list_for_each_entry(tmp_timer, &active_timers, list) + if (tmp_timer == timer) { + timer->count = timer->expires; + return; + } + timer->count = timer->expires; + + if (list_empty(&active_timers)) { + val32 = readl(iotg->base + CI_OTGSC); + writel(val32 | OTGSC_1MSE, iotg->base + CI_OTGSC); + } + + list_add_tail(&timer->list, &active_timers); +} + +/* Remove timer from the timer list; clear timeout status */ +static void langwell_otg_del_timer(void *gtimer) +{ + struct langwell_otg *lnw = the_transceiver; + struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; + struct langwell_otg_timer *tmp_timer, *del_tmp; + u32 val32; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) + if (tmp_timer == timer) + list_del(&timer->list); + + if (list_empty(&active_timers)) { + val32 = readl(lnw->iotg.base + CI_OTGSC); + writel(val32 & ~OTGSC_1MSE, lnw->iotg.base + CI_OTGSC); + } +} + +/* Reduce timer count by 1, and find timeout conditions.*/ +static int langwell_otg_tick_timer(u32 *int_sts) +{ + struct langwell_otg *lnw = the_transceiver; + struct langwell_otg_timer *tmp_timer, *del_tmp; + int expired = 0; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { + tmp_timer->count--; + /* check if timer expires */ + if (!tmp_timer->count) { + list_del(&tmp_timer->list); + tmp_timer->function(tmp_timer->data); + expired = 1; + } + } + + if (list_empty(&active_timers)) { + dev_dbg(lnw->dev, "tick timer: disable 1ms int\n"); + *int_sts = *int_sts & ~OTGSC_1MSE; + } + return expired; +} + +static void reset_otg(void) +{ + struct langwell_otg *lnw = the_transceiver; + int delay_time = 1000; + u32 val; + + dev_dbg(lnw->dev, "reseting OTG controller ...\n"); + val = readl(lnw->iotg.base + CI_USBCMD); + writel(val | USBCMD_RST, lnw->iotg.base + CI_USBCMD); + do { + udelay(100); + if (!delay_time--) + dev_dbg(lnw->dev, "reset timeout\n"); + val = readl(lnw->iotg.base + CI_USBCMD); + val &= USBCMD_RST; + } while (val != 0); + dev_dbg(lnw->dev, "reset done.\n"); +} + +static void set_host_mode(void) +{ + struct langwell_otg *lnw = the_transceiver; + u32 val; + + reset_otg(); + val = readl(lnw->iotg.base + CI_USBMODE); + val = (val & (~USBMODE_CM)) | USBMODE_HOST; + writel(val, lnw->iotg.base + CI_USBMODE); +} + +static void set_client_mode(void) +{ + struct langwell_otg *lnw = the_transceiver; + u32 val; + + reset_otg(); + val = readl(lnw->iotg.base + CI_USBMODE); + val = (val & (~USBMODE_CM)) | USBMODE_DEVICE; + writel(val, lnw->iotg.base + CI_USBMODE); +} + +static void init_hsm(void) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 val32; + + /* read OTGSC after reset */ + val32 = readl(lnw->iotg.base + CI_OTGSC); + dev_dbg(lnw->dev, "%s: OTGSC init value = 0x%x\n", __func__, val32); + + /* set init state */ + if (val32 & OTGSC_ID) { + iotg->hsm.id = 1; + iotg->otg.default_a = 0; + set_client_mode(); + iotg->otg.state = OTG_STATE_B_IDLE; + } else { + iotg->hsm.id = 0; + iotg->otg.default_a = 1; + set_host_mode(); + iotg->otg.state = OTG_STATE_A_IDLE; + } + + /* set session indicator */ + if (val32 & OTGSC_BSE) + iotg->hsm.b_sess_end = 1; + if (val32 & OTGSC_BSV) + iotg->hsm.b_sess_vld = 1; + if (val32 & OTGSC_ASV) + iotg->hsm.a_sess_vld = 1; + if (val32 & OTGSC_AVV) + iotg->hsm.a_vbus_vld = 1; + + /* defautly power the bus */ + iotg->hsm.a_bus_req = 1; + iotg->hsm.a_bus_drop = 0; + /* defautly don't request bus as B device */ + iotg->hsm.b_bus_req = 0; + /* no system error */ + iotg->hsm.a_clr_err = 0; + + langwell_otg_phy_low_power_wait(1); +} + +static void update_hsm(void) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 val32; + + /* read OTGSC */ + val32 = readl(lnw->iotg.base + CI_OTGSC); + dev_dbg(lnw->dev, "%s: OTGSC value = 0x%x\n", __func__, val32); + + iotg->hsm.id = !!(val32 & OTGSC_ID); + iotg->hsm.b_sess_end = !!(val32 & OTGSC_BSE); + iotg->hsm.b_sess_vld = !!(val32 & OTGSC_BSV); + iotg->hsm.a_sess_vld = !!(val32 & OTGSC_ASV); + iotg->hsm.a_vbus_vld = !!(val32 & OTGSC_AVV); +} + +static irqreturn_t otg_dummy_irq(int irq, void *_dev) +{ + struct langwell_otg *lnw = the_transceiver; + void __iomem *reg_base = _dev; + u32 val; + u32 int_mask = 0; + + val = readl(reg_base + CI_USBMODE); + if ((val & USBMODE_CM) != USBMODE_DEVICE) + return IRQ_NONE; + + val = readl(reg_base + CI_USBSTS); + int_mask = val & INTR_DUMMY_MASK; + + if (int_mask == 0) + return IRQ_NONE; + + /* clear hsm.b_conn here since host driver can't detect it + * otg_dummy_irq called means B-disconnect happened. + */ + if (lnw->iotg.hsm.b_conn) { + lnw->iotg.hsm.b_conn = 0; + if (spin_trylock(&lnw->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&lnw->wq_lock); + } + } + + /* Clear interrupts */ + writel(int_mask, reg_base + CI_USBSTS); + return IRQ_HANDLED; +} + +static irqreturn_t otg_irq(int irq, void *_dev) +{ + struct langwell_otg *lnw = _dev; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 int_sts, int_en; + u32 int_mask = 0; + int flag = 0; + + int_sts = readl(lnw->iotg.base + CI_OTGSC); + int_en = (int_sts & OTGSC_INTEN_MASK) >> 8; + int_mask = int_sts & int_en; + if (int_mask == 0) + return IRQ_NONE; + + if (int_mask & OTGSC_IDIS) { + dev_dbg(lnw->dev, "%s: id change int\n", __func__); + iotg->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0; + dev_dbg(lnw->dev, "id = %d\n", iotg->hsm.id); + flag = 1; + } + if (int_mask & OTGSC_DPIS) { + dev_dbg(lnw->dev, "%s: data pulse int\n", __func__); + iotg->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0; + dev_dbg(lnw->dev, "data pulse = %d\n", iotg->hsm.a_srp_det); + flag = 1; + } + if (int_mask & OTGSC_BSEIS) { + dev_dbg(lnw->dev, "%s: b session end int\n", __func__); + iotg->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0; + dev_dbg(lnw->dev, "b_sess_end = %d\n", iotg->hsm.b_sess_end); + flag = 1; + } + if (int_mask & OTGSC_BSVIS) { + dev_dbg(lnw->dev, "%s: b session valid int\n", __func__); + iotg->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0; + dev_dbg(lnw->dev, "b_sess_vld = %d\n", iotg->hsm.b_sess_end); + flag = 1; + } + if (int_mask & OTGSC_ASVIS) { + dev_dbg(lnw->dev, "%s: a session valid int\n", __func__); + iotg->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0; + dev_dbg(lnw->dev, "a_sess_vld = %d\n", iotg->hsm.a_sess_vld); + flag = 1; + } + if (int_mask & OTGSC_AVVIS) { + dev_dbg(lnw->dev, "%s: a vbus valid int\n", __func__); + iotg->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0; + dev_dbg(lnw->dev, "a_vbus_vld = %d\n", iotg->hsm.a_vbus_vld); + flag = 1; + } + + if (int_mask & OTGSC_1MSS) { + /* need to schedule otg_work if any timer is expired */ + if (langwell_otg_tick_timer(&int_sts)) + flag = 1; + } + + writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask, + lnw->iotg.base + CI_OTGSC); + if (flag) + langwell_update_transceiver(); + + return IRQ_HANDLED; +} + +static int langwell_otg_iotg_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = data; + int flag = 0; + + if (iotg == NULL) + return NOTIFY_BAD; + + if (lnw == NULL) + return NOTIFY_BAD; + + switch (action) { + case MID_OTG_NOTIFY_CONNECT: + dev_dbg(lnw->dev, "Lnw OTG Notify Connect Event\n"); + if (iotg->otg.default_a == 1) + iotg->hsm.b_conn = 1; + else + iotg->hsm.a_conn = 1; + flag = 1; + break; + case MID_OTG_NOTIFY_DISCONN: + dev_dbg(lnw->dev, "Lnw OTG Notify Disconnect Event\n"); + if (iotg->otg.default_a == 1) + iotg->hsm.b_conn = 0; + else + iotg->hsm.a_conn = 0; + flag = 1; + break; + case MID_OTG_NOTIFY_HSUSPEND: + dev_dbg(lnw->dev, "Lnw OTG Notify Host Bus suspend Event\n"); + if (iotg->otg.default_a == 1) + iotg->hsm.a_suspend_req = 1; + else + iotg->hsm.b_bus_req = 0; + flag = 1; + break; + case MID_OTG_NOTIFY_HRESUME: + dev_dbg(lnw->dev, "Lnw OTG Notify Host Bus resume Event\n"); + if (iotg->otg.default_a == 1) + iotg->hsm.b_bus_resume = 1; + flag = 1; + break; + case MID_OTG_NOTIFY_CSUSPEND: + dev_dbg(lnw->dev, "Lnw OTG Notify Client Bus suspend Event\n"); + if (iotg->otg.default_a == 1) { + if (iotg->hsm.b_bus_suspend_vld == 2) { + iotg->hsm.b_bus_suspend = 1; + iotg->hsm.b_bus_suspend_vld = 0; + flag = 1; + } else { + iotg->hsm.b_bus_suspend_vld++; + flag = 0; + } + } else { + if (iotg->hsm.a_bus_suspend == 0) { + iotg->hsm.a_bus_suspend = 1; + flag = 1; + } + } + break; + case MID_OTG_NOTIFY_CRESUME: + dev_dbg(lnw->dev, "Lnw OTG Notify Client Bus resume Event\n"); + if (iotg->otg.default_a == 0) + iotg->hsm.a_bus_suspend = 0; + flag = 0; + break; + case MID_OTG_NOTIFY_HOSTADD: + dev_dbg(lnw->dev, "Lnw OTG Nofity Host Driver Add\n"); + flag = 1; + break; + case MID_OTG_NOTIFY_HOSTREMOVE: + dev_dbg(lnw->dev, "Lnw OTG Nofity Host Driver remove\n"); + flag = 1; + break; + case MID_OTG_NOTIFY_CLIENTADD: + dev_dbg(lnw->dev, "Lnw OTG Nofity Client Driver Add\n"); + flag = 1; + break; + case MID_OTG_NOTIFY_CLIENTREMOVE: + dev_dbg(lnw->dev, "Lnw OTG Nofity Client Driver remove\n"); + flag = 1; + break; + default: + dev_dbg(lnw->dev, "Lnw OTG Nofity unknown notify message\n"); + return NOTIFY_DONE; + } + + if (flag) + langwell_update_transceiver(); + + return NOTIFY_OK; +} + +static void langwell_otg_work(struct work_struct *work) +{ + struct langwell_otg *lnw; + struct intel_mid_otg_xceiv *iotg; + int retval; + struct pci_dev *pdev; + + lnw = container_of(work, struct langwell_otg, work); + iotg = &lnw->iotg; + pdev = to_pci_dev(lnw->dev); + + dev_dbg(lnw->dev, "%s: old state = %s\n", __func__, + state_string(iotg->otg.state)); + + switch (iotg->otg.state) { + case OTG_STATE_UNDEFINED: + case OTG_STATE_B_IDLE: + if (!iotg->hsm.id) { + langwell_otg_del_timer(b_srp_init_tmr); + del_timer_sync(&lnw->hsm_timer); + + iotg->otg.default_a = 1; + iotg->hsm.a_srp_det = 0; + + langwell_otg_chrg_vbus(0); + set_host_mode(); + langwell_otg_phy_low_power(1); + + iotg->otg.state = OTG_STATE_A_IDLE; + langwell_update_transceiver(); + } else if (iotg->hsm.b_sess_vld) { + langwell_otg_del_timer(b_srp_init_tmr); + del_timer_sync(&lnw->hsm_timer); + iotg->hsm.b_sess_end = 0; + iotg->hsm.a_bus_suspend = 0; + langwell_otg_chrg_vbus(0); + + if (lnw->iotg.start_peripheral) { + lnw->iotg.start_peripheral(&lnw->iotg); + iotg->otg.state = OTG_STATE_B_PERIPHERAL; + } else + dev_dbg(lnw->dev, "client driver not loaded\n"); + + } else if (iotg->hsm.b_srp_init_tmout) { + iotg->hsm.b_srp_init_tmout = 0; + dev_warn(lnw->dev, "SRP init timeout\n"); + } else if (iotg->hsm.b_srp_fail_tmout) { + iotg->hsm.b_srp_fail_tmout = 0; + iotg->hsm.b_bus_req = 0; + + /* No silence failure */ + langwell_otg_nsf_msg(6); + } else if (iotg->hsm.b_bus_req && iotg->hsm.b_sess_end) { + del_timer_sync(&lnw->hsm_timer); + /* workaround for b_se0_srp detection */ + retval = langwell_otg_check_se0_srp(0); + if (retval) { + iotg->hsm.b_bus_req = 0; + dev_dbg(lnw->dev, "LS isn't SE0, try later\n"); + } else { + /* clear the PHCD before start srp */ + langwell_otg_phy_low_power(0); + + /* Start SRP */ + langwell_otg_add_timer(b_srp_init_tmr); + iotg->otg.start_srp(&iotg->otg); + langwell_otg_del_timer(b_srp_init_tmr); + langwell_otg_add_ktimer(TB_SRP_FAIL_TMR); + + /* reset PHY low power mode here */ + langwell_otg_phy_low_power_wait(1); + } + } + break; + case OTG_STATE_B_SRP_INIT: + if (!iotg->hsm.id) { + iotg->otg.default_a = 1; + iotg->hsm.a_srp_det = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + langwell_otg_chrg_vbus(0); + set_host_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_A_IDLE; + langwell_update_transceiver(); + } else if (iotg->hsm.b_sess_vld) { + langwell_otg_chrg_vbus(0); + if (lnw->iotg.start_peripheral) { + lnw->iotg.start_peripheral(&lnw->iotg); + iotg->otg.state = OTG_STATE_B_PERIPHERAL; + } else + dev_dbg(lnw->dev, "client driver not loaded\n"); + } + break; + case OTG_STATE_B_PERIPHERAL: + if (!iotg->hsm.id) { + iotg->otg.default_a = 1; + iotg->hsm.a_srp_det = 0; + + langwell_otg_chrg_vbus(0); + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + set_host_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_A_IDLE; + langwell_update_transceiver(); + } else if (!iotg->hsm.b_sess_vld) { + iotg->hsm.b_hnp_enable = 0; + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + iotg->otg.state = OTG_STATE_B_IDLE; + } else if (iotg->hsm.b_bus_req && iotg->otg.gadget && + iotg->otg.gadget->b_hnp_enable && + iotg->hsm.a_bus_suspend) { + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + langwell_otg_HAAR(1); + iotg->hsm.a_conn = 0; + + if (lnw->iotg.start_host) { + lnw->iotg.start_host(&lnw->iotg); + iotg->otg.state = OTG_STATE_B_WAIT_ACON; + } else + dev_dbg(lnw->dev, + "host driver not loaded.\n"); + + iotg->hsm.a_bus_resume = 0; + langwell_otg_add_ktimer(TB_ASE0_BRST_TMR); + } + break; + + case OTG_STATE_B_WAIT_ACON: + if (!iotg->hsm.id) { + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); + + iotg->otg.default_a = 1; + iotg->hsm.a_srp_det = 0; + + langwell_otg_chrg_vbus(0); + + langwell_otg_HAAR(0); + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + set_host_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_A_IDLE; + langwell_update_transceiver(); + } else if (!iotg->hsm.b_sess_vld) { + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); + + iotg->hsm.b_hnp_enable = 0; + iotg->hsm.b_bus_req = 0; + + langwell_otg_chrg_vbus(0); + langwell_otg_HAAR(0); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + set_client_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_B_IDLE; + } else if (iotg->hsm.a_conn) { + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); + + langwell_otg_HAAR(0); + iotg->otg.state = OTG_STATE_B_HOST; + langwell_update_transceiver(); + } else if (iotg->hsm.a_bus_resume || + iotg->hsm.b_ase0_brst_tmout) { + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); + + langwell_otg_HAAR(0); + langwell_otg_nsf_msg(7); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + iotg->hsm.a_bus_suspend = 0; + iotg->hsm.b_bus_req = 0; + + if (lnw->iotg.start_peripheral) + lnw->iotg.start_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver not loaded.\n"); + + iotg->otg.state = OTG_STATE_B_PERIPHERAL; + } + break; + + case OTG_STATE_B_HOST: + if (!iotg->hsm.id) { + iotg->otg.default_a = 1; + iotg->hsm.a_srp_det = 0; + + langwell_otg_chrg_vbus(0); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + set_host_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_A_IDLE; + langwell_update_transceiver(); + } else if (!iotg->hsm.b_sess_vld) { + iotg->hsm.b_hnp_enable = 0; + iotg->hsm.b_bus_req = 0; + + langwell_otg_chrg_vbus(0); + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + set_client_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_B_IDLE; + } else if ((!iotg->hsm.b_bus_req) || + (!iotg->hsm.a_conn)) { + iotg->hsm.b_bus_req = 0; + langwell_otg_loc_sof(0); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + iotg->hsm.a_bus_suspend = 0; + + if (lnw->iotg.start_peripheral) + lnw->iotg.start_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver not loaded.\n"); + + iotg->otg.state = OTG_STATE_B_PERIPHERAL; + } + break; + + case OTG_STATE_A_IDLE: + iotg->otg.default_a = 1; + if (iotg->hsm.id) { + iotg->otg.default_a = 0; + iotg->hsm.b_bus_req = 0; + iotg->hsm.vbus_srp_up = 0; + + langwell_otg_chrg_vbus(0); + set_client_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (!iotg->hsm.a_bus_drop && + (iotg->hsm.a_srp_det || iotg->hsm.a_bus_req)) { + langwell_otg_phy_low_power(0); + + /* Turn on VBus */ + iotg->otg.set_vbus(&iotg->otg, true); + + iotg->hsm.vbus_srp_up = 0; + iotg->hsm.a_wait_vrise_tmout = 0; + langwell_otg_add_timer(a_wait_vrise_tmr); + iotg->otg.state = OTG_STATE_A_WAIT_VRISE; + langwell_update_transceiver(); + } else if (!iotg->hsm.a_bus_drop && iotg->hsm.a_sess_vld) { + iotg->hsm.vbus_srp_up = 1; + } else if (!iotg->hsm.a_sess_vld && iotg->hsm.vbus_srp_up) { + msleep(10); + langwell_otg_phy_low_power(0); + + /* Turn on VBus */ + iotg->otg.set_vbus(&iotg->otg, true); + iotg->hsm.a_srp_det = 1; + iotg->hsm.vbus_srp_up = 0; + iotg->hsm.a_wait_vrise_tmout = 0; + langwell_otg_add_timer(a_wait_vrise_tmr); + iotg->otg.state = OTG_STATE_A_WAIT_VRISE; + langwell_update_transceiver(); + } else if (!iotg->hsm.a_sess_vld && + !iotg->hsm.vbus_srp_up) { + langwell_otg_phy_low_power(1); + } + break; + case OTG_STATE_A_WAIT_VRISE: + if (iotg->hsm.id) { + langwell_otg_del_timer(a_wait_vrise_tmr); + iotg->hsm.b_bus_req = 0; + iotg->otg.default_a = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + set_client_mode(); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_B_IDLE; + } else if (iotg->hsm.a_vbus_vld) { + langwell_otg_del_timer(a_wait_vrise_tmr); + iotg->hsm.b_conn = 0; + if (lnw->iotg.start_host) + lnw->iotg.start_host(&lnw->iotg); + else { + dev_dbg(lnw->dev, "host driver not loaded.\n"); + break; + } + + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); + iotg->otg.state = OTG_STATE_A_WAIT_BCON; + } else if (iotg->hsm.a_wait_vrise_tmout) { + iotg->hsm.b_conn = 0; + if (iotg->hsm.a_vbus_vld) { + if (lnw->iotg.start_host) + lnw->iotg.start_host(&lnw->iotg); + else { + dev_dbg(lnw->dev, + "host driver not loaded.\n"); + break; + } + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); + iotg->otg.state = OTG_STATE_A_WAIT_BCON; + } else { + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_A_VBUS_ERR; + } + } + break; + case OTG_STATE_A_WAIT_BCON: + if (iotg->hsm.id) { + /* delete hsm timer for a_wait_bcon_tmr */ + del_timer_sync(&lnw->hsm_timer); + + iotg->otg.default_a = 0; + iotg->hsm.b_bus_req = 0; + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + set_client_mode(); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (!iotg->hsm.a_vbus_vld) { + /* delete hsm timer for a_wait_bcon_tmr */ + del_timer_sync(&lnw->hsm_timer); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_A_VBUS_ERR; + } else if (iotg->hsm.a_bus_drop || + (iotg->hsm.a_wait_bcon_tmout && + !iotg->hsm.a_bus_req)) { + /* delete hsm timer for a_wait_bcon_tmr */ + del_timer_sync(&lnw->hsm_timer); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_WAIT_VFALL; + } else if (iotg->hsm.b_conn) { + /* delete hsm timer for a_wait_bcon_tmr */ + del_timer_sync(&lnw->hsm_timer); + + iotg->hsm.a_suspend_req = 0; + iotg->otg.state = OTG_STATE_A_HOST; + if (iotg->hsm.a_srp_det && iotg->otg.host && + !iotg->otg.host->b_hnp_enable) { + /* SRP capable peripheral-only device */ + iotg->hsm.a_bus_req = 1; + iotg->hsm.a_srp_det = 0; + } else if (!iotg->hsm.a_bus_req && iotg->otg.host && + iotg->otg.host->b_hnp_enable) { + /* It is not safe enough to do a fast + * transistion from A_WAIT_BCON to + * A_SUSPEND */ + msleep(10000); + if (iotg->hsm.a_bus_req) + break; + + if (request_irq(pdev->irq, + otg_dummy_irq, IRQF_SHARED, + driver_name, iotg->base) != 0) { + dev_dbg(lnw->dev, + "request interrupt %d fail\n", + pdev->irq); + } + + langwell_otg_HABA(1); + iotg->hsm.b_bus_resume = 0; + iotg->hsm.a_aidl_bdis_tmout = 0; + + langwell_otg_loc_sof(0); + /* clear PHCD to enable HW timer */ + langwell_otg_phy_low_power(0); + langwell_otg_add_timer(a_aidl_bdis_tmr); + iotg->otg.state = OTG_STATE_A_SUSPEND; + } else if (!iotg->hsm.a_bus_req && iotg->otg.host && + !iotg->otg.host->b_hnp_enable) { + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_WAIT_VFALL; + } + } + break; + case OTG_STATE_A_HOST: + if (iotg->hsm.id) { + iotg->otg.default_a = 0; + iotg->hsm.b_bus_req = 0; + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + set_client_mode(); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (iotg->hsm.a_bus_drop || + (iotg->otg.host && + !iotg->otg.host->b_hnp_enable && + !iotg->hsm.a_bus_req)) { + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_WAIT_VFALL; + } else if (!iotg->hsm.a_vbus_vld) { + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_A_VBUS_ERR; + } else if (iotg->otg.host && + iotg->otg.host->b_hnp_enable && + !iotg->hsm.a_bus_req) { + /* Set HABA to enable hardware assistance to signal + * A-connect after receiver B-disconnect. Hardware + * will then set client mode and enable URE, SLE and + * PCE after the assistance. otg_dummy_irq is used to + * clean these ints when client driver is not resumed. + */ + if (request_irq(pdev->irq, otg_dummy_irq, IRQF_SHARED, + driver_name, iotg->base) != 0) { + dev_dbg(lnw->dev, + "request interrupt %d failed\n", + pdev->irq); + } + + /* set HABA */ + langwell_otg_HABA(1); + iotg->hsm.b_bus_resume = 0; + iotg->hsm.a_aidl_bdis_tmout = 0; + langwell_otg_loc_sof(0); + /* clear PHCD to enable HW timer */ + langwell_otg_phy_low_power(0); + langwell_otg_add_timer(a_aidl_bdis_tmr); + iotg->otg.state = OTG_STATE_A_SUSPEND; + } else if (!iotg->hsm.b_conn || !iotg->hsm.a_bus_req) { + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); + iotg->otg.state = OTG_STATE_A_WAIT_BCON; + } + break; + case OTG_STATE_A_SUSPEND: + if (iotg->hsm.id) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(pdev->irq, iotg->base); + iotg->otg.default_a = 0; + iotg->hsm.b_bus_req = 0; + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + set_client_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (iotg->hsm.a_bus_req || + iotg->hsm.b_bus_resume) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(pdev->irq, iotg->base); + iotg->hsm.a_suspend_req = 0; + langwell_otg_loc_sof(1); + iotg->otg.state = OTG_STATE_A_HOST; + } else if (iotg->hsm.a_aidl_bdis_tmout || + iotg->hsm.a_bus_drop) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(pdev->irq, iotg->base); + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_WAIT_VFALL; + } else if (!iotg->hsm.b_conn && iotg->otg.host && + iotg->otg.host->b_hnp_enable) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(pdev->irq, iotg->base); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + iotg->hsm.b_bus_suspend = 0; + iotg->hsm.b_bus_suspend_vld = 0; + + /* msleep(200); */ + if (lnw->iotg.start_peripheral) + lnw->iotg.start_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver not loaded.\n"); + + langwell_otg_add_ktimer(TB_BUS_SUSPEND_TMR); + iotg->otg.state = OTG_STATE_A_PERIPHERAL; + break; + } else if (!iotg->hsm.a_vbus_vld) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(pdev->irq, iotg->base); + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_A_VBUS_ERR; + } + break; + case OTG_STATE_A_PERIPHERAL: + if (iotg->hsm.id) { + /* delete hsm timer for b_bus_suspend_tmr */ + del_timer_sync(&lnw->hsm_timer); + iotg->otg.default_a = 0; + iotg->hsm.b_bus_req = 0; + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + set_client_mode(); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (!iotg->hsm.a_vbus_vld) { + /* delete hsm timer for b_bus_suspend_tmr */ + del_timer_sync(&lnw->hsm_timer); + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_A_VBUS_ERR; + } else if (iotg->hsm.a_bus_drop) { + /* delete hsm timer for b_bus_suspend_tmr */ + del_timer_sync(&lnw->hsm_timer); + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_WAIT_VFALL; + } else if (iotg->hsm.b_bus_suspend) { + /* delete hsm timer for b_bus_suspend_tmr */ + del_timer_sync(&lnw->hsm_timer); + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + if (lnw->iotg.start_host) + lnw->iotg.start_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver not loaded.\n"); + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); + iotg->otg.state = OTG_STATE_A_WAIT_BCON; + } else if (iotg->hsm.b_bus_suspend_tmout) { + u32 val; + val = readl(lnw->iotg.base + CI_PORTSC1); + if (!(val & PORTSC_SUSP)) + break; + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + if (lnw->iotg.start_host) + lnw->iotg.start_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver not loaded.\n"); + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); + iotg->otg.state = OTG_STATE_A_WAIT_BCON; + } + break; + case OTG_STATE_A_VBUS_ERR: + if (iotg->hsm.id) { + iotg->otg.default_a = 0; + iotg->hsm.a_clr_err = 0; + iotg->hsm.a_srp_det = 0; + set_client_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (iotg->hsm.a_clr_err) { + iotg->hsm.a_clr_err = 0; + iotg->hsm.a_srp_det = 0; + reset_otg(); + init_hsm(); + if (iotg->otg.state == OTG_STATE_A_IDLE) + langwell_update_transceiver(); + } else { + /* FW will clear PHCD bit when any VBus + * event detected. Reset PHCD to 1 again */ + langwell_otg_phy_low_power(1); + } + break; + case OTG_STATE_A_WAIT_VFALL: + if (iotg->hsm.id) { + iotg->otg.default_a = 0; + set_client_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (iotg->hsm.a_bus_req) { + + /* Turn on VBus */ + iotg->otg.set_vbus(&iotg->otg, true); + iotg->hsm.a_wait_vrise_tmout = 0; + langwell_otg_add_timer(a_wait_vrise_tmr); + iotg->otg.state = OTG_STATE_A_WAIT_VRISE; + } else if (!iotg->hsm.a_sess_vld) { + iotg->hsm.a_srp_det = 0; + set_host_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_A_IDLE; + } + break; + default: + ; + } + + dev_dbg(lnw->dev, "%s: new state = %s\n", __func__, + state_string(iotg->otg.state)); +} + +static ssize_t +show_registers(struct device *_dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *lnw = the_transceiver; + char *next; + unsigned size, t; + + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, + "\n" + "USBCMD = 0x%08x\n" + "USBSTS = 0x%08x\n" + "USBINTR = 0x%08x\n" + "ASYNCLISTADDR = 0x%08x\n" + "PORTSC1 = 0x%08x\n" + "HOSTPC1 = 0x%08x\n" + "OTGSC = 0x%08x\n" + "USBMODE = 0x%08x\n", + readl(lnw->iotg.base + 0x30), + readl(lnw->iotg.base + 0x34), + readl(lnw->iotg.base + 0x38), + readl(lnw->iotg.base + 0x48), + readl(lnw->iotg.base + 0x74), + readl(lnw->iotg.base + 0xb4), + readl(lnw->iotg.base + 0xf4), + readl(lnw->iotg.base + 0xf8) + ); + size -= t; + next += t; + + return PAGE_SIZE - size; +} +static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL); + +static ssize_t +show_hsm(struct device *_dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + char *next; + unsigned size, t; + + next = buf; + size = PAGE_SIZE; + + if (iotg->otg.host) + iotg->hsm.a_set_b_hnp_en = iotg->otg.host->b_hnp_enable; + + if (iotg->otg.gadget) + iotg->hsm.b_hnp_enable = iotg->otg.gadget->b_hnp_enable; + + t = scnprintf(next, size, + "\n" + "current state = %s\n" + "a_bus_resume = \t%d\n" + "a_bus_suspend = \t%d\n" + "a_conn = \t%d\n" + "a_sess_vld = \t%d\n" + "a_srp_det = \t%d\n" + "a_vbus_vld = \t%d\n" + "b_bus_resume = \t%d\n" + "b_bus_suspend = \t%d\n" + "b_conn = \t%d\n" + "b_se0_srp = \t%d\n" + "b_sess_end = \t%d\n" + "b_sess_vld = \t%d\n" + "id = \t%d\n" + "a_set_b_hnp_en = \t%d\n" + "b_srp_done = \t%d\n" + "b_hnp_enable = \t%d\n" + "a_wait_vrise_tmout = \t%d\n" + "a_wait_bcon_tmout = \t%d\n" + "a_aidl_bdis_tmout = \t%d\n" + "b_ase0_brst_tmout = \t%d\n" + "a_bus_drop = \t%d\n" + "a_bus_req = \t%d\n" + "a_clr_err = \t%d\n" + "a_suspend_req = \t%d\n" + "b_bus_req = \t%d\n" + "b_bus_suspend_tmout = \t%d\n" + "b_bus_suspend_vld = \t%d\n", + state_string(iotg->otg.state), + iotg->hsm.a_bus_resume, + iotg->hsm.a_bus_suspend, + iotg->hsm.a_conn, + iotg->hsm.a_sess_vld, + iotg->hsm.a_srp_det, + iotg->hsm.a_vbus_vld, + iotg->hsm.b_bus_resume, + iotg->hsm.b_bus_suspend, + iotg->hsm.b_conn, + iotg->hsm.b_se0_srp, + iotg->hsm.b_sess_end, + iotg->hsm.b_sess_vld, + iotg->hsm.id, + iotg->hsm.a_set_b_hnp_en, + iotg->hsm.b_srp_done, + iotg->hsm.b_hnp_enable, + iotg->hsm.a_wait_vrise_tmout, + iotg->hsm.a_wait_bcon_tmout, + iotg->hsm.a_aidl_bdis_tmout, + iotg->hsm.b_ase0_brst_tmout, + iotg->hsm.a_bus_drop, + iotg->hsm.a_bus_req, + iotg->hsm.a_clr_err, + iotg->hsm.a_suspend_req, + iotg->hsm.b_bus_req, + iotg->hsm.b_bus_suspend_tmout, + iotg->hsm.b_bus_suspend_vld + ); + size -= t; + next += t; + + return PAGE_SIZE - size; +} +static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL); + +static ssize_t +get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *lnw = the_transceiver; + char *next; + unsigned size, t; + + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, "%d", lnw->iotg.hsm.a_bus_req); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_a_bus_req(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + + if (!iotg->otg.default_a) + return -1; + if (count > 2) + return -1; + + if (buf[0] == '0') { + iotg->hsm.a_bus_req = 0; + dev_dbg(lnw->dev, "User request: a_bus_req = 0\n"); + } else if (buf[0] == '1') { + /* If a_bus_drop is TRUE, a_bus_req can't be set */ + if (iotg->hsm.a_bus_drop) + return -1; + iotg->hsm.a_bus_req = 1; + dev_dbg(lnw->dev, "User request: a_bus_req = 1\n"); + } + if (spin_trylock(&lnw->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&lnw->wq_lock); + } + return count; +} +static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUGO, get_a_bus_req, set_a_bus_req); + +static ssize_t +get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *lnw = the_transceiver; + char *next; + unsigned size, t; + + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, "%d", lnw->iotg.hsm.a_bus_drop); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_a_bus_drop(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + + if (!iotg->otg.default_a) + return -1; + if (count > 2) + return -1; + + if (buf[0] == '0') { + iotg->hsm.a_bus_drop = 0; + dev_dbg(lnw->dev, "User request: a_bus_drop = 0\n"); + } else if (buf[0] == '1') { + iotg->hsm.a_bus_drop = 1; + iotg->hsm.a_bus_req = 0; + dev_dbg(lnw->dev, "User request: a_bus_drop = 1\n"); + dev_dbg(lnw->dev, "User request: and a_bus_req = 0\n"); + } + if (spin_trylock(&lnw->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&lnw->wq_lock); + } + return count; +} +static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUGO, + get_a_bus_drop, set_a_bus_drop); + +static ssize_t +get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *lnw = the_transceiver; + char *next; + unsigned size, t; + + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, "%d", lnw->iotg.hsm.b_bus_req); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_b_bus_req(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + + if (iotg->otg.default_a) + return -1; + + if (count > 2) + return -1; + + if (buf[0] == '0') { + iotg->hsm.b_bus_req = 0; + dev_dbg(lnw->dev, "User request: b_bus_req = 0\n"); + } else if (buf[0] == '1') { + iotg->hsm.b_bus_req = 1; + dev_dbg(lnw->dev, "User request: b_bus_req = 1\n"); + } + if (spin_trylock(&lnw->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&lnw->wq_lock); + } + return count; +} +static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUGO, get_b_bus_req, set_b_bus_req); + +static ssize_t +set_a_clr_err(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + + if (!iotg->otg.default_a) + return -1; + if (count > 2) + return -1; + + if (buf[0] == '1') { + iotg->hsm.a_clr_err = 1; + dev_dbg(lnw->dev, "User request: a_clr_err = 1\n"); + } + if (spin_trylock(&lnw->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&lnw->wq_lock); + } + return count; +} +static DEVICE_ATTR(a_clr_err, S_IWUGO, NULL, set_a_clr_err); + +static struct attribute *inputs_attrs[] = { + &dev_attr_a_bus_req.attr, + &dev_attr_a_bus_drop.attr, + &dev_attr_b_bus_req.attr, + &dev_attr_a_clr_err.attr, + NULL, +}; + +static struct attribute_group debug_dev_attr_group = { + .name = "inputs", + .attrs = inputs_attrs, +}; + +static int langwell_otg_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned long resource, len; + void __iomem *base = NULL; + int retval; + u32 val32; + struct langwell_otg *lnw; + char qname[] = "langwell_otg_queue"; + + retval = 0; + dev_dbg(&pdev->dev, "\notg controller is detected.\n"); + if (pci_enable_device(pdev) < 0) { + retval = -ENODEV; + goto done; + } + + lnw = kzalloc(sizeof *lnw, GFP_KERNEL); + if (lnw == NULL) { + retval = -ENOMEM; + goto done; + } + the_transceiver = lnw; + + /* control register: BAR 0 */ + resource = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + if (!request_mem_region(resource, len, driver_name)) { + retval = -EBUSY; + goto err; + } + lnw->region = 1; + + base = ioremap_nocache(resource, len); + if (base == NULL) { + retval = -EFAULT; + goto err; + } + lnw->iotg.base = base; + + if (!request_mem_region(USBCFG_ADDR, USBCFG_LEN, driver_name)) { + retval = -EBUSY; + goto err; + } + lnw->cfg_region = 1; + + /* For the SCCB.USBCFG register */ + base = ioremap_nocache(USBCFG_ADDR, USBCFG_LEN); + if (base == NULL) { + retval = -EFAULT; + goto err; + } + lnw->usbcfg = base; + + if (!pdev->irq) { + dev_dbg(&pdev->dev, "No IRQ.\n"); + retval = -ENODEV; + goto err; + } + + lnw->qwork = create_singlethread_workqueue(qname); + if (!lnw->qwork) { + dev_dbg(&pdev->dev, "cannot create workqueue %s\n", qname); + retval = -ENOMEM; + goto err; + } + INIT_WORK(&lnw->work, langwell_otg_work); + + /* OTG common part */ + lnw->dev = &pdev->dev; + lnw->iotg.otg.dev = lnw->dev; + lnw->iotg.otg.label = driver_name; + lnw->iotg.otg.set_host = langwell_otg_set_host; + lnw->iotg.otg.set_peripheral = langwell_otg_set_peripheral; + lnw->iotg.otg.set_power = langwell_otg_set_power; + lnw->iotg.otg.set_vbus = langwell_otg_set_vbus; + lnw->iotg.otg.start_srp = langwell_otg_start_srp; + lnw->iotg.otg.state = OTG_STATE_UNDEFINED; + + if (otg_set_transceiver(&lnw->iotg.otg)) { + dev_dbg(lnw->dev, "can't set transceiver\n"); + retval = -EBUSY; + goto err; + } + + reset_otg(); + init_hsm(); + + spin_lock_init(&lnw->lock); + spin_lock_init(&lnw->wq_lock); + INIT_LIST_HEAD(&active_timers); + retval = langwell_otg_init_timers(&lnw->iotg.hsm); + if (retval) { + dev_dbg(&pdev->dev, "Failed to init timers\n"); + goto err; + } + + init_timer(&lnw->hsm_timer); + ATOMIC_INIT_NOTIFIER_HEAD(&lnw->iotg.iotg_notifier); + + lnw->iotg_notifier.notifier_call = langwell_otg_iotg_notify; + + retval = intel_mid_otg_register_notifier(&lnw->iotg, + &lnw->iotg_notifier); + if (retval) { + dev_dbg(lnw->dev, "Failed to register notifier\n"); + goto err; + } + + if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, + driver_name, lnw) != 0) { + dev_dbg(lnw->dev, "request interrupt %d failed\n", pdev->irq); + retval = -EBUSY; + goto err; + } + + /* enable OTGSC int */ + val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE | + OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU; + writel(val32, lnw->iotg.base + CI_OTGSC); + + retval = device_create_file(&pdev->dev, &dev_attr_registers); + if (retval < 0) { + dev_dbg(lnw->dev, + "Can't register sysfs attribute: %d\n", retval); + goto err; + } + + retval = device_create_file(&pdev->dev, &dev_attr_hsm); + if (retval < 0) { + dev_dbg(lnw->dev, "Can't hsm sysfs attribute: %d\n", retval); + goto err; + } + + retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group); + if (retval < 0) { + dev_dbg(lnw->dev, + "Can't register sysfs attr group: %d\n", retval); + goto err; + } + + if (lnw->iotg.otg.state == OTG_STATE_A_IDLE) + langwell_update_transceiver(); + + return 0; + +err: + if (the_transceiver) + langwell_otg_remove(pdev); +done: + return retval; +} + +static void langwell_otg_remove(struct pci_dev *pdev) +{ + struct langwell_otg *lnw = the_transceiver; + + if (lnw->qwork) { + flush_workqueue(lnw->qwork); + destroy_workqueue(lnw->qwork); + } + intel_mid_otg_unregister_notifier(&lnw->iotg, &lnw->iotg_notifier); + langwell_otg_free_timers(); + + /* disable OTGSC interrupt as OTGSC doesn't change in reset */ + writel(0, lnw->iotg.base + CI_OTGSC); + + if (pdev->irq) + free_irq(pdev->irq, lnw); + if (lnw->usbcfg) + iounmap(lnw->usbcfg); + if (lnw->cfg_region) + release_mem_region(USBCFG_ADDR, USBCFG_LEN); + if (lnw->iotg.base) + iounmap(lnw->iotg.base); + if (lnw->region) + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + + otg_set_transceiver(NULL); + pci_disable_device(pdev); + sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group); + device_remove_file(&pdev->dev, &dev_attr_hsm); + device_remove_file(&pdev->dev, &dev_attr_registers); + kfree(lnw); + lnw = NULL; +} + +static void transceiver_suspend(struct pci_dev *pdev) +{ + pci_save_state(pdev); + pci_set_power_state(pdev, PCI_D3hot); + langwell_otg_phy_low_power(1); +} + +static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + int ret = 0; + + /* Disbale OTG interrupts */ + langwell_otg_intr(0); + + if (pdev->irq) + free_irq(pdev->irq, lnw); + + /* Prevent more otg_work */ + flush_workqueue(lnw->qwork); + destroy_workqueue(lnw->qwork); + lnw->qwork = NULL; + + /* start actions */ + switch (iotg->otg.state) { + case OTG_STATE_A_WAIT_VFALL: + iotg->otg.state = OTG_STATE_A_IDLE; + case OTG_STATE_A_IDLE: + case OTG_STATE_B_IDLE: + case OTG_STATE_A_VBUS_ERR: + transceiver_suspend(pdev); + break; + case OTG_STATE_A_WAIT_VRISE: + langwell_otg_del_timer(a_wait_vrise_tmr); + iotg->hsm.a_srp_det = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_A_WAIT_BCON: + del_timer_sync(&lnw->hsm_timer); + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(&pdev->dev, "host driver has been removed.\n"); + + iotg->hsm.a_srp_det = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_A_HOST: + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(&pdev->dev, "host driver has been removed.\n"); + + iotg->hsm.a_srp_det = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + + iotg->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_A_SUSPEND: + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, "host driver has been removed.\n"); + iotg->hsm.a_srp_det = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_A_PERIPHERAL: + del_timer_sync(&lnw->hsm_timer); + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(&pdev->dev, + "client driver has been removed.\n"); + iotg->hsm.a_srp_det = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_B_HOST: + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(&pdev->dev, "host driver has been removed.\n"); + iotg->hsm.b_bus_req = 0; + iotg->otg.state = OTG_STATE_B_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_B_PERIPHERAL: + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(&pdev->dev, + "client driver has been removed.\n"); + iotg->otg.state = OTG_STATE_B_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_B_WAIT_ACON: + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); + + langwell_otg_HAAR(0); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(&pdev->dev, "host driver has been removed.\n"); + iotg->hsm.b_bus_req = 0; + iotg->otg.state = OTG_STATE_B_IDLE; + transceiver_suspend(pdev); + break; + default: + dev_dbg(lnw->dev, "error state before suspend\n"); + break; + } + + return ret; +} + +static void transceiver_resume(struct pci_dev *pdev) +{ + pci_restore_state(pdev); + pci_set_power_state(pdev, PCI_D0); +} + +static int langwell_otg_resume(struct pci_dev *pdev) +{ + struct langwell_otg *lnw = the_transceiver; + int ret = 0; + + transceiver_resume(pdev); + + lnw->qwork = create_singlethread_workqueue("langwell_otg_queue"); + if (!lnw->qwork) { + dev_dbg(&pdev->dev, "cannot create langwell otg workqueuen"); + ret = -ENOMEM; + goto error; + } + + if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, + driver_name, lnw) != 0) { + dev_dbg(&pdev->dev, "request interrupt %d failed\n", pdev->irq); + ret = -EBUSY; + goto error; + } + + /* enable OTG interrupts */ + langwell_otg_intr(1); + + update_hsm(); + + langwell_update_transceiver(); + + return ret; +error: + langwell_otg_intr(0); + transceiver_suspend(pdev); + return ret; +} + +static int __init langwell_otg_init(void) +{ + return pci_register_driver(&otg_pci_driver); +} +module_init(langwell_otg_init); + +static void __exit langwell_otg_cleanup(void) +{ + pci_unregister_driver(&otg_pci_driver); +} +module_exit(langwell_otg_cleanup); diff --git a/include/linux/usb/langwell_otg.h b/include/linux/usb/langwell_otg.h new file mode 100644 index 000000000000..a6562f1d4e2b --- /dev/null +++ b/include/linux/usb/langwell_otg.h @@ -0,0 +1,139 @@ +/* + * Intel Langwell USB OTG transceiver driver + * Copyright (C) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef __LANGWELL_OTG_H +#define __LANGWELL_OTG_H + +#include + +#define CI_USBCMD 0x30 +# define USBCMD_RST BIT(1) +# define USBCMD_RS BIT(0) +#define CI_USBSTS 0x34 +# define USBSTS_SLI BIT(8) +# define USBSTS_URI BIT(6) +# define USBSTS_PCI BIT(2) +#define CI_PORTSC1 0x74 +# define PORTSC_PP BIT(12) +# define PORTSC_LS (BIT(11) | BIT(10)) +# define PORTSC_SUSP BIT(7) +# define PORTSC_CCS BIT(0) +#define CI_HOSTPC1 0xb4 +# define HOSTPC1_PHCD BIT(22) +#define CI_OTGSC 0xf4 +# define OTGSC_DPIE BIT(30) +# define OTGSC_1MSE BIT(29) +# define OTGSC_BSEIE BIT(28) +# define OTGSC_BSVIE BIT(27) +# define OTGSC_ASVIE BIT(26) +# define OTGSC_AVVIE BIT(25) +# define OTGSC_IDIE BIT(24) +# define OTGSC_DPIS BIT(22) +# define OTGSC_1MSS BIT(21) +# define OTGSC_BSEIS BIT(20) +# define OTGSC_BSVIS BIT(19) +# define OTGSC_ASVIS BIT(18) +# define OTGSC_AVVIS BIT(17) +# define OTGSC_IDIS BIT(16) +# define OTGSC_DPS BIT(14) +# define OTGSC_1MST BIT(13) +# define OTGSC_BSE BIT(12) +# define OTGSC_BSV BIT(11) +# define OTGSC_ASV BIT(10) +# define OTGSC_AVV BIT(9) +# define OTGSC_ID BIT(8) +# define OTGSC_HABA BIT(7) +# define OTGSC_HADP BIT(6) +# define OTGSC_IDPU BIT(5) +# define OTGSC_DP BIT(4) +# define OTGSC_OT BIT(3) +# define OTGSC_HAAR BIT(2) +# define OTGSC_VC BIT(1) +# define OTGSC_VD BIT(0) +# define OTGSC_INTEN_MASK (0x7f << 24) +# define OTGSC_INT_MASK (0x5f << 24) +# define OTGSC_INTSTS_MASK (0x7f << 16) +#define CI_USBMODE 0xf8 +# define USBMODE_CM (BIT(1) | BIT(0)) +# define USBMODE_IDLE 0 +# define USBMODE_DEVICE 0x2 +# define USBMODE_HOST 0x3 +#define USBCFG_ADDR 0xff10801c +#define USBCFG_LEN 4 +# define USBCFG_VBUSVAL BIT(14) +# define USBCFG_AVALID BIT(13) +# define USBCFG_BVALID BIT(12) +# define USBCFG_SESEND BIT(11) + +#define INTR_DUMMY_MASK (USBSTS_SLI | USBSTS_URI | USBSTS_PCI) + +enum langwell_otg_timer_type { + TA_WAIT_VRISE_TMR, + TA_WAIT_BCON_TMR, + TA_AIDL_BDIS_TMR, + TB_ASE0_BRST_TMR, + TB_SE0_SRP_TMR, + TB_SRP_INIT_TMR, + TB_SRP_FAIL_TMR, + TB_BUS_SUSPEND_TMR +}; + +#define TA_WAIT_VRISE 100 +#define TA_WAIT_BCON 30000 +#define TA_AIDL_BDIS 15000 +#define TB_ASE0_BRST 5000 +#define TB_SE0_SRP 2 +#define TB_SRP_INIT 100 +#define TB_SRP_FAIL 5500 +#define TB_BUS_SUSPEND 500 + +struct langwell_otg_timer { + unsigned long expires; /* Number of count increase to timeout */ + unsigned long count; /* Tick counter */ + void (*function)(unsigned long); /* Timeout function */ + unsigned long data; /* Data passed to function */ + struct list_head list; +}; + +struct langwell_otg { + struct intel_mid_otg_xceiv iotg; + struct device *dev; + + void __iomem *usbcfg; /* SCCBUSB config Reg */ + + unsigned region; + unsigned cfg_region; + + struct work_struct work; + struct workqueue_struct *qwork; + struct timer_list hsm_timer; + + spinlock_t lock; + spinlock_t wq_lock; + + struct notifier_block iotg_notifier; +}; + +static inline +struct langwell_otg *mid_xceiv_to_lnw(struct intel_mid_otg_xceiv *iotg) +{ + return container_of(iotg, struct langwell_otg, iotg); +} + +#endif /* __LANGWELL_OTG_H__ */ -- cgit v1.2.3 From 37b5801e16d2e192fe2b20f4af33aa8c6e8786f3 Mon Sep 17 00:00:00 2001 From: Parirajan Muthalagu Date: Wed, 25 Aug 2010 16:33:26 +0530 Subject: USB Gadget: Verify VBUS current before setting the device self-powered bit Acked-by: David Brownell Acked-by: Linus Walleij Signed-off-by: Praveena Nadahally Signed-off-by: Parirajan Muthalagu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 8 +++++++- include/linux/usb/ch9.h | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 1160c55de7f2..eaa9a599df63 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1074,7 +1074,13 @@ static int composite_bind(struct usb_gadget *gadget) cdev->bufsiz = USB_BUFSIZ; cdev->driver = composite; - usb_gadget_set_selfpowered(gadget); + /* + * As per USB compliance update, a device that is actively drawing + * more than 100mA from USB must report itself as bus-powered in + * the GetStatus(DEVICE) call. + */ + if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW) + usb_gadget_set_selfpowered(gadget); /* interface and string IDs start at zero via kzalloc. * we force endpoints to start unassigned; few controller diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index da2ed77d3e8d..b0f7e9f57176 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -808,4 +808,14 @@ enum usb_device_state { */ }; +/*-------------------------------------------------------------------------*/ + +/* + * As per USB compliance update, a device that is actively drawing + * more than 100mA from USB must report itself as bus-powered in + * the GetStatus(DEVICE) call. + * http://compliance.usb.org/index.asp?UpdateFile=Electrical&Format=Standard#34 + */ +#define USB_SELF_POWER_VBUS_MAX_DRAW 100 + #endif /* __LINUX_USB_CH9_H */ -- cgit v1.2.3 From ad1a8102f957f4d25fc58cdc10736c5ade7557e1 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Thu, 12 Aug 2010 17:43:46 +0200 Subject: USB: gadget: composite: Better string override handling The iManufatcurer, iProduct and iSerialNumber composite module parameters were only used when the gadget driver registers strings for manufacturer, product and serial number. If the gadget never bothered to set corresponding fields in USB device descriptors those module parameters are ignored. This commit makes the parameters work even if the strings ID have not been assigned. It also changes the way IDs are overridden -- what IDs are overridden is now saved in usb_composite_dev structure -- which makes it unnecessary to modify the string tables the way previous code did. The commit also adds a iProduct and iManufatcurer fields to the usb_composite_device structure. If they are set, appropriate strings are reserved and added to device descriptor. This makes it unnecessary for gadget drivers to maintain code for setting those. If iProduct is not set it defaults to usb_composite_device::name; if iManufatcurer is not set a default " with " is used. The last thing is that if needs_serial field of usb_composite_device is set and user failed to provided iSerialNumber parameter a warning is issued. Signed-off-by: Michal Nazarewicz Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 96 ++++++++++++++++++++++++++++-------------- include/linux/usb/composite.h | 13 ++++++ 2 files changed, 77 insertions(+), 32 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index eaa9a599df63..717de39627c7 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -69,6 +70,8 @@ static char *iSerialNumber; module_param(iSerialNumber, charp, 0); MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); +static char composite_manufacturer[50]; + /*-------------------------------------------------------------------------*/ /** @@ -599,6 +602,7 @@ static int get_string(struct usb_composite_dev *cdev, struct usb_configuration *c; struct usb_function *f; int len; + const char *str; /* Yes, not only is USB's I18N support probably more than most * folk will ever care about ... also, it's all supported here. @@ -638,9 +642,29 @@ static int get_string(struct usb_composite_dev *cdev, return s->bLength; } - /* Otherwise, look up and return a specified string. String IDs - * are device-scoped, so we look up each string table we're told - * about. These lookups are infrequent; simpler-is-better here. + /* Otherwise, look up and return a specified string. First + * check if the string has not been overridden. + */ + if (cdev->manufacturer_override == id) + str = iManufacturer ?: composite->iManufacturer ?: + composite_manufacturer; + else if (cdev->product_override == id) + str = iProduct ?: composite->iProduct; + else if (cdev->serial_override == id) + str = iSerialNumber; + else + str = NULL; + if (str) { + struct usb_gadget_strings strings = { + .language = language, + .strings = &(struct usb_string) { 0xff, str } + }; + return usb_gadget_get_string(&strings, 0xff, buf); + } + + /* String IDs are device-scoped, so we look up each string + * table we're told about. These lookups are infrequent; + * simpler-is-better here. */ if (composite->strings) { len = lookup_string(composite->strings, buf, language, id); @@ -1025,26 +1049,17 @@ composite_unbind(struct usb_gadget *gadget) composite = NULL; } -static void -string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) +static u8 override_id(struct usb_composite_dev *cdev, u8 *desc) { - struct usb_string *str = tab->strings; - - for (str = tab->strings; str->s; str++) { - if (str->id == id) { - str->s = s; - return; - } + if (!*desc) { + int ret = usb_string_id(cdev); + if (unlikely(ret < 0)) + WARNING(cdev, "failed to override string ID\n"); + else + *desc = ret; } -} -static void -string_override(struct usb_gadget_strings **tab, u8 id, const char *s) -{ - while (*tab) { - string_override_one(*tab, id, s); - tab++; - } + return *desc; } static int composite_bind(struct usb_gadget *gadget) @@ -1107,19 +1122,34 @@ static int composite_bind(struct usb_gadget *gadget) cdev->desc = *composite->dev; cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - /* strings can't be assigned before bind() allocates the - * releavnt identifiers - */ - if (cdev->desc.iManufacturer && iManufacturer) - string_override(composite->strings, - cdev->desc.iManufacturer, iManufacturer); - if (cdev->desc.iProduct && iProduct) - string_override(composite->strings, - cdev->desc.iProduct, iProduct); - if (cdev->desc.iSerialNumber && iSerialNumber) - string_override(composite->strings, - cdev->desc.iSerialNumber, iSerialNumber); + /* stirng overrides */ + if (iManufacturer || !cdev->desc.iManufacturer) { + if (!iManufacturer && !composite->iManufacturer && + !*composite_manufacturer) + snprintf(composite_manufacturer, + sizeof composite_manufacturer, + "%s %s with %s", + init_utsname()->sysname, + init_utsname()->release, + gadget->name); + + cdev->manufacturer_override = + override_id(cdev, &cdev->desc.iManufacturer); + } + + if (iProduct || (!cdev->desc.iProduct && composite->iProduct)) + cdev->product_override = + override_id(cdev, &cdev->desc.iProduct); + + if (iSerialNumber) + cdev->serial_override = + override_id(cdev, &cdev->desc.iSerialNumber); + + /* has userspace failed to provide a serial number? */ + if (composite->needs_serial && !cdev->desc.iSerialNumber) + WARNING(cdev, "userspace failed to provide iSerialNumber\n"); + /* finish up */ status = device_create_file(&gadget->dev, &dev_attr_suspended); if (status) goto fail; @@ -1217,6 +1247,8 @@ int usb_composite_register(struct usb_composite_driver *driver) if (!driver || !driver->dev || !driver->bind || composite) return -EINVAL; + if (!driver->iProduct) + driver->iProduct = driver->name; if (!driver->name) driver->name = "composite"; composite_driver.function = (char *) driver->name; diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 617068134ae8..a78e813d27e4 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -237,10 +237,17 @@ int usb_add_config(struct usb_composite_dev *, /** * struct usb_composite_driver - groups configurations into a gadget * @name: For diagnostics, identifies the driver. + * @iProduct: Used as iProduct override if @dev->iProduct is not set. + * If NULL value of @name is taken. + * @iManufacturer: Used as iManufacturer override if @dev->iManufacturer is + * not set. If NULL a default " with " value + * will be used. * @dev: Template descriptor for the device, including default device * identifiers. * @strings: tables of strings, keyed by identifiers assigned during bind() * and language IDs provided in control requests + * @needs_serial: set to 1 if the gadget needs userspace to provide + * a serial number. If one is not provided, warning will be printed. * @bind: (REQUIRED) Used to allocate resources that are shared across the * whole device, such as string IDs, and add its configurations using * @usb_add_config(). This may fail by returning a negative errno @@ -266,8 +273,11 @@ int usb_add_config(struct usb_composite_dev *, */ struct usb_composite_driver { const char *name; + const char *iProduct; + const char *iManufacturer; const struct usb_device_descriptor *dev; struct usb_gadget_strings **strings; + unsigned needs_serial:1; /* REVISIT: bind() functions can be marked __init, which * makes trouble for section mismatch analysis. See if @@ -334,6 +344,9 @@ struct usb_composite_dev { struct list_head configs; struct usb_composite_driver *driver; u8 next_string_id; + u8 manufacturer_override; + u8 product_override; + u8 serial_override; /* the gadget driver won't enable the data pullup * while the deactivation count is nonzero. -- cgit v1.2.3 From b0fca50f5a94a268ed02cfddf44448051ed9343f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 12 Aug 2010 17:43:53 +0200 Subject: usb gadget: don't save bind callback in struct usb_gadget_driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To accomplish this the function to register a gadget driver takes the bind function as a second argument. To make things clearer rename the function to resemble platform_driver_probe. This fixes many section mismatches like WARNING: drivers/usb/gadget/g_printer.o(.data+0xc): Section mismatch in reference from the variable printer_driver to the function .init.text:printer_bind() The variable printer_driver references the function __init printer_bind() All callers are fixed. Signed-off-by: Uwe Kleine-König [m.nazarewicz@samsung.com: added dbgp] Signed-off-by: Michał Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/amd5536udc.c | 9 +++++---- drivers/usb/gadget/at91_udc.c | 11 ++++++----- drivers/usb/gadget/atmel_usba_udc.c | 7 ++++--- drivers/usb/gadget/ci13xxx_udc.c | 18 ++++++++++-------- drivers/usb/gadget/composite.c | 3 +-- drivers/usb/gadget/dbgp.c | 3 +-- drivers/usb/gadget/dummy_hcd.c | 10 +++++----- drivers/usb/gadget/file_storage.c | 3 +-- drivers/usb/gadget/fsl_qe_udc.c | 12 ++++++------ drivers/usb/gadget/fsl_udc_core.c | 10 +++++----- drivers/usb/gadget/gmidi.c | 3 +-- drivers/usb/gadget/goku_udc.c | 9 +++++---- drivers/usb/gadget/imx_udc.c | 9 +++++---- drivers/usb/gadget/inode.c | 6 ++---- drivers/usb/gadget/langwell_udc.c | 9 +++++---- drivers/usb/gadget/lh7a40x_udc.c | 10 +++++----- drivers/usb/gadget/m66592-udc.c | 9 +++++---- drivers/usb/gadget/net2280.c | 10 +++++----- drivers/usb/gadget/omap_udc.c | 10 +++++----- drivers/usb/gadget/printer.c | 5 ++--- drivers/usb/gadget/pxa25x_udc.c | 9 +++++---- drivers/usb/gadget/pxa27x_udc.c | 12 +++++++----- drivers/usb/gadget/r8a66597-udc.c | 9 +++++---- drivers/usb/gadget/s3c-hsotg.c | 9 +++++---- drivers/usb/gadget/s3c2410_udc.c | 17 ++++++++--------- drivers/usb/musb/musb_gadget.c | 11 ++++++----- include/linux/usb/gadget.h | 20 ++++++++------------ 27 files changed, 128 insertions(+), 125 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 731150d4b1d9..fadebfd53b47 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -1954,13 +1954,14 @@ static int setup_ep0(struct udc *dev) } /* Called by gadget driver to register itself */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct udc *dev = udc; int retval; u32 tmp; - if (!driver || !driver->bind || !driver->setup + if (!driver || !bind || !driver->setup || driver->speed != USB_SPEED_HIGH) return -EINVAL; if (!dev) @@ -1972,7 +1973,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) dev->driver = driver; dev->gadget.dev.driver = &driver->driver; - retval = driver->bind(&dev->gadget); + retval = bind(&dev->gadget); /* Some gadget drivers use both ep0 directions. * NOTE: to gadget driver, ep0 is just one endpoint... @@ -2000,7 +2001,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) return 0; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); /* shutdown requests and disconnect from gadget */ static void diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 93ead19507b6..387e503b9d14 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1628,7 +1628,8 @@ static void at91_vbus_timer(unsigned long data) schedule_work(&udc->vbus_timer_work); } -int usb_gadget_register_driver (struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct at91_udc *udc = &controller; int retval; @@ -1636,7 +1637,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) if (!driver || driver->speed < USB_SPEED_FULL - || !driver->bind + || !bind || !driver->setup) { DBG("bad parameter.\n"); return -EINVAL; @@ -1653,9 +1654,9 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) udc->enabled = 1; udc->selfpowered = 1; - retval = driver->bind(&udc->gadget); + retval = bind(&udc->gadget); if (retval) { - DBG("driver->bind() returned %d\n", retval); + DBG("bind() returned %d\n", retval); udc->driver = NULL; udc->gadget.dev.driver = NULL; dev_set_drvdata(&udc->gadget.dev, NULL); @@ -1671,7 +1672,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) DBG("bound to %s\n", driver->driver.name); return 0; } -EXPORT_SYMBOL (usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) { diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index d623c7bda1f6..e4810c6a0b1f 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1789,7 +1789,8 @@ out: return IRQ_HANDLED; } -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct usba_udc *udc = &the_udc; unsigned long flags; @@ -1812,7 +1813,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) clk_enable(udc->pclk); clk_enable(udc->hclk); - ret = driver->bind(&udc->gadget); + ret = bind(&udc->gadget); if (ret) { DBG(DBG_ERR, "Could not bind to driver %s: error %d\n", driver->driver.name, ret); @@ -1841,7 +1842,7 @@ err_driver_bind: udc->gadget.dev.driver = NULL; return ret; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 699695128e33..98b36fc88c77 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -2340,12 +2340,15 @@ static const struct usb_ep_ops usb_ep_ops = { static const struct usb_gadget_ops usb_gadget_ops; /** - * usb_gadget_register_driver: register a gadget driver + * usb_gadget_probe_driver: register a gadget driver + * @driver: the driver being registered + * @bind: the driver's bind callback * - * Check usb_gadget_register_driver() at "usb_gadget.h" for details - * Interrupts are enabled here + * Check usb_gadget_probe_driver() at for details. + * Interrupts are enabled here. */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct ci13xxx *udc = _udc; unsigned long i, k, flags; @@ -2354,7 +2357,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) trace("%p", driver); if (driver == NULL || - driver->bind == NULL || + bind == NULL || driver->unbind == NULL || driver->setup == NULL || driver->disconnect == NULL || @@ -2430,7 +2433,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) udc->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(udc->lock, flags); - retval = driver->bind(&udc->gadget); /* MAY SLEEP */ + retval = bind(&udc->gadget); /* MAY SLEEP */ spin_lock_irqsave(udc->lock, flags); if (retval) { @@ -2447,7 +2450,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) usb_gadget_unregister_driver(driver); return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); /** * usb_gadget_unregister_driver: unregister a gadget driver @@ -2462,7 +2465,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) trace("%p", driver); if (driver == NULL || - driver->bind == NULL || driver->unbind == NULL || driver->setup == NULL || driver->disconnect == NULL || diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 717de39627c7..a3009bf01229 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1213,7 +1213,6 @@ composite_resume(struct usb_gadget *gadget) static struct usb_gadget_driver composite_driver = { .speed = USB_SPEED_HIGH, - .bind = composite_bind, .unbind = composite_unbind, .setup = composite_setup, @@ -1255,7 +1254,7 @@ int usb_composite_register(struct usb_composite_driver *driver) composite_driver.driver.name = driver->name; composite = driver; - return usb_gadget_register_driver(&composite_driver); + return usb_gadget_probe_driver(&composite_driver, composite_bind); } /** diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index abe4a2ec5625..e5ac8a316fec 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -403,7 +403,6 @@ fail: static struct usb_gadget_driver dbgp_driver = { .function = "dbgp", .speed = USB_SPEED_HIGH, - .bind = dbgp_bind, .unbind = dbgp_unbind, .setup = dbgp_setup, .disconnect = dbgp_disconnect, @@ -415,7 +414,7 @@ static struct usb_gadget_driver dbgp_driver = { static int __init dbgp_init(void) { - return usb_gadget_register_driver(&dbgp_driver); + return usb_gadget_probe_driver(&dbgp_driver, dbgp_bind); } static void __exit dbgp_exit(void) diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index dc6546248ed9..7bb9d78aac27 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -748,7 +748,8 @@ static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); */ int -usb_gadget_register_driver (struct usb_gadget_driver *driver) +usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct dummy *dum = the_controller; int retval, i; @@ -757,8 +758,7 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) return -EINVAL; if (dum->driver) return -EBUSY; - if (!driver->bind || !driver->setup - || driver->speed == USB_SPEED_UNKNOWN) + if (!bind || !driver->setup || driver->speed == USB_SPEED_UNKNOWN) return -EINVAL; /* @@ -796,7 +796,7 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) dum->gadget.dev.driver = &driver->driver; dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n", driver->driver.name); - retval = driver->bind(&dum->gadget); + retval = bind(&dum->gadget); if (retval) { dum->driver = NULL; dum->gadget.dev.driver = NULL; @@ -812,7 +812,7 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0; } -EXPORT_SYMBOL (usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index aae04c20b0ea..132a1c0877bd 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -3608,7 +3608,6 @@ static struct usb_gadget_driver fsg_driver = { .speed = USB_SPEED_FULL, #endif .function = (char *) fsg_string_product, - .bind = fsg_bind, .unbind = fsg_unbind, .disconnect = fsg_disconnect, .setup = fsg_setup, @@ -3650,7 +3649,7 @@ static int __init fsg_init(void) if ((rc = fsg_alloc()) != 0) return rc; fsg = the_fsg; - if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0) + if ((rc = usb_gadget_probe_driver(&fsg_driver, fsg_bind)) != 0) kref_put(&fsg->ref, fsg_release); return rc; } diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index a5ea2c1d8c93..792d5ef40137 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2302,9 +2302,10 @@ static irqreturn_t qe_udc_irq(int irq, void *_udc) } /*------------------------------------------------------------------------- - Gadget driver register and unregister. + Gadget driver probe and unregister. --------------------------------------------------------------------------*/ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { int retval; unsigned long flags = 0; @@ -2315,8 +2316,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) if (!driver || (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH) - || !driver->bind || !driver->disconnect - || !driver->setup) + || !bind || !driver->disconnect || !driver->setup) return -EINVAL; if (udc_controller->driver) @@ -2332,7 +2332,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) udc_controller->gadget.speed = (enum usb_device_speed)(driver->speed); spin_unlock_irqrestore(&udc_controller->lock, flags); - retval = driver->bind(&udc_controller->gadget); + retval = bind(&udc_controller->gadget); if (retval) { dev_err(udc_controller->dev, "bind to %s --> %d", driver->driver.name, retval); @@ -2353,7 +2353,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) udc_controller->gadget.name, driver->driver.name); return 0; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 08a9a62a39e3..c16b402a876b 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -1765,7 +1765,8 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) * Hook to gadget drivers * Called by initialization code of gadget drivers *----------------------------------------------------------------*/ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { int retval = -ENODEV; unsigned long flags = 0; @@ -1775,8 +1776,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) if (!driver || (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH) - || !driver->bind || !driver->disconnect - || !driver->setup) + || !bind || !driver->disconnect || !driver->setup) return -EINVAL; if (udc_controller->driver) @@ -1792,7 +1792,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) spin_unlock_irqrestore(&udc_controller->lock, flags); /* bind udc driver to gadget driver */ - retval = driver->bind(&udc_controller->gadget); + retval = bind(&udc_controller->gadget); if (retval) { VDBG("bind to %s --> %d", driver->driver.name, retval); udc_controller->gadget.dev.driver = NULL; @@ -1814,7 +1814,7 @@ out: retval); return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); /* Disconnect from gadget driver */ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index b7bf88019b06..0ab7e141d494 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -1292,7 +1292,6 @@ static void gmidi_resume(struct usb_gadget *gadget) static struct usb_gadget_driver gmidi_driver = { .speed = USB_SPEED_FULL, .function = (char *)longname, - .bind = gmidi_bind, .unbind = gmidi_unbind, .setup = gmidi_setup, @@ -1309,7 +1308,7 @@ static struct usb_gadget_driver gmidi_driver = { static int __init gmidi_init(void) { - return usb_gadget_register_driver(&gmidi_driver); + return usb_gadget_probe_driver(&gmidi_driver, gmidi_bind); } module_init(gmidi_init); diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 1088d08c7ed8..49fbd4dbeb94 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1343,14 +1343,15 @@ static struct goku_udc *the_controller; * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct goku_udc *dev = the_controller; int retval; if (!driver || driver->speed < USB_SPEED_FULL - || !driver->bind + || !bind || !driver->disconnect || !driver->setup) return -EINVAL; @@ -1363,7 +1364,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) driver->driver.bus = NULL; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; - retval = driver->bind(&dev->gadget); + retval = bind(&dev->gadget); if (retval) { DBG(dev, "bind to driver %s --> error %d\n", driver->driver.name, retval); @@ -1380,7 +1381,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) DBG(dev, "registered gadget driver '%s'\n", driver->driver.name); return 0; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); static void stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver) diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index e743122fcd93..ed0266462c57 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -1319,14 +1319,15 @@ static struct imx_udc_struct controller = { * USB gadged driver functions ******************************************************************************* */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct imx_udc_struct *imx_usb = &controller; int retval; if (!driver || driver->speed < USB_SPEED_FULL - || !driver->bind + || !bind || !driver->disconnect || !driver->setup) return -EINVAL; @@ -1342,7 +1343,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) retval = device_add(&imx_usb->gadget.dev); if (retval) goto fail; - retval = driver->bind(&imx_usb->gadget); + retval = bind(&imx_usb->gadget); if (retval) { D_ERR(imx_usb->dev, "<%s> bind to driver %s --> error %d\n", __func__, driver->driver.name, retval); @@ -1362,7 +1363,7 @@ fail: imx_usb->gadget.dev.driver = NULL; return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index fc35406fc80c..e2e8eda83f0d 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -1775,7 +1775,6 @@ static struct usb_gadget_driver gadgetfs_driver = { .speed = USB_SPEED_FULL, #endif .function = (char *) driver_desc, - .bind = gadgetfs_bind, .unbind = gadgetfs_unbind, .setup = gadgetfs_setup, .disconnect = gadgetfs_disconnect, @@ -1798,7 +1797,6 @@ static int gadgetfs_probe (struct usb_gadget *gadget) static struct usb_gadget_driver probe_driver = { .speed = USB_SPEED_HIGH, - .bind = gadgetfs_probe, .unbind = gadgetfs_nop, .setup = (void *)gadgetfs_nop, .disconnect = gadgetfs_nop, @@ -1908,7 +1906,7 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) /* triggers gadgetfs_bind(); then we can enumerate. */ spin_unlock_irq (&dev->lock); - value = usb_gadget_register_driver (&gadgetfs_driver); + value = usb_gadget_probe_driver(&gadgetfs_driver, gadgetfs_bind); if (value != 0) { kfree (dev->buf); dev->buf = NULL; @@ -2047,7 +2045,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent) return -ESRCH; /* fake probe to determine $CHIP */ - (void) usb_gadget_register_driver (&probe_driver); + (void) usb_gadget_probe_driver(&probe_driver, gadgetfs_probe); if (!CHIP) return -ENODEV; diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index ccc9c070a30d..1ef17a6dcb51 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -1855,7 +1855,8 @@ static DEVICE_ATTR(remote_wakeup, S_IWUSR, NULL, store_remote_wakeup); * the driver might get unbound. */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct langwell_udc *dev = the_controller; unsigned long flags; @@ -1878,7 +1879,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) spin_unlock_irqrestore(&dev->lock, flags); - retval = driver->bind(&dev->gadget); + retval = bind(&dev->gadget); if (retval) { dev_dbg(&dev->pdev->dev, "bind to driver %s --> %d\n", driver->driver.name, retval); @@ -1916,7 +1917,7 @@ err_unbind: dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__); return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); /* unregister gadget driver */ @@ -1930,7 +1931,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); - if (unlikely(!driver || !driver->bind || !driver->unbind)) + if (unlikely(!driver || !driver->unbind)) return -EINVAL; /* exit PHY low power suspend */ diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c index fded3fca793b..6b58bd8ce623 100644 --- a/drivers/usb/gadget/lh7a40x_udc.c +++ b/drivers/usb/gadget/lh7a40x_udc.c @@ -408,7 +408,8 @@ static void udc_enable(struct lh7a40x_udc *dev) /* Register entry point for the peripheral controller driver. */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct lh7a40x_udc *dev = the_controller; int retval; @@ -417,7 +418,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) if (!driver || driver->speed != USB_SPEED_FULL - || !driver->bind + || !bind || !driver->disconnect || !driver->setup) return -EINVAL; @@ -431,7 +432,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) dev->gadget.dev.driver = &driver->driver; device_add(&dev->gadget.dev); - retval = driver->bind(&dev->gadget); + retval = bind(&dev->gadget); if (retval) { printk(KERN_WARNING "%s: bind to driver %s --> error %d\n", dev->gadget.name, driver->driver.name, retval); @@ -453,8 +454,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) return 0; } - -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); /* Unregister entry point for the peripheral controller driver. diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index e03058fe23cb..51b19f3027e7 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1454,14 +1454,15 @@ static struct usb_ep_ops m66592_ep_ops = { /*-------------------------------------------------------------------------*/ static struct m66592 *the_controller; -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct m66592 *m66592 = the_controller; int retval; if (!driver || driver->speed != USB_SPEED_HIGH - || !driver->bind + || !bind || !driver->setup) return -EINVAL; if (!m66592) @@ -1480,7 +1481,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) goto error; } - retval = driver->bind (&m66592->gadget); + retval = bind(&m66592->gadget); if (retval) { pr_err("bind to driver error (%d)\n", retval); device_del(&m66592->gadget.dev); @@ -1505,7 +1506,7 @@ error: return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 9498be87a724..d09155b25d73 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -1929,7 +1929,8 @@ static void ep0_start (struct net2280 *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -int usb_gadget_register_driver (struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct net2280 *dev = the_controller; int retval; @@ -1941,8 +1942,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) */ if (!driver || driver->speed != USB_SPEED_HIGH - || !driver->bind - || !driver->setup) + || !bind || !driver->setup) return -EINVAL; if (!dev) return -ENODEV; @@ -1957,7 +1957,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) driver->driver.bus = NULL; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; - retval = driver->bind (&dev->gadget); + retval = bind(&dev->gadget); if (retval) { DEBUG (dev, "bind to driver %s --> %d\n", driver->driver.name, retval); @@ -1993,7 +1993,7 @@ err_unbind: dev->driver = NULL; return retval; } -EXPORT_SYMBOL (usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); static void stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index f81e4f025f23..61d3ca6619bb 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2102,7 +2102,8 @@ static inline int machine_without_vbus_sense(void) ); } -int usb_gadget_register_driver (struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { int status = -ENODEV; struct omap_ep *ep; @@ -2114,8 +2115,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) if (!driver // FIXME if otg, check: driver->is_otg || driver->speed < USB_SPEED_FULL - || !driver->bind - || !driver->setup) + || !bind || !driver->setup) return -EINVAL; spin_lock_irqsave(&udc->lock, flags); @@ -2145,7 +2145,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) if (udc->dc_clk != NULL) omap_udc_enable_clock(1); - status = driver->bind (&udc->gadget); + status = bind(&udc->gadget); if (status) { DBG("bind to %s --> %d\n", driver->driver.name, status); udc->gadget.dev.driver = NULL; @@ -2186,7 +2186,7 @@ done: omap_udc_enable_clock(0); return status; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) { diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 04c7bbf121af..ded080a1c8ce 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -1543,7 +1543,6 @@ static struct usb_gadget_driver printer_driver = { .speed = DEVSPEED, .function = (char *) driver_desc, - .bind = printer_bind, .unbind = printer_unbind, .setup = printer_setup, @@ -1579,11 +1578,11 @@ init(void) return status; } - status = usb_gadget_register_driver(&printer_driver); + status = usb_gadget_probe_driver(&printer_driver, printer_bind); if (status) { class_destroy(usb_gadget_class); unregister_chrdev_region(g_printer_devno, 1); - DBG(dev, "usb_gadget_register_driver %x\n", status); + DBG(dev, "usb_gadget_probe_driver %x\n", status); } return status; diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index be5fb34d9602..b37f92cb71bc 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -1280,14 +1280,15 @@ static void udc_enable (struct pxa25x_udc *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct pxa25x_udc *dev = the_controller; int retval; if (!driver || driver->speed < USB_SPEED_FULL - || !driver->bind + || !bind || !driver->disconnect || !driver->setup) return -EINVAL; @@ -1308,7 +1309,7 @@ fail: dev->gadget.dev.driver = NULL; return retval; } - retval = driver->bind(&dev->gadget); + retval = bind(&dev->gadget); if (retval) { DMSG("bind to driver %s --> error %d\n", driver->driver.name, retval); @@ -1338,7 +1339,7 @@ fail: bind_fail: return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); static void stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 980762453a9c..027d66f81620 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -1792,8 +1792,9 @@ static void udc_enable(struct pxa_udc *udc) } /** - * usb_gadget_register_driver - Register gadget driver + * usb_gadget_probe_driver - Register gadget driver * @driver: gadget driver + * @bind: bind function * * When a driver is successfully registered, it will receive control requests * including set_configuration(), which enables non-control requests. Then @@ -1805,12 +1806,13 @@ static void udc_enable(struct pxa_udc *udc) * * Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct pxa_udc *udc = the_controller; int retval; - if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind + if (!driver || driver->speed < USB_SPEED_FULL || !bind || !driver->disconnect || !driver->setup) return -EINVAL; if (!udc) @@ -1828,7 +1830,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) dev_err(udc->dev, "device_add error %d\n", retval); goto add_fail; } - retval = driver->bind(&udc->gadget); + retval = bind(&udc->gadget); if (retval) { dev_err(udc->dev, "bind to driver %s --> error %d\n", driver->driver.name, retval); @@ -1859,7 +1861,7 @@ add_fail: udc->gadget.dev.driver = NULL; return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); /** diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 2456ccd9965e..95092151f901 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1405,14 +1405,15 @@ static struct usb_ep_ops r8a66597_ep_ops = { /*-------------------------------------------------------------------------*/ static struct r8a66597 *the_controller; -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct r8a66597 *r8a66597 = the_controller; int retval; if (!driver || driver->speed != USB_SPEED_HIGH - || !driver->bind + || !bind || !driver->setup) return -EINVAL; if (!r8a66597) @@ -1431,7 +1432,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) goto error; } - retval = driver->bind(&r8a66597->gadget); + retval = bind(&r8a66597->gadget); if (retval) { printk(KERN_ERR "bind to driver error (%d)\n", retval); device_del(&r8a66597->gadget.dev); @@ -1456,7 +1457,7 @@ error: return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index a229744a8c7d..ef825c3baed9 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2523,7 +2523,8 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg) return 0; } -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct s3c_hsotg *hsotg = our_hsotg; int ret; @@ -2543,7 +2544,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) dev_err(hsotg->dev, "%s: bad speed\n", __func__); } - if (!driver->bind || !driver->setup) { + if (!bind || !driver->setup) { dev_err(hsotg->dev, "%s: missing entry points\n", __func__); return -EINVAL; } @@ -2562,7 +2563,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) goto err; } - ret = driver->bind(&hsotg->gadget); + ret = bind(&hsotg->gadget); if (ret) { dev_err(hsotg->dev, "failed bind %s\n", driver->driver.name); @@ -2687,7 +2688,7 @@ err: hsotg->gadget.dev.driver = NULL; return ret; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index ea2b3c7ebee5..c2448950a8d8 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1632,15 +1632,15 @@ static void s3c2410_udc_enable(struct s3c2410_udc *dev) } /* - * usb_gadget_register_driver + * usb_gadget_probe_driver */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct s3c2410_udc *udc = the_controller; int retval; - dprintk(DEBUG_NORMAL, "usb_gadget_register_driver() '%s'\n", - driver->driver.name); + dprintk(DEBUG_NORMAL, "%s() '%s'\n", __func__, driver->driver.name); /* Sanity checks */ if (!udc) @@ -1649,10 +1649,9 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) if (udc->driver) return -EBUSY; - if (!driver->bind || !driver->setup - || driver->speed < USB_SPEED_FULL) { + if (!bind || !driver->setup || driver->speed < USB_SPEED_FULL) { printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n", - driver->bind, driver->setup, driver->speed); + bind, driver->setup, driver->speed); return -EINVAL; } #if defined(MODULE) @@ -1675,7 +1674,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n", driver->driver.name); - if ((retval = driver->bind (&udc->gadget)) != 0) { + if ((retval = bind(&udc->gadget)) != 0) { device_del(&udc->gadget.dev); goto register_error; } @@ -1690,6 +1689,7 @@ register_error: udc->gadget.dev.driver = NULL; return retval; } +EXPORT_SYMBOL(usb_gadget_probe_driver); /* * usb_gadget_unregister_driver @@ -2049,7 +2049,6 @@ static void __exit udc_exit(void) } EXPORT_SYMBOL(usb_gadget_unregister_driver); -EXPORT_SYMBOL(usb_gadget_register_driver); module_init(udc_init); module_exit(udc_exit); diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index d065e23f123e..ecd5f8cffcbf 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1699,9 +1699,11 @@ void musb_gadget_cleanup(struct musb *musb) * -ENOMEM no memeory to perform the operation * * @param driver the gadget driver + * @param bind the driver's bind function * @return <0 if error, 0 if everything is fine */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { int retval; unsigned long flags; @@ -1709,8 +1711,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) if (!driver || driver->speed != USB_SPEED_HIGH - || !driver->bind - || !driver->setup) + || !bind || !driver->setup) return -EINVAL; /* driver must be initialized to support peripheral mode */ @@ -1738,7 +1739,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) spin_unlock_irqrestore(&musb->lock, flags); if (retval == 0) { - retval = driver->bind(&musb->g); + retval = bind(&musb->g); if (retval != 0) { DBG(3, "bind to driver %s failed --> %d\n", driver->driver.name, retval); @@ -1786,7 +1787,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver) { diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index d3ef42d7d2f0..006412ce2303 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -705,11 +705,6 @@ static inline int usb_gadget_disconnect(struct usb_gadget *gadget) * struct usb_gadget_driver - driver for usb 'slave' devices * @function: String describing the gadget's function * @speed: Highest speed the driver handles. - * @bind: Invoked when the driver is bound to a gadget, usually - * after registering the driver. - * At that point, ep0 is fully initialized, and ep_list holds - * the currently-available endpoints. - * Called in a context that permits sleeping. * @setup: Invoked for ep0 control requests that aren't handled by * the hardware level driver. Most calls must be handled by * the gadget driver, including descriptor and configuration @@ -774,7 +769,6 @@ static inline int usb_gadget_disconnect(struct usb_gadget *gadget) struct usb_gadget_driver { char *function; enum usb_device_speed speed; - int (*bind)(struct usb_gadget *); void (*unbind)(struct usb_gadget *); int (*setup)(struct usb_gadget *, const struct usb_ctrlrequest *); @@ -798,17 +792,19 @@ struct usb_gadget_driver { */ /** - * usb_gadget_register_driver - register a gadget driver - * @driver:the driver being registered + * usb_gadget_probe_driver - probe a gadget driver + * @driver: the driver being registered + * @bind: the driver's bind callback * Context: can sleep * * Call this in your gadget driver's module initialization function, * to tell the underlying usb controller driver about your driver. - * The driver's bind() function will be called to bind it to a - * gadget before this registration call returns. It's expected that - * the bind() functions will be in init sections. + * The @bind() function will be called to bind it to a gadget before this + * registration call returns. It's expected that the @bind() function will + * be in init sections. */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver); +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)); /** * usb_gadget_unregister_driver - unregister a gadget driver -- cgit v1.2.3 From 07a18bd716ed5dea336429404b132568cfaaef95 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Thu, 12 Aug 2010 17:43:54 +0200 Subject: usb gadget: don't save bind callback in struct usb_composite_driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bind function is most of the time only called at init time so there is no need to save a pointer to it in the composite driver structure. This fixes many section mismatches reported by modpost. Signed-off-by: Michał Nazarewicz Acked-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/audio.c | 3 +-- drivers/usb/gadget/cdc2.c | 3 +-- drivers/usb/gadget/composite.c | 15 +++++++++++---- drivers/usb/gadget/ether.c | 3 +-- drivers/usb/gadget/g_ffs.c | 3 +-- drivers/usb/gadget/hid.c | 3 +-- drivers/usb/gadget/mass_storage.c | 3 +-- drivers/usb/gadget/multi.c | 3 +-- drivers/usb/gadget/nokia.c | 3 +-- drivers/usb/gadget/serial.c | 3 +-- drivers/usb/gadget/webcam.c | 3 +-- drivers/usb/gadget/zero.c | 3 +-- include/linux/usb/composite.h | 19 +++++-------------- 13 files changed, 27 insertions(+), 40 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index a62af7b59094..5a65fbb4e20b 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -166,13 +166,12 @@ static struct usb_composite_driver audio_driver = { .name = "g_audio", .dev = &device_desc, .strings = audio_strings, - .bind = audio_bind, .unbind = __exit_p(audio_unbind), }; static int __init init(void) { - return usb_composite_register(&audio_driver); + return usb_composite_probe(&audio_driver, audio_bind); } module_init(init); diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 928137d3dbdc..1f2a9b1e4f2d 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -245,7 +245,6 @@ static struct usb_composite_driver cdc_driver = { .name = "g_cdc", .dev = &device_desc, .strings = dev_strings, - .bind = cdc_bind, .unbind = __exit_p(cdc_unbind), }; @@ -255,7 +254,7 @@ MODULE_LICENSE("GPL"); static int __init init(void) { - return usb_composite_register(&cdc_driver); + return usb_composite_probe(&cdc_driver, cdc_bind); } module_init(init); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index a3009bf01229..c531a7e05f1e 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -40,6 +40,7 @@ #define USB_BUFSIZ 1024 static struct usb_composite_driver *composite; +static int (*composite_gadget_bind)(struct usb_composite_dev *cdev); /* Some systems will need runtime overrides for the product identifers * published in the device descriptor, either numbers or strings or both. @@ -1115,7 +1116,7 @@ static int composite_bind(struct usb_gadget *gadget) * serial number), register function drivers, potentially update * power state and consumption, etc */ - status = composite->bind(cdev); + status = composite_gadget_bind(cdev); if (status < 0) goto fail; @@ -1227,8 +1228,12 @@ static struct usb_gadget_driver composite_driver = { }; /** - * usb_composite_register() - register a composite driver + * usb_composite_probe() - register a composite driver * @driver: the driver to register + * @bind: the callback used to allocate resources that are shared across the + * whole device, such as string IDs, and add its configurations using + * @usb_add_config(). This may fail by returning a negative errno + * value; it should return zero on successful initialization. * Context: single threaded during gadget setup * * This function is used to register drivers using the composite driver @@ -1241,9 +1246,10 @@ static struct usb_gadget_driver composite_driver = { * while it was binding. That would usually be done in order to wait for * some userspace participation. */ -int usb_composite_register(struct usb_composite_driver *driver) +extern int usb_composite_probe(struct usb_composite_driver *driver, + int (*bind)(struct usb_composite_dev *cdev)) { - if (!driver || !driver->dev || !driver->bind || composite) + if (!driver || !driver->dev || !bind || composite) return -EINVAL; if (!driver->iProduct) @@ -1253,6 +1259,7 @@ int usb_composite_register(struct usb_composite_driver *driver) composite_driver.function = (char *) driver->name; composite_driver.driver.name = driver->name; composite = driver; + composite_gadget_bind = bind; return usb_gadget_probe_driver(&composite_driver, composite_bind); } diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 400f80372d93..33076bca9936 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -402,7 +402,6 @@ static struct usb_composite_driver eth_driver = { .name = "g_ether", .dev = &device_desc, .strings = dev_strings, - .bind = eth_bind, .unbind = __exit_p(eth_unbind), }; @@ -412,7 +411,7 @@ MODULE_LICENSE("GPL"); static int __init init(void) { - return usb_composite_register(ð_driver); + return usb_composite_probe(ð_driver, eth_bind); } module_init(init); diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 52fd3fa0d130..9fcb15879506 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -147,7 +147,6 @@ static struct usb_composite_driver gfs_driver = { .name = DRIVER_NAME, .dev = &gfs_dev_desc, .strings = gfs_dev_strings, - .bind = gfs_bind, .unbind = gfs_unbind, .iProduct = DRIVER_DESC, }; @@ -187,7 +186,7 @@ static int functionfs_ready_callback(struct ffs_data *ffs) return -EBUSY; gfs_ffs_data = ffs; - ret = usb_composite_register(&gfs_driver); + ret = usb_composite_probe(&gfs_driver, gfs_bind); if (unlikely(ret < 0)) clear_bit(0, &gfs_registered); return ret; diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index 775722686ed8..77f495212fb9 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -256,7 +256,6 @@ static struct usb_composite_driver hidg_driver = { .name = "g_hid", .dev = &device_desc, .strings = dev_strings, - .bind = hid_bind, .unbind = __exit_p(hid_unbind), }; @@ -282,7 +281,7 @@ static int __init hidg_init(void) if (status < 0) return status; - status = usb_composite_register(&hidg_driver); + status = usb_composite_probe(&hidg_driver, hid_bind); if (status < 0) platform_driver_unregister(&hidg_plat_driver); diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 05e9bd330348..a5e4a777d922 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -169,7 +169,6 @@ static int __init msg_bind(struct usb_composite_dev *cdev) static struct usb_composite_driver msg_driver = { .name = "g_mass_storage", .dev = &msg_device_desc, - .bind = msg_bind, .iProduct = DRIVER_DESC, .needs_serial = 1, }; @@ -180,7 +179,7 @@ MODULE_LICENSE("GPL"); static int __init msg_init(void) { - return usb_composite_register(&msg_driver); + return usb_composite_probe(&msg_driver, msg_bind); } module_init(msg_init); diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index ca51661d71db..91170a02a9a3 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -353,7 +353,6 @@ static struct usb_composite_driver multi_driver = { .name = "g_multi", .dev = &device_desc, .strings = dev_strings, - .bind = multi_bind, .unbind = __exit_p(multi_unbind), .iProduct = DRIVER_DESC, .needs_serial = 1, @@ -362,7 +361,7 @@ static struct usb_composite_driver multi_driver = { static int __init multi_init(void) { - return usb_composite_register(&multi_driver); + return usb_composite_probe(&multi_driver, multi_bind); } module_init(multi_init); diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 7d6b66a85724..8aec728882a0 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -241,13 +241,12 @@ static struct usb_composite_driver nokia_driver = { .name = "g_nokia", .dev = &device_desc, .strings = dev_strings, - .bind = nokia_bind, .unbind = __exit_p(nokia_unbind), }; static int __init nokia_init(void) { - return usb_composite_register(&nokia_driver); + return usb_composite_probe(&nokia_driver, nokia_bind); } module_init(nokia_init); diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index f46a60962dab..0b81d7b741d1 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -242,7 +242,6 @@ static struct usb_composite_driver gserial_driver = { .name = "g_serial", .dev = &device_desc, .strings = dev_strings, - .bind = gs_bind, }; static int __init init(void) @@ -271,7 +270,7 @@ static int __init init(void) } strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; - return usb_composite_register(&gserial_driver); + return usb_composite_probe(&gserial_driver, gs_bind); } module_init(init); diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index 288d21155abe..de65b8078e05 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -373,14 +373,13 @@ static struct usb_composite_driver webcam_driver = { .name = "g_webcam", .dev = &webcam_device_descriptor, .strings = webcam_device_strings, - .bind = webcam_bind, .unbind = webcam_unbind, }; static int __init webcam_init(void) { - return usb_composite_register(&webcam_driver); + return usb_composite_probe(&webcam_driver, webcam_bind); } static void __exit diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 807280d069f9..6d16db9d9d2d 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -340,7 +340,6 @@ static struct usb_composite_driver zero_driver = { .name = "zero", .dev = &device_desc, .strings = dev_strings, - .bind = zero_bind, .unbind = zero_unbind, .suspend = zero_suspend, .resume = zero_resume, @@ -351,7 +350,7 @@ MODULE_LICENSE("GPL"); static int __init init(void) { - return usb_composite_register(&zero_driver); + return usb_composite_probe(&zero_driver, zero_bind); } module_init(init); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index a78e813d27e4..e28b6626802c 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -248,11 +248,7 @@ int usb_add_config(struct usb_composite_dev *, * and language IDs provided in control requests * @needs_serial: set to 1 if the gadget needs userspace to provide * a serial number. If one is not provided, warning will be printed. - * @bind: (REQUIRED) Used to allocate resources that are shared across the - * whole device, such as string IDs, and add its configurations using - * @usb_add_config(). This may fail by returning a negative errno - * value; it should return zero on successful initialization. - * @unbind: Reverses @bind(); called as a side effect of unregistering + * @unbind: Reverses bind; called as a side effect of unregistering * this driver. * @disconnect: optional driver disconnect method * @suspend: Notifies when the host stops sending USB traffic, @@ -263,7 +259,7 @@ int usb_add_config(struct usb_composite_dev *, * Devices default to reporting self powered operation. Devices which rely * on bus powered operation should report this in their @bind() method. * - * Before returning from @bind, various fields in the template descriptor + * Before returning from bind, various fields in the template descriptor * may be overridden. These include the idVendor/idProduct/bcdDevice values * normally to bind the appropriate host side driver, and the three strings * (iManufacturer, iProduct, iSerialNumber) normally used to provide user @@ -279,12 +275,6 @@ struct usb_composite_driver { struct usb_gadget_strings **strings; unsigned needs_serial:1; - /* REVISIT: bind() functions can be marked __init, which - * makes trouble for section mismatch analysis. See if - * we can't restructure things to avoid mismatching... - */ - - int (*bind)(struct usb_composite_dev *); int (*unbind)(struct usb_composite_dev *); void (*disconnect)(struct usb_composite_dev *); @@ -294,8 +284,9 @@ struct usb_composite_driver { void (*resume)(struct usb_composite_dev *); }; -extern int usb_composite_register(struct usb_composite_driver *); -extern void usb_composite_unregister(struct usb_composite_driver *); +extern int usb_composite_probe(struct usb_composite_driver *driver, + int (*bind)(struct usb_composite_dev *cdev)); +extern void usb_composite_unregister(struct usb_composite_driver *driver); /** -- cgit v1.2.3 From c9bfff9c98671ad50e4abbfe1ab606a9957f7539 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 12 Aug 2010 17:43:55 +0200 Subject: usb gadget: don't save bind callback in struct usb_configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bind function is most of the time only called at init time so there is no need to save a pointer to it in the configuration structure. This fixes many section mismatches reported by modpost. Signed-off-by: Uwe Kleine-König [m.nazarewicz@samsung.com: updated for -next] Signed-off-by: Michał Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/audio.c | 3 +-- drivers/usb/gadget/cdc2.c | 3 +-- drivers/usb/gadget/composite.c | 14 ++++++++------ drivers/usb/gadget/ether.c | 7 +++---- drivers/usb/gadget/f_loopback.c | 3 +-- drivers/usb/gadget/f_sourcesink.c | 3 +-- drivers/usb/gadget/g_ffs.c | 3 +-- drivers/usb/gadget/hid.c | 3 +-- drivers/usb/gadget/mass_storage.c | 3 +-- drivers/usb/gadget/multi.c | 10 ++++------ drivers/usb/gadget/nokia.c | 8 ++++---- drivers/usb/gadget/serial.c | 4 ++-- drivers/usb/gadget/webcam.c | 4 ++-- include/linux/usb/composite.h | 8 +++----- 14 files changed, 33 insertions(+), 43 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 5a65fbb4e20b..93b999e49ef3 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -105,7 +105,6 @@ static int __init audio_do_config(struct usb_configuration *c) static struct usb_configuration audio_config_driver = { .label = DRIVER_DESC, - .bind = audio_do_config, .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, @@ -145,7 +144,7 @@ static int __init audio_bind(struct usb_composite_dev *cdev) strings_dev[STRING_PRODUCT_IDX].id = status; device_desc.iProduct = status; - status = usb_add_config(cdev, &audio_config_driver); + status = usb_add_config(cdev, &audio_config_driver, audio_do_config); if (status < 0) goto fail; diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 1f2a9b1e4f2d..2720ab07ef1a 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -151,7 +151,6 @@ static int __init cdc_do_config(struct usb_configuration *c) static struct usb_configuration cdc_config_driver = { .label = "CDC Composite (ECM + ACM)", - .bind = cdc_do_config, .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, @@ -218,7 +217,7 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) device_desc.iProduct = status; /* register our configuration */ - status = usb_add_config(cdev, &cdc_config_driver); + status = usb_add_config(cdev, &cdc_config_driver, cdc_do_config); if (status < 0) goto fail1; diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index c531a7e05f1e..5e2bd7428424 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -474,18 +474,20 @@ done: * usb_add_config() - add a configuration to a device. * @cdev: wraps the USB gadget * @config: the configuration, with bConfigurationValue assigned + * @bind: the configuration's bind function * Context: single threaded during gadget setup * - * One of the main tasks of a composite driver's bind() routine is to + * One of the main tasks of a composite @bind() routine is to * add each of the configurations it supports, using this routine. * - * This function returns the value of the configuration's bind(), which + * This function returns the value of the configuration's @bind(), which * is zero for success else a negative errno value. Binding configurations * assigns global resources including string IDs, and per-configuration * resources such as interface IDs and endpoints. */ int usb_add_config(struct usb_composite_dev *cdev, - struct usb_configuration *config) + struct usb_configuration *config, + int (*bind)(struct usb_configuration *)) { int status = -EINVAL; struct usb_configuration *c; @@ -494,7 +496,7 @@ int usb_add_config(struct usb_composite_dev *cdev, config->bConfigurationValue, config->label, config); - if (!config->bConfigurationValue || !config->bind) + if (!config->bConfigurationValue || !bind) goto done; /* Prevent duplicate configuration identifiers */ @@ -511,7 +513,7 @@ int usb_add_config(struct usb_composite_dev *cdev, INIT_LIST_HEAD(&config->functions); config->next_interface_id = 0; - status = config->bind(config); + status = bind(config); if (status < 0) { list_del(&config->list); config->cdev = NULL; @@ -537,7 +539,7 @@ int usb_add_config(struct usb_composite_dev *cdev, } } - /* set_alt(), or next config->bind(), sets up + /* set_alt(), or next bind(), sets up * ep->driver_data as needed. */ usb_ep_autoconfig_reset(cdev->gadget); diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 33076bca9936..1690c9d68256 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -251,7 +251,6 @@ static int __init rndis_do_config(struct usb_configuration *c) static struct usb_configuration rndis_config_driver = { .label = "RNDIS", - .bind = rndis_do_config, .bConfigurationValue = 2, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, @@ -289,7 +288,6 @@ static int __init eth_do_config(struct usb_configuration *c) static struct usb_configuration eth_config_driver = { /* .label = f(hardware) */ - .bind = eth_do_config, .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, @@ -373,12 +371,13 @@ static int __init eth_bind(struct usb_composite_dev *cdev) /* register our configuration(s); RNDIS first, if it's used */ if (has_rndis()) { - status = usb_add_config(cdev, &rndis_config_driver); + status = usb_add_config(cdev, &rndis_config_driver, + rndis_do_config); if (status < 0) goto fail; } - status = usb_add_config(cdev, ð_config_driver); + status = usb_add_config(cdev, ð_config_driver, eth_do_config); if (status < 0) goto fail; diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index e91d1b16d9be..b37960f9e753 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -349,7 +349,6 @@ static int __init loopback_bind_config(struct usb_configuration *c) static struct usb_configuration loopback_driver = { .label = "loopback", .strings = loopback_strings, - .bind = loopback_bind_config, .bConfigurationValue = 2, .bmAttributes = USB_CONFIG_ATT_SELFPOWER, /* .iConfiguration = DYNAMIC */ @@ -382,5 +381,5 @@ int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume) loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return usb_add_config(cdev, &loopback_driver); + return usb_add_config(cdev, &loopback_driver, loopback_bind_config); } diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index 6d3cc443d914..e403a534dd55 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -498,7 +498,6 @@ unknown: static struct usb_configuration sourcesink_driver = { .label = "source/sink", .strings = sourcesink_strings, - .bind = sourcesink_bind_config, .setup = sourcesink_setup, .bConfigurationValue = 3, .bmAttributes = USB_CONFIG_ATT_SELFPOWER, @@ -532,5 +531,5 @@ int __init sourcesink_add(struct usb_composite_dev *cdev, bool autoresume) sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return usb_add_config(cdev, &sourcesink_driver); + return usb_add_config(cdev, &sourcesink_driver, sourcesink_bind_config); } diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 9fcb15879506..af75e3620849 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -234,11 +234,10 @@ static int gfs_bind(struct usb_composite_dev *cdev) c->c.label = gfs_strings[i].s; c->c.iConfiguration = gfs_strings[i].id; - c->c.bind = gfs_do_config; c->c.bConfigurationValue = 1 + i; c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER; - ret = usb_add_config(cdev, &c->c); + ret = usb_add_config(cdev, &c->c, gfs_do_config); if (unlikely(ret < 0)) goto error_unbind; } diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index 77f495212fb9..2523e54097bd 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -148,7 +148,6 @@ static int __init do_config(struct usb_configuration *c) static struct usb_configuration config_driver = { .label = "HID Gadget", - .bind = do_config, .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, @@ -201,7 +200,7 @@ static int __init hid_bind(struct usb_composite_dev *cdev) device_desc.iProduct = status; /* register our configuration */ - status = usb_add_config(cdev, &config_driver); + status = usb_add_config(cdev, &config_driver, do_config); if (status < 0) return status; diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index a5e4a777d922..0769179dbdb0 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -141,7 +141,6 @@ static int __init msg_do_config(struct usb_configuration *c) static struct usb_configuration msg_config_driver = { .label = "Linux File-Backed Storage", - .bind = msg_do_config, .bConfigurationValue = 1, .bmAttributes = USB_CONFIG_ATT_SELFPOWER, }; @@ -153,7 +152,7 @@ static int __init msg_bind(struct usb_composite_dev *cdev) { int status; - status = usb_add_config(cdev, &msg_config_driver); + status = usb_add_config(cdev, &msg_config_driver, msg_do_config); if (status < 0) return status; diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 91170a02a9a3..d9feced348e3 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -164,7 +164,7 @@ static u8 hostaddr[ETH_ALEN]; #ifdef USB_ETH_RNDIS -static __ref int rndis_do_config(struct usb_configuration *c) +static __init int rndis_do_config(struct usb_configuration *c) { int ret; @@ -191,7 +191,6 @@ static __ref int rndis_do_config(struct usb_configuration *c) static int rndis_config_register(struct usb_composite_dev *cdev) { static struct usb_configuration config = { - .bind = rndis_do_config, .bConfigurationValue = MULTI_RNDIS_CONFIG_NUM, .bmAttributes = USB_CONFIG_ATT_SELFPOWER, }; @@ -199,7 +198,7 @@ static int rndis_config_register(struct usb_composite_dev *cdev) config.label = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].s; config.iConfiguration = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].id; - return usb_add_config(cdev, &config); + return usb_add_config(cdev, &config, rndis_do_config); } #else @@ -216,7 +215,7 @@ static int rndis_config_register(struct usb_composite_dev *cdev) #ifdef CONFIG_USB_G_MULTI_CDC -static __ref int cdc_do_config(struct usb_configuration *c) +static __init int cdc_do_config(struct usb_configuration *c) { int ret; @@ -243,7 +242,6 @@ static __ref int cdc_do_config(struct usb_configuration *c) static int cdc_config_register(struct usb_composite_dev *cdev) { static struct usb_configuration config = { - .bind = cdc_do_config, .bConfigurationValue = MULTI_CDC_CONFIG_NUM, .bmAttributes = USB_CONFIG_ATT_SELFPOWER, }; @@ -251,7 +249,7 @@ static int cdc_config_register(struct usb_composite_dev *cdev) config.label = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].s; config.iConfiguration = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].id; - return usb_add_config(cdev, &config); + return usb_add_config(cdev, &config, cdc_do_config); } #else diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 8aec728882a0..b5364f9d7cd2 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -135,7 +135,6 @@ static int __init nokia_bind_config(struct usb_configuration *c) static struct usb_configuration nokia_config_500ma_driver = { .label = "Bus Powered", - .bind = nokia_bind_config, .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_ONE, @@ -144,7 +143,6 @@ static struct usb_configuration nokia_config_500ma_driver = { static struct usb_configuration nokia_config_100ma_driver = { .label = "Self Powered", - .bind = nokia_bind_config, .bConfigurationValue = 2, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, @@ -206,11 +204,13 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) } /* finaly register the configuration */ - status = usb_add_config(cdev, &nokia_config_500ma_driver); + status = usb_add_config(cdev, &nokia_config_500ma_driver, + nokia_bind_config); if (status < 0) goto err_usb; - status = usb_add_config(cdev, &nokia_config_100ma_driver); + status = usb_add_config(cdev, &nokia_config_100ma_driver, + nokia_bind_config); if (status < 0) goto err_usb; diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 0b81d7b741d1..1ac57a973aa9 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -155,7 +155,6 @@ static int __init serial_bind_config(struct usb_configuration *c) static struct usb_configuration serial_config_driver = { /* .label = f(use_acm) */ - .bind = serial_bind_config, /* .bConfigurationValue = f(use_acm) */ /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, @@ -225,7 +224,8 @@ static int __init gs_bind(struct usb_composite_dev *cdev) } /* register our configuration */ - status = usb_add_config(cdev, &serial_config_driver); + status = usb_add_config(cdev, &serial_config_driver, + serial_bind_config); if (status < 0) goto fail; diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index de65b8078e05..a5a0fdb808c7 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -317,7 +317,6 @@ webcam_config_bind(struct usb_configuration *c) static struct usb_configuration webcam_config_driver = { .label = webcam_config_label, - .bind = webcam_config_bind, .bConfigurationValue = 1, .iConfiguration = 0, /* dynamic */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, @@ -354,7 +353,8 @@ webcam_bind(struct usb_composite_dev *cdev) webcam_config_driver.iConfiguration = ret; /* Register our configuration. */ - if ((ret = usb_add_config(cdev, &webcam_config_driver)) < 0) + if ((ret = usb_add_config(cdev, &webcam_config_driver, + webcam_config_bind)) < 0) goto error; INFO(cdev, "Webcam Video Gadget\n"); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index e28b6626802c..3d29a7dcac2d 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -161,8 +161,6 @@ ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, * and by language IDs provided in control requests. * @descriptors: Table of descriptors preceding all function descriptors. * Examples include OTG and vendor-specific descriptors. - * @bind: Called from @usb_add_config() to allocate resources unique to this - * configuration and to call @usb_add_function() for each function used. * @unbind: Reverses @bind; called as a side effect of unregistering the * driver which added this configuration. * @setup: Used to delegate control requests that aren't handled by standard @@ -207,8 +205,7 @@ struct usb_configuration { * we can't restructure things to avoid mismatching... */ - /* configuration management: bind/unbind */ - int (*bind)(struct usb_configuration *); + /* configuration management: unbind/setup */ void (*unbind)(struct usb_configuration *); int (*setup)(struct usb_configuration *, const struct usb_ctrlrequest *); @@ -232,7 +229,8 @@ struct usb_configuration { }; int usb_add_config(struct usb_composite_dev *, - struct usb_configuration *); + struct usb_configuration *, + int (*)(struct usb_configuration *)); /** * struct usb_composite_driver - groups configurations into a gadget -- cgit v1.2.3 From d39a0edad60dc65cf4774ee732aa7a84cf35c27a Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Thu, 9 Sep 2010 22:35:39 +0100 Subject: USB OTG: Add common data structure for Intel MID Platform (Langwell/Penwell) This patch adds one new header file for the common data structure used in Intel Penwell/Langwell MID Platform OTG Transceiver drivers. After switched to the common data structure, Langwell/Penwell OTG Transceiver driver will provide an unified interface to host/client driver. Reported-by: Randy Dunlap Signed-off-by: Hao Wu Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/intel_mid_otg.h | 180 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 include/linux/usb/intel_mid_otg.h (limited to 'include/linux/usb') diff --git a/include/linux/usb/intel_mid_otg.h b/include/linux/usb/intel_mid_otg.h new file mode 100644 index 000000000000..a0ccf795f362 --- /dev/null +++ b/include/linux/usb/intel_mid_otg.h @@ -0,0 +1,180 @@ +/* + * Intel MID (Langwell/Penwell) USB OTG Transceiver driver + * Copyright (C) 2008 - 2010, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef __INTEL_MID_OTG_H +#define __INTEL_MID_OTG_H + +#include +#include +#include + +struct intel_mid_otg_xceiv; + +/* This is a common data structure for Intel MID platform to + * save values of the OTG state machine */ +struct otg_hsm { + /* Input */ + int a_bus_resume; + int a_bus_suspend; + int a_conn; + int a_sess_vld; + int a_srp_det; + int a_vbus_vld; + int b_bus_resume; + int b_bus_suspend; + int b_conn; + int b_se0_srp; + int b_ssend_srp; + int b_sess_end; + int b_sess_vld; + int id; +/* id values */ +#define ID_B 0x05 +#define ID_A 0x04 +#define ID_ACA_C 0x03 +#define ID_ACA_B 0x02 +#define ID_ACA_A 0x01 + int power_up; + int adp_change; + int test_device; + + /* Internal variables */ + int a_set_b_hnp_en; + int b_srp_done; + int b_hnp_enable; + int hnp_poll_enable; + + /* Timeout indicator for timers */ + int a_wait_vrise_tmout; + int a_wait_bcon_tmout; + int a_aidl_bdis_tmout; + int a_bidl_adis_tmout; + int a_bidl_adis_tmr; + int a_wait_vfall_tmout; + int b_ase0_brst_tmout; + int b_bus_suspend_tmout; + int b_srp_init_tmout; + int b_srp_fail_tmout; + int b_srp_fail_tmr; + int b_adp_sense_tmout; + + /* Informative variables */ + int a_bus_drop; + int a_bus_req; + int a_clr_err; + int b_bus_req; + int a_suspend_req; + int b_bus_suspend_vld; + + /* Output */ + int drv_vbus; + int loc_conn; + int loc_sof; + + /* Others */ + int vbus_srp_up; +}; + +/* must provide ULPI access function to read/write registers implemented in + * ULPI address space */ +struct iotg_ulpi_access_ops { + int (*read)(struct intel_mid_otg_xceiv *iotg, u8 reg, u8 *val); + int (*write)(struct intel_mid_otg_xceiv *iotg, u8 reg, u8 val); +}; + +#define OTG_A_DEVICE 0x0 +#define OTG_B_DEVICE 0x1 + +/* + * the Intel MID (Langwell/Penwell) otg transceiver driver needs to interact + * with device and host drivers to implement the USB OTG related feature. More + * function members are added based on otg_transceiver data structure for this + * purpose. + */ +struct intel_mid_otg_xceiv { + struct otg_transceiver otg; + struct otg_hsm hsm; + + /* base address */ + void __iomem *base; + + /* ops to access ulpi */ + struct iotg_ulpi_access_ops ulpi_ops; + + /* atomic notifier for interrupt context */ + struct atomic_notifier_head iotg_notifier; + + /* start/stop USB Host function */ + int (*start_host)(struct intel_mid_otg_xceiv *iotg); + int (*stop_host)(struct intel_mid_otg_xceiv *iotg); + + /* start/stop USB Peripheral function */ + int (*start_peripheral)(struct intel_mid_otg_xceiv *iotg); + int (*stop_peripheral)(struct intel_mid_otg_xceiv *iotg); + + /* start/stop ADP sense/probe function */ + int (*set_adp_probe)(struct intel_mid_otg_xceiv *iotg, + bool enabled, int dev); + int (*set_adp_sense)(struct intel_mid_otg_xceiv *iotg, + bool enabled); + +#ifdef CONFIG_PM + /* suspend/resume USB host function */ + int (*suspend_host)(struct intel_mid_otg_xceiv *iotg, + pm_message_t message); + int (*resume_host)(struct intel_mid_otg_xceiv *iotg); + + int (*suspend_peripheral)(struct intel_mid_otg_xceiv *iotg, + pm_message_t message); + int (*resume_peripheral)(struct intel_mid_otg_xceiv *iotg); +#endif + +}; +static inline +struct intel_mid_otg_xceiv *otg_to_mid_xceiv(struct otg_transceiver *otg) +{ + return container_of(otg, struct intel_mid_otg_xceiv, otg); +} + +#define MID_OTG_NOTIFY_CONNECT 0x0001 +#define MID_OTG_NOTIFY_DISCONN 0x0002 +#define MID_OTG_NOTIFY_HSUSPEND 0x0003 +#define MID_OTG_NOTIFY_HRESUME 0x0004 +#define MID_OTG_NOTIFY_CSUSPEND 0x0005 +#define MID_OTG_NOTIFY_CRESUME 0x0006 +#define MID_OTG_NOTIFY_HOSTADD 0x0007 +#define MID_OTG_NOTIFY_HOSTREMOVE 0x0008 +#define MID_OTG_NOTIFY_CLIENTADD 0x0009 +#define MID_OTG_NOTIFY_CLIENTREMOVE 0x000a + +static inline int +intel_mid_otg_register_notifier(struct intel_mid_otg_xceiv *iotg, + struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&iotg->iotg_notifier, nb); +} + +static inline void +intel_mid_otg_unregister_notifier(struct intel_mid_otg_xceiv *iotg, + struct notifier_block *nb) +{ + atomic_notifier_chain_unregister(&iotg->iotg_notifier, nb); +} + +#endif /* __INTEL_MID_OTG_H */ -- cgit v1.2.3 From 56e9406ca22968e3c9dc27d6dc0f1825e13bfff9 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Thu, 9 Sep 2010 22:35:54 +0100 Subject: USB OTG Langwell: Update OTG Kconfig and driver version. This patch updated Kconfig for langwell otg transceiver driver. Add ipc driver(INTEL_SCU_IPC) as a dependency. Driver version is updated too. Signed-off-by: Hao Wu Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/Kconfig | 2 +- drivers/usb/otg/langwell_otg.c | 4 ++-- include/linux/usb/langwell_otg.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 299dfd2510cb..5ce07528cd0c 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -69,7 +69,7 @@ config NOP_USB_XCEIV config USB_LANGWELL_OTG tristate "Intel Langwell USB OTG dual-role support" - depends on USB && X86_MRST + depends on USB && PCI && INTEL_SCU_IPC select USB_OTG select USB_OTG_UTILS help diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c index 879188086daf..bdc3ea66be69 100644 --- a/drivers/usb/otg/langwell_otg.c +++ b/drivers/usb/otg/langwell_otg.c @@ -1,6 +1,6 @@ /* * Intel Langwell USB OTG transceiver driver - * Copyright (C) 2008 - 2009, Intel Corporation. + * Copyright (C) 2008 - 2010, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -40,7 +40,7 @@ #include #define DRIVER_DESC "Intel Langwell USB OTG transceiver driver" -#define DRIVER_VERSION "3.0.0.32L.0003" +#define DRIVER_VERSION "July 10, 2010" MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Henry Yuan , Hao Wu "); diff --git a/include/linux/usb/langwell_otg.h b/include/linux/usb/langwell_otg.h index a6562f1d4e2b..51f17b16d312 100644 --- a/include/linux/usb/langwell_otg.h +++ b/include/linux/usb/langwell_otg.h @@ -1,6 +1,6 @@ /* * Intel Langwell USB OTG transceiver driver - * Copyright (C) 2008, Intel Corporation. + * Copyright (C) 2008 - 2010, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, -- cgit v1.2.3 From 1f53c0e9bbf654ed93f63deee2bf5c9a1816966e Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Mon, 20 Sep 2010 15:40:26 +0300 Subject: USB: cdc.h: ncm: typo and style fixes Some typos were in the initial commit, make the spelling according to the spec. Add some more comments. Also change constant names according to the style of the rest of the file Signed-off-by: Yauheni Kaliuta Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/cdc.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'include/linux/usb') diff --git a/include/linux/usb/cdc.h b/include/linux/usb/cdc.h index c117a68d04a7..583264abca0c 100644 --- a/include/linux/usb/cdc.h +++ b/include/linux/usb/cdc.h @@ -274,13 +274,13 @@ struct usb_cdc_notification { /* * Class Specific structures and constants * - * CDC NCM parameter structure, CDC NCM subclass 6.2.1 + * CDC NCM NTB parameters structure, CDC NCM subclass 6.2.1 * */ -struct usb_cdc_ncm_ntb_parameter { +struct usb_cdc_ncm_ntb_parameters { __le16 wLength; - __le16 bmNtbFormatSupported; + __le16 bmNtbFormatsSupported; __le32 dwNtbInMaxSize; __le16 wNdpInDivisor; __le16 wNdpInPayloadRemainder; @@ -297,8 +297,8 @@ struct usb_cdc_ncm_ntb_parameter { * CDC NCM transfer headers, CDC NCM subclass 3.2 */ -#define NCM_NTH16_SIGN 0x484D434E /* NCMH */ -#define NCM_NTH32_SIGN 0x686D636E /* ncmh */ +#define USB_CDC_NCM_NTH16_SIGN 0x484D434E /* NCMH */ +#define USB_CDC_NCM_NTH32_SIGN 0x686D636E /* ncmh */ struct usb_cdc_ncm_nth16 { __le32 dwSignature; @@ -320,11 +320,12 @@ struct usb_cdc_ncm_nth32 { * CDC NCM datagram pointers, CDC NCM subclass 3.3 */ -#define NCM_NDP16_CRC_SIGN 0x314D434E /* NCM1 */ -#define NCM_NDP16_NOCRC_SIGN 0x304D434E /* NCM0 */ -#define NCM_NDP32_CRC_SIGN 0x316D636E /* ncm1 */ -#define NCM_NDP32_NOCRC_SIGN 0x306D636E /* ncm0 */ +#define USB_CDC_NCM_NDP16_CRC_SIGN 0x314D434E /* NCM1 */ +#define USB_CDC_NCM_NDP16_NOCRC_SIGN 0x304D434E /* NCM0 */ +#define USB_CDC_NCM_NDP32_CRC_SIGN 0x316D636E /* ncm1 */ +#define USB_CDC_NCM_NDP32_NOCRC_SIGN 0x306D636E /* ncm0 */ +/* 16-bit NCM Datagram Pointer Table */ struct usb_cdc_ncm_ndp16 { __le32 dwSignature; __le16 wLength; @@ -332,11 +333,12 @@ struct usb_cdc_ncm_ndp16 { __u8 data[0]; } __attribute__ ((packed)); +/* 32-bit NCM Datagram Pointer Table */ struct usb_cdc_ncm_ndp32 { __le32 dwSignature; __le16 wLength; __le16 wReserved6; - __le32 dwNextFpIndex; + __le32 dwNextNdpIndex; __le32 dwReserved12; __u8 data[0]; } __attribute__ ((packed)); -- cgit v1.2.3 From 7fc09170cedc329ad274433b4f1a653e603600b5 Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Mon, 20 Sep 2010 15:40:27 +0300 Subject: Revert "USB: ncm: added ncm.h with auxiliary definitions" This reverts commit 65e0b499105ec8ff3bc4ab7680873dec20127f9d. Since the host and gadget implementations are different, there is no common code for the file, remove for now. Signed-off-by: Yauheni Kaliuta Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/ncm.h | 114 ------------------------------------------------ 1 file changed, 114 deletions(-) delete mode 100644 include/linux/usb/ncm.h (limited to 'include/linux/usb') diff --git a/include/linux/usb/ncm.h b/include/linux/usb/ncm.h deleted file mode 100644 index 006d1064c8b2..000000000000 --- a/include/linux/usb/ncm.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * USB CDC NCM auxiliary definitions - */ - -#ifndef __LINUX_USB_NCM_H -#define __LINUX_USB_NCM_H - -#include -#include -#include - -#define NCM_NTB_MIN_IN_SIZE 2048 -#define NCM_NTB_MIN_OUT_SIZE 2048 - -#define NCM_CONTROL_TIMEOUT (5 * 1000) - -/* bmNetworkCapabilities */ - -#define NCM_NCAP_ETH_FILTER (1 << 0) -#define NCM_NCAP_NET_ADDRESS (1 << 1) -#define NCM_NCAP_ENCAP_COMM (1 << 2) -#define NCM_NCAP_MAX_DGRAM (1 << 3) -#define NCM_NCAP_CRC_MODE (1 << 4) - -/* - * Here are options for NCM Datagram Pointer table (NDP) parser. - * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3), - * in NDP16 offsets and sizes fields are 1 16bit word wide, - * in NDP32 -- 2 16bit words wide. Also signatures are different. - * To make the parser code the same, put the differences in the structure, - * and switch pointers to the structures when the format is changed. - */ - -struct ndp_parser_opts { - u32 nth_sign; - u32 ndp_sign; - unsigned nth_size; - unsigned ndp_size; - unsigned ndplen_align; - /* sizes in u16 units */ - unsigned dgram_item_len; /* index or length */ - unsigned block_length; - unsigned fp_index; - unsigned reserved1; - unsigned reserved2; - unsigned next_fp_index; -}; - -#define INIT_NDP16_OPTS { \ - .nth_sign = NCM_NTH16_SIGN, \ - .ndp_sign = NCM_NDP16_NOCRC_SIGN, \ - .nth_size = sizeof(struct usb_cdc_ncm_nth16), \ - .ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \ - .ndplen_align = 4, \ - .dgram_item_len = 1, \ - .block_length = 1, \ - .fp_index = 1, \ - .reserved1 = 0, \ - .reserved2 = 0, \ - .next_fp_index = 1, \ - } - - -#define INIT_NDP32_OPTS { \ - .nth_sign = NCM_NTH32_SIGN, \ - .ndp_sign = NCM_NDP32_NOCRC_SIGN, \ - .nth_size = sizeof(struct usb_cdc_ncm_nth32), \ - .ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \ - .ndplen_align = 8, \ - .dgram_item_len = 2, \ - .block_length = 2, \ - .fp_index = 2, \ - .reserved1 = 1, \ - .reserved2 = 2, \ - .next_fp_index = 2, \ - } - -static inline void put_ncm(__le16 **p, unsigned size, unsigned val) -{ - switch (size) { - case 1: - put_unaligned_le16((u16)val, *p); - break; - case 2: - put_unaligned_le32((u32)val, *p); - - break; - default: - BUG(); - } - - *p += size; -} - -static inline unsigned get_ncm(__le16 **p, unsigned size) -{ - unsigned tmp; - - switch (size) { - case 1: - tmp = get_unaligned_le16(*p); - break; - case 2: - tmp = get_unaligned_le32(*p); - break; - default: - BUG(); - } - - *p += size; - return tmp; -} - -#endif /* __LINUX_USB_NCM_H */ -- cgit v1.2.3 From e5dcd531ac7a040f1b4d35f58914a36ad6174e84 Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Mon, 20 Sep 2010 15:40:28 +0300 Subject: USB: cdc.h: ncm: add missed constants and structures Make a dedicated structure for datagram pointer entry. There is no explicit declaration in the spec, but it's used by the host implementation and makes the structure more clear. Add some missed constants from the spec Signed-off-by: Yauheni Kaliuta Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/cdc.h | 57 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) (limited to 'include/linux/usb') diff --git a/include/linux/usb/cdc.h b/include/linux/usb/cdc.h index 583264abca0c..2d5b6f296aa3 100644 --- a/include/linux/usb/cdc.h +++ b/include/linux/usb/cdc.h @@ -32,6 +32,8 @@ #define USB_CDC_PROTO_EEM 7 +#define USB_CDC_NCM_PROTO_NTB 1 + /*-------------------------------------------------------------------------*/ /* @@ -325,14 +327,26 @@ struct usb_cdc_ncm_nth32 { #define USB_CDC_NCM_NDP32_CRC_SIGN 0x316D636E /* ncm1 */ #define USB_CDC_NCM_NDP32_NOCRC_SIGN 0x306D636E /* ncm0 */ +/* 16-bit NCM Datagram Pointer Entry */ +struct usb_cdc_ncm_dpe16 { + __le16 wDatagramIndex; + __le16 wDatagramLength; +} __attribute__((__packed__)); + /* 16-bit NCM Datagram Pointer Table */ struct usb_cdc_ncm_ndp16 { __le32 dwSignature; __le16 wLength; __le16 wNextFpIndex; - __u8 data[0]; + struct usb_cdc_ncm_dpe16 dpe16[0]; } __attribute__ ((packed)); +/* 32-bit NCM Datagram Pointer Entry */ +struct usb_cdc_ncm_dpe32 { + __le32 wDatagramIndex; + __le32 wDatagramLength; +} __attribute__((__packed__)); + /* 32-bit NCM Datagram Pointer Table */ struct usb_cdc_ncm_ndp32 { __le32 dwSignature; @@ -340,7 +354,46 @@ struct usb_cdc_ncm_ndp32 { __le16 wReserved6; __le32 dwNextNdpIndex; __le32 dwReserved12; - __u8 data[0]; + struct usb_cdc_ncm_dpe32 dpe32[0]; } __attribute__ ((packed)); +/* CDC NCM subclass 3.2.1 and 3.2.2 */ +#define USB_CDC_NCM_NDP16_INDEX_MIN 0x000C +#define USB_CDC_NCM_NDP32_INDEX_MIN 0x0010 + +/* CDC NCM subclass 3.3.3 Datagram Formatting */ +#define USB_CDC_NCM_DATAGRAM_FORMAT_CRC 0x30 +#define USB_CDC_NCM_DATAGRAM_FORMAT_NOCRC 0X31 + +/* CDC NCM subclass 4.2 NCM Communications Interface Protocol Code */ +#define USB_CDC_NCM_PROTO_CODE_NO_ENCAP_COMMANDS 0x00 +#define USB_CDC_NCM_PROTO_CODE_EXTERN_PROTO 0xFE + +/* CDC NCM subclass 5.2.1 NCM Functional Descriptor, bmNetworkCapabilities */ +#define USB_CDC_NCM_NCAP_ETH_FILTER (1 << 0) +#define USB_CDC_NCM_NCAP_NET_ADDRESS (1 << 1) +#define USB_CDC_NCM_NCAP_ENCAP_COMMAND (1 << 2) +#define USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE (1 << 3) +#define USB_CDC_NCM_NCAP_CRC_MODE (1 << 4) + +/* CDC NCM subclass Table 6-3: NTB Parameter Structure */ +#define USB_CDC_NCM_NTB16_SUPPORTED (1 << 0) +#define USB_CDC_NCM_NTB32_SUPPORTED (1 << 1) + +/* CDC NCM subclass Table 6-3: NTB Parameter Structure */ +#define USB_CDC_NCM_NDP_ALIGN_MIN_SIZE 0x04 +#define USB_CDC_NCM_NTB_MAX_LENGTH 0x1C + +/* CDC NCM subclass 6.2.5 SetNtbFormat */ +#define USB_CDC_NCM_NTB16_FORMAT 0x00 +#define USB_CDC_NCM_NTB32_FORMAT 0x01 + +/* CDC NCM subclass 6.2.7 SetNtbInputSize */ +#define USB_CDC_NCM_NTB_MIN_IN_SIZE 2048 +#define USB_CDC_NCM_NTB_MIN_OUT_SIZE 2048 + +/* CDC NCM subclass 6.2.11 SetCrcMode */ +#define USB_CDC_NCM_CRC_NOT_APPENDED 0x00 +#define USB_CDC_NCM_CRC_APPENDED 0x01 + #endif /* __LINUX_USB_CDC_H */ -- cgit v1.2.3 From 6195e3c6aa84dbbf80a60731168118824bd58bba Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Fri, 24 Sep 2010 09:43:27 +0300 Subject: USB: cdc.h: ncm: fix one more typo In usb_cdc_ncm_dpe32 the fields are 32 bit long and according to usb style (hungarian notation) should be called dwDatagramIndex and dwDatagramLength (see CDC NCM subclass spec, 3.3.2). Actually, they were called wDatagramIndex, wDatagramLength. Signed-off-by: Yauheni Kaliuta Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/cdc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/usb') diff --git a/include/linux/usb/cdc.h b/include/linux/usb/cdc.h index 2d5b6f296aa3..5e86dc771da4 100644 --- a/include/linux/usb/cdc.h +++ b/include/linux/usb/cdc.h @@ -343,8 +343,8 @@ struct usb_cdc_ncm_ndp16 { /* 32-bit NCM Datagram Pointer Entry */ struct usb_cdc_ncm_dpe32 { - __le32 wDatagramIndex; - __le32 wDatagramLength; + __le32 dwDatagramIndex; + __le32 dwDatagramLength; } __attribute__((__packed__)); /* 32-bit NCM Datagram Pointer Table */ -- cgit v1.2.3 From ae6d22fe1812ce8d40add3eb74ede9cfd2eae44f Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 7 Oct 2010 13:05:22 +0200 Subject: USB: Move USB Storage definitions to their own header file The libusual header file is hard to use from code that isn't part of libusual. As the comment suggests, these definitions are moved to their own header file, paralleling other USB classes. Signed-off-by: Matthew Wilcox Cc: Alan Stern [mina86@mina86.com: updated to use USB_ prefix and added #include guard] Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman index 0000000..d7fc910 --- include/linux/usb/storage.h | 48 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb_usual.h | 38 +---------------------------------- 2 files changed, 49 insertions(+), 37 deletions(-) create mode 100644 include/linux/usb/storage.h (limited to 'include/linux/usb') diff --git a/include/linux/usb/storage.h b/include/linux/usb/storage.h new file mode 100644 index 000000000000..d7fc910f1dc4 --- /dev/null +++ b/include/linux/usb/storage.h @@ -0,0 +1,48 @@ +#ifndef __LINUX_USB_STORAGE_H +#define __LINUX_USB_STORAGE_H + +/* + * linux/usb/storage.h + * + * Copyright Matthew Wilcox for Intel Corp, 2010 + * + * This file contains definitions taken from the + * USB Mass Storage Class Specification Overview + * + * Distributed under the terms of the GNU GPL, version two. + */ + +/* Storage subclass codes */ + +#define USB_SC_RBC 0x01 /* Typically, flash devices */ +#define USB_SC_8020 0x02 /* CD-ROM */ +#define USB_SC_QIC 0x03 /* QIC-157 Tapes */ +#define USB_SC_UFI 0x04 /* Floppy */ +#define USB_SC_8070 0x05 /* Removable media */ +#define USB_SC_SCSI 0x06 /* Transparent */ +#define USB_SC_LOCKABLE 0x07 /* Password-protected */ + +#define USB_SC_ISD200 0xf0 /* ISD200 ATA */ +#define USB_SC_CYP_ATACB 0xf1 /* Cypress ATACB */ +#define USB_SC_DEVICE 0xff /* Use device's value */ + +/* Storage protocol codes */ + +#define USB_PR_CBI 0x00 /* Control/Bulk/Interrupt */ +#define USB_PR_CB 0x01 /* Control/Bulk w/o interrupt */ +#define USB_PR_BULK 0x50 /* bulk only */ +#define USB_PR_UAS 0x62 /* USB Attached SCSI */ + +#define USB_PR_USBAT 0x80 /* SCM-ATAPI bridge */ +#define USB_PR_EUSB_SDDR09 0x81 /* SCM-SCSI bridge for SDDR-09 */ +#define USB_PR_SDDR55 0x82 /* SDDR-55 (made up) */ +#define USB_PR_DPCM_USB 0xf0 /* Combination CB/SDDR09 */ +#define USB_PR_FREECOM 0xf1 /* Freecom */ +#define USB_PR_DATAFAB 0xf2 /* Datafab chipsets */ +#define USB_PR_JUMPSHOT 0xf3 /* Lexar Jumpshot */ +#define USB_PR_ALAUDA 0xf4 /* Alauda chipsets */ +#define USB_PR_KARMA 0xf5 /* Rio Karma */ + +#define USB_PR_DEVICE 0xff /* Use device's value */ + +#endif diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h index f387c436042e..f091dc6e5a00 100644 --- a/include/linux/usb_usual.h +++ b/include/linux/usb_usual.h @@ -74,43 +74,7 @@ enum { US_DO_ALL_FLAGS }; #define USB_US_TYPE(flags) (((flags) >> 24) & 0xFF) #define USB_US_ORIG_FLAGS(flags) ((flags) & 0x00FFFFFF) -/* - * This is probably not the best place to keep these constants, conceptually. - * But it's the only header included into all places which need them. - */ - -/* Sub Classes */ - -#define USB_SC_RBC 0x01 /* Typically, flash devices */ -#define USB_SC_8020 0x02 /* CD-ROM */ -#define USB_SC_QIC 0x03 /* QIC-157 Tapes */ -#define USB_SC_UFI 0x04 /* Floppy */ -#define USB_SC_8070 0x05 /* Removable media */ -#define USB_SC_SCSI 0x06 /* Transparent */ -#define USB_SC_LOCKABLE 0x07 /* Password-protected */ - -#define USB_SC_ISD200 0xf0 /* ISD200 ATA */ -#define USB_SC_CYP_ATACB 0xf1 /* Cypress ATACB */ -#define USB_SC_DEVICE 0xff /* Use device's value */ - -/* Storage protocol codes */ - -#define USB_PR_CBI 0x00 /* Control/Bulk/Interrupt */ -#define USB_PR_CB 0x01 /* Control/Bulk w/o interrupt */ -#define USB_PR_BULK 0x50 /* bulk only */ -#define USB_PR_UAS 0x62 /* USB Attached SCSI */ - -#define USB_PR_USBAT 0x80 /* SCM-ATAPI bridge */ -#define USB_PR_EUSB_SDDR09 0x81 /* SCM-SCSI bridge for SDDR-09 */ -#define USB_PR_SDDR55 0x82 /* SDDR-55 (made up) */ -#define USB_PR_DPCM_USB 0xf0 /* Combination CB/SDDR09 */ -#define USB_PR_FREECOM 0xf1 /* Freecom */ -#define USB_PR_DATAFAB 0xf2 /* Datafab chipsets */ -#define USB_PR_JUMPSHOT 0xf3 /* Lexar Jumpshot */ -#define USB_PR_ALAUDA 0xf4 /* Alauda chipsets */ -#define USB_PR_KARMA 0xf5 /* Rio Karma */ - -#define USB_PR_DEVICE 0xff /* Use device's value */ +#include /* */ -- cgit v1.2.3 From 496dda704bca1208e08773ba39b29a69536f5381 Mon Sep 17 00:00:00 2001 From: Maulik Mankad Date: Fri, 24 Sep 2010 13:44:06 +0300 Subject: usb: musb: host: unmap the buffer for PIO data transfers The USB stack maps the buffer for DMA if the controller supports DMA. MUSB controller can perform DMA as well as PIO transfers. The buffer needs to be unmapped before CPU can perform PIO data transfers. Export unmap_urb_for_dma() so that drivers can perform the DMA unmapping in a sane way. Signed-off-by: Maulik Mankad Acked-by: Alan Stern Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 3 ++- drivers/usb/musb/musb_host.c | 5 +++++ include/linux/usb/hcd.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 5cca00a6d09d..cb2d894321da 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1263,7 +1263,7 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, *dma_handle = 0; } -static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) { enum dma_data_direction dir; @@ -1307,6 +1307,7 @@ static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE | URB_MAP_LOCAL); } +EXPORT_SYMBOL_GPL(unmap_urb_for_dma); static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 9e65c47cc98b..62e39fc57211 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "musb_core.h" #include "musb_host.h" @@ -1332,6 +1333,8 @@ void musb_host_tx(struct musb *musb, u8 epnum) */ if (length > qh->maxpacket) length = qh->maxpacket; + /* Unmap the buffer so that CPU can use it */ + unmap_urb_for_dma(musb_to_hcd(musb), urb); musb_write_fifo(hw_ep, length, urb->transfer_buffer + offset); qh->segsize = length; @@ -1752,6 +1755,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) #endif /* Mentor DMA */ if (!dma) { + /* Unmap the buffer so that CPU can use it */ + unmap_urb_for_dma(musb_to_hcd(musb), urb); done = musb_host_packet_rx(musb, urb, epnum, iso_err); DBG(6, "read %spacket\n", done ? "last " : ""); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 3b571f1ffbb3..fe89f7c298aa 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -329,6 +329,7 @@ extern int usb_hcd_submit_urb(struct urb *urb, gfp_t mem_flags); extern int usb_hcd_unlink_urb(struct urb *urb, int status); extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status); +extern void unmap_urb_for_dma(struct usb_hcd *, struct urb *); extern void usb_hcd_flush_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep); extern void usb_hcd_disable_endpoint(struct usb_device *udev, -- cgit v1.2.3 From 748eee0986f0d51c7bc39f194d515a8d8248ebdd Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Mon, 27 Sep 2010 15:17:18 +0300 Subject: USB: Add more empty functions in otg.h Add empty functions for get/put transceiver functions too, so that drivers that optionally use them can call them without worrying that they might not exist, eliminating ifdefs. Signed-off-by: Grazvydas Ignotas Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/otg.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include/linux/usb') diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 545cba73ccaf..0a5b3711e502 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -164,8 +164,19 @@ otg_shutdown(struct otg_transceiver *otg) } /* for usb host and peripheral controller drivers */ +#ifdef CONFIG_USB_OTG_UTILS extern struct otg_transceiver *otg_get_transceiver(void); extern void otg_put_transceiver(struct otg_transceiver *); +#else +static inline struct otg_transceiver *otg_get_transceiver(void) +{ + return NULL; +} + +static inline void otg_put_transceiver(struct otg_transceiver *x) +{ +} +#endif /* Context: can sleep */ static inline int -- cgit v1.2.3 From 1dae423dd9b247b048eda00cb598c755e5933213 Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Fri, 1 Oct 2010 00:21:55 +0200 Subject: USB: introduce unmap_urb_setup_for_dma() Split unmap_urb_for_dma() to allow just the setup buffer to be unmapped. This allows HCDs to use PIO for the setup buffer if it is not suitable for DMA. Signed-off-by: Martin Fuzzey Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 18 +++++++++++++----- include/linux/usb/hcd.h | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index cb2d894321da..61800f77dac8 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1263,10 +1263,8 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, *dma_handle = 0; } -void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +void unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb) { - enum dma_data_direction dir; - if (urb->transfer_flags & URB_SETUP_MAP_SINGLE) dma_unmap_single(hcd->self.controller, urb->setup_dma, @@ -1279,6 +1277,17 @@ void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) sizeof(struct usb_ctrlrequest), DMA_TO_DEVICE); + /* Make it safe to call this routine more than once */ + urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL); +} +EXPORT_SYMBOL_GPL(unmap_urb_setup_for_dma); + +void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + enum dma_data_direction dir; + + unmap_urb_setup_for_dma(hcd, urb); + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; if (urb->transfer_flags & URB_DMA_MAP_SG) dma_unmap_sg(hcd->self.controller, @@ -1303,8 +1312,7 @@ void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) dir); /* Make it safe to call this routine more than once */ - urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL | - URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | + urb->transfer_flags &= ~(URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE | URB_MAP_LOCAL); } EXPORT_SYMBOL_GPL(unmap_urb_for_dma); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index fe89f7c298aa..0b6e751ea0b1 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -329,6 +329,7 @@ extern int usb_hcd_submit_urb(struct urb *urb, gfp_t mem_flags); extern int usb_hcd_unlink_urb(struct urb *urb, int status); extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status); +extern void unmap_urb_setup_for_dma(struct usb_hcd *, struct urb *); extern void unmap_urb_for_dma(struct usb_hcd *, struct urb *); extern void usb_hcd_flush_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep); -- cgit v1.2.3 From 562e7c71c6708353bfe7b615576bcbcf7afd522e Mon Sep 17 00:00:00 2001 From: Tatyana Brokhman Date: Sat, 9 Oct 2010 16:46:12 +0200 Subject: usb: usb3.0 ch9 definitions Adding SuperSpeed usb definitions as defined by ch9 of the USB3.0 spec. This patch is a preparation for adding SuperSpeed support to the gadget framework. Signed-off-by: Tatyana Brokhman Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/ch9.h | 58 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) (limited to 'include/linux/usb') diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index b0f7e9f57176..f917bbbc8901 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -123,8 +123,23 @@ #define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */ #define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */ +/* + * New Feature Selectors as added by USB 3.0 + * See USB 3.0 spec Table 9-6 + */ +#define USB_DEVICE_U1_ENABLE 48 /* dev may initiate U1 transition */ +#define USB_DEVICE_U2_ENABLE 49 /* dev may initiate U2 transition */ +#define USB_DEVICE_LTM_ENABLE 50 /* dev may send LTM */ +#define USB_INTRF_FUNC_SUSPEND 0 /* function suspend */ + +#define USB_INTR_FUNC_SUSPEND_OPT_MASK 0xFF00 + #define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */ +/* Bit array elements as returned by the USB_REQ_GET_STATUS request. */ +#define USB_DEV_STAT_U1_ENABLED 2 /* transition into U1 state */ +#define USB_DEV_STAT_U2_ENABLED 3 /* transition into U2 state */ +#define USB_DEV_STAT_LTM_ENABLED 4 /* Latency tolerance messages */ /** * struct usb_ctrlrequest - SETUP data for a USB device control request @@ -675,6 +690,7 @@ struct usb_bos_descriptor { __u8 bNumDeviceCaps; } __attribute__((packed)); +#define USB_DT_BOS_SIZE 5 /*-------------------------------------------------------------------------*/ /* USB_DT_DEVICE_CAPABILITY: grouped with BOS */ @@ -712,16 +728,56 @@ struct usb_wireless_cap_descriptor { /* Ultra Wide Band */ __u8 bReserved; } __attribute__((packed)); +/* USB 2.0 Extension descriptor */ #define USB_CAP_TYPE_EXT 2 struct usb_ext_cap_descriptor { /* Link Power Management */ __u8 bLength; __u8 bDescriptorType; __u8 bDevCapabilityType; - __u8 bmAttributes; + __le32 bmAttributes; #define USB_LPM_SUPPORT (1 << 1) /* supports LPM */ } __attribute__((packed)); +#define USB_DT_USB_EXT_CAP_SIZE 7 + +/* + * SuperSpeed USB Capability descriptor: Defines the set of SuperSpeed USB + * specific device level capabilities + */ +#define USB_SS_CAP_TYPE 3 +struct usb_ss_cap_descriptor { /* Link Power Management */ + __u8 bLength; + __u8 bDescriptorType; + __u8 bDevCapabilityType; + __u8 bmAttributes; +#define USB_LTM_SUPPORT (1 << 1) /* supports LTM */ + __le16 wSpeedSupported; +#define USB_LOW_SPEED_OPERATION (1) /* Low speed operation */ +#define USB_FULL_SPEED_OPERATION (1 << 1) /* Full speed operation */ +#define USB_HIGH_SPEED_OPERATION (1 << 2) /* High speed operation */ +#define USB_5GBPS_OPERATION (1 << 3) /* Operation at 5Gbps */ + __u8 bFunctionalitySupport; + __u8 bU1devExitLat; + __le16 bU2DevExitLat; +} __attribute__((packed)); + +#define USB_DT_USB_SS_CAP_SIZE 10 + +/* + * Container ID Capability descriptor: Defines the instance unique ID used to + * identify the instance across all operating modes + */ +#define CONTAINER_ID_TYPE 4 +struct usb_ss_container_id_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDevCapabilityType; + __u8 bReserved; + __u8 ContainerID[16]; /* 128-bit number */ +} __attribute__((packed)); + +#define USB_DT_USB_SS_CONTN_ID_SIZE 20 /*-------------------------------------------------------------------------*/ /* USB_DT_WIRELESS_ENDPOINT_COMP: companion descriptor associated with -- cgit v1.2.3 From 9c7564620f82e55a9c8713311bffd401ec9d60fe Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Sat, 23 Oct 2010 05:12:01 -0500 Subject: USB: musb: blackfin: push clkin value to platform resources In order to not touch the driver file for different xtal usage, push the clkin value to board file and calculate the register value instead of hardcoding it. Signed-off-by: Bob Liu Signed-off-by: Mike Frysinger Signed-off-by: Felipe Balbi --- drivers/usb/musb/blackfin.c | 3 ++- include/linux/usb/musb.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index ade45a219c47..fcb5206a65bd 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -337,7 +337,8 @@ static void musb_platform_reg_init(struct musb *musb) } /* Configure PLL oscillator register */ - bfin_write_USB_PLLOSC_CTRL(0x30a8); + bfin_write_USB_PLLOSC_CTRL(0x3080 | + ((480/musb->config->clkin) << 1)); SSYNC(); bfin_write_USB_SRP_CLKDIV((get_sclk()/1000) / 32 - 1); diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h index ee2dd1d506ed..2387f9fc8138 100644 --- a/include/linux/usb/musb.h +++ b/include/linux/usb/musb.h @@ -89,6 +89,8 @@ struct musb_hdrc_config { /* A GPIO controlling VRSEL in Blackfin */ unsigned int gpio_vrsel; unsigned int gpio_vrsel_active; + /* musb CLKIN in Blackfin in MHZ */ + unsigned char clkin; #endif }; -- cgit v1.2.3