From f1407d5c66240b33d11a7f1a41d55ccf6a9d7647 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 4 Apr 2011 13:44:59 +0900 Subject: usb: renesas_usbhs: Add Renesas USBHS common code Renesas SuperH has USBHS IP which can switch Host / Function. This driver is designed so that Host / Function may dynamically change. This patch add usb/renesas_usbhs and common code for SuperH USBHS. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/Makefile | 1 + drivers/usb/Kconfig | 2 + drivers/usb/renesas_usbhs/Kconfig | 15 + drivers/usb/renesas_usbhs/Makefile | 7 + drivers/usb/renesas_usbhs/common.c | 394 +++++++++++++++++ drivers/usb/renesas_usbhs/common.h | 225 ++++++++++ drivers/usb/renesas_usbhs/mod.c | 261 +++++++++++ drivers/usb/renesas_usbhs/mod.h | 106 +++++ drivers/usb/renesas_usbhs/pipe.c | 880 +++++++++++++++++++++++++++++++++++++ drivers/usb/renesas_usbhs/pipe.h | 105 +++++ include/linux/usb/renesas_usbhs.h | 149 +++++++ 11 files changed, 2145 insertions(+) create mode 100644 drivers/usb/renesas_usbhs/Kconfig create mode 100644 drivers/usb/renesas_usbhs/Makefile create mode 100644 drivers/usb/renesas_usbhs/common.c create mode 100644 drivers/usb/renesas_usbhs/common.h create mode 100644 drivers/usb/renesas_usbhs/mod.c create mode 100644 drivers/usb/renesas_usbhs/mod.h create mode 100644 drivers/usb/renesas_usbhs/pipe.c create mode 100644 drivers/usb/renesas_usbhs/pipe.h create mode 100644 include/linux/usb/renesas_usbhs.h diff --git a/drivers/Makefile b/drivers/Makefile index 3f135b6fb01..ad67b7d4c27 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_UWB) += uwb/ obj-$(CONFIG_USB_OTG_UTILS) += usb/otg/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/ +obj-$(CONFIG_USB_RENESAS_USBHS) += usb/renesas_usbhs/ obj-$(CONFIG_PCI) += usb/ obj-$(CONFIG_USB_GADGET) += usb/gadget/ obj-$(CONFIG_SERIO) += input/serio/ diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 41b6e51188e..d299906e4f0 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -115,6 +115,8 @@ source "drivers/usb/host/Kconfig" source "drivers/usb/musb/Kconfig" +source "drivers/usb/renesas_usbhs/Kconfig" + source "drivers/usb/class/Kconfig" source "drivers/usb/storage/Kconfig" diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig new file mode 100644 index 00000000000..481490e5500 --- /dev/null +++ b/drivers/usb/renesas_usbhs/Kconfig @@ -0,0 +1,15 @@ +# +# Renesas USB Controller Drivers +# + +config USB_RENESAS_USBHS + tristate 'Renesas USBHS controller' + default n + help + Renesas USBHS is a discrete USB host and peripheral controller chip + that supports both full and high speed USB 2.0 data transfers. + It has nine or more configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "renesas_usbhs" and force all + gadget drivers to also be dynamically linked. diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile new file mode 100644 index 00000000000..d76f3dd3b9d --- /dev/null +++ b/drivers/usb/renesas_usbhs/Makefile @@ -0,0 +1,7 @@ +# +# for Renesas USB +# + +obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o + +renesas_usbhs-y := common.o mod.o pipe.o diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c new file mode 100644 index 00000000000..d9ad60d1c15 --- /dev/null +++ b/drivers/usb/renesas_usbhs/common.c @@ -0,0 +1,394 @@ +/* + * Renesas USB driver + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 + * + */ +#include +#include +#include +#include +#include +#include "./common.h" + +/* + * platform call back + * + * renesas usb support platform callback function. + * Below macro call it. + * if platform doesn't have callback, it return 0 (no error) + */ +#define usbhs_platform_call(priv, func, args...)\ + (!(priv) ? -ENODEV : \ + !((priv)->pfunc->func) ? 0 : \ + (priv)->pfunc->func(args)) + +/* + * common functions + */ +u16 usbhs_read(struct usbhs_priv *priv, u32 reg) +{ + return ioread16(priv->base + reg); +} + +void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data) +{ + iowrite16(data, priv->base + reg); +} + +void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data) +{ + u16 val = usbhs_read(priv, reg); + + val &= ~mask; + val |= data & mask; + + usbhs_write(priv, reg, val); +} + +/* + * syscfg functions + */ +void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable) +{ + usbhs_bset(priv, SYSCFG, SCKE, enable ? SCKE : 0); +} + +void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable) +{ + usbhs_bset(priv, SYSCFG, HSE, enable ? HSE : 0); +} + +void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable) +{ + usbhs_bset(priv, SYSCFG, USBE, enable ? USBE : 0); +} + +void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable) +{ + u16 mask = DCFM | DRPD | DPRPU; + u16 val = DCFM | DRPD; + + /* + * if enable + * + * - select Host mode + * - D+ Line/D- Line Pull-down + */ + usbhs_bset(priv, SYSCFG, mask, enable ? val : 0); +} + +void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable) +{ + u16 mask = DCFM | DRPD | DPRPU; + u16 val = DPRPU; + + /* + * if enable + * + * - select Function mode + * - D+ Line Pull-up + */ + usbhs_bset(priv, SYSCFG, mask, enable ? val : 0); +} + +/* + * frame functions + */ +int usbhs_frame_get_num(struct usbhs_priv *priv) +{ + return usbhs_read(priv, FRMNUM) & FRNM_MASK; +} + +/* + * local functions + */ +static struct usbhs_priv *usbhsc_pdev_to_priv(struct platform_device *pdev) +{ + return dev_get_drvdata(&pdev->dev); +} + +static void usbhsc_bus_ctrl(struct usbhs_priv *priv, int enable) +{ + int wait = usbhs_get_dparam(priv, buswait_bwait); + u16 data = 0; + + if (enable) { + /* set bus wait if platform have */ + if (wait) + usbhs_bset(priv, BUSWAIT, 0x000F, wait); + } + usbhs_write(priv, DVSTCTR, data); +} + +/* + * platform default param + */ +static u32 usbhsc_default_pipe_type[] = { + USB_ENDPOINT_XFER_CONTROL, + USB_ENDPOINT_XFER_ISOC, + USB_ENDPOINT_XFER_ISOC, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, +}; + +/* + * driver callback functions + */ +static void usbhsc_notify_hotplug(struct work_struct *work) +{ + struct usbhs_priv *priv = container_of(work, + struct usbhs_priv, + notify_hotplug_work); + struct platform_device *pdev = usbhs_priv_to_pdev(priv); + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + int id; + int enable; + int ret; + + /* + * get vbus status from platform + */ + enable = usbhs_platform_call(priv, get_vbus, pdev); + + /* + * get id from platform + */ + id = usbhs_platform_call(priv, get_id, pdev); + + if (enable && !mod) { + ret = usbhs_mod_change(priv, id); + if (ret < 0) + return; + + dev_dbg(&pdev->dev, "%s enable\n", __func__); + + /* enable PM */ + pm_runtime_get_sync(&pdev->dev); + + /* USB on */ + usbhs_sys_clock_ctrl(priv, enable); + usbhsc_bus_ctrl(priv, enable); + + /* module start */ + usbhs_mod_call(priv, start, priv); + + } else if (!enable && mod) { + dev_dbg(&pdev->dev, "%s disable\n", __func__); + + /* module stop */ + usbhs_mod_call(priv, stop, priv); + + /* USB off */ + usbhsc_bus_ctrl(priv, enable); + usbhs_sys_clock_ctrl(priv, enable); + + /* disable PM */ + pm_runtime_put_sync(&pdev->dev); + + usbhs_mod_change(priv, -1); + + /* reset phy for next connection */ + usbhs_platform_call(priv, phy_reset, pdev); + } +} + +static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev); + + /* + * This functions will be called in interrupt. + * To make sure safety context, + * use workqueue for usbhs_notify_hotplug + */ + schedule_work(&priv->notify_hotplug_work); + return 0; +} + +/* + * platform functions + */ +static int __devinit usbhs_probe(struct platform_device *pdev) +{ + struct renesas_usbhs_platform_info *info = pdev->dev.platform_data; + struct renesas_usbhs_driver_callback *dfunc; + struct usbhs_priv *priv; + struct resource *res; + unsigned int irq; + int ret; + + /* check platform information */ + if (!info || + !info->platform_callback.get_id || + !info->platform_callback.get_vbus) { + dev_err(&pdev->dev, "no platform information\n"); + return -EINVAL; + } + + /* platform data */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!res || (int)irq <= 0) { + dev_err(&pdev->dev, "Not enough Renesas USB platform resources.\n"); + return -ENODEV; + } + + /* usb private data */ + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "Could not allocate priv\n"); + return -ENOMEM; + } + + priv->base = ioremap_nocache(res->start, resource_size(res)); + if (!priv->base) { + dev_err(&pdev->dev, "ioremap error.\n"); + ret = -ENOMEM; + goto probe_end_kfree; + } + + /* + * care platform info + */ + priv->pfunc = &info->platform_callback; + priv->dparam = &info->driver_param; + + /* set driver callback functions for platform */ + dfunc = &info->driver_callback; + dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug; + + /* set default param if platform doesn't have */ + if (!priv->dparam->pipe_type) { + priv->dparam->pipe_type = usbhsc_default_pipe_type; + priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type); + } + + /* + * priv settings + */ + priv->irq = irq; + priv->pdev = pdev; + INIT_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug); + spin_lock_init(usbhs_priv_to_lock(priv)); + + /* call pipe and module init */ + ret = usbhs_pipe_probe(priv); + if (ret < 0) + goto probe_end_mod_exit; + + ret = usbhs_mod_probe(priv); + if (ret < 0) + goto probe_end_iounmap; + + /* dev_set_drvdata should be called after usbhs_mod_init */ + dev_set_drvdata(&pdev->dev, priv); + + /* + * deviece reset here because + * USB device might be used in boot loader. + */ + usbhs_sys_clock_ctrl(priv, 0); + + /* + * platform call + * + * USB phy setup might depend on CPU/Board. + * If platform has its callback functions, + * call it here. + */ + ret = usbhs_platform_call(priv, hardware_init, pdev); + if (ret < 0) { + dev_err(&pdev->dev, "platform prove failed.\n"); + goto probe_end_pipe_exit; + } + + /* reset phy for connection */ + usbhs_platform_call(priv, phy_reset, pdev); + + /* + * manual call notify_hotplug for cold plug + */ + pm_runtime_enable(&pdev->dev); + ret = usbhsc_drvcllbck_notify_hotplug(pdev); + if (ret < 0) + goto probe_end_call_remove; + + dev_info(&pdev->dev, "probed\n"); + + return ret; + +probe_end_call_remove: + usbhs_platform_call(priv, hardware_exit, pdev); +probe_end_pipe_exit: + usbhs_pipe_remove(priv); +probe_end_mod_exit: + usbhs_mod_remove(priv); +probe_end_iounmap: + iounmap(priv->base); +probe_end_kfree: + kfree(priv); + + dev_info(&pdev->dev, "probe failed\n"); + + return ret; +} + +static int __devexit usbhs_remove(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev); + + dev_dbg(&pdev->dev, "usb remove\n"); + + pm_runtime_disable(&pdev->dev); + + usbhsc_bus_ctrl(priv, 0); + + usbhs_platform_call(priv, hardware_exit, pdev); + usbhs_pipe_remove(priv); + usbhs_mod_remove(priv); + iounmap(priv->base); + kfree(priv); + + return 0; +} + +static struct platform_driver renesas_usbhs_driver = { + .driver = { + .name = "renesas_usbhs", + }, + .probe = usbhs_probe, + .remove = __devexit_p(usbhs_remove), +}; + +static int __init usbhs_init(void) +{ + return platform_driver_register(&renesas_usbhs_driver); +} + +static void __exit usbhs_exit(void) +{ + platform_driver_unregister(&renesas_usbhs_driver); +} + +module_init(usbhs_init); +module_exit(usbhs_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas USB driver"); +MODULE_AUTHOR("Kuninori Morimoto "); diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h new file mode 100644 index 00000000000..f1a2b62f93f --- /dev/null +++ b/drivers/usb/renesas_usbhs/common.h @@ -0,0 +1,225 @@ +/* + * Renesas USB driver + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 RENESAS_USB_DRIVER_H +#define RENESAS_USB_DRIVER_H + +#include +#include + +struct usbhs_priv; + +#include "./mod.h" +#include "./pipe.h" + +/* + * + * register define + * + */ +#define SYSCFG 0x0000 +#define BUSWAIT 0x0002 +#define DVSTCTR 0x0008 +#define CFIFO 0x0014 +#define CFIFOSEL 0x0020 +#define CFIFOCTR 0x0022 +#define INTENB0 0x0030 +#define INTENB1 0x0032 +#define BRDYENB 0x0036 +#define NRDYENB 0x0038 +#define BEMPENB 0x003A +#define INTSTS0 0x0040 +#define INTSTS1 0x0042 +#define BRDYSTS 0x0046 +#define NRDYSTS 0x0048 +#define BEMPSTS 0x004A +#define FRMNUM 0x004C +#define USBREQ 0x0054 /* USB request type register */ +#define USBVAL 0x0056 /* USB request value register */ +#define USBINDX 0x0058 /* USB request index register */ +#define USBLENG 0x005A /* USB request length register */ +#define DCPCFG 0x005C +#define DCPMAXP 0x005E +#define DCPCTR 0x0060 +#define PIPESEL 0x0064 +#define PIPECFG 0x0068 +#define PIPEBUF 0x006A +#define PIPEMAXP 0x006C +#define PIPEPERI 0x006E +#define PIPEnCTR 0x0070 + +/* SYSCFG */ +#define SCKE (1 << 10) /* USB Module Clock Enable */ +#define HSE (1 << 7) /* High-Speed Operation Enable */ +#define DCFM (1 << 6) /* Controller Function Select */ +#define DRPD (1 << 5) /* D+ Line/D- Line Resistance Control */ +#define DPRPU (1 << 4) /* D+ Line Resistance Control */ +#define USBE (1 << 0) /* USB Module Operation Enable */ + +/* DVSTCTR */ +#define EXTLP (1 << 10) /* Controls the EXTLP pin output state */ +#define PWEN (1 << 9) /* Controls the PWEN pin output state */ +#define RHST (0x7) /* Reset Handshake */ +#define RHST_LOW_SPEED 1 /* Low-speed connection */ +#define RHST_FULL_SPEED 2 /* Full-speed connection */ +#define RHST_HIGH_SPEED 3 /* High-speed connection */ + +/* CFIFOSEL */ +#define MBW_32 (0x2 << 10) /* CFIFO Port Access Bit Width */ + +/* CFIFOCTR */ +#define BVAL (1 << 15) /* Buffer Memory Enable Flag */ +#define BCLR (1 << 14) /* CPU buffer clear */ +#define FRDY (1 << 13) /* FIFO Port Ready */ +#define DTLN_MASK (0x0FFF) /* Receive Data Length */ + +/* INTENB0 */ +#define VBSE (1 << 15) /* Enable IRQ VBUS_0 and VBUSIN_0 */ +#define RSME (1 << 14) /* Enable IRQ Resume */ +#define SOFE (1 << 13) /* Enable IRQ Frame Number Update */ +#define DVSE (1 << 12) /* Enable IRQ Device State Transition */ +#define CTRE (1 << 11) /* Enable IRQ Control Stage Transition */ +#define BEMPE (1 << 10) /* Enable IRQ Buffer Empty */ +#define NRDYE (1 << 9) /* Enable IRQ Buffer Not Ready Response */ +#define BRDYE (1 << 8) /* Enable IRQ Buffer Ready */ + +/* INTENB1 */ +#define BCHGE (1 << 14) /* USB Bus Change Interrupt Enable */ +#define DTCHE (1 << 12) /* Disconnection Detect Interrupt Enable */ +#define ATTCHE (1 << 11) /* Connection Detect Interrupt Enable */ +#define EOFERRE (1 << 6) /* EOF Error Detect Interrupt Enable */ +#define SIGNE (1 << 5) /* Setup Transaction Error Interrupt Enable */ +#define SACKE (1 << 4) /* Setup Transaction ACK Interrupt Enable */ + +/* INTSTS0 */ +#define DVST (1 << 12) /* Device State Transition Interrupt Status */ +#define CTRT (1 << 11) /* Control Stage Interrupt Status */ +#define BEMP (1 << 10) /* Buffer Empty Interrupt Status */ +#define BRDY (1 << 8) /* Buffer Ready Interrupt Status */ +#define VBSTS (1 << 7) /* VBUS_0 and VBUSIN_0 Input Status */ +#define VALID (1 << 3) /* USB Request Receive */ + +#define DVSQ_MASK (0x3 << 4) /* Device State */ +#define POWER_STATE (0 << 4) +#define DEFAULT_STATE (1 << 4) +#define ADDRESS_STATE (2 << 4) +#define CONFIGURATION_STATE (3 << 4) + +#define CTSQ_MASK (0x7) /* Control Transfer Stage */ +#define IDLE_SETUP_STAGE 0 /* Idle stage or setup stage */ +#define READ_DATA_STAGE 1 /* Control read data stage */ +#define READ_STATUS_STAGE 2 /* Control read status stage */ +#define WRITE_DATA_STAGE 3 /* Control write data stage */ +#define WRITE_STATUS_STAGE 4 /* Control write status stage */ +#define NODATA_STATUS_STAGE 5 /* Control write NoData status stage */ +#define SEQUENCE_ERROR 6 /* Control transfer sequence error */ + +/* PIPECFG */ +/* DCPCFG */ +#define TYPE_NONE (0 << 14) /* Transfer Type */ +#define TYPE_BULK (1 << 14) +#define TYPE_INT (2 << 14) +#define TYPE_ISO (3 << 14) +#define DBLB (1 << 9) /* Double Buffer Mode */ +#define SHTNAK (1 << 7) /* Pipe Disable in Transfer End */ +#define DIR_OUT (1 << 4) /* Transfer Direction */ + +/* PIPEMAXP */ +/* DCPMAXP */ +#define DEVSEL_MASK (0xF << 12) /* Device Select */ +#define DCP_MAXP_MASK (0x7F) +#define PIPE_MAXP_MASK (0x7FF) + +/* PIPEBUF */ +#define BUFSIZE_SHIFT 10 +#define BUFSIZE_MASK (0x1F << BUFSIZE_SHIFT) +#define BUFNMB_MASK (0xFF) + +/* PIPEnCTR */ +/* DCPCTR */ +#define BSTS (1 << 15) /* Buffer Status */ +#define CSSTS (1 << 12) /* CSSTS Status */ +#define SQCLR (1 << 8) /* Toggle Bit Clear */ +#define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */ +#define PBUSY (1 << 5) /* Pipe Busy */ +#define PID_MASK (0x3) /* Response PID */ +#define PID_NAK 0 +#define PID_BUF 1 +#define PID_STALL10 2 +#define PID_STALL11 3 + +#define CCPL (1 << 2) /* Control Transfer End Enable */ + +/* FRMNUM */ +#define FRNM_MASK (0x7FF) + +/* + * struct + */ +struct usbhs_priv { + + void __iomem *base; + unsigned int irq; + + struct renesas_usbhs_platform_callback *pfunc; + struct renesas_usbhs_driver_param *dparam; + + struct work_struct notify_hotplug_work; + struct platform_device *pdev; + + spinlock_t lock; + + /* + * module control + */ + struct usbhs_mod_info mod_info; + + /* + * pipe control + */ + struct usbhs_pipe_info pipe_info; +}; + +/* + * common + */ +u16 usbhs_read(struct usbhs_priv *priv, u32 reg); +void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data); +void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data); + +/* + * sysconfig + */ +void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable); +void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable); +void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable); +void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable); +void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable); + +/* + * frame + */ +int usbhs_frame_get_num(struct usbhs_priv *priv); + +/* + * data + */ +#define usbhs_get_dparam(priv, param) (priv->dparam->param) +#define usbhs_priv_to_pdev(priv) (priv->pdev) +#define usbhs_priv_to_dev(priv) (&priv->pdev->dev) +#define usbhs_priv_to_lock(priv) (&priv->lock) + +#endif /* RENESAS_USB_DRIVER_H */ diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c new file mode 100644 index 00000000000..4a3398484cd --- /dev/null +++ b/drivers/usb/renesas_usbhs/mod.c @@ -0,0 +1,261 @@ +/* + * Renesas USB driver + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 + * + */ +#include + +#include "./common.h" +#include "./mod.h" + +#define usbhs_priv_to_modinfo(priv) (&priv->mod_info) + +/* + * host / gadget functions + * + * renesas_usbhs host/gadget can register itself by below functions. + * these functions are called when probe + * + */ +void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *mod, int id) +{ + struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); + + info->mod[id] = mod; + mod->priv = priv; +} + +struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id) +{ + struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); + struct usbhs_mod *ret = NULL; + + switch (id) { + case USBHS_HOST: + case USBHS_GADGET: + ret = info->mod[id]; + break; + } + + return ret; +} + +int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod) +{ + struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); + + if (!mod) + return -EINVAL; + + return info->mod[USBHS_HOST] == mod; +} + +struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv) +{ + struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); + + return info->curt; +} + +int usbhs_mod_change(struct usbhs_priv *priv, int id) +{ + struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); + struct usbhs_mod *mod = NULL; + int ret = 0; + + /* id < 0 mean no current */ + switch (id) { + case USBHS_HOST: + case USBHS_GADGET: + mod = info->mod[id]; + break; + default: + ret = -EINVAL; + } + info->curt = mod; + + return ret; +} + +static irqreturn_t usbhs_interrupt(int irq, void *data); +int usbhs_mod_probe(struct usbhs_priv *priv) +{ + struct device *dev = usbhs_priv_to_dev(priv); + int ret; + + /* irq settings */ + ret = request_irq(priv->irq, usbhs_interrupt, + IRQF_DISABLED, dev_name(dev), priv); + if (ret) + dev_err(dev, "irq request err\n"); + + return ret; +} + +void usbhs_mod_remove(struct usbhs_priv *priv) +{ + free_irq(priv->irq, priv); +} + +/* + * status functions + */ +int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state) +{ + switch (irq_state->dvstctr & RHST) { + case RHST_LOW_SPEED: + return USB_SPEED_LOW; + case RHST_FULL_SPEED: + return USB_SPEED_FULL; + case RHST_HIGH_SPEED: + return USB_SPEED_HIGH; + } + + return USB_SPEED_UNKNOWN; +} + +int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state) +{ + int state = irq_state->intsts0 & DVSQ_MASK; + + switch (state) { + case POWER_STATE: + case DEFAULT_STATE: + case ADDRESS_STATE: + case CONFIGURATION_STATE: + return state; + } + + return -EIO; +} + +int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state) +{ + /* + * return value + * + * IDLE_SETUP_STAGE + * READ_DATA_STAGE + * READ_STATUS_STAGE + * WRITE_DATA_STAGE + * WRITE_STATUS_STAGE + * NODATA_STATUS_STAGE + * SEQUENCE_ERROR + */ + return (int)irq_state->intsts0 & CTSQ_MASK; +} + +static void usbhs_status_get_each_irq(struct usbhs_priv *priv, + struct usbhs_irq_state *state) +{ + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + + state->intsts0 = usbhs_read(priv, INTSTS0); + state->intsts1 = usbhs_read(priv, INTSTS1); + + state->brdysts = usbhs_read(priv, BRDYSTS); + state->nrdysts = usbhs_read(priv, NRDYSTS); + state->bempsts = usbhs_read(priv, BEMPSTS); + + state->dvstctr = usbhs_read(priv, DVSTCTR); + + /* mask */ + state->bempsts &= mod->irq_bempsts; + state->brdysts &= mod->irq_brdysts; +} + +/* + * interrupt + */ +#define INTSTS0_MAGIC 0xF800 /* acknowledge magical interrupt sources */ +#define INTSTS1_MAGIC 0xA870 /* acknowledge magical interrupt sources */ +static irqreturn_t usbhs_interrupt(int irq, void *data) +{ + struct usbhs_priv *priv = data; + struct usbhs_irq_state irq_state; + + usbhs_status_get_each_irq(priv, &irq_state); + + /* + * clear interrupt + * + * The hardware is _very_ picky to clear interrupt bit. + * Especially INTSTS0_MAGIC, INTSTS1_MAGIC value. + * + * see + * "Operation" + * - "Control Transfer (DCP)" + * - Function :: VALID bit should 0 + */ + usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC); + usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); + + usbhs_write(priv, BRDYSTS, 0); + usbhs_write(priv, NRDYSTS, 0); + usbhs_write(priv, BEMPSTS, 0); + + /* + * call irq callback functions + * see also + * usbhs_irq_setting_update + */ + if (irq_state.intsts0 & DVST) + usbhs_mod_call(priv, irq_dev_state, priv, &irq_state); + + if (irq_state.intsts0 & CTRT) + usbhs_mod_call(priv, irq_ctrl_stage, priv, &irq_state); + + if (irq_state.intsts0 & BEMP) + usbhs_mod_call(priv, irq_empty, priv, &irq_state); + + if (irq_state.intsts0 & BRDY) + usbhs_mod_call(priv, irq_ready, priv, &irq_state); + + return IRQ_HANDLED; +} + +void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) +{ + u16 intenb0 = 0; + + usbhs_write(priv, INTENB0, 0); + + usbhs_write(priv, BEMPENB, 0); + usbhs_write(priv, BRDYENB, 0); + + /* + * see also + * usbhs_interrupt + */ + + /* + * it don't enable DVSE (intenb0) here + * but "mod->irq_dev_state" will be called. + */ + + if (mod->irq_ctrl_stage) + intenb0 |= CTRE; + + if (mod->irq_empty && mod->irq_bempsts) { + usbhs_write(priv, BEMPENB, mod->irq_bempsts); + intenb0 |= BEMPE; + } + + if (mod->irq_ready && mod->irq_brdysts) { + usbhs_write(priv, BRDYENB, mod->irq_brdysts); + intenb0 |= BRDYE; + } + + usbhs_write(priv, INTENB0, intenb0); +} diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h new file mode 100644 index 00000000000..bd873d5b432 --- /dev/null +++ b/drivers/usb/renesas_usbhs/mod.h @@ -0,0 +1,106 @@ +/* + * Renesas USB driver + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 RENESAS_USB_MOD_H +#define RENESAS_USB_MOD_H + +#include +#include +#include "./common.h" + +/* + * struct + */ +struct usbhs_irq_state { + u16 intsts0; + u16 intsts1; + u16 brdysts; + u16 nrdysts; + u16 bempsts; + u16 dvstctr; +}; + +struct usbhs_mod { + char *name; + + /* + * entry point from common.c + */ + int (*start)(struct usbhs_priv *priv); + int (*stop)(struct usbhs_priv *priv); + + /* INTSTS0 :: DVST (DVSQ) */ + int (*irq_dev_state)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + + /* INTSTS0 :: CTRT (CTSQ) */ + int (*irq_ctrl_stage)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + + /* INTSTS0 :: BEMP */ + /* BEMPSTS */ + int (*irq_empty)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + u16 irq_bempsts; + + /* INTSTS0 :: BRDY */ + /* BRDYSTS */ + int (*irq_ready)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + u16 irq_brdysts; + + struct usbhs_priv *priv; +}; + +struct usbhs_mod_info { + struct usbhs_mod *mod[USBHS_MAX]; + struct usbhs_mod *curt; /* current mod */ +}; + +/* + * for host/gadget module + */ +struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id); +struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv); +void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *usb, int id); +int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod); +int usbhs_mod_change(struct usbhs_priv *priv, int id); +int usbhs_mod_probe(struct usbhs_priv *priv); +void usbhs_mod_remove(struct usbhs_priv *priv); + +/* + * status functions + */ +int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state); +int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state); +int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state); + +/* + * callback functions + */ +void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod); + + +#define usbhs_mod_call(priv, func, param...) \ + ({ \ + struct usbhs_mod *mod; \ + mod = usbhs_mod_get_current(priv); \ + !mod ? -ENODEV : \ + !mod->func ? 0 : \ + mod->func(param); \ + }) + +#endif /* RENESAS_USB_MOD_H */ diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c new file mode 100644 index 00000000000..b7a9137f599 --- /dev/null +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -0,0 +1,880 @@ +/* + * Renesas USB driver + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 + * + */ +#include +#include +#include +#include "./common.h" +#include "./pipe.h" + +/* + * macros + */ +#define usbhsp_priv_to_pipeinfo(pr) (&(pr)->pipe_info) +#define usbhsp_pipe_to_priv(p) ((p)->priv) + +#define usbhsp_addr_offset(p) ((usbhs_pipe_number(p) - 1) * 2) + +#define usbhsp_is_dcp(p) ((p)->priv->pipe_info.pipe == (p)) + +#define usbhsp_flags_set(p, f) ((p)->flags |= USBHS_PIPE_FLAGS_##f) +#define usbhsp_flags_clr(p, f) ((p)->flags &= ~USBHS_PIPE_FLAGS_##f) +#define usbhsp_flags_has(p, f) ((p)->flags & USBHS_PIPE_FLAGS_##f) +#define usbhsp_flags_init(p) do {(p)->flags = 0; } while (0) + +#define usbhsp_type(p) ((p)->pipe_type) +#define usbhsp_type_is(p, t) ((p)->pipe_type == t) + +/* + * for debug + */ +static char *usbhsp_pipe_name[] = { + [USB_ENDPOINT_XFER_CONTROL] = "DCP", + [USB_ENDPOINT_XFER_BULK] = "BULK", + [USB_ENDPOINT_XFER_INT] = "INT", + [USB_ENDPOINT_XFER_ISOC] = "ISO", +}; + +/* + * usb request functions + */ +void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) +{ + u16 val; + + val = usbhs_read(priv, USBREQ); + req->bRequest = (val >> 8) & 0xFF; + req->bRequestType = (val >> 0) & 0xFF; + + req->wValue = usbhs_read(priv, USBVAL); + req->wIndex = usbhs_read(priv, USBINDX); + req->wLength = usbhs_read(priv, USBLENG); +} + +void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) +{ + usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType); + usbhs_write(priv, USBVAL, req->wValue); + usbhs_write(priv, USBINDX, req->wIndex); + usbhs_write(priv, USBLENG, req->wLength); +} + +/* + * DCPCTR/PIPEnCTR functions + */ +static void usbhsp_pipectrl_set(struct usbhs_pipe *pipe, u16 mask, u16 val) +{ + struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); + int offset = usbhsp_addr_offset(pipe); + + if (usbhsp_is_dcp(pipe)) + usbhs_bset(priv, DCPCTR, mask, val); + else + usbhs_bset(priv, PIPEnCTR + offset, mask, val); +} + +static u16 usbhsp_pipectrl_get(struct usbhs_pipe *pipe) +{ + struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); + int offset = usbhsp_addr_offset(pipe); + + if (usbhsp_is_dcp(pipe)) + return usbhs_read(priv, DCPCTR); + else + return usbhs_read(priv, PIPEnCTR + offset); +} + +/* + * DCP/PIPE functions + */ +static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe, + u16 dcp_reg, u16 pipe_reg, + u16 mask, u16 val) +{ + struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); + + if (usbhsp_is_dcp(pipe)) + usbhs_bset(priv, dcp_reg, mask, val); + else + usbhs_bset(priv, pipe_reg, mask, val); +} + +static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe, + u16 dcp_reg, u16 pipe_reg) +{ + struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); + + if (usbhsp_is_dcp(pipe)) + return usbhs_read(priv, dcp_reg); + else + return usbhs_read(priv, pipe_reg); +} + +/* + * DCPCFG/PIPECFG functions + */ +static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val) +{ + __usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val); +} + +/* + * PIPEBUF + */ +static void usbhsp_pipe_buf_set(struct usbhs_pipe *pipe, u16 mask, u16 val) +{ + if (usbhsp_is_dcp(pipe)) + return; + + __usbhsp_pipe_xxx_set(pipe, 0, PIPEBUF, mask, val); +} + +/* + * DCPMAXP/PIPEMAXP + */ +static void usbhsp_pipe_maxp_set(struct usbhs_pipe *pipe, u16 mask, u16 val) +{ + __usbhsp_pipe_xxx_set(pipe, DCPMAXP, PIPEMAXP, mask, val); +} + +static u16 usbhsp_pipe_maxp_get(struct usbhs_pipe *pipe) +{ + return __usbhsp_pipe_xxx_get(pipe, DCPMAXP, PIPEMAXP); +} + +/* + * pipe control functions + */ +static void usbhsp_pipe_select(struct usbhs_pipe *pipe) +{ + struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); + + /* + * On pipe, this is necessary before + * accesses to below registers. + * + * PIPESEL : usbhsp_pipe_select + * PIPECFG : usbhsp_pipe_cfg_xxx + * PIPEBUF : usbhsp_pipe_buf_xxx + * PIPEMAXP : usbhsp_pipe_maxp_xxx + * PIPEPERI + */ + + /* + * if pipe is dcp, no pipe is selected. + * it is no problem, because dcp have its register + */ + usbhs_write(priv, PIPESEL, 0xF & usbhs_pipe_number(pipe)); +} + +static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe) +{ + struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); + struct device *dev = usbhs_priv_to_dev(priv); + int timeout = 1024; + u16 val; + + /* + * make sure.... + * + * Modify these bits when CSSTS = 0, PID = NAK, and no pipe number is + * specified by the CURPIPE bits. + * When changing the setting of this bit after changing + * the PID bits for the selected pipe from BUF to NAK, + * check that CSSTS = 0 and PBUSY = 0. + */ + + /* + * CURPIPE bit = 0 + * + * see also + * "Operation" + * - "Pipe Control" + * - "Pipe Control Registers Switching Procedure" + */ + usbhs_write(priv, CFIFOSEL, 0); + + do { + val = usbhsp_pipectrl_get(pipe); + val &= CSSTS | PID_MASK; + if (!val) + return 0; + + udelay(10); + + } while (timeout--); + + /* + * force NAK + */ + timeout = 1024; + usbhs_fifo_disable(pipe); + do { + val = usbhsp_pipectrl_get(pipe); + val &= PBUSY; + if (!val) + return 0; + + } while (timeout--); + + dev_err(dev, "pipe barrier failed\n"); + + return -EBUSY; +} + +static int usbhsp_pipe_is_accessible(struct usbhs_pipe *pipe) +{ + u16 val; + + val = usbhsp_pipectrl_get(pipe); + if (val & BSTS) + return 0; + + return -EBUSY; +} + +/* + * PID ctrl + */ +static void __usbhsp_pid_try_nak_if_stall(struct usbhs_pipe *pipe) +{ + u16 pid = usbhsp_pipectrl_get(pipe); + + pid &= PID_MASK; + + /* + * see + * "Pipe n Control Register" - "PID" + */ + switch (pid) { + case PID_STALL11: + usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10); + /* fall-through */ + case PID_STALL10: + usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK); + } +} + +void usbhs_fifo_disable(struct usbhs_pipe *pipe) +{ + /* see "Pipe n Control Register" - "PID" */ + __usbhsp_pid_try_nak_if_stall(pipe); + + usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK); +} + +void usbhs_fifo_enable(struct usbhs_pipe *pipe) +{ + /* see "Pipe n Control Register" - "PID" */ + __usbhsp_pid_try_nak_if_stall(pipe); + + usbhsp_pipectrl_set(pipe, PID_MASK, PID_BUF); +} + +void usbhs_fifo_stall(struct usbhs_pipe *pipe) +{ + u16 pid = usbhsp_pipectrl_get(pipe); + + pid &= PID_MASK; + + /* + * see + * "Pipe n Control Register" - "PID" + */ + switch (pid) { + case PID_NAK: + usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10); + break; + case PID_BUF: + usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL11); + break; + } +} + +/* + * CFIFO ctrl + */ +void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe) +{ + struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); + + usbhs_bset(priv, CFIFOCTR, BVAL, BVAL); +} + +static void usbhsp_fifo_clear(struct usbhs_pipe *pipe) +{ + struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); + + usbhs_write(priv, CFIFOCTR, BCLR); +} + +static int usbhsp_fifo_barrier(struct usbhs_priv *priv) +{ + int timeout = 1024; + + do { + /* The FIFO port is accessible */ + if (usbhs_read(priv, CFIFOCTR) & FRDY) + return 0; + + udelay(10); + } while (timeout--); + + return -EBUSY; +} + +static int usbhsp_fifo_rcv_len(struct usbhs_priv *priv) +{ + return usbhs_read(priv, CFIFOCTR) & DTLN_MASK; +} + +static int usbhsp_fifo_select(struct usbhs_pipe *pipe, int write) +{ + struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); + struct device *dev = usbhs_priv_to_dev(priv); + int timeout = 1024; + u16 mask = ((1 << 5) | 0xF); /* mask of ISEL | CURPIPE */ + u16 base = usbhs_pipe_number(pipe); /* CURPIPE */ + + if (usbhsp_is_dcp(pipe)) + base |= (1 == write) << 5; /* ISEL */ + + /* "base" will be used below */ + usbhs_write(priv, CFIFOSEL, base | MBW_32); + + /* check ISEL and CURPIPE value */ + while (timeout--) { + if (base == (mask & usbhs_read(priv, CFIFOSEL))) + return 0; + udelay(10); + } + + dev_err(dev, "fifo select error\n"); + + return -EIO; +} + +int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe) +{ + int ret; + + ret = usbhsp_fifo_select(pipe, 1); + if (ret < 0) + return ret; + + usbhsp_fifo_clear(pipe); + + return ret; +} + +int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len) +{ + struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); + void __iomem *addr = priv->base + CFIFO; + int maxp = usbhs_pipe_get_maxpacket(pipe); + int total_len; + int i, ret; + + ret = usbhsp_pipe_is_accessible(pipe); + if (ret < 0) + return ret; + + ret = usbhs_fifo_prepare_write(pipe); + if (ret < 0) + return ret; + + ret = usbhsp_fifo_barrier(priv); + if (ret < 0) + return ret; + + len = min(len, maxp); + total_len = len; + + /* + * FIXME + * + * 32-bit access only + */ + if (len >= 4 && + !((unsigned long)buf & 0x03)) { + iowrite32_rep(addr, buf, len / 4); + len %= 4; + buf += total_len - len; + } + + /* the rest operation */ + for (i = 0; i < len; i++) + iowrite8(buf[i], addr + (0x03 - (i & 0x03))); + + if (total_len < maxp) + usbhs_fifo_send_terminator(pipe); + + return total_len; +} + +int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe) +{ + int ret; + + /* + * select pipe and enable it to prepare packet receive + */ + ret = usbhsp_fifo_select(pipe, 0); + if (ret < 0) + return ret; + + usbhs_fifo_enable(pipe); + + return ret; +} + +int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len) +{ + struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); + void __iomem *addr = priv->base + CFIFO; + int rcv_len; + int i, ret; + int total_len; + u32 data = 0; + + ret = usbhsp_fifo_select(pipe, 0); + if (ret < 0) + return ret; + + ret = usbhsp_fifo_barrier(priv); + if (ret < 0) + return ret; + + rcv_len = usbhsp_fifo_rcv_len(priv); + + /* + * Buffer clear if Zero-Length packet + * + * see + * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function" + */ + if (0 == rcv_len) { + usbhsp_fifo_clear(pipe); + return 0; + } + + len = min(rcv_len, len); + total_len = len; + + /* + * FIXME + * + * 32-bit access only + */ + if (len >= 4 && + !((unsigned long)buf & 0x03)) { + ioread32_rep(addr, buf, len / 4); + len %= 4; + buf += rcv_len - len; + } + + /* the rest operation */ + for (i = 0; i < len; i++) { + if (!(i & 0x03)) + data = ioread32(addr); + + buf[i] = (data >> ((i & 0x03) * 8)) & 0xff; + } + + return total_len; +} + +/* + * pipe setup + */ +static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe) +{ + /* + * only ISO / BULK pipe can use double buffer + */ + if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) || + usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC)) + return 1; + + return 0; +} + +static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, + const struct usb_endpoint_descriptor *desc, + int is_host) +{ + u16 type = 0; + u16 bfre = 0; + u16 dblb = 0; + u16 cntmd = 0; + u16 dir = 0; + u16 epnum = 0; + u16 shtnak = 0; + u16 type_array[] = { + [USB_ENDPOINT_XFER_BULK] = TYPE_BULK, + [USB_ENDPOINT_XFER_INT] = TYPE_INT, + [USB_ENDPOINT_XFER_ISOC] = TYPE_ISO, + }; + int is_double = usbhsp_possible_double_buffer(pipe); + + if (usbhsp_is_dcp(pipe)) + return -EINVAL; + + /* + * PIPECFG + * + * see + * - "Register Descriptions" - "PIPECFG" register + * - "Features" - "Pipe configuration" + * - "Operation" - "Pipe Control" + */ + + /* TYPE */ + type = type_array[usbhsp_type(pipe)]; + + /* BFRE */ + if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || + usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK)) + bfre = 0; /* FIXME */ + + /* DBLB */ + if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || + usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK)) + dblb = (is_double) ? DBLB : 0; + + /* CNTMD */ + if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK)) + cntmd = 0; /* FIXME */ + + /* DIR */ + if (usb_endpoint_dir_in(desc)) + usbhsp_flags_set(pipe, IS_DIR_IN); + + if ((is_host && usb_endpoint_dir_out(desc)) || + (!is_host && usb_endpoint_dir_in(desc))) + dir |= DIR_OUT; + + /* SHTNAK */ + if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) && + !dir) + shtnak = SHTNAK; + + /* EPNUM */ + epnum = 0xF & usb_endpoint_num(desc); + + return type | + bfre | + dblb | + cntmd | + dir | + shtnak | + epnum; +} + +static u16 usbhsp_setup_pipemaxp(struct usbhs_pipe *pipe, + const struct usb_endpoint_descriptor *desc, + int is_host) +{ + /* host should set DEVSEL */ + + /* reutn MXPS */ + return PIPE_MAXP_MASK & le16_to_cpu(desc->wMaxPacketSize); +} + +static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe, + const struct usb_endpoint_descriptor *desc, + int is_host) +{ + struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); + struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv); + struct device *dev = usbhs_priv_to_dev(priv); + int pipe_num = usbhs_pipe_number(pipe); + int is_double = usbhsp_possible_double_buffer(pipe); + u16 buff_size; + u16 bufnmb; + u16 bufnmb_cnt; + + /* + * PIPEBUF + * + * see + * - "Register Descriptions" - "PIPEBUF" register + * - "Features" - "Pipe configuration" + * - "Operation" - "FIFO Buffer Memory" + * - "Operation" - "Pipe Control" + * + * ex) if pipe6 - pipe9 are USB_ENDPOINT_XFER_INT (SH7724) + * + * BUFNMB: PIPE + * 0: pipe0 (DCP 256byte) + * 1: - + * 2: - + * 3: - + * 4: pipe6 (INT 64byte) + * 5: pipe7 (INT 64byte) + * 6: pipe8 (INT 64byte) + * 7: pipe9 (INT 64byte) + * 8 - xx: free (for BULK, ISOC) + */ + + /* + * FIXME + * + * it doesn't have good buffer allocator + * + * DCP : 256 byte + * BULK: 512 byte + * INT : 64 byte + * ISOC: 512 byte + */ + if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_CONTROL)) + buff_size = 256; + else if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) + buff_size = 64; + else + buff_size = 512; + + /* change buff_size to register value */ + bufnmb_cnt = (buff_size / 64) - 1; + + /* BUFNMB has been reserved for INT pipe + * see above */ + if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) { + bufnmb = pipe_num - 2; + } else { + bufnmb = info->bufnmb_last; + info->bufnmb_last += bufnmb_cnt + 1; + + /* + * double buffer + */ + if (is_double) + info->bufnmb_last += bufnmb_cnt + 1; + } + + dev_dbg(dev, "pipe : %d : buff_size 0x%x: bufnmb 0x%x\n", + pipe_num, buff_size, bufnmb); + + return (0x1f & bufnmb_cnt) << 10 | + (0xff & bufnmb) << 0; +} + +/* + * pipe control + */ +int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe) +{ + u16 mask = usbhsp_is_dcp(pipe) ? DCP_MAXP_MASK : PIPE_MAXP_MASK; + + usbhsp_pipe_select(pipe); + + return (int)(usbhsp_pipe_maxp_get(pipe) & mask); +} + +int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe) +{ + return usbhsp_flags_has(pipe, IS_DIR_IN); +} + +void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe) +{ + usbhsp_pipectrl_set(pipe, SQCLR, SQCLR); +} + +static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) +{ + struct usbhs_pipe *pos, *pipe; + int i; + + /* + * find target pipe + */ + pipe = NULL; + usbhs_for_each_pipe_with_dcp(pos, priv, i) { + if (!usbhsp_type_is(pos, type)) + continue; + if (usbhsp_flags_has(pos, IS_USED)) + continue; + + pipe = pos; + break; + } + + if (!pipe) + return NULL; + + /* + * initialize pipe flags + */ + usbhsp_flags_init(pipe); + usbhsp_flags_set(pipe, IS_USED); + + return pipe; +} + +void usbhs_pipe_init(struct usbhs_priv *priv) +{ + struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv); + struct usbhs_pipe *pipe; + int i; + + /* + * FIXME + * + * driver needs good allocator. + * + * find first free buffer area (BULK, ISOC) + * (DCP, INT area is fixed) + * + * buffer number 0 - 3 have been reserved for DCP + * see + * usbhsp_to_bufnmb + */ + info->bufnmb_last = 4; + usbhs_for_each_pipe_with_dcp(pipe, priv, i) { + if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) + info->bufnmb_last++; + + usbhsp_flags_init(pipe); + pipe->mod_private = NULL; + } +} + +struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, + const struct usb_endpoint_descriptor *desc) +{ + struct device *dev = usbhs_priv_to_dev(priv); + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + struct usbhs_pipe *pipe; + int is_host = usbhs_mod_is_host(priv, mod); + int ret; + u16 pipecfg, pipebuf, pipemaxp; + + pipe = usbhsp_get_pipe(priv, usb_endpoint_type(desc)); + if (!pipe) + return NULL; + + usbhs_fifo_disable(pipe); + + /* make sure pipe is not busy */ + ret = usbhsp_pipe_barrier(pipe); + if (ret < 0) { + dev_err(dev, "pipe setup failed %d\n", usbhs_pipe_number(pipe)); + return NULL; + } + + pipecfg = usbhsp_setup_pipecfg(pipe, desc, is_host); + pipebuf = usbhsp_setup_pipebuff(pipe, desc, is_host); + pipemaxp = usbhsp_setup_pipemaxp(pipe, desc, is_host); + + /* buffer clear + * see PIPECFG :: BFRE */ + usbhsp_pipectrl_set(pipe, ACLRM, ACLRM); + usbhsp_pipectrl_set(pipe, ACLRM, 0); + + usbhsp_pipe_select(pipe); + usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg); + usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf); + usbhsp_pipe_maxp_set(pipe, 0xFFFF, pipemaxp); + + usbhs_pipe_clear_sequence(pipe); + + dev_dbg(dev, "enable pipe %d : %s (%s)\n", + usbhs_pipe_number(pipe), + usbhsp_pipe_name[usb_endpoint_type(desc)], + usbhs_pipe_is_dir_in(pipe) ? "in" : "out"); + + return pipe; +} + +/* + * dcp control + */ +struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv) +{ + struct usbhs_pipe *pipe; + + pipe = usbhsp_get_pipe(priv, USB_ENDPOINT_XFER_CONTROL); + if (!pipe) + return NULL; + + /* + * dcpcfg : default + * dcpmaxp : default + * pipebuf : nothing to do + */ + + usbhsp_pipe_select(pipe); + usbhs_pipe_clear_sequence(pipe); + + return pipe; +} + +void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe) +{ + WARN_ON(!usbhsp_is_dcp(pipe)); + + usbhs_fifo_enable(pipe); + usbhsp_pipectrl_set(pipe, CCPL, CCPL); +} + + +/* + * pipe module function + */ +int usbhs_pipe_probe(struct usbhs_priv *priv) +{ + struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv); + struct usbhs_pipe *pipe; + struct device *dev = usbhs_priv_to_dev(priv); + u32 *pipe_type = usbhs_get_dparam(priv, pipe_type); + int pipe_size = usbhs_get_dparam(priv, pipe_size); + int i; + + /* This driver expects 1st pipe is DCP */ + if (pipe_type[0] != USB_ENDPOINT_XFER_CONTROL) { + dev_err(dev, "1st PIPE is not DCP\n"); + return -EINVAL; + } + + info->pipe = kzalloc(sizeof(struct usbhs_pipe) * pipe_size, GFP_KERNEL); + if (!info->pipe) { + dev_err(dev, "Could not allocate pipe\n"); + return -ENOMEM; + } + + info->size = pipe_size; + + /* + * init pipe + */ + usbhs_for_each_pipe_with_dcp(pipe, priv, i) { + pipe->priv = priv; + usbhsp_type(pipe) = pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK; + + dev_dbg(dev, "pipe %x\t: %s\n", + i, usbhsp_pipe_name[pipe_type[i]]); + } + + return 0; +} + +void usbhs_pipe_remove(struct usbhs_priv *priv) +{ + struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv); + + kfree(info->pipe); +} diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h new file mode 100644 index 00000000000..4a60dcef967 --- /dev/null +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -0,0 +1,105 @@ +/* + * Renesas USB driver + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 RENESAS_USB_PIPE_H +#define RENESAS_USB_PIPE_H + +#include "./common.h" + +/* + * struct + */ +struct usbhs_pipe { + u32 pipe_type; /* USB_ENDPOINT_XFER_xxx */ + + struct usbhs_priv *priv; + + u32 flags; +#define USBHS_PIPE_FLAGS_IS_USED (1 << 0) +#define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1) + + void *mod_private; +}; + +struct usbhs_pipe_info { + struct usbhs_pipe *pipe; + int size; /* array size of "pipe" */ + int bufnmb_last; /* FIXME : driver needs good allocator */ +}; + +/* + * pipe list + */ +#define __usbhs_for_each_pipe(start, pos, info, i) \ + for (i = start, pos = (info)->pipe; \ + i < (info)->size; \ + i++, pos = (info)->pipe + i) + +#define usbhs_for_each_pipe(pos, priv, i) \ + __usbhs_for_each_pipe(1, pos, &((priv)->pipe_info), i) + +#define usbhs_for_each_pipe_with_dcp(pos, priv, i) \ + __usbhs_for_each_pipe(0, pos, &((priv)->pipe_info), i) + +/* + * pipe module probe / remove + */ +int usbhs_pipe_probe(struct usbhs_priv *priv); +void usbhs_pipe_remove(struct usbhs_priv *priv); + +/* + * cfifo + */ +int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len); +int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len); +int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe); +int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe); + +void usbhs_fifo_enable(struct usbhs_pipe *pipe); +void usbhs_fifo_disable(struct usbhs_pipe *pipe); +void usbhs_fifo_stall(struct usbhs_pipe *pipe); + +void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe); + + +/* + * usb request + */ +void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); +void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); + +/* + * pipe control + */ +struct usbhs_pipe +*usbhs_pipe_malloc(struct usbhs_priv *priv, + const struct usb_endpoint_descriptor *desc); + +int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); +void usbhs_pipe_init(struct usbhs_priv *priv); +int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); +void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe); + +#define usbhs_pipe_number(p) (((u32)(p) - (u32)(p)->priv->pipe_info.pipe) / \ + sizeof(struct usbhs_pipe)) + +/* + * dcp control + */ +struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv); +void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe); + +#endif /* RENESAS_USB_PIPE_H */ diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h new file mode 100644 index 00000000000..565bca3aa44 --- /dev/null +++ b/include/linux/usb/renesas_usbhs.h @@ -0,0 +1,149 @@ +/* + * Renesas USB + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 RENESAS_USB_H +#define RENESAS_USB_H +#include +#include + +/* + * module type + * + * it will be return value from get_id + */ +enum { + USBHS_HOST = 0, + USBHS_GADGET, + USBHS_MAX, +}; + +/* + * callback functions table for driver + * + * These functions are called from platform for driver. + * Callback function's pointer will be set before + * renesas_usbhs_platform_callback :: hardware_init was called + */ +struct renesas_usbhs_driver_callback { + int (*notify_hotplug)(struct platform_device *pdev); +}; + +/* + * callback functions for platform + * + * These functions are called from driver for platform + */ +struct renesas_usbhs_platform_callback { + + /* + * option: + * + * Hardware init function for platform. + * it is called when driver was probed. + */ + int (*hardware_init)(struct platform_device *pdev); + + /* + * option: + * + * Hardware exit function for platform. + * it is called when driver was removed + */ + void (*hardware_exit)(struct platform_device *pdev); + + /* + * option: + * + * Phy reset for platform + */ + void (*phy_reset)(struct platform_device *pdev); + + /* + * get USB ID function + * - USBHS_HOST + * - USBHS_GADGET + */ + int (*get_id)(struct platform_device *pdev); + + /* + * get VBUS status function. + */ + int (*get_vbus)(struct platform_device *pdev); +}; + +/* + * parameters for renesas usbhs + * + * some register needs USB chip specific parameters. + * This struct show it to driver + */ +struct renesas_usbhs_driver_param { + /* + * pipe settings + */ + u32 *pipe_type; /* array of USB_ENDPOINT_XFER_xxx (from ep0) */ + int pipe_size; /* pipe_type array size */ + + /* + * option: + * + * for BUSWAIT :: BWAIT + * */ + int buswait_bwait; +}; + +/* + * option: + * + * platform information for renesas_usbhs driver. + */ +struct renesas_usbhs_platform_info { + /* + * option: + * + * platform set these functions before + * call platform_add_devices if needed + */ + struct renesas_usbhs_platform_callback platform_callback; + + /* + * driver set these callback functions pointer. + * platform can use it on callback functions + */ + struct renesas_usbhs_driver_callback driver_callback; + + /* + * option: + * + * driver use these param for some register + */ + struct renesas_usbhs_driver_param driver_param; +}; + +/* + * macro for platform + */ +#define renesas_usbhs_get_info(pdev)\ + ((struct renesas_usbhs_platform_info *)(pdev)->dev.platform_data) + +#define renesas_usbhs_call_notify_hotplug(pdev) \ + ({ \ + struct renesas_usbhs_driver_callback *dc; \ + dc = &(renesas_usbhs_get_info(pdev)->driver_callback); \ + if (dc) \ + dc->notify_hotplug(pdev); \ + }) +#endif /* RENESAS_USB_H */ -- cgit v1.2.3 From 2f98382dcdfe1f0048b447da35f34507ffb514dc Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 5 Apr 2011 11:40:54 +0900 Subject: usb: renesas_usbhs: Add Renesas USBHS Gadget This patch add usb gadget code to SuperH USBHS. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 18 + drivers/usb/gadget/gadget_chips.h | 9 + drivers/usb/renesas_usbhs/Makefile | 2 + drivers/usb/renesas_usbhs/mod.c | 17 +- drivers/usb/renesas_usbhs/mod.h | 16 + drivers/usb/renesas_usbhs/mod_gadget.c | 1341 ++++++++++++++++++++++++++++++++ 6 files changed, 1402 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/renesas_usbhs/mod_gadget.c diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index bc5123cf41c..4c02b9f1597 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -260,6 +260,24 @@ config USB_R8A66597 default USB_GADGET select USB_GADGET_SELECTED +config USB_GADGET_RENESAS_USBHS + boolean "Renesas USBHS" + depends on USB_RENESAS_USBHS + select USB_GADGET_DUALSPEED + help + Renesas USBHS is a discrete USB host and peripheral controller + chip that supports both full and high speed USB 2.0 data transfers. + platform is able to configure endpoint (pipe) style + + Say "y" to enable the gadget specific portion of the USBHS driver. + + +config USB_RENESAS_USBHS_UDC + tristate + depends on USB_GADGET_RENESAS_USBHS + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_PXA27X boolean "PXA 27x" depends on ARCH_PXA && (PXA27x || PXA3xx) diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index e896f6359df..ec3fd979c71 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -148,6 +148,12 @@ #define gadget_is_ci13xxx_msm(g) 0 #endif +#ifdef CONFIG_USB_GADGET_RENESAS_USBHS +#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name)) +#else +#define gadget_is_renesas_usbhs(g) 0 +#endif + /** * usb_gadget_controller_number - support bcdDevice id convention * @gadget: the controller being driven @@ -207,6 +213,9 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x27; else if (gadget_is_ci13xxx_msm(gadget)) return 0x28; + else if (gadget_is_renesas_usbhs(gadget)) + return 0x29; + return -ENOENT; } diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index d76f3dd3b9d..b8798ad1627 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -5,3 +5,5 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o renesas_usbhs-y := common.o mod.o pipe.o + +renesas_usbhs-$(CONFIG_USB_RENESAS_USBHS_UDC) += mod_gadget.o diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 4a3398484cd..73604a1d684 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -94,17 +94,32 @@ int usbhs_mod_probe(struct usbhs_priv *priv) struct device *dev = usbhs_priv_to_dev(priv); int ret; + /* + * install host/gadget driver + */ + ret = usbhs_mod_gadget_probe(priv); + if (ret < 0) + return ret; + /* irq settings */ ret = request_irq(priv->irq, usbhs_interrupt, IRQF_DISABLED, dev_name(dev), priv); - if (ret) + if (ret) { dev_err(dev, "irq request err\n"); + goto mod_init_gadget_err; + } + + return ret; + +mod_init_gadget_err: + usbhs_mod_gadget_remove(priv); return ret; } void usbhs_mod_remove(struct usbhs_priv *priv) { + usbhs_mod_gadget_remove(priv); free_irq(priv->irq, priv); } diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h index bd873d5b432..8644191e164 100644 --- a/drivers/usb/renesas_usbhs/mod.h +++ b/drivers/usb/renesas_usbhs/mod.h @@ -103,4 +103,20 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod); mod->func(param); \ }) +/* + * gadget control + */ +#ifdef CONFIG_USB_RENESAS_USBHS_UDC +extern int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv); +extern void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv); +#else +static inline int usbhs_mod_gadget_probe(struct usbhs_priv *priv) +{ + return 0; +} +static inline void usbhs_mod_gadget_remove(struct usbhs_priv *priv) +{ +} +#endif + #endif /* RENESAS_USB_MOD_H */ diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c new file mode 100644 index 00000000000..9a5ac02077b --- /dev/null +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -0,0 +1,1341 @@ +/* + * Renesas USB driver + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 + * + */ +#include +#include +#include +#include +#include +#include "common.h" + +/* + * struct + */ +struct usbhsg_request { + struct usb_request req; + struct list_head node; +}; + +#define EP_NAME_SIZE 8 +struct usbhsg_gpriv; +struct usbhsg_pipe_handle; +struct usbhsg_uep { + struct usb_ep ep; + struct usbhs_pipe *pipe; + struct list_head list; + + char ep_name[EP_NAME_SIZE]; + + struct usbhsg_gpriv *gpriv; + struct usbhsg_pipe_handle *handler; +}; + +struct usbhsg_gpriv { + struct usb_gadget gadget; + struct usbhs_mod mod; + + struct usbhsg_uep *uep; + int uep_size; + + struct usb_gadget_driver *driver; + + u32 status; +#define USBHSG_STATUS_STARTED (1 << 0) +#define USBHSG_STATUS_REGISTERD (1 << 1) +#define USBHSG_STATUS_WEDGE (1 << 2) +}; + +struct usbhsg_pipe_handle { + int (*prepare)(struct usbhsg_uep *uep, struct usbhsg_request *ureq); + int (*try_run)(struct usbhsg_uep *uep, struct usbhsg_request *ureq); + void (*irq_mask)(struct usbhsg_uep *uep, int enable); +}; + +struct usbhsg_recip_handle { + char *name; + int (*device)(struct usbhs_priv *priv, struct usbhsg_uep *uep, + struct usb_ctrlrequest *ctrl); + int (*interface)(struct usbhs_priv *priv, struct usbhsg_uep *uep, + struct usb_ctrlrequest *ctrl); + int (*endpoint)(struct usbhs_priv *priv, struct usbhsg_uep *uep, + struct usb_ctrlrequest *ctrl); +}; + +/* + * macro + */ +#define usbhsg_priv_to_gpriv(priv) \ + container_of( \ + usbhs_mod_get(priv, USBHS_GADGET), \ + struct usbhsg_gpriv, mod) + +#define __usbhsg_for_each_uep(start, pos, g, i) \ + for (i = start, pos = (g)->uep; \ + i < (g)->uep_size; \ + i++, pos = (g)->uep + i) + +#define usbhsg_for_each_uep(pos, gpriv, i) \ + __usbhsg_for_each_uep(1, pos, gpriv, i) + +#define usbhsg_for_each_uep_with_dcp(pos, gpriv, i) \ + __usbhsg_for_each_uep(0, pos, gpriv, i) + +#define usbhsg_gadget_to_gpriv(g)\ + container_of(g, struct usbhsg_gpriv, gadget) + +#define usbhsg_req_to_ureq(r)\ + container_of(r, struct usbhsg_request, req) + +#define usbhsg_ep_to_uep(e) container_of(e, struct usbhsg_uep, ep) +#define usbhsg_gpriv_to_lock(gp) usbhs_priv_to_lock((gp)->mod.priv) +#define usbhsg_gpriv_to_dev(gp) usbhs_priv_to_dev((gp)->mod.priv) +#define usbhsg_gpriv_to_priv(gp) ((gp)->mod.priv) +#define usbhsg_gpriv_to_dcp(gp) ((gp)->uep) +#define usbhsg_gpriv_to_nth_uep(gp, i) ((gp)->uep + i) +#define usbhsg_uep_to_gpriv(u) ((u)->gpriv) +#define usbhsg_uep_to_pipe(u) ((u)->pipe) +#define usbhsg_pipe_to_uep(p) ((p)->mod_private) +#define usbhsg_is_dcp(u) ((u) == usbhsg_gpriv_to_dcp((u)->gpriv)) + +#define usbhsg_is_not_connected(gp) ((gp)->gadget.speed == USB_SPEED_UNKNOWN) + +/* status */ +#define usbhsg_status_init(gp) do {(gp)->status = 0; } while (0) +#define usbhsg_status_set(gp, b) (gp->status |= b) +#define usbhsg_status_clr(gp, b) (gp->status &= ~b) +#define usbhsg_status_has(gp, b) (gp->status & b) + +/* + * list push/pop + */ +static void usbhsg_queue_push(struct usbhsg_uep *uep, + struct usbhsg_request *ureq) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + + /* + ********* assume under spin lock ********* + */ + list_del_init(&ureq->node); + list_add_tail(&ureq->node, &uep->list); + ureq->req.actual = 0; + ureq->req.status = -EINPROGRESS; + + dev_dbg(dev, "pipe %d : queue push (%d)\n", + usbhs_pipe_number(pipe), + ureq->req.length); +} + +static struct usbhsg_request *usbhsg_queue_get(struct usbhsg_uep *uep) +{ + /* + ********* assume under spin lock ********* + */ + if (list_empty(&uep->list)) + return NULL; + + return list_entry(uep->list.next, struct usbhsg_request, node); +} + +#define usbhsg_queue_prepare(uep) __usbhsg_queue_handler(uep, 1); +#define usbhsg_queue_handle(uep) __usbhsg_queue_handler(uep, 0); +static int __usbhsg_queue_handler(struct usbhsg_uep *uep, int prepare) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + struct usbhsg_request *ureq; + spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + unsigned long flags; + int is_locked; + int ret = 0; + + if (!uep->handler) { + dev_err(dev, "no handler function\n"); + return -EIO; + } + + /* + * CAUTION [*queue handler*] + * + * This function will be called for start/restart queue operation. + * OTOH the most much worry for USB driver is spinlock nest. + * Specially it are + * - usb_ep_ops :: queue + * - usb_request :: complete + * + * But the caller of this function need not care about spinlock. + * This function is using spin_trylock_irqsave for it. + * if "is_locked" is 1, this mean this function lock it. + * but if it is 0, this mean it is already under spin lock. + * see also + * CAUTION [*endpoint queue*] + * CAUTION [*request complete*] + */ + + /****************** spin try lock *******************/ + is_locked = spin_trylock_irqsave(lock, flags); + ureq = usbhsg_queue_get(uep); + if (ureq) { + if (prepare) + ret = uep->handler->prepare(uep, ureq); + else + ret = uep->handler->try_run(uep, ureq); + } + if (is_locked) + spin_unlock_irqrestore(lock, flags); + /******************** spin unlock ******************/ + + return ret; +} + +static void usbhsg_queue_pop(struct usbhsg_uep *uep, + struct usbhsg_request *ureq, + int status) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + + /* + ********* assume under spin lock ********* + */ + + /* + * CAUTION [*request complete*] + * + * There is a possibility not to be called in correct order + * if "complete" is called without spinlock. + * + * So, this function assume it is under spinlock, + * and call usb_request :: complete. + * + * But this "complete" will push next usb_request. + * It mean "usb_ep_ops :: queue" which is using spinlock is called + * under spinlock. + * + * To avoid dead-lock, this driver is using spin_trylock. + * CAUTION [*endpoint queue*] + * CAUTION [*queue handler*] + */ + + dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe)); + + list_del_init(&ureq->node); + + ureq->req.status = status; + ureq->req.complete(&uep->ep, &ureq->req); + + /* more request ? */ + if (0 == status) + usbhsg_queue_prepare(uep); +} + +/* + * irq enable/disable function + */ +#define usbhsg_irq_callback_ctrl(uep, status, enable) \ + ({ \ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); \ + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); \ + struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); \ + struct usbhs_mod *mod = usbhs_mod_get_current(priv); \ + if (!mod) \ + return; \ + if (enable) \ + mod->irq_##status |= (1 << usbhs_pipe_number(pipe)); \ + else \ + mod->irq_##status &= ~(1 << usbhs_pipe_number(pipe)); \ + usbhs_irq_callback_update(priv, mod); \ + }) + +static void usbhsg_irq_empty_ctrl(struct usbhsg_uep *uep, int enable) +{ + usbhsg_irq_callback_ctrl(uep, bempsts, enable); +} + +static void usbhsg_irq_ready_ctrl(struct usbhsg_uep *uep, int enable) +{ + usbhsg_irq_callback_ctrl(uep, brdysts, enable); +} + +/* + * handler function + */ +static int usbhsg_try_run_ctrl_stage_end(struct usbhsg_uep *uep, + struct usbhsg_request *ureq) +{ + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + + /* + ********* assume under spin lock ********* + */ + + usbhs_dcp_control_transfer_done(pipe); + usbhsg_queue_pop(uep, ureq, 0); + + return 0; +} + +static int usbhsg_try_run_send_packet(struct usbhsg_uep *uep, + struct usbhsg_request *ureq) +{ + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + struct usb_request *req = &ureq->req; + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + void *buf; + int remainder, send; + int is_done = 0; + int enable; + int maxp; + + /* + ********* assume under spin lock ********* + */ + + maxp = usbhs_pipe_get_maxpacket(pipe); + buf = req->buf + req->actual; + remainder = req->length - req->actual; + + send = usbhs_fifo_write(pipe, buf, remainder); + + /* + * send < 0 : pipe busy + * send = 0 : send zero packet + * send > 0 : send data + * + * send <= max_packet + */ + if (send > 0) + req->actual += send; + + /* send all packet ? */ + if (send < remainder) + is_done = 0; /* there are remainder data */ + else if (send < maxp) + is_done = 1; /* short packet */ + else + is_done = !req->zero; /* send zero packet ? */ + + dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n", + usbhs_pipe_number(pipe), + remainder, send, is_done, req->zero); + + /* + * enable interrupt and send again in irq handler + * if it still have remainder data which should be sent. + */ + enable = !is_done; + uep->handler->irq_mask(uep, enable); + + /* + * usbhs_fifo_enable execute + * - after callback_update, + * - before queue_pop / stage_end + */ + usbhs_fifo_enable(pipe); + + /* + * all data were sent ? + */ + if (is_done) { + /* it care below call in + "function mode" */ + if (usbhsg_is_dcp(uep)) + usbhs_dcp_control_transfer_done(pipe); + + usbhsg_queue_pop(uep, ureq, 0); + } + + return 0; +} + +static int usbhsg_prepare_send_packet(struct usbhsg_uep *uep, + struct usbhsg_request *ureq) +{ + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + + /* + ********* assume under spin lock ********* + */ + + usbhs_fifo_prepare_write(pipe); + usbhsg_try_run_send_packet(uep, ureq); + + return 0; +} + +static int usbhsg_try_run_receive_packet(struct usbhsg_uep *uep, + struct usbhsg_request *ureq) +{ + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + struct usb_request *req = &ureq->req; + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + void *buf; + int maxp; + int remainder, recv; + int is_done = 0; + + /* + ********* assume under spin lock ********* + */ + + maxp = usbhs_pipe_get_maxpacket(pipe); + buf = req->buf + req->actual; + remainder = req->length - req->actual; + + recv = usbhs_fifo_read(pipe, buf, remainder); + /* + * recv < 0 : pipe busy + * recv >= 0 : receive data + * + * recv <= max_packet + */ + if (recv < 0) + return -EBUSY; + + /* update parameters */ + req->actual += recv; + + if ((recv == remainder) || /* receive all data */ + (recv < maxp)) /* short packet */ + is_done = 1; + + dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n", + usbhs_pipe_number(pipe), + remainder, recv, is_done, req->zero); + + /* read all data ? */ + if (is_done) { + int disable = 0; + + uep->handler->irq_mask(uep, disable); + usbhs_fifo_disable(pipe); + usbhsg_queue_pop(uep, ureq, 0); + } + + return 0; +} + +static int usbhsg_prepare_receive_packet(struct usbhsg_uep *uep, + struct usbhsg_request *ureq) +{ + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + int enable = 1; + int ret; + + /* + ********* assume under spin lock ********* + */ + + ret = usbhs_fifo_prepare_read(pipe); + if (ret < 0) + return ret; + + /* + * data will be read in interrupt handler + */ + uep->handler->irq_mask(uep, enable); + + return ret; +} + +static struct usbhsg_pipe_handle usbhsg_handler_send_by_empty = { + .prepare = usbhsg_prepare_send_packet, + .try_run = usbhsg_try_run_send_packet, + .irq_mask = usbhsg_irq_empty_ctrl, +}; + +static struct usbhsg_pipe_handle usbhsg_handler_send_by_ready = { + .prepare = usbhsg_prepare_send_packet, + .try_run = usbhsg_try_run_send_packet, + .irq_mask = usbhsg_irq_ready_ctrl, +}; + +static struct usbhsg_pipe_handle usbhsg_handler_recv_by_ready = { + .prepare = usbhsg_prepare_receive_packet, + .try_run = usbhsg_try_run_receive_packet, + .irq_mask = usbhsg_irq_ready_ctrl, +}; + +static struct usbhsg_pipe_handle usbhsg_handler_ctrl_stage_end = { + .prepare = usbhsg_try_run_ctrl_stage_end, + .try_run = usbhsg_try_run_ctrl_stage_end, +}; + +/* + * DCP pipe can NOT use "ready interrupt" for "send" + * it should use "empty" interrupt. + * see + * "Operation" - "Interrupt Function" - "BRDY Interrupt" + * + * on the other hand, normal pipe can use "ready interrupt" for "send" + * even though it is single/double buffer + */ +#define usbhsg_handler_send_ctrl usbhsg_handler_send_by_empty +#define usbhsg_handler_recv_ctrl usbhsg_handler_recv_by_ready + +#define usbhsg_handler_send_packet usbhsg_handler_send_by_ready +#define usbhsg_handler_recv_packet usbhsg_handler_recv_by_ready + +/* + * USB_TYPE_STANDARD / clear feature functions + */ +static int usbhsg_recip_handler_std_control_done(struct usbhs_priv *priv, + struct usbhsg_uep *uep, + struct usb_ctrlrequest *ctrl) +{ + struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); + struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp); + + usbhs_dcp_control_transfer_done(pipe); + + return 0; +} + +static int usbhsg_recip_handler_std_clear_endpoint(struct usbhs_priv *priv, + struct usbhsg_uep *uep, + struct usb_ctrlrequest *ctrl) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + + if (!usbhsg_status_has(gpriv, USBHSG_STATUS_WEDGE)) { + usbhs_fifo_disable(pipe); + usbhs_pipe_clear_sequence(pipe); + usbhs_fifo_enable(pipe); + } + + usbhsg_recip_handler_std_control_done(priv, uep, ctrl); + + usbhsg_queue_prepare(uep); + + return 0; +} + +struct usbhsg_recip_handle req_clear_feature = { + .name = "clear feature", + .device = usbhsg_recip_handler_std_control_done, + .interface = usbhsg_recip_handler_std_control_done, + .endpoint = usbhsg_recip_handler_std_clear_endpoint, +}; + +/* + * USB_TYPE handler + */ +static int usbhsg_recip_run_handle(struct usbhs_priv *priv, + struct usbhsg_recip_handle *handler, + struct usb_ctrlrequest *ctrl) +{ + struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + struct usbhsg_uep *uep; + int recip = ctrl->bRequestType & USB_RECIP_MASK; + int nth = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; + int ret; + int (*func)(struct usbhs_priv *priv, struct usbhsg_uep *uep, + struct usb_ctrlrequest *ctrl); + char *msg; + + uep = usbhsg_gpriv_to_nth_uep(gpriv, nth); + + switch (recip) { + case USB_RECIP_DEVICE: + msg = "DEVICE"; + func = handler->device; + break; + case USB_RECIP_INTERFACE: + msg = "INTERFACE"; + func = handler->interface; + break; + case USB_RECIP_ENDPOINT: + msg = "ENDPOINT"; + func = handler->endpoint; + break; + default: + dev_warn(dev, "unsupported RECIP(%d)\n", recip); + func = NULL; + ret = -EINVAL; + } + + if (func) { + dev_dbg(dev, "%s (pipe %d :%s)\n", handler->name, nth, msg); + ret = func(priv, uep, ctrl); + } + + return ret; +} + +/* + * irq functions + * + * it will be called from usbhs_interrupt + */ +static int usbhsg_irq_dev_state(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + + gpriv->gadget.speed = usbhs_status_get_usb_speed(irq_state); + + dev_dbg(dev, "state = %x : speed : %d\n", + usbhs_status_get_device_state(irq_state), + gpriv->gadget.speed); + + return 0; +} + +static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); + struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + struct usb_ctrlrequest ctrl; + struct usbhsg_recip_handle *recip_handler = NULL; + int stage = usbhs_status_get_ctrl_stage(irq_state); + int ret = 0; + + dev_dbg(dev, "stage = %d\n", stage); + + /* + * see Manual + * + * "Operation" + * - "Interrupt Function" + * - "Control Transfer Stage Transition Interrupt" + * - Fig. "Control Transfer Stage Transitions" + */ + + switch (stage) { + case READ_DATA_STAGE: + dcp->handler = &usbhsg_handler_send_ctrl; + break; + case WRITE_DATA_STAGE: + dcp->handler = &usbhsg_handler_recv_ctrl; + break; + case NODATA_STATUS_STAGE: + dcp->handler = &usbhsg_handler_ctrl_stage_end; + break; + default: + return ret; + } + + /* + * get usb request + */ + usbhs_usbreq_get_val(priv, &ctrl); + + switch (ctrl.bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + switch (ctrl.bRequest) { + case USB_REQ_CLEAR_FEATURE: + recip_handler = &req_clear_feature; + break; + } + } + + /* + * setup stage / run recip + */ + if (recip_handler) + ret = usbhsg_recip_run_handle(priv, recip_handler, &ctrl); + else + ret = gpriv->driver->setup(&gpriv->gadget, &ctrl); + + if (ret < 0) + usbhs_fifo_stall(pipe); + + return ret; +} + +static int usbhsg_irq_empty(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); + struct usbhsg_uep *uep; + struct usbhs_pipe *pipe; + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + int i, ret; + + if (!irq_state->bempsts) { + dev_err(dev, "debug %s !!\n", __func__); + return -EIO; + } + + dev_dbg(dev, "irq empty [0x%04x]\n", irq_state->bempsts); + + /* + * search interrupted "pipe" + * not "uep". + */ + usbhs_for_each_pipe_with_dcp(pipe, priv, i) { + if (!(irq_state->bempsts & (1 << i))) + continue; + + uep = usbhsg_pipe_to_uep(pipe); + ret = usbhsg_queue_handle(uep); + if (ret < 0) + dev_err(dev, "send error %d : %d\n", i, ret); + } + + return 0; +} + +static int usbhsg_irq_ready(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); + struct usbhsg_uep *uep; + struct usbhs_pipe *pipe; + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + int i, ret; + + if (!irq_state->brdysts) { + dev_err(dev, "debug %s !!\n", __func__); + return -EIO; + } + + dev_dbg(dev, "irq ready [0x%04x]\n", irq_state->brdysts); + + /* + * search interrupted "pipe" + * not "uep". + */ + usbhs_for_each_pipe_with_dcp(pipe, priv, i) { + if (!(irq_state->brdysts & (1 << i))) + continue; + + uep = usbhsg_pipe_to_uep(pipe); + ret = usbhsg_queue_handle(uep); + if (ret < 0) + dev_err(dev, "receive error %d : %d\n", i, ret); + } + + return 0; +} + +/* + * + * usb_dcp_ops + * + */ +static int usbhsg_dcp_enable(struct usbhsg_uep *uep) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); + struct usbhs_pipe *pipe; + + /* + ********* assume under spin lock ********* + */ + + pipe = usbhs_dcp_malloc(priv); + if (!pipe) + return -EIO; + + uep->pipe = pipe; + uep->pipe->mod_private = uep; + INIT_LIST_HEAD(&uep->list); + + return 0; +} + +#define usbhsg_dcp_disable usbhsg_pipe_disable +static int usbhsg_pipe_disable(struct usbhsg_uep *uep) +{ + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + struct usbhsg_request *ureq; + int disable = 0; + + /* + ********* assume under spin lock ********* + */ + + usbhs_fifo_disable(pipe); + + /* + * disable pipe irq + */ + usbhsg_irq_empty_ctrl(uep, disable); + usbhsg_irq_ready_ctrl(uep, disable); + + while (1) { + ureq = usbhsg_queue_get(uep); + if (!ureq) + break; + + usbhsg_queue_pop(uep, ureq, -ECONNRESET); + } + + uep->pipe->mod_private = NULL; + uep->pipe = NULL; + + return 0; +} + +/* + * + * usb_ep_ops + * + */ +static int usbhsg_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); + struct usbhs_pipe *pipe; + spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + unsigned long flags; + int ret = -EIO; + + /******************** spin lock ********************/ + spin_lock_irqsave(lock, flags); + + pipe = usbhs_pipe_malloc(priv, desc); + if (pipe) { + uep->pipe = pipe; + pipe->mod_private = uep; + INIT_LIST_HEAD(&uep->list); + + if (usb_endpoint_dir_in(desc)) + uep->handler = &usbhsg_handler_send_packet; + else + uep->handler = &usbhsg_handler_recv_packet; + + ret = 0; + } + spin_unlock_irqrestore(lock, flags); + /******************** spin unlock ******************/ + + return ret; +} + +static int usbhsg_ep_disable(struct usb_ep *ep) +{ + struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + unsigned long flags; + int ret; + + /******************** spin lock ********************/ + spin_lock_irqsave(lock, flags); + ret = usbhsg_pipe_disable(uep); + spin_unlock_irqrestore(lock, flags); + /******************** spin unlock ******************/ + + return ret; +} + +static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + struct usbhsg_request *ureq; + + ureq = kzalloc(sizeof *ureq, gfp_flags); + if (!ureq) + return NULL; + + INIT_LIST_HEAD(&ureq->node); + return &ureq->req; +} + +static void usbhsg_ep_free_request(struct usb_ep *ep, + struct usb_request *req) +{ + struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); + + WARN_ON(!list_empty(&ureq->node)); + kfree(ureq); +} + +static int usbhsg_ep_queue(struct usb_ep *ep, struct usb_request *req, + gfp_t gfp_flags) +{ + struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + unsigned long flags; + int ret = 0; + int is_locked; + + /* + * CAUTION [*endpoint queue*] + * + * This function will be called from usb_request :: complete + * or usb driver timing. + * If this function is called from usb_request :: complete, + * it is already under spinlock on this driver. + * but it is called frm usb driver, this function should call spinlock. + * + * This function is using spin_trylock_irqsave to solve this issue. + * if "is_locked" is 1, this mean this function lock it. + * but if it is 0, this mean it is already under spin lock. + * see also + * CAUTION [*queue handler*] + * CAUTION [*request complete*] + */ + + /******************** spin lock ********************/ + is_locked = spin_trylock_irqsave(lock, flags); + + /* param check */ + if (usbhsg_is_not_connected(gpriv) || + unlikely(!gpriv->driver) || + unlikely(!pipe)) + ret = -ESHUTDOWN; + else + usbhsg_queue_push(uep, ureq); + + if (is_locked) + spin_unlock_irqrestore(lock, flags); + /******************** spin unlock ******************/ + + usbhsg_queue_prepare(uep); + + return ret; +} + +static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) +{ + struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); + struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + unsigned long flags; + int is_locked; + + /* + * see + * CAUTION [*queue handler*] + * CAUTION [*endpoint queue*] + * CAUTION [*request complete*] + */ + + /******************** spin lock ********************/ + is_locked = spin_trylock_irqsave(lock, flags); + + usbhsg_queue_pop(uep, ureq, -ECONNRESET); + + if (is_locked) + spin_unlock_irqrestore(lock, flags); + /******************** spin unlock ******************/ + + return 0; +} + +static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge) +{ + struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + unsigned long flags; + int ret = -EAGAIN; + int is_locked; + + /* + * see + * CAUTION [*queue handler*] + * CAUTION [*endpoint queue*] + * CAUTION [*request complete*] + */ + + /******************** spin lock ********************/ + is_locked = spin_trylock_irqsave(lock, flags); + if (!usbhsg_queue_get(uep)) { + + dev_dbg(dev, "set halt %d (pipe %d)\n", + halt, usbhs_pipe_number(pipe)); + + if (halt) + usbhs_fifo_stall(pipe); + else + usbhs_fifo_disable(pipe); + + if (halt && wedge) + usbhsg_status_set(gpriv, USBHSG_STATUS_WEDGE); + else + usbhsg_status_clr(gpriv, USBHSG_STATUS_WEDGE); + + ret = 0; + } + + if (is_locked) + spin_unlock_irqrestore(lock, flags); + /******************** spin unlock ******************/ + + return ret; +} + +static int usbhsg_ep_set_halt(struct usb_ep *ep, int value) +{ + return __usbhsg_ep_set_halt_wedge(ep, value, 0); +} + +static int usbhsg_ep_set_wedge(struct usb_ep *ep) +{ + return __usbhsg_ep_set_halt_wedge(ep, 1, 1); +} + +static struct usb_ep_ops usbhsg_ep_ops = { + .enable = usbhsg_ep_enable, + .disable = usbhsg_ep_disable, + + .alloc_request = usbhsg_ep_alloc_request, + .free_request = usbhsg_ep_free_request, + + .queue = usbhsg_ep_queue, + .dequeue = usbhsg_ep_dequeue, + + .set_halt = usbhsg_ep_set_halt, + .set_wedge = usbhsg_ep_set_wedge, +}; + +/* + * usb module start/end + */ +static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) +{ + struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); + struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + struct device *dev = usbhs_priv_to_dev(priv); + spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + unsigned long flags; + + /******************** spin lock ********************/ + spin_lock_irqsave(lock, flags); + + /* + * enable interrupt and systems if ready + */ + usbhsg_status_set(gpriv, status); + if (!(usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) && + usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD))) + goto usbhsg_try_start_unlock; + + dev_dbg(dev, "start gadget\n"); + + /* + * pipe initialize and enable DCP + */ + usbhs_pipe_init(priv); + usbhsg_dcp_enable(dcp); + + /* + * system config enble + * - HI speed + * - function + * - usb module + */ + usbhs_sys_hispeed_ctrl(priv, 1); + usbhs_sys_function_ctrl(priv, 1); + usbhs_sys_usb_ctrl(priv, 1); + + /* + * enable irq callback + */ + mod->irq_dev_state = usbhsg_irq_dev_state; + mod->irq_ctrl_stage = usbhsg_irq_ctrl_stage; + mod->irq_empty = usbhsg_irq_empty; + mod->irq_ready = usbhsg_irq_ready; + mod->irq_bempsts = 0; + mod->irq_brdysts = 0; + usbhs_irq_callback_update(priv, mod); + +usbhsg_try_start_unlock: + spin_unlock_irqrestore(lock, flags); + /******************** spin unlock ********************/ + + return 0; +} + +static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) +{ + struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); + struct device *dev = usbhs_priv_to_dev(priv); + spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + unsigned long flags; + + /******************** spin lock ********************/ + spin_lock_irqsave(lock, flags); + + /* + * disable interrupt and systems if 1st try + */ + usbhsg_status_clr(gpriv, status); + if (!usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) && + !usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD)) + goto usbhsg_try_stop_unlock; + + /* disable all irq */ + mod->irq_dev_state = NULL; + mod->irq_ctrl_stage = NULL; + mod->irq_empty = NULL; + mod->irq_ready = NULL; + mod->irq_bempsts = 0; + mod->irq_brdysts = 0; + usbhs_irq_callback_update(priv, mod); + + usbhsg_dcp_disable(dcp); + + gpriv->gadget.speed = USB_SPEED_UNKNOWN; + + /* disable sys */ + usbhs_sys_hispeed_ctrl(priv, 0); + usbhs_sys_function_ctrl(priv, 0); + usbhs_sys_usb_ctrl(priv, 0); + + spin_unlock_irqrestore(lock, flags); + /******************** spin unlock ********************/ + + if (gpriv->driver && + gpriv->driver->disconnect) + gpriv->driver->disconnect(&gpriv->gadget); + + dev_dbg(dev, "stop gadget\n"); + + return 0; + +usbhsg_try_stop_unlock: + spin_unlock_irqrestore(lock, flags); + + return 0; +} + +/* + * + * linux usb function + * + */ +struct usbhsg_gpriv *the_controller; +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) +{ + struct usbhsg_gpriv *gpriv = the_controller; + struct usbhs_priv *priv; + struct device *dev; + int ret; + + if (!bind || + !driver || + !driver->setup || + driver->speed != USB_SPEED_HIGH) + return -EINVAL; + if (!gpriv) + return -ENODEV; + if (gpriv->driver) + return -EBUSY; + + dev = usbhsg_gpriv_to_dev(gpriv); + priv = usbhsg_gpriv_to_priv(gpriv); + + /* first hook up the driver ... */ + gpriv->driver = driver; + gpriv->gadget.dev.driver = &driver->driver; + + ret = device_add(&gpriv->gadget.dev); + if (ret) { + dev_err(dev, "device_add error %d\n", ret); + goto add_fail; + } + + ret = bind(&gpriv->gadget); + if (ret) { + dev_err(dev, "bind to driver %s error %d\n", + driver->driver.name, ret); + goto bind_fail; + } + + dev_dbg(dev, "bind %s\n", driver->driver.name); + + return usbhsg_try_start(priv, USBHSG_STATUS_REGISTERD); + +bind_fail: + device_del(&gpriv->gadget.dev); +add_fail: + gpriv->driver = NULL; + gpriv->gadget.dev.driver = NULL; + + return ret; +} +EXPORT_SYMBOL(usb_gadget_probe_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct usbhsg_gpriv *gpriv = the_controller; + struct usbhs_priv *priv; + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + + if (!gpriv) + return -ENODEV; + + if (!driver || + !driver->unbind || + driver != gpriv->driver) + return -EINVAL; + + dev = usbhsg_gpriv_to_dev(gpriv); + priv = usbhsg_gpriv_to_priv(gpriv); + + usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD); + device_del(&gpriv->gadget.dev); + gpriv->driver = NULL; + + if (driver->disconnect) + driver->disconnect(&gpriv->gadget); + + driver->unbind(&gpriv->gadget); + dev_dbg(dev, "unbind %s\n", driver->driver.name); + + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/* + * usb gadget ops + */ +static int usbhsg_get_frame(struct usb_gadget *gadget) +{ + struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); + struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); + + return usbhs_frame_get_num(priv); +} + +static struct usb_gadget_ops usbhsg_gadget_ops = { + .get_frame = usbhsg_get_frame, +}; + +static int usbhsg_start(struct usbhs_priv *priv) +{ + return usbhsg_try_start(priv, USBHSG_STATUS_STARTED); +} + +static int usbhsg_stop(struct usbhs_priv *priv) +{ + return usbhsg_try_stop(priv, USBHSG_STATUS_STARTED); +} + +int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv) +{ + struct usbhsg_gpriv *gpriv; + struct usbhsg_uep *uep; + struct device *dev = usbhs_priv_to_dev(priv); + int pipe_size = usbhs_get_dparam(priv, pipe_size); + int i; + + gpriv = kzalloc(sizeof(struct usbhsg_gpriv), GFP_KERNEL); + if (!gpriv) { + dev_err(dev, "Could not allocate gadget priv\n"); + return -ENOMEM; + } + + uep = kzalloc(sizeof(struct usbhsg_uep) * pipe_size, GFP_KERNEL); + if (!uep) { + dev_err(dev, "Could not allocate ep\n"); + goto usbhs_mod_gadget_probe_err_gpriv; + } + + /* + * CAUTION + * + * There is no guarantee that it is possible to access usb module here. + * Don't accesses to it. + * The accesse will be enable after "usbhsg_start" + */ + + /* + * register itself + */ + usbhs_mod_register(priv, &gpriv->mod, USBHS_GADGET); + + /* init gpriv */ + gpriv->mod.name = "gadget"; + gpriv->mod.start = usbhsg_start; + gpriv->mod.stop = usbhsg_stop; + gpriv->uep = uep; + gpriv->uep_size = pipe_size; + usbhsg_status_init(gpriv); + + /* + * init gadget + */ + device_initialize(&gpriv->gadget.dev); + dev_set_name(&gpriv->gadget.dev, "gadget"); + gpriv->gadget.dev.parent = dev; + gpriv->gadget.name = "renesas_usbhs_udc"; + gpriv->gadget.ops = &usbhsg_gadget_ops; + gpriv->gadget.is_dualspeed = 1; + + INIT_LIST_HEAD(&gpriv->gadget.ep_list); + + /* + * init usb_ep + */ + usbhsg_for_each_uep_with_dcp(uep, gpriv, i) { + uep->gpriv = gpriv; + snprintf(uep->ep_name, EP_NAME_SIZE, "ep%d", i); + + uep->ep.name = uep->ep_name; + uep->ep.ops = &usbhsg_ep_ops; + INIT_LIST_HEAD(&uep->ep.ep_list); + INIT_LIST_HEAD(&uep->list); + + /* init DCP */ + if (usbhsg_is_dcp(uep)) { + gpriv->gadget.ep0 = &uep->ep; + uep->ep.maxpacket = 64; + } + /* init normal pipe */ + else { + uep->ep.maxpacket = 512; + list_add_tail(&uep->ep.ep_list, &gpriv->gadget.ep_list); + } + } + + the_controller = gpriv; + + dev_info(dev, "gadget probed\n"); + + return 0; + +usbhs_mod_gadget_probe_err_gpriv: + kfree(gpriv); + + return -ENOMEM; +} + +void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv) +{ + struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); + + kfree(gpriv); +} -- cgit v1.2.3 From bce1a702ed9bd9aa3549352b3134e110bf076586 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 20 Mar 2011 02:02:39 -0700 Subject: USB: change the way we initialize format strings Changing initialization from static const char *string = "blah"; to static const char string[] = "blah"; saves us one pointer per each string. Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devices.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index a3d2e239965..d382291d1b1 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -64,49 +64,49 @@ /* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */ #define ALLOW_SERIAL_NUMBER -static const char *format_topo = +static const char format_topo[] = /* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=dddd MxCh=dd */ "\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%-4s MxCh=%2d\n"; -static const char *format_string_manufacturer = +static const char format_string_manufacturer[] = /* S: Manufacturer=xxxx */ "S: Manufacturer=%.100s\n"; -static const char *format_string_product = +static const char format_string_product[] = /* S: Product=xxxx */ "S: Product=%.100s\n"; #ifdef ALLOW_SERIAL_NUMBER -static const char *format_string_serialnumber = +static const char format_string_serialnumber[] = /* S: SerialNumber=xxxx */ "S: SerialNumber=%.100s\n"; #endif -static const char *format_bandwidth = +static const char format_bandwidth[] = /* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */ "B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n"; -static const char *format_device1 = +static const char format_device1[] = /* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */ "D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n"; -static const char *format_device2 = +static const char format_device2[] = /* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */ "P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n"; -static const char *format_config = +static const char format_config[] = /* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n"; -static const char *format_iad = +static const char format_iad[] = /* A: FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */ "A: FirstIf#=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n"; -static const char *format_iface = +static const char format_iface[] = /* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ "I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; -static const char *format_endpt = +static const char format_endpt[] = /* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */ "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n"; -- cgit v1.2.3 From 59d7fec7c6908604862658a3679ac44c2c3eea44 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:12 +0100 Subject: USB: cdc-acm: add missing newlines to dev_dbg and dev_err Add missing newline to two dev_dbg and dev_err messages. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index e057e538146..849aa04e609 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -341,7 +341,7 @@ exit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with " - "result %d", __func__, retval); + "result %d\n", __func__, retval); } /* data interface returns incoming bytes, or we got unthrottled */ @@ -355,7 +355,7 @@ static void acm_read_bulk(struct urb *urb) dbg("Entering acm_read_bulk with status %d", status); if (!ACM_READY(acm)) { - dev_dbg(&acm->data->dev, "Aborting, acm not ready"); + dev_dbg(&acm->data->dev, "Aborting, acm not ready\n"); return; } usb_mark_last_busy(acm->dev); @@ -1224,7 +1224,7 @@ made_compressed_probe: snd->urb = usb_alloc_urb(0, GFP_KERNEL); if (snd->urb == NULL) { dev_dbg(&intf->dev, - "out of memory (write urbs usb_alloc_urb)"); + "out of memory (write urbs usb_alloc_urb)\n"); goto alloc_fail8; } -- cgit v1.2.3 From 255ab56c5ae30165d506b837f6576944a02ecdf0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:13 +0100 Subject: USB: cdc-acm: use dev_err to report failed allocations Upgrade out-of-memory dev_dbg to dev_err. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 849aa04e609..c9f8a837c5f 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1143,7 +1143,7 @@ made_compressed_probe: acm = kzalloc(sizeof(struct acm), GFP_KERNEL); if (acm == NULL) { - dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n"); + dev_err(&intf->dev, "out of memory (acm kzalloc)\n"); goto alloc_fail; } @@ -1179,19 +1179,19 @@ made_compressed_probe: buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); if (!buf) { - dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n"); + dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n"); goto alloc_fail2; } acm->ctrl_buffer = buf; if (acm_write_buffers_alloc(acm) < 0) { - dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); + dev_err(&intf->dev, "out of memory (write buffer alloc)\n"); goto alloc_fail4; } acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); if (!acm->ctrlurb) { - dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); + dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); goto alloc_fail5; } for (i = 0; i < num_rx_buf; i++) { @@ -1199,7 +1199,7 @@ made_compressed_probe: rcv->urb = usb_alloc_urb(0, GFP_KERNEL); if (rcv->urb == NULL) { - dev_dbg(&intf->dev, + dev_err(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n"); goto alloc_fail6; } @@ -1213,7 +1213,7 @@ made_compressed_probe: rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL, &rb->dma); if (!rb->base) { - dev_dbg(&intf->dev, + dev_err(&intf->dev, "out of memory (read bufs usb_alloc_coherent)\n"); goto alloc_fail7; } @@ -1223,7 +1223,7 @@ made_compressed_probe: snd->urb = usb_alloc_urb(0, GFP_KERNEL); if (snd->urb == NULL) { - dev_dbg(&intf->dev, + dev_err(&intf->dev, "out of memory (write urbs usb_alloc_urb)\n"); goto alloc_fail8; } -- cgit v1.2.3 From 1d9846e505febb71255b098910ace741433312b7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:14 +0100 Subject: USB: cdc-acm: clean up dev_err and dev_dbg Clean up some dev_err and dev_dbg messages and make sure that the appropriate interface device is used for reporting consistently throughout. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index c9f8a837c5f..612b9ff0fcf 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -340,8 +340,8 @@ static void acm_ctrl_irq(struct urb *urb) exit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with " - "result %d\n", __func__, retval); + dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n", + __func__, retval); } /* data interface returns incoming bytes, or we got unthrottled */ @@ -355,13 +355,14 @@ static void acm_read_bulk(struct urb *urb) dbg("Entering acm_read_bulk with status %d", status); if (!ACM_READY(acm)) { - dev_dbg(&acm->data->dev, "Aborting, acm not ready\n"); + dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__); return; } usb_mark_last_busy(acm->dev); if (status) - dev_dbg(&acm->data->dev, "bulk rx status %d\n", status); + dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", + __func__, status); buf = rcv->buffer; buf->size = urb->actual_length; @@ -511,7 +512,8 @@ static void acm_write_bulk(struct urb *urb) if (verbose || urb->status || (urb->actual_length != urb->transfer_buffer_length)) - dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n", + dev_dbg(&acm->data->dev, "%s - len %d/%d, status %d\n", + __func__, urb->actual_length, urb->transfer_buffer_length, urb->status); @@ -530,7 +532,8 @@ static void acm_softint(struct work_struct *work) struct acm *acm = container_of(work, struct acm, work); struct tty_struct *tty; - dev_vdbg(&acm->data->dev, "tx work\n"); + dev_vdbg(&acm->data->dev, "%s\n", __func__); + if (!ACM_READY(acm)) return; tty = tty_port_tty_get(&acm->port); -- cgit v1.2.3 From a5cc7ef92f69a88a1984cc3e09f6c19656efeb2e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:15 +0100 Subject: USB: cdc-acm: replace dbg macros with dev_dbg Replace all remaining instances of dbg with dev_dbg. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 80 +++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 612b9ff0fcf..fee7d8b94fe 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -111,8 +111,9 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, request, USB_RT_ACM, value, acm->control->altsetting[0].desc.bInterfaceNumber, buf, len, 5000); - dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", - request, value, len, retval); + dev_dbg(&acm->control->dev, + "%s - rq 0x%02x, val %#x, len %#x, result %d\n", + __func__, request, value, len, retval); return retval < 0 ? retval : 0; } @@ -192,7 +193,9 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb) rc = usb_submit_urb(wb->urb, GFP_ATOMIC); if (rc < 0) { - dbg("usb_submit_urb(write bulk) failed: %d", rc); + dev_err(&acm->data->dev, + "%s - usb_submit_urb(write bulk) failed: %d\n", + __func__, rc); acm_write_done(acm, wb); } return rc; @@ -211,7 +214,8 @@ static int acm_write_start(struct acm *acm, int wbn) return -ENODEV; } - dbg("%s susp_count: %d", __func__, acm->susp_count); + dev_dbg(&acm->data->dev, "%s - susp_count %d\n", __func__, + acm->susp_count); usb_autopm_get_interface_async(acm->control); if (acm->susp_count) { if (!acm->delayed_wb) @@ -287,10 +291,14 @@ static void acm_ctrl_irq(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, status); + dev_dbg(&acm->control->dev, + "%s - urb shutting down with status: %d\n", + __func__, status); return; default: - dbg("%s - nonzero urb status received: %d", __func__, status); + dev_dbg(&acm->control->dev, + "%s - nonzero urb status received: %d\n", + __func__, status); goto exit; } @@ -302,8 +310,8 @@ static void acm_ctrl_irq(struct urb *urb) data = (unsigned char *)(dr + 1); switch (dr->bNotificationType) { case USB_CDC_NOTIFY_NETWORK_CONNECTION: - dbg("%s network", dr->wValue ? - "connected to" : "disconnected from"); + dev_dbg(&acm->control->dev, "%s - network connection: %d\n", + __func__, dr->wValue); break; case USB_CDC_NOTIFY_SERIAL_STATE: @@ -313,7 +321,8 @@ static void acm_ctrl_irq(struct urb *urb) if (tty) { if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { - dbg("calling hangup"); + dev_dbg(&acm->control->dev, + "%s - calling hangup\n", __func__); tty_hangup(tty); } tty_kref_put(tty); @@ -321,7 +330,10 @@ static void acm_ctrl_irq(struct urb *urb) acm->ctrlin = newctrl; - dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c", + dev_dbg(&acm->control->dev, + "%s - input control lines: dcd%c dsr%c break%c " + "ring%c framing%c parity%c overrun%c\n", + __func__, acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', @@ -332,7 +344,10 @@ static void acm_ctrl_irq(struct urb *urb) break; default: - dbg("unknown notification %d received: index %d len %d data0 %d data1 %d", + dev_dbg(&acm->control->dev, + "%s - unknown notification %d received: index %d " + "len %d data0 %d data1 %d\n", + __func__, dr->bNotificationType, dr->wIndex, dr->wLength, data[0], data[1]); break; @@ -352,7 +367,7 @@ static void acm_read_bulk(struct urb *urb) struct acm *acm = rcv->instance; int status = urb->status; - dbg("Entering acm_read_bulk with status %d", status); + dev_dbg(&acm->data->dev, "%s - status %d\n", __func__, status); if (!ACM_READY(acm)) { dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__); @@ -395,10 +410,10 @@ static void acm_rx_tasklet(unsigned long _acm) unsigned long flags; unsigned char throttled; - dbg("Entering acm_rx_tasklet"); + dev_dbg(&acm->data->dev, "%s\n", __func__); if (!ACM_READY(acm)) { - dbg("acm_rx_tasklet: ACM not ready"); + dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__); return; } @@ -406,7 +421,7 @@ static void acm_rx_tasklet(unsigned long _acm) throttled = acm->throttle; spin_unlock_irqrestore(&acm->throttle_lock, flags); if (throttled) { - dbg("acm_rx_tasklet: throttled"); + dev_dbg(&acm->data->dev, "%s - throttled\n", __func__); return; } @@ -423,8 +438,8 @@ next_buffer: list_del(&buf->list); spin_unlock_irqrestore(&acm->read_lock, flags); - dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size); - + dev_dbg(&acm->data->dev, "%s - processing buf 0x%p, size = %d\n", + __func__, buf, buf->size); if (tty) { spin_lock_irqsave(&acm->throttle_lock, flags); throttled = acm->throttle; @@ -434,7 +449,8 @@ next_buffer: tty_flip_buffer_push(tty); } else { tty_kref_put(tty); - dbg("Throttling noticed"); + dev_dbg(&acm->data->dev, "%s - throttling noticed\n", + __func__); spin_lock_irqsave(&acm->read_lock, flags); list_add(&buf->list, &acm->filled_read_bufs); spin_unlock_irqrestore(&acm->read_lock, flags); @@ -495,7 +511,9 @@ urbs: return; } else { spin_unlock_irqrestore(&acm->read_lock, flags); - dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); + dev_dbg(&acm->data->dev, + "%s - sending urb 0x%p, rcv 0x%p, buf 0x%p\n", + __func__, rcv->urb, rcv, buf); } } spin_lock_irqsave(&acm->read_lock, flags); @@ -552,7 +570,6 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) struct acm *acm; int rv = -ENODEV; int i; - dbg("Entering acm_tty_open."); mutex_lock(&open_mutex); @@ -562,6 +579,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) else rv = 0; + dev_dbg(&acm->control->dev, "%s\n", __func__); + set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); tty->driver_data = acm; @@ -581,7 +600,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) acm->ctrlurb->dev = acm->dev; if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { - dbg("usb_submit_urb(ctrl irq) failed"); + dev_err(&acm->control->dev, + "%s - usb_submit_urb(ctrl irq) failed\n", __func__); goto bail_out; } @@ -701,13 +721,13 @@ static int acm_tty_write(struct tty_struct *tty, int wbn; struct acm_wb *wb; - dbg("Entering acm_tty_write to write %d bytes,", count); - if (!ACM_READY(acm)) return -EINVAL; if (!count) return 0; + dev_dbg(&acm->data->dev, "%s - count %d\n", __func__, count); + spin_lock_irqsave(&acm->write_lock, flags); wbn = acm_wb_alloc(acm); if (wbn < 0) { @@ -717,7 +737,7 @@ static int acm_tty_write(struct tty_struct *tty, wb = &acm->wb[wbn]; count = (count > acm->writesize) ? acm->writesize : count; - dbg("Get %d bytes...", count); + dev_dbg(&acm->data->dev, "%s - write %d\n", __func__, count); memcpy(wb->buf, buf, count); wb->len = count; spin_unlock_irqrestore(&acm->write_lock, flags); @@ -780,7 +800,8 @@ static int acm_tty_break_ctl(struct tty_struct *tty, int state) return -EINVAL; retval = acm_send_break(acm, state ? 0xffff : 0); if (retval < 0) - dbg("send break failed"); + dev_dbg(&acm->control->dev, "%s - send break failed\n", + __func__); return retval; } @@ -875,7 +896,9 @@ static void acm_tty_set_termios(struct tty_struct *tty, if (memcmp(&acm->line, &newline, sizeof newline)) { memcpy(&acm->line, &newline, sizeof newline); - dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate), + dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n", + __func__, + le32_to_cpu(newline.dwDTERate), newline.bCharFormat, newline.bParityType, newline.bDataBits); acm_set_line(acm, &acm->line); @@ -1136,7 +1159,7 @@ skip_normal_probe: epwrite = t; } made_compressed_probe: - dbg("interfaces are valid"); + dev_dbg(&intf->dev, "interfaces are valid\n"); for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); if (minor == ACM_TTY_MINORS) { @@ -1321,7 +1344,8 @@ alloc_fail: static void stop_data_traffic(struct acm *acm) { int i; - dbg("Entering stop_data_traffic"); + + dev_dbg(&acm->control->dev, "%s\n", __func__); tasklet_disable(&acm->urb_task); -- cgit v1.2.3 From 4fa4626cd43679dc62a73ee3e347665e761abc9c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:16 +0100 Subject: USB: cdc-acm: clean up verbose debug Clean up use of verbose debug. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index fee7d8b94fe..44a5291c569 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -94,12 +94,6 @@ static DEFINE_MUTEX(open_mutex); static const struct tty_port_operations acm_port_ops = { }; -#ifdef VERBOSE_DEBUG -#define verbose 1 -#else -#define verbose 0 -#endif - /* * Functions for ACM control messages. */ @@ -528,9 +522,8 @@ static void acm_write_bulk(struct urb *urb) struct acm *acm = wb->instance; unsigned long flags; - if (verbose || urb->status - || (urb->actual_length != urb->transfer_buffer_length)) - dev_dbg(&acm->data->dev, "%s - len %d/%d, status %d\n", + if (urb->status || (urb->actual_length != urb->transfer_buffer_length)) + dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n", __func__, urb->actual_length, urb->transfer_buffer_length, -- cgit v1.2.3 From 5e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:17 +0100 Subject: USB: cdc-acm: use dev_vdbg in read/write paths Replace dev_dbg with verbose dev_vdbg in read/write paths where appropriate. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 44a5291c569..3c0d4b913a8 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -208,7 +208,7 @@ static int acm_write_start(struct acm *acm, int wbn) return -ENODEV; } - dev_dbg(&acm->data->dev, "%s - susp_count %d\n", __func__, + dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__, acm->susp_count); usb_autopm_get_interface_async(acm->control); if (acm->susp_count) { @@ -361,7 +361,7 @@ static void acm_read_bulk(struct urb *urb) struct acm *acm = rcv->instance; int status = urb->status; - dev_dbg(&acm->data->dev, "%s - status %d\n", __func__, status); + dev_vdbg(&acm->data->dev, "%s - status %d\n", __func__, status); if (!ACM_READY(acm)) { dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__); @@ -404,7 +404,7 @@ static void acm_rx_tasklet(unsigned long _acm) unsigned long flags; unsigned char throttled; - dev_dbg(&acm->data->dev, "%s\n", __func__); + dev_vdbg(&acm->data->dev, "%s\n", __func__); if (!ACM_READY(acm)) { dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__); @@ -432,7 +432,7 @@ next_buffer: list_del(&buf->list); spin_unlock_irqrestore(&acm->read_lock, flags); - dev_dbg(&acm->data->dev, "%s - processing buf 0x%p, size = %d\n", + dev_vdbg(&acm->data->dev, "%s - processing buf 0x%p, size = %d\n", __func__, buf, buf->size); if (tty) { spin_lock_irqsave(&acm->throttle_lock, flags); @@ -505,7 +505,7 @@ urbs: return; } else { spin_unlock_irqrestore(&acm->read_lock, flags); - dev_dbg(&acm->data->dev, + dev_vdbg(&acm->data->dev, "%s - sending urb 0x%p, rcv 0x%p, buf 0x%p\n", __func__, rcv->urb, rcv, buf); } @@ -719,7 +719,7 @@ static int acm_tty_write(struct tty_struct *tty, if (!count) return 0; - dev_dbg(&acm->data->dev, "%s - count %d\n", __func__, count); + dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count); spin_lock_irqsave(&acm->write_lock, flags); wbn = acm_wb_alloc(acm); @@ -730,7 +730,7 @@ static int acm_tty_write(struct tty_struct *tty, wb = &acm->wb[wbn]; count = (count > acm->writesize) ? acm->writesize : count; - dev_dbg(&acm->data->dev, "%s - write %d\n", __func__, count); + dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count); memcpy(wb->buf, buf, count); wb->len = count; spin_unlock_irqrestore(&acm->write_lock, flags); -- cgit v1.2.3 From a2c7b9353e8f782590852052fe2948692020147e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:18 +0100 Subject: USB: cdc-acm: remove version information and changelog Remove driver version and changelog which can be retrieved from git history. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 3c0d4b913a8..a693381ed3c 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -12,30 +12,6 @@ * * Sponsored by SuSE * - * ChangeLog: - * v0.9 - thorough cleaning, URBification, almost a rewrite - * v0.10 - some more cleanups - * v0.11 - fixed flow control, read error doesn't stop reads - * v0.12 - added TIOCM ioctls, added break handling, made struct acm - * kmalloced - * v0.13 - added termios, added hangup - * v0.14 - sized down struct acm - * v0.15 - fixed flow control again - characters could be lost - * v0.16 - added code for modems with swapped data and control interfaces - * v0.17 - added new style probing - * v0.18 - fixed new style probing for devices with more configurations - * v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan) - * v0.20 - switched to probing on interface (rather than device) class - * v0.21 - revert to probing on device for devices with multiple configs - * v0.22 - probe only the control interface. if usbcore doesn't choose the - * config we want, sysadmin changes bConfigurationValue in sysfs. - * v0.23 - use softirq for rx processing, as needed by tty layer - * v0.24 - change probe method to evaluate CDC union descriptor - * v0.25 - downstream tasks paralelized to maximize throughput - * v0.26 - multiple write urbs, writesize increased - */ - -/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -76,10 +52,7 @@ #define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */ -/* - * Version Information - */ -#define DRIVER_VERSION "v0.26" + #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" @@ -1736,8 +1709,7 @@ static int __init acm_init(void) return retval; } - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" - DRIVER_DESC "\n"); + printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n"); return 0; } -- cgit v1.2.3 From 3a42610812c55e8a60e2660e01c6cb6eb01d4171 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:19 +0100 Subject: USB: cdc-acm: remove superfluous prototype Remove unnecessary acm_tty_chars_in_buffer prototype. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index a693381ed3c..b84ccec8fc7 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -626,8 +626,6 @@ static void acm_tty_unregister(struct acm *acm) kfree(acm); } -static int acm_tty_chars_in_buffer(struct tty_struct *tty); - static void acm_port_down(struct acm *acm) { int i, nr = acm->rx_buflimit; -- cgit v1.2.3 From 6fb6b8846458afb7b5689f86cf35705c40eb70e5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:20 +0100 Subject: USB: cdc-acm: remove unused drain-delay code The drain-delay code is no longer used, so remove it. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 6 ------ drivers/usb/class/cdc-acm.h | 1 - 2 files changed, 7 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index b84ccec8fc7..e0f9febee7e 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -50,9 +50,6 @@ #include "cdc-acm.h" -#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */ - - #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" @@ -507,8 +504,6 @@ static void acm_write_bulk(struct urb *urb) spin_unlock_irqrestore(&acm->write_lock, flags); if (ACM_READY(acm)) schedule_work(&acm->work); - else - wake_up_interruptible(&acm->drain_wait); } static void acm_softint(struct work_struct *work) @@ -1155,7 +1150,6 @@ made_compressed_probe: acm->urb_task.func = acm_rx_tasklet; acm->urb_task.data = (unsigned long) acm; INIT_WORK(&acm->work, acm_softint); - init_waitqueue_head(&acm->drain_wait); spin_lock_init(&acm->throttle_lock); spin_lock_init(&acm->write_lock); spin_lock_init(&acm->read_lock); diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index b4ea54dbf32..7282d1f4912 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -112,7 +112,6 @@ struct acm { struct mutex mutex; struct usb_cdc_line_coding line; /* bits, stop, parity */ struct work_struct work; /* work queue entry for line discipline waking up */ - wait_queue_head_t drain_wait; /* close processing */ struct tasklet_struct urb_task; /* rx processing */ spinlock_t throttle_lock; /* synchronize throtteling and read callback */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ -- cgit v1.2.3 From dab54c9f1e26f47a3313300bc1f4dc0eecb47375 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:21 +0100 Subject: USB: cdc-acm: clean up rx_buflimit references Clean up references to rx_buflimit. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index e0f9febee7e..2d8d6b83823 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -606,16 +606,15 @@ early_bail: static void acm_tty_unregister(struct acm *acm) { - int i, nr; + int i; - nr = acm->rx_buflimit; tty_unregister_device(acm_tty_driver, acm->minor); usb_put_intf(acm->control); acm_table[acm->minor] = NULL; usb_free_urb(acm->ctrlurb); for (i = 0; i < ACM_NW; i++) usb_free_urb(acm->wb[i].urb); - for (i = 0; i < nr; i++) + for (i = 0; i < acm->rx_buflimit; i++) usb_free_urb(acm->ru[i].urb); kfree(acm->country_codes); kfree(acm); @@ -623,7 +622,8 @@ static void acm_tty_unregister(struct acm *acm) static void acm_port_down(struct acm *acm) { - int i, nr = acm->rx_buflimit; + int i; + mutex_lock(&open_mutex); if (acm->dev) { usb_autopm_get_interface(acm->control); @@ -632,7 +632,7 @@ static void acm_port_down(struct acm *acm) for (i = 0; i < ACM_NW; i++) usb_kill_urb(acm->wb[i].urb); tasklet_disable(&acm->urb_task); - for (i = 0; i < nr; i++) + for (i = 0; i < acm->rx_buflimit; i++) usb_kill_urb(acm->ru[i].urb); tasklet_enable(&acm->urb_task); acm->control->needs_remote_wakeup = 0; @@ -882,9 +882,9 @@ static void acm_write_buffers_free(struct acm *acm) static void acm_read_buffers_free(struct acm *acm) { struct usb_device *usb_dev = interface_to_usbdev(acm->control); - int i, n = acm->rx_buflimit; + int i; - for (i = 0; i < n; i++) + for (i = 0; i < acm->rx_buflimit; i++) usb_free_coherent(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); } -- cgit v1.2.3 From 94d4c8919de3ae9e2e029ed121adfed43803bb5d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:22 +0100 Subject: USB: cdc-acm: clean up open error handling No need to kill ctrl urb on errors as this is done later during close. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 2d8d6b83823..f239e3bd642 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -568,7 +568,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && (acm->ctrl_caps & USB_CDC_CAP_LINE)) - goto full_bailout; + goto bail_out; usb_autopm_put_interface(acm->control); @@ -592,8 +592,6 @@ out: mutex_unlock(&open_mutex); return rv; -full_bailout: - usb_kill_urb(acm->ctrlurb); bail_out: acm->port.count--; mutex_unlock(&acm->mutex); -- cgit v1.2.3 From 74f5e1babde76149c2bb35ca5dbf4d0b9b38f161 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:23 +0100 Subject: USB: cdc-acm: clean up read urb allocation Allocate read urbs and read buffers in the same loop during probe. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index f239e3bd642..519c7b93350 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1177,8 +1177,17 @@ made_compressed_probe: goto alloc_fail5; } for (i = 0; i < num_rx_buf; i++) { + struct acm_rb *rb = &(acm->rb[i]); struct acm_ru *rcv = &(acm->ru[i]); + rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL, + &rb->dma); + if (!rb->base) { + dev_err(&intf->dev, "out of memory " + "(read bufs usb_alloc_coherent)\n"); + goto alloc_fail6; + } + rcv->urb = usb_alloc_urb(0, GFP_KERNEL); if (rcv->urb == NULL) { dev_err(&intf->dev, @@ -1189,17 +1198,6 @@ made_compressed_probe: rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; rcv->instance = acm; } - for (i = 0; i < num_rx_buf; i++) { - struct acm_rb *rb = &(acm->rb[i]); - - rb->base = usb_alloc_coherent(acm->dev, readsize, - GFP_KERNEL, &rb->dma); - if (!rb->base) { - dev_err(&intf->dev, - "out of memory (read bufs usb_alloc_coherent)\n"); - goto alloc_fail7; - } - } for (i = 0; i < ACM_NW; i++) { struct acm_wb *snd = &(acm->wb[i]); @@ -1207,7 +1205,7 @@ made_compressed_probe: if (snd->urb == NULL) { dev_err(&intf->dev, "out of memory (write urbs usb_alloc_urb)\n"); - goto alloc_fail8; + goto alloc_fail7; } if (usb_endpoint_xfer_int(epwrite)) @@ -1226,7 +1224,7 @@ made_compressed_probe: i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); if (i < 0) - goto alloc_fail8; + goto alloc_fail7; if (cfd) { /* export the country data */ acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); @@ -1278,14 +1276,13 @@ skip_countries: acm_table[minor] = acm; return 0; -alloc_fail8: +alloc_fail7: for (i = 0; i < ACM_NW; i++) usb_free_urb(acm->wb[i].urb); -alloc_fail7: - acm_read_buffers_free(acm); alloc_fail6: for (i = 0; i < num_rx_buf; i++) usb_free_urb(acm->ru[i].urb); + acm_read_buffers_free(acm); usb_free_urb(acm->ctrlurb); alloc_fail5: acm_write_buffers_free(acm); -- cgit v1.2.3 From 088c64f812847b3623b03d167ed329f90f3e38a4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 25 Mar 2011 11:06:02 +0100 Subject: USB: cdc-acm: re-write read processing Kill rx tasklet, which is no longer needed, and re-write read processing. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 295 +++++++++++++++++--------------------------- drivers/usb/class/cdc-acm.h | 22 +--- 2 files changed, 120 insertions(+), 197 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 519c7b93350..58754b508a0 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -7,6 +7,7 @@ * Copyright (c) 2000 Vojtech Pavlik * Copyright (c) 2004 Oliver Neukum * Copyright (c) 2005 David Kubicek + * Copyright (c) 2011 Johan Hovold * * USB Abstract Control Model driver for USB modems and ISDN adapters * @@ -50,7 +51,7 @@ #include "cdc-acm.h" -#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" +#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek, Johan Hovold" #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" static struct usb_driver acm_driver; @@ -323,166 +324,92 @@ exit: __func__, retval); } -/* data interface returns incoming bytes, or we got unthrottled */ -static void acm_read_bulk(struct urb *urb) +static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags) { - struct acm_rb *buf; - struct acm_ru *rcv = urb->context; - struct acm *acm = rcv->instance; - int status = urb->status; + int res; - dev_vdbg(&acm->data->dev, "%s - status %d\n", __func__, status); + if (!test_and_clear_bit(index, &acm->read_urbs_free)) + return 0; - if (!ACM_READY(acm)) { - dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__); - return; + dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index); + + res = usb_submit_urb(acm->read_urbs[index], mem_flags); + if (res) { + if (res != -EPERM) { + dev_err(&acm->data->dev, + "%s - usb_submit_urb failed: %d\n", + __func__, res); + } + set_bit(index, &acm->read_urbs_free); + return res; } - usb_mark_last_busy(acm->dev); - if (status) - dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", - __func__, status); + return 0; +} - buf = rcv->buffer; - buf->size = urb->actual_length; +static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags) +{ + int res; + int i; - if (likely(status == 0)) { - spin_lock(&acm->read_lock); - acm->processing++; - list_add_tail(&rcv->list, &acm->spare_read_urbs); - list_add_tail(&buf->list, &acm->filled_read_bufs); - spin_unlock(&acm->read_lock); - } else { - /* we drop the buffer due to an error */ - spin_lock(&acm->read_lock); - list_add_tail(&rcv->list, &acm->spare_read_urbs); - list_add(&buf->list, &acm->spare_read_bufs); - spin_unlock(&acm->read_lock); - /* nevertheless the tasklet must be kicked unconditionally - so the queue cannot dry up */ + for (i = 0; i < acm->rx_buflimit; ++i) { + res = acm_submit_read_urb(acm, i, mem_flags); + if (res) + return res; } - if (likely(!acm->susp_count)) - tasklet_schedule(&acm->urb_task); + + return 0; } -static void acm_rx_tasklet(unsigned long _acm) +static void acm_process_read_urb(struct acm *acm, struct urb *urb) { - struct acm *acm = (void *)_acm; - struct acm_rb *buf; struct tty_struct *tty; - struct acm_ru *rcv; - unsigned long flags; - unsigned char throttled; - - dev_vdbg(&acm->data->dev, "%s\n", __func__); - if (!ACM_READY(acm)) { - dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__); + if (!urb->actual_length) return; - } - - spin_lock_irqsave(&acm->throttle_lock, flags); - throttled = acm->throttle; - spin_unlock_irqrestore(&acm->throttle_lock, flags); - if (throttled) { - dev_dbg(&acm->data->dev, "%s - throttled\n", __func__); - return; - } tty = tty_port_tty_get(&acm->port); + if (!tty) + return; -next_buffer: - spin_lock_irqsave(&acm->read_lock, flags); - if (list_empty(&acm->filled_read_bufs)) { - spin_unlock_irqrestore(&acm->read_lock, flags); - goto urbs; - } - buf = list_entry(acm->filled_read_bufs.next, - struct acm_rb, list); - list_del(&buf->list); - spin_unlock_irqrestore(&acm->read_lock, flags); - - dev_vdbg(&acm->data->dev, "%s - processing buf 0x%p, size = %d\n", - __func__, buf, buf->size); - if (tty) { - spin_lock_irqsave(&acm->throttle_lock, flags); - throttled = acm->throttle; - spin_unlock_irqrestore(&acm->throttle_lock, flags); - if (!throttled) { - tty_insert_flip_string(tty, buf->base, buf->size); - tty_flip_buffer_push(tty); - } else { - tty_kref_put(tty); - dev_dbg(&acm->data->dev, "%s - throttling noticed\n", - __func__); - spin_lock_irqsave(&acm->read_lock, flags); - list_add(&buf->list, &acm->filled_read_bufs); - spin_unlock_irqrestore(&acm->read_lock, flags); - return; - } - } - - spin_lock_irqsave(&acm->read_lock, flags); - list_add(&buf->list, &acm->spare_read_bufs); - spin_unlock_irqrestore(&acm->read_lock, flags); - goto next_buffer; + tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length); + tty_flip_buffer_push(tty); -urbs: tty_kref_put(tty); +} - while (!list_empty(&acm->spare_read_bufs)) { - spin_lock_irqsave(&acm->read_lock, flags); - if (list_empty(&acm->spare_read_urbs)) { - acm->processing = 0; - spin_unlock_irqrestore(&acm->read_lock, flags); - return; - } - rcv = list_entry(acm->spare_read_urbs.next, - struct acm_ru, list); - list_del(&rcv->list); - spin_unlock_irqrestore(&acm->read_lock, flags); +static void acm_read_bulk_callback(struct urb *urb) +{ + struct acm_rb *rb = urb->context; + struct acm *acm = rb->instance; + unsigned long flags; - buf = list_entry(acm->spare_read_bufs.next, - struct acm_rb, list); - list_del(&buf->list); + dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__, + rb->index, urb->actual_length); + set_bit(rb->index, &acm->read_urbs_free); - rcv->buffer = buf; + if (!acm->dev) { + dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__); + return; + } + usb_mark_last_busy(acm->dev); - if (acm->is_int_ep) - usb_fill_int_urb(rcv->urb, acm->dev, - acm->rx_endpoint, - buf->base, - acm->readsize, - acm_read_bulk, rcv, acm->bInterval); - else - usb_fill_bulk_urb(rcv->urb, acm->dev, - acm->rx_endpoint, - buf->base, - acm->readsize, - acm_read_bulk, rcv); - rcv->urb->transfer_dma = buf->dma; - rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - /* This shouldn't kill the driver as unsuccessful URBs are - returned to the free-urbs-pool and resubmited ASAP */ - spin_lock_irqsave(&acm->read_lock, flags); - if (acm->susp_count || - usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { - list_add(&buf->list, &acm->spare_read_bufs); - list_add(&rcv->list, &acm->spare_read_urbs); - acm->processing = 0; - spin_unlock_irqrestore(&acm->read_lock, flags); - return; - } else { - spin_unlock_irqrestore(&acm->read_lock, flags); - dev_vdbg(&acm->data->dev, - "%s - sending urb 0x%p, rcv 0x%p, buf 0x%p\n", - __func__, rcv->urb, rcv, buf); - } + if (urb->status) { + dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", + __func__, urb->status); + return; } + acm_process_read_urb(acm, urb); + + /* throttle device if requested by tty */ spin_lock_irqsave(&acm->read_lock, flags); - acm->processing = 0; - spin_unlock_irqrestore(&acm->read_lock, flags); + acm->throttled = acm->throttle_req; + if (!acm->throttled && !acm->susp_count) { + spin_unlock_irqrestore(&acm->read_lock, flags); + acm_submit_read_urb(acm, rb->index, GFP_ATOMIC); + } else { + spin_unlock_irqrestore(&acm->read_lock, flags); + } } /* data interface wrote those outgoing bytes */ @@ -530,7 +457,6 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) { struct acm *acm; int rv = -ENODEV; - int i; mutex_lock(&open_mutex); @@ -572,20 +498,11 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) usb_autopm_put_interface(acm->control); - INIT_LIST_HEAD(&acm->spare_read_urbs); - INIT_LIST_HEAD(&acm->spare_read_bufs); - INIT_LIST_HEAD(&acm->filled_read_bufs); - - for (i = 0; i < acm->rx_buflimit; i++) - list_add(&(acm->ru[i].list), &acm->spare_read_urbs); - for (i = 0; i < acm->rx_buflimit; i++) - list_add(&(acm->rb[i].list), &acm->spare_read_bufs); - - acm->throttle = 0; + if (acm_submit_read_urbs(acm, GFP_KERNEL)) + goto bail_out; set_bit(ASYNCB_INITIALIZED, &acm->port.flags); rv = tty_port_block_til_ready(&acm->port, tty, filp); - tasklet_schedule(&acm->urb_task); mutex_unlock(&acm->mutex); out: @@ -613,7 +530,7 @@ static void acm_tty_unregister(struct acm *acm) for (i = 0; i < ACM_NW; i++) usb_free_urb(acm->wb[i].urb); for (i = 0; i < acm->rx_buflimit; i++) - usb_free_urb(acm->ru[i].urb); + usb_free_urb(acm->read_urbs[i]); kfree(acm->country_codes); kfree(acm); } @@ -629,10 +546,8 @@ static void acm_port_down(struct acm *acm) usb_kill_urb(acm->ctrlurb); for (i = 0; i < ACM_NW; i++) usb_kill_urb(acm->wb[i].urb); - tasklet_disable(&acm->urb_task); for (i = 0; i < acm->rx_buflimit; i++) - usb_kill_urb(acm->ru[i].urb); - tasklet_enable(&acm->urb_task); + usb_kill_urb(acm->read_urbs[i]); acm->control->needs_remote_wakeup = 0; usb_autopm_put_interface(acm->control); } @@ -731,22 +646,31 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty) static void acm_tty_throttle(struct tty_struct *tty) { struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return; - spin_lock_bh(&acm->throttle_lock); - acm->throttle = 1; - spin_unlock_bh(&acm->throttle_lock); + + spin_lock_irq(&acm->read_lock); + acm->throttle_req = 1; + spin_unlock_irq(&acm->read_lock); } static void acm_tty_unthrottle(struct tty_struct *tty) { struct acm *acm = tty->driver_data; + unsigned int was_throttled; + if (!ACM_READY(acm)) return; - spin_lock_bh(&acm->throttle_lock); - acm->throttle = 0; - spin_unlock_bh(&acm->throttle_lock); - tasklet_schedule(&acm->urb_task); + + spin_lock_irq(&acm->read_lock); + was_throttled = acm->throttled; + acm->throttled = 0; + acm->throttle_req = 0; + spin_unlock_irq(&acm->read_lock); + + if (was_throttled) + acm_submit_read_urbs(acm, GFP_KERNEL); } static int acm_tty_break_ctl(struct tty_struct *tty, int state) @@ -884,7 +808,7 @@ static void acm_read_buffers_free(struct acm *acm) for (i = 0; i < acm->rx_buflimit; i++) usb_free_coherent(usb_dev, acm->readsize, - acm->rb[i].base, acm->rb[i].dma); + acm->read_buffers[i].base, acm->read_buffers[i].dma); } /* Little helper: write buffers allocate */ @@ -1145,10 +1069,7 @@ made_compressed_probe: acm->ctrlsize = ctrlsize; acm->readsize = readsize; acm->rx_buflimit = num_rx_buf; - acm->urb_task.func = acm_rx_tasklet; - acm->urb_task.data = (unsigned long) acm; INIT_WORK(&acm->work, acm_softint); - spin_lock_init(&acm->throttle_lock); spin_lock_init(&acm->write_lock); spin_lock_init(&acm->read_lock); mutex_init(&acm->mutex); @@ -1177,8 +1098,8 @@ made_compressed_probe: goto alloc_fail5; } for (i = 0; i < num_rx_buf; i++) { - struct acm_rb *rb = &(acm->rb[i]); - struct acm_ru *rcv = &(acm->ru[i]); + struct acm_rb *rb = &(acm->read_buffers[i]); + struct urb *urb; rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL, &rb->dma); @@ -1187,16 +1108,34 @@ made_compressed_probe: "(read bufs usb_alloc_coherent)\n"); goto alloc_fail6; } + rb->index = i; + rb->instance = acm; - rcv->urb = usb_alloc_urb(0, GFP_KERNEL); - if (rcv->urb == NULL) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { dev_err(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n"); goto alloc_fail6; } + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + urb->transfer_dma = rb->dma; + if (acm->is_int_ep) { + usb_fill_int_urb(urb, acm->dev, + acm->rx_endpoint, + rb->base, + acm->readsize, + acm_read_bulk_callback, rb, + acm->bInterval); + } else { + usb_fill_bulk_urb(urb, acm->dev, + acm->rx_endpoint, + rb->base, + acm->readsize, + acm_read_bulk_callback, rb); + } - rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - rcv->instance = acm; + acm->read_urbs[i] = urb; + __set_bit(i, &acm->read_urbs_free); } for (i = 0; i < ACM_NW; i++) { struct acm_wb *snd = &(acm->wb[i]); @@ -1281,7 +1220,7 @@ alloc_fail7: usb_free_urb(acm->wb[i].urb); alloc_fail6: for (i = 0; i < num_rx_buf; i++) - usb_free_urb(acm->ru[i].urb); + usb_free_urb(acm->read_urbs[i]); acm_read_buffers_free(acm); usb_free_urb(acm->ctrlurb); alloc_fail5: @@ -1300,15 +1239,11 @@ static void stop_data_traffic(struct acm *acm) dev_dbg(&acm->control->dev, "%s\n", __func__); - tasklet_disable(&acm->urb_task); - usb_kill_urb(acm->ctrlurb); for (i = 0; i < ACM_NW; i++) usb_kill_urb(acm->wb[i].urb); for (i = 0; i < acm->rx_buflimit; i++) - usb_kill_urb(acm->ru[i].urb); - - tasklet_enable(&acm->urb_task); + usb_kill_urb(acm->read_urbs[i]); cancel_work_sync(&acm->work); } @@ -1369,11 +1304,9 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message) if (message.event & PM_EVENT_AUTO) { int b; - spin_lock_irq(&acm->read_lock); - spin_lock(&acm->write_lock); - b = acm->processing + acm->transmitting; - spin_unlock(&acm->write_lock); - spin_unlock_irq(&acm->read_lock); + spin_lock_irq(&acm->write_lock); + b = acm->transmitting; + spin_unlock_irq(&acm->write_lock); if (b) return -EBUSY; } @@ -1435,7 +1368,7 @@ static int acm_resume(struct usb_interface *intf) if (rv < 0) goto err_out; - tasklet_schedule(&acm->urb_task); + rv = acm_submit_read_urbs(acm, GFP_NOIO); } err_out: diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 7282d1f4912..7b5c0bd07f8 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -72,16 +72,10 @@ struct acm_wb { }; struct acm_rb { - struct list_head list; int size; unsigned char *base; dma_addr_t dma; -}; - -struct acm_ru { - struct list_head list; - struct acm_rb *buffer; - struct urb *urb; + int index; struct acm *instance; }; @@ -97,34 +91,30 @@ struct acm { unsigned int country_code_size; /* size of this buffer */ unsigned int country_rel_date; /* release date of version */ struct acm_wb wb[ACM_NW]; - struct acm_ru ru[ACM_NR]; - struct acm_rb rb[ACM_NR]; + unsigned long read_urbs_free; + struct urb *read_urbs[ACM_NR]; + struct acm_rb read_buffers[ACM_NR]; int rx_buflimit; int rx_endpoint; spinlock_t read_lock; - struct list_head spare_read_urbs; - struct list_head spare_read_bufs; - struct list_head filled_read_bufs; int write_used; /* number of non-empty write buffers */ - int processing; int transmitting; spinlock_t write_lock; struct mutex mutex; struct usb_cdc_line_coding line; /* bits, stop, parity */ struct work_struct work; /* work queue entry for line discipline waking up */ - struct tasklet_struct urb_task; /* rx processing */ - spinlock_t throttle_lock; /* synchronize throtteling and read callback */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ unsigned int ctrlout; /* output control lines (DTR, RTS) */ unsigned int writesize; /* max packet size for the output bulk endpoint */ unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ unsigned int minor; /* acm minor number */ - unsigned char throttle; /* throttled by tty layer */ unsigned char clocal; /* termios CLOCAL */ unsigned int ctrl_caps; /* control capabilities from the class specific header */ unsigned int susp_count; /* number of suspended interfaces */ unsigned int combined_interfaces:1; /* control and data collapsed */ unsigned int is_int_ep:1; /* interrupt endpoints contrary to spec used */ + unsigned int throttled:1; /* actually throttled */ + unsigned int throttle_req:1; /* throttle requested */ u8 bInterval; struct acm_wb *delayed_wb; /* write queued for a device about to be woken */ }; -- cgit v1.2.3 From 806e8f8fcc27e1753947bd9f059ba2316cf8f92a Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Thu, 24 Mar 2011 12:20:13 +0100 Subject: usb: usb_storage: do not align length of request for CBW to maxp size Mass-storage and file-storage gadgets align the length to maximum-packet-size when preparing the request to receive CBW. This is unnecessary and prevents the controller driver from knowing that a short-packet is expected. It is incorrect to set short_not_ok when preparing the request to receive CBW. CBW will be a short-packet so short_not_ok must not be set. This makes bh->bulk_out_intended_length unnecessary so it is also removed. Signed-off-by: Mian Yousaf Kaukab Acked-by: Michal Nazarewicz Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 24 ++++-------------------- drivers/usb/gadget/file_storage.c | 28 ++++++---------------------- drivers/usb/gadget/storage_common.c | 7 ------- 3 files changed, 10 insertions(+), 49 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 6d8e533949e..125587ac5d0 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -474,20 +474,6 @@ static int exception_in_progress(struct fsg_common *common) return common->state > FSG_STATE_IDLE; } -/* Make bulk-out requests be divisible by the maxpacket size */ -static void set_bulk_out_req_length(struct fsg_common *common, - struct fsg_buffhd *bh, unsigned int length) -{ - unsigned int rem; - - bh->bulk_out_intended_length = length; - rem = length % common->bulk_out_maxpacket; - if (rem > 0) - length += common->bulk_out_maxpacket - rem; - bh->outreq->length = length; -} - - /*-------------------------------------------------------------------------*/ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) @@ -586,9 +572,9 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) struct fsg_buffhd *bh = req->context; dump_msg(common, "bulk-out", req->buf, req->actual); - if (req->status || req->actual != bh->bulk_out_intended_length) + if (req->status || req->actual != req->length) DBG(common, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, bh->bulk_out_intended_length); + req->status, req->actual, req->length); if (req->status == -ECONNRESET) /* Request was cancelled */ usb_ep_fifo_flush(ep); @@ -975,7 +961,6 @@ static int do_write(struct fsg_common *common) * the bulk-out maxpacket size */ bh->outreq->length = amount; - bh->bulk_out_intended_length = amount; bh->outreq->short_not_ok = 1; if (!start_out_transfer(common, bh)) /* Dunno what to do if common->fsg is NULL */ @@ -1652,7 +1637,6 @@ static int throw_away_data(struct fsg_common *common) * the bulk-out maxpacket size. */ bh->outreq->length = amount; - bh->bulk_out_intended_length = amount; bh->outreq->short_not_ok = 1; if (!start_out_transfer(common, bh)) /* Dunno what to do if common->fsg is NULL */ @@ -2322,8 +2306,8 @@ static int get_next_command(struct fsg_common *common) } /* Queue a request to read a Bulk-only CBW */ - set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN); - bh->outreq->short_not_ok = 1; + bh->outreq->length = USB_BULK_CB_WRAP_LEN; + bh->outreq->short_not_ok = 0; if (!start_out_transfer(common, bh)) /* Don't know what to do if common->fsg is NULL */ return -EIO; diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index a6eacb59571..d04e0e6b019 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -497,19 +497,6 @@ static int exception_in_progress(struct fsg_dev *fsg) return (fsg->state > FSG_STATE_IDLE); } -/* Make bulk-out requests be divisible by the maxpacket size */ -static void set_bulk_out_req_length(struct fsg_dev *fsg, - struct fsg_buffhd *bh, unsigned int length) -{ - unsigned int rem; - - bh->bulk_out_intended_length = length; - rem = length % fsg->bulk_out_maxpacket; - if (rem > 0) - length += fsg->bulk_out_maxpacket - rem; - bh->outreq->length = length; -} - static struct fsg_dev *the_fsg; static struct usb_gadget_driver fsg_driver; @@ -730,10 +717,9 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) struct fsg_buffhd *bh = req->context; dump_msg(fsg, "bulk-out", req->buf, req->actual); - if (req->status || req->actual != bh->bulk_out_intended_length) + if (req->status || req->actual != req->length) DBG(fsg, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, - bh->bulk_out_intended_length); + req->status, req->actual, req->length); if (req->status == -ECONNRESET) // Request was cancelled usb_ep_fifo_flush(ep); @@ -1349,8 +1335,7 @@ static int do_write(struct fsg_dev *fsg) /* amount is always divisible by 512, hence by * the bulk-out maxpacket size */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; + bh->outreq->length = amount; bh->outreq->short_not_ok = 1; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); @@ -2010,8 +1995,7 @@ static int throw_away_data(struct fsg_dev *fsg) /* amount is always divisible by 512, hence by * the bulk-out maxpacket size */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; + bh->outreq->length = amount; bh->outreq->short_not_ok = 1; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); @@ -2688,8 +2672,8 @@ static int get_next_command(struct fsg_dev *fsg) } /* Queue a request to read a Bulk-only CBW */ - set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); - bh->outreq->short_not_ok = 1; + bh->outreq->length = USB_BULK_CB_WRAP_LEN; + bh->outreq->short_not_ok = 0; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index b015561fd60..3179b8bb6ce 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -286,13 +286,6 @@ struct fsg_buffhd { enum fsg_buffer_state state; struct fsg_buffhd *next; - /* - * The NetChip 2280 is faster, and handles some protocol faults - * better, if we don't submit any short bulk-out read requests. - * So we will record the intended request length here. - */ - unsigned int bulk_out_intended_length; - struct usb_request *inreq; int inreq_busy; struct usb_request *outreq; -- cgit v1.2.3 From edf847decc4159128041d2a81f8d93e6eb567ecb Mon Sep 17 00:00:00 2001 From: Libor Pechacek Date: Fri, 25 Mar 2011 09:36:49 +0100 Subject: USB: remove dead code from usb_deregister_dev() The `name' variable is unused in usb_deregister_dev() since commit d6e5bcf (devfs: Remove the mode field from usb_class_driver as it's no longer needed). Signed-off-by: Libor Pechacek Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/file.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index cf6a5423de0..99458c843d6 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -236,13 +236,6 @@ EXPORT_SYMBOL_GPL(usb_register_dev); void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver) { - int minor_base = class_driver->minor_base; - char name[20]; - -#ifdef CONFIG_USB_DYNAMIC_MINORS - minor_base = 0; -#endif - if (intf->minor == -1) return; @@ -252,7 +245,6 @@ void usb_deregister_dev(struct usb_interface *intf, usb_minors[intf->minor] = NULL; up_write(&minor_rwsem); - snprintf(name, sizeof(name), class_driver->name, intf->minor - minor_base); device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor)); intf->usb_dev = NULL; intf->minor = -1; -- cgit v1.2.3 From ee81b3e086c907a3347b15ef219a24fc8bf900f6 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 25 Mar 2011 11:46:27 -0400 Subject: USB: g_file_storage: don't send padding when stall=n This patch (as1455) removes the extra padding sent by g_file_storage and g_mass_storage when the gadget wants to send less data than requested by the host and isn't allowed to halt the bulk-IN endpoint. Although the Bulk-Only Transport specification requires the padding to be present, it isn't truly needed since the transfer will be terminated by a short packet anyway. Furthermore, many existing devices don't bother to send any padding. Signed-off-by: Alan Stern Acked-By: Michal Nazarewicz CC: Roger Quadros Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 54 ++++++++----------------------------- drivers/usb/gadget/file_storage.c | 53 +++++++++--------------------------- 2 files changed, 23 insertions(+), 84 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 125587ac5d0..98d6b39061d 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -1569,37 +1569,6 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) return rc; } -static int pad_with_zeros(struct fsg_dev *fsg) -{ - struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; - u32 nkeep = bh->inreq->length; - u32 nsend; - int rc; - - bh->state = BUF_STATE_EMPTY; /* For the first iteration */ - fsg->common->usb_amount_left = nkeep + fsg->common->residue; - while (fsg->common->usb_amount_left > 0) { - - /* Wait for the next buffer to be free */ - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg->common); - if (rc) - return rc; - } - - nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN); - memset(bh->buf + nkeep, 0, nsend - nkeep); - bh->inreq->length = nsend; - bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - bh = fsg->common->next_buffhd_to_fill = bh->next; - fsg->common->usb_amount_left -= nsend; - nkeep = 0; - } - return 0; -} - static int throw_away_data(struct fsg_common *common) { struct fsg_buffhd *bh; @@ -1686,6 +1655,10 @@ static int finish_reply(struct fsg_common *common) if (common->data_size == 0) { /* Nothing to send */ + /* Don't know what to do if common->fsg is NULL */ + } else if (!fsg_is_set(common)) { + rc = -EIO; + /* If there's no residue, simply send the last buffer */ } else if (common->residue == 0) { bh->inreq->zero = 0; @@ -1694,24 +1667,19 @@ static int finish_reply(struct fsg_common *common) common->next_buffhd_to_fill = bh->next; /* - * For Bulk-only, if we're allowed to stall then send the - * short packet and halt the bulk-in endpoint. If we can't - * stall, pad out the remaining data with 0's. + * For Bulk-only, mark the end of the data with a short + * packet. If we are allowed to stall, halt the bulk-in + * endpoint. (Note: This violates the Bulk-Only Transport + * specification, which requires us to pad the data if we + * don't halt the endpoint. Presumably nobody will mind.) */ - } else if (common->can_stall) { + } else { bh->inreq->zero = 1; if (!start_in_transfer(common, bh)) - /* Don't know what to do if - * common->fsg is NULL */ rc = -EIO; common->next_buffhd_to_fill = bh->next; - if (common->fsg) + if (common->can_stall) rc = halt_bulk_in_endpoint(common->fsg); - } else if (fsg_is_set(common)) { - rc = pad_with_zeros(common->fsg); - } else { - /* Don't know what to do if common->fsg is NULL */ - rc = -EIO; } break; diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index d04e0e6b019..aebfb81f3ba 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -1932,37 +1932,6 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) return rc; } -static int pad_with_zeros(struct fsg_dev *fsg) -{ - struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; - u32 nkeep = bh->inreq->length; - u32 nsend; - int rc; - - bh->state = BUF_STATE_EMPTY; // For the first iteration - fsg->usb_amount_left = nkeep + fsg->residue; - while (fsg->usb_amount_left > 0) { - - /* Wait for the next buffer to be free */ - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - nsend = min(fsg->usb_amount_left, (u32) mod_data.buflen); - memset(bh->buf + nkeep, 0, nsend - nkeep); - bh->inreq->length = nsend; - bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - bh = fsg->next_buffhd_to_fill = bh->next; - fsg->usb_amount_left -= nsend; - nkeep = 0; - } - return 0; -} - static int throw_away_data(struct fsg_dev *fsg) { struct fsg_buffhd *bh; @@ -2066,18 +2035,20 @@ static int finish_reply(struct fsg_dev *fsg) } } - /* For Bulk-only, if we're allowed to stall then send the - * short packet and halt the bulk-in endpoint. If we can't - * stall, pad out the remaining data with 0's. */ + /* + * For Bulk-only, mark the end of the data with a short + * packet. If we are allowed to stall, halt the bulk-in + * endpoint. (Note: This violates the Bulk-Only Transport + * specification, which requires us to pad the data if we + * don't halt the endpoint. Presumably nobody will mind.) + */ else { - if (mod_data.can_stall) { - bh->inreq->zero = 1; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; + bh->inreq->zero = 1; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + fsg->next_buffhd_to_fill = bh->next; + if (mod_data.can_stall) rc = halt_bulk_in_endpoint(fsg); - } else - rc = pad_with_zeros(fsg); } break; -- cgit v1.2.3 From ad7c56f07e24c758d78e797ceeb9cf049dec66aa Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 22 Mar 2011 15:35:39 -0400 Subject: USB: sl811: add Kconfig option for ISOCHRONOUS mode Some bluetooth dongles want ISO mode, and the limited support that the sl811 offers today is sufficient. So add a Kconfig option for people to optionally get access to the partial functionality. Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 10 ++++++++++ drivers/usb/host/sl811-hcd.c | 8 +------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index e0e0787b724..5f518ded195 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -444,6 +444,16 @@ config USB_SL811_HCD To compile this driver as a module, choose M here: the module will be called sl811-hcd. +config USB_SL811_HCD_ISO + bool "partial ISO support" + depends on USB_SL811_HCD + help + The driver doesn't support iso_frame_desc (yet), but for some simple + devices that just queue one ISO frame per URB, then ISO transfers + "should" work using the normal urb status fields. + + If unsure, say N. + config USB_SL811_CS tristate "CF/PCMCIA support for SL811HS HCD" depends on USB_SL811_HCD && PCMCIA diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 18b7099a812..5adcba016fc 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -71,12 +71,6 @@ MODULE_ALIAS("platform:sl811-hcd"); /* for now, use only one transfer register bank */ #undef USE_B -/* this doesn't understand urb->iso_frame_desc[], but if you had a driver - * that just queued one ISO frame per URB then iso transfers "should" work - * using the normal urb status fields. - */ -#define DISABLE_ISO - // #define QUIRK2 #define QUIRK3 @@ -807,7 +801,7 @@ static int sl811h_urb_enqueue( int retval; struct usb_host_endpoint *hep = urb->ep; -#ifdef DISABLE_ISO +#ifndef CONFIG_USB_SL811_HCD_ISO if (type == PIPE_ISOCHRONOUS) return -ENOSPC; #endif -- cgit v1.2.3 From 3ab810f19d71f4083be44b41770bcd784ff82e51 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 1 Apr 2011 11:24:30 -0700 Subject: usb gadget: fix all Section mismatch warnings Fix 41 occurrences of this type of Section mismatch warning in g_mass_storage, g_serial, g_cdc, g_multi, g_nokia, g_ether, g_ffs: (the 75 number reported earlier contained some duplicates.) WARNING: drivers/usb/gadget/g_mass_storage.o(.text+0x687a): Section mismatch in reference from the function fsg_bind() to the function .devinit.text:usb_ep_autoconfig() The function fsg_bind() references the function __devinit usb_ep_autoconfig(). This is often because fsg_bind lacks a __devinit annotation or the annotation of usb_ep_autoconfig is wrong. Also remove __devinit from usb_ep_autoconfig_reset() to prevent possible section mismatch problems with it. Signed-off-by: Randy Dunlap Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/gadget.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index e538172c0f6..dd1571db55e 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -890,8 +890,8 @@ static inline void usb_free_descriptors(struct usb_descriptor_header **v) /* utility wrapping a simple endpoint selection policy */ extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *, - struct usb_endpoint_descriptor *) __devinit; + struct usb_endpoint_descriptor *); -extern void usb_ep_autoconfig_reset(struct usb_gadget *) __devinit; +extern void usb_ep_autoconfig_reset(struct usb_gadget *); #endif /* __LINUX_USB_GADGET_H */ -- cgit v1.2.3 From 4661ffc91befc8c5ee080720120da1d53851060a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Apr 2011 10:59:34 +0300 Subject: usb: don't enter usb subdirectories directly Instead, make we enter usb/ directory on all needed cases and enter the subdirectories from drivers/usb/Makefile. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/Makefile | 6 ++---- drivers/usb/Makefile | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/Makefile b/drivers/Makefile index ad67b7d4c27..0cf73a90fca 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -64,12 +64,10 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/ obj-$(CONFIG_PARIDE) += block/paride/ obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_UWB) += uwb/ -obj-$(CONFIG_USB_OTG_UTILS) += usb/otg/ +obj-$(CONFIG_USB_OTG_UTILS) += usb/ obj-$(CONFIG_USB) += usb/ -obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/ -obj-$(CONFIG_USB_RENESAS_USBHS) += usb/renesas_usbhs/ obj-$(CONFIG_PCI) += usb/ -obj-$(CONFIG_USB_GADGET) += usb/gadget/ +obj-$(CONFIG_USB_GADGET) += usb/ obj-$(CONFIG_SERIO) += input/serio/ obj-$(CONFIG_GAMEPORT) += input/gameport/ obj-$(CONFIG_INPUT) += input/ diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 239f050efa3..9bc8aeb3c96 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -45,3 +45,8 @@ obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/ obj-$(CONFIG_USB_ATM) += atm/ obj-$(CONFIG_USB_SPEEDTOUCH) += atm/ + +obj-$(CONFIG_USB_MUSB_HDRC) += musb/ +obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/ +obj-$(CONFIG_USB_OTG_UTILS) += otg/ +obj-$(CONFIG_USB_GADGET) += gadget/ -- cgit v1.2.3 From 7d670a2ed770a3405a7edb1159e3fa9b3f43fe46 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 5 Apr 2011 13:36:04 -0400 Subject: USB: UHCI: remove uses of hcd->state This patch (as1456) removes all uses of hcd->state from the uhci-hcd driver, as part of the overall strategy to eliminate hcd->state completely. Now when a controller dies we call usb_hc_died() directly, instead of relying on the core interrupt handler to see that hcd->state has changed to HC_STATE_HALT and make the call for us. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 4f65b14e5e0..73db5569f57 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -139,7 +139,6 @@ static void finish_reset(struct uhci_hcd *uhci) uhci->port_c_suspend = uhci->resuming_ports = 0; uhci->rh_state = UHCI_RH_RESET; uhci->is_stopped = UHCI_IS_STOPPED; - uhci_to_hcd(uhci)->state = HC_STATE_HALT; clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); uhci->dead = 0; /* Full reset resurrects the controller */ @@ -188,10 +187,6 @@ static void configure_hc(struct uhci_hcd *uhci) outw(uhci->frame_number & UHCI_MAX_SOF_NUMBER, uhci->io_addr + USBFRNUM); - /* Mark controller as not halted before we enable interrupts */ - uhci_to_hcd(uhci)->state = HC_STATE_SUSPENDED; - mb(); - /* Enable PIRQ */ pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT); @@ -360,7 +355,6 @@ __acquires(uhci->lock) static void start_rh(struct uhci_hcd *uhci) { - uhci_to_hcd(uhci)->state = HC_STATE_RUNNING; uhci->is_stopped = 0; /* Mark it configured and running with a 64-byte max packet. @@ -449,6 +443,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd) lprintk(errbuf); } uhci_hc_died(uhci); + usb_hc_died(hcd); /* Force a callback in case there are * pending unlinks */ -- cgit v1.2.3 From 99083f16f04e050eab0059167b4980cd67e7aa5a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 5 Apr 2011 13:35:53 -0400 Subject: USB: UHCI: don't try to revive a dead controller This patch (as1457) abandons the curious strategy of declaring a controller dead following hibernation merely in order to reset and then revive it. The core no longer allow dead controllers to spring back to life when the system resumes, so there's no reason to declare a working controller temporarily dead. Instead we do an explicit reset. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 73db5569f57..83344d688ff 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -140,8 +140,6 @@ static void finish_reset(struct uhci_hcd *uhci) uhci->rh_state = UHCI_RH_RESET; uhci->is_stopped = UHCI_IS_STOPPED; clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); - - uhci->dead = 0; /* Full reset resurrects the controller */ } /* @@ -837,16 +835,17 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) spin_lock_irq(&uhci->lock); /* Make sure resume from hibernation re-enumerates everything */ - if (hibernated) - uhci_hc_died(uhci); + if (hibernated) { + uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); + finish_reset(uhci); + } - /* The firmware or a boot kernel may have changed the controller - * settings during a system wakeup. Check it and reconfigure - * to avoid problems. + /* The firmware may have changed the controller settings during + * a system wakeup. Check it and reconfigure to avoid problems. */ - check_and_reset_hc(uhci); - - /* If the controller was dead before, it's back alive now */ + else { + check_and_reset_hc(uhci); + } configure_hc(uhci); /* Tell the core if the controller had to be reset */ -- cgit v1.2.3 From 3482f00d018fb5e476beb867272c1d82f4f5c7d6 Mon Sep 17 00:00:00 2001 From: Michal Marek Date: Tue, 5 Apr 2011 16:59:12 +0200 Subject: usb: ftdi-elan: Drop __TIME__ usage The kernel already prints its build timestamp during boot, no need to repeat it in random drivers and produce different object files each time. Cc: Tony Olech Cc: linux-usb@vger.kernel.org Signed-off-by: Michal Marek Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ftdi-elan.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index 7839c98fa74..b16bd3ce391 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -2889,8 +2889,7 @@ static struct usb_driver ftdi_elan_driver = { static int __init ftdi_elan_init(void) { int result; - printk(KERN_INFO "driver %s built at %s on %s\n", ftdi_elan_driver.name, - __TIME__, __DATE__); + printk(KERN_INFO "driver %s\n", ftdi_elan_driver.name); mutex_init(&ftdi_module_lock); INIT_LIST_HEAD(&ftdi_static_list); status_queue = create_singlethread_workqueue("ftdi-status-control"); -- cgit v1.2.3 From 654d121ad8c84e3442efee20b2a0703edb18c212 Mon Sep 17 00:00:00 2001 From: Michal Marek Date: Tue, 5 Apr 2011 16:59:11 +0200 Subject: usb: u132-hcd: Drop __TIME__ usage The kernel already prints its build timestamp during boot, no need to repeat it in random drivers and produce different object files each time. Cc: Tony Olech Cc: linux-usb@vger.kernel.org Signed-off-by: Michal Marek Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/u132-hcd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index b4785934e09..533d12cca37 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -3230,8 +3230,7 @@ static int __init u132_hcd_init(void) mutex_init(&u132_module_lock); if (usb_disabled()) return -ENODEV; - printk(KERN_INFO "driver %s built at %s on %s\n", hcd_name, __TIME__, - __DATE__); + printk(KERN_INFO "driver %s\n", hcd_name); workqueue = create_singlethread_workqueue("u132"); retval = platform_driver_register(&u132_platform_driver); return retval; -- cgit v1.2.3 From 73ee4da994e7b97bd8241e39099cf3dd94675d79 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 5 Apr 2011 18:36:38 +0300 Subject: usb: gadget: f_mass_storage: Fix Bulk-only RESET handling The ep0 request tag was not recorded thus resulting in phase problems while sending status/response in handle_execption() handler. This was resulting in MSC compliance test failures with USBCV tool. With this patch, the Bulk-Only Mass storage RESET request is handled correctly and the MSC compliance tests pass. Signed-off-by: Roger Quadros Acked-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 98d6b39061d..e9de33d1c9a 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -599,6 +599,11 @@ static int fsg_setup(struct usb_function *f, if (!fsg_is_set(fsg->common)) return -EOPNOTSUPP; + ++fsg->common->ep0_req_tag; /* Record arrival of a new request */ + req->context = NULL; + req->length = 0; + dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); + switch (ctrl->bRequest) { case USB_BULK_RESET_REQUEST: -- cgit v1.2.3 From 3c624d4962a583516acadcbf60a8ca2a48421f5c Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 5 Apr 2011 18:36:39 +0300 Subject: usb: gadget: f_mass_storage: If 'ro'/'cdrom' specified, open file as read-only If we don't need Write access then attempt to open backing file in Read Only mode instead of bailing out too soon. Signed-off-by: Roger Quadros Acked-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index e9de33d1c9a..7d95a2cf58a 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -2757,6 +2757,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) { curlun->cdrom = !!lcfg->cdrom; curlun->ro = lcfg->cdrom || lcfg->ro; + curlun->initially_ro = curlun->ro; curlun->removable = lcfg->removable; curlun->dev.release = fsg_lun_release; curlun->dev.parent = &gadget->dev; -- cgit v1.2.3 From 84f6c3fb7e8eef51f994e959015e18bb927127e0 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 8 Apr 2011 10:11:44 -0700 Subject: usb/otg: fix twl6030 macro Fix warning caused by stray semi-colon at end of macro: drivers/usb/otg/twl6030-usb.c:183: warning: ISO C90 forbids mixed declarations and code Signed-off-by: Randy Dunlap Cc: Hema HK Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/twl6030-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index 8a91b4b832a..6e920de64ef 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -101,7 +101,7 @@ struct twl6030_usb { bool irq_enabled; }; -#define xceiv_to_twl(x) container_of((x), struct twl6030_usb, otg); +#define xceiv_to_twl(x) container_of((x), struct twl6030_usb, otg) /*-------------------------------------------------------------------------*/ -- cgit v1.2.3 From 74d1dc8d8d13fef55c33b49f06ab84eeebf967c4 Mon Sep 17 00:00:00 2001 From: "Weiping Pan(潘卫平)" Date: Mon, 11 Apr 2011 18:16:15 +0800 Subject: usb: fix a typo in a comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit replace tranmitted with transmitted. Signed-off-by: Weiping Pan(潘卫平) Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h index e35e24fd64b..1da5fb03d21 100644 --- a/drivers/usb/gadget/fsl_qe_udc.h +++ b/drivers/usb/gadget/fsl_qe_udc.h @@ -207,7 +207,7 @@ struct qe_frame{ /* Frame status field */ /* Receive side */ -#define FRAME_OK 0x00000000 /* Frame tranmitted or received OK */ +#define FRAME_OK 0x00000000 /* Frame transmitted or received OK */ #define FRAME_ERROR 0x80000000 /* Error occurred on frame */ #define START_FRAME_LOST 0x40000000 /* START_FRAME_LOST */ #define END_FRAME_LOST 0x20000000 /* END_FRAME_LOST */ -- cgit v1.2.3 From 0b0cd6c81defc4e4fccb9e9103666547fefdb9c0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 12 Apr 2011 09:31:54 -0300 Subject: USB: Mark ehci_adjust_port_wakeup_flags as __maybe_unused Mark ehci_adjust_port_wakeup_flags as __maybe_unused to avoid the following warning when building the ehci-mxc driver: CC drivers/usb/host/ehci-hcd.o drivers/usb/host/ehci-hub.c:130: warning: 'ehci_adjust_port_wakeup_flags' defined but not used Current ehci-mxc driver implementation does not support suspend/resume. Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index d05ea03cfb4..1a21799195a 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -127,7 +127,7 @@ static int ehci_port_change(struct ehci_hcd *ehci) return 0; } -static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, +static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, bool suspending, bool do_wakeup) { int port; -- cgit v1.2.3 From a87103a6d4a91cbb9be49d3bbd2f35dcf5510da1 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Fri, 1 Apr 2011 23:02:07 +0200 Subject: USB: twl4030-usb: Report correct vbus value for accessory charger adapters1 The twl4030-usb driver exports the status of VBUS as sysfs attribute. In case an accessory charger adapter (ACA) is connected to the OTG transceiver the attribute is always 'off', even when the charger provides VBUS. Added a variable to keep track of the status of VBUS and report it correctly Signed-off-by: Matthias Kaehlcke Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/twl4030-usb.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index e01b073cc48..efeb4d1517f 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -160,6 +160,7 @@ struct twl4030_usb { int irq; u8 linkstat; + bool vbus_supplied; u8 asleep; bool irq_enabled; }; @@ -250,6 +251,8 @@ static enum usb_xceiv_events twl4030_usb_linkstat(struct twl4030_usb *twl) int status; int linkstat = USB_EVENT_NONE; + twl->vbus_supplied = false; + /* * For ID/VBUS sensing, see manual section 15.4.8 ... * except when using only battery backup power, two @@ -265,6 +268,9 @@ static enum usb_xceiv_events twl4030_usb_linkstat(struct twl4030_usb *twl) if (status < 0) dev_err(twl->dev, "USB link status err %d\n", status); else if (status & (BIT(7) | BIT(2))) { + if (status & (BIT(7))) + twl->vbus_supplied = true; + if (status & BIT(2)) linkstat = USB_EVENT_ID; else @@ -484,7 +490,7 @@ static ssize_t twl4030_usb_vbus_show(struct device *dev, spin_lock_irqsave(&twl->lock, flags); ret = sprintf(buf, "%s\n", - (twl->linkstat == USB_EVENT_VBUS) ? "on" : "off"); + twl->vbus_supplied ? "on" : "off"); spin_unlock_irqrestore(&twl->lock, flags); return ret; @@ -608,6 +614,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) twl->otg.set_peripheral = twl4030_set_peripheral; twl->otg.set_suspend = twl4030_set_suspend; twl->usb_mode = pdata->usb_mode; + twl->vbus_supplied = false; twl->asleep = 1; /* init spinlock for workqueue */ -- cgit v1.2.3 From b750106bafbf9e8cb888a2b3681f444d8183a296 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 Apr 2011 11:38:07 +0200 Subject: Revert "USB: sam-ba: add driver for Atmel SAM Boot Assistant (SAM-BA)" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 90593899de83a6e6fdea563d058acd2f4334e3f9. SAM-BA devices identify themselves CDC-ACM devices and should be using the cdc-acm driver. Since commit 5b239f0aebd4dd6f85b13decf5e18e86e35d57f0 (USB: cdc-acm: Add pseudo modem without AT command capabilities) cdc-acm also binds to them. Note that the Atmel SAM-BA tools expect to use a USB-serial driver and thus require a symlink from /dev/ttyACMn to some /dev/ttyUSBm (with m < 30) to be able to select the device. This is simply a UI-issue that should be fixed by Atmel. Tested with the SAM-BA 2.10 tools and an Atmel at91sam9260-ek. Signed-off-by: Johan Hovold Cc: Sven Köhler Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/Kconfig | 9 -- drivers/usb/serial/Makefile | 1 - drivers/usb/serial/sam-ba.c | 206 -------------------------------------------- 3 files changed, 216 deletions(-) delete mode 100644 drivers/usb/serial/sam-ba.c diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index c2b29761fa9..b71e309116a 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -527,15 +527,6 @@ config USB_SERIAL_SAFE_PADDED bool "USB Secure Encapsulated Driver - Padded" depends on USB_SERIAL_SAFE -config USB_SERIAL_SAMBA - tristate "USB Atmel SAM Boot Assistant (SAM-BA) driver" - help - Say Y here if you want to access the SAM-BA boot application of an - Atmel AT91SAM device. - - To compile this driver as a module, choose M here: the - module will be called sam-ba. - config USB_SERIAL_SIEMENS_MPI tristate "USB Siemens MPI driver" help diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 9a2117f2b06..9e536eefb32 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -48,7 +48,6 @@ obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o obj-$(CONFIG_USB_SERIAL_QCAUX) += qcaux.o obj-$(CONFIG_USB_SERIAL_QUALCOMM) += qcserial.o obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o -obj-$(CONFIG_USB_SERIAL_SAMBA) += sam-ba.o obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o diff --git a/drivers/usb/serial/sam-ba.c b/drivers/usb/serial/sam-ba.c deleted file mode 100644 index e3bba64afc5..00000000000 --- a/drivers/usb/serial/sam-ba.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Atmel SAM Boot Assistant (SAM-BA) driver - * - * Copyright (C) 2010 Johan Hovold - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - - -#define DRIVER_VERSION "v1.0" -#define DRIVER_AUTHOR "Johan Hovold " -#define DRIVER_DESC "Atmel SAM Boot Assistant (SAM-BA) driver" - -#define SAMBA_VENDOR_ID 0x3eb -#define SAMBA_PRODUCT_ID 0x6124 - - -static int debug; - -static const struct usb_device_id id_table[] = { - /* - * NOTE: Only match the CDC Data interface. - */ - { USB_DEVICE_AND_INTERFACE_INFO(SAMBA_VENDOR_ID, SAMBA_PRODUCT_ID, - USB_CLASS_CDC_DATA, 0, 0) }, - { } -}; -MODULE_DEVICE_TABLE(usb, id_table); - -static struct usb_driver samba_driver = { - .name = "sam-ba", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = id_table, - .no_dynamic_id = 1, -}; - - -/* - * NOTE: The SAM-BA firmware cannot handle merged write requests so we cannot - * use the generic write implementation (which uses the port write fifo). - */ -static int samba_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count) -{ - struct urb *urb; - unsigned long flags; - int result; - int i; - - if (!count) - return 0; - - count = min_t(int, count, port->bulk_out_size); - - spin_lock_irqsave(&port->lock, flags); - if (!port->write_urbs_free) { - spin_unlock_irqrestore(&port->lock, flags); - return 0; - } - i = find_first_bit(&port->write_urbs_free, - ARRAY_SIZE(port->write_urbs)); - __clear_bit(i, &port->write_urbs_free); - port->tx_bytes += count; - spin_unlock_irqrestore(&port->lock, flags); - - urb = port->write_urbs[i]; - memcpy(urb->transfer_buffer, buf, count); - urb->transfer_buffer_length = count; - usb_serial_debug_data(debug, &port->dev, __func__, count, - urb->transfer_buffer); - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result) { - dev_err(&port->dev, "%s - error submitting urb: %d\n", - __func__, result); - spin_lock_irqsave(&port->lock, flags); - __set_bit(i, &port->write_urbs_free); - port->tx_bytes -= count; - spin_unlock_irqrestore(&port->lock, flags); - - return result; - } - - return count; -} - -static int samba_write_room(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - unsigned long flags; - unsigned long free; - int count; - int room; - - spin_lock_irqsave(&port->lock, flags); - free = port->write_urbs_free; - spin_unlock_irqrestore(&port->lock, flags); - - count = hweight_long(free); - room = count * port->bulk_out_size; - - dbg("%s - returns %d", __func__, room); - - return room; -} - -static int samba_chars_in_buffer(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - unsigned long flags; - int chars; - - spin_lock_irqsave(&port->lock, flags); - chars = port->tx_bytes; - spin_unlock_irqrestore(&port->lock, flags); - - dbg("%s - returns %d", __func__, chars); - - return chars; -} - -static void samba_write_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - unsigned long flags; - int i; - - dbg("%s - port %d", __func__, port->number); - - for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) { - if (port->write_urbs[i] == urb) - break; - } - spin_lock_irqsave(&port->lock, flags); - __set_bit(i, &port->write_urbs_free); - port->tx_bytes -= urb->transfer_buffer_length; - spin_unlock_irqrestore(&port->lock, flags); - - if (urb->status) - dbg("%s - non-zero urb status: %d", __func__, urb->status); - - usb_serial_port_softint(port); -} - -static struct usb_serial_driver samba_device = { - .driver = { - .owner = THIS_MODULE, - .name = "sam-ba", - }, - .usb_driver = &samba_driver, - .id_table = id_table, - .num_ports = 1, - .bulk_in_size = 512, - .bulk_out_size = 2048, - .write = samba_write, - .write_room = samba_write_room, - .chars_in_buffer = samba_chars_in_buffer, - .write_bulk_callback = samba_write_bulk_callback, - .throttle = usb_serial_generic_throttle, - .unthrottle = usb_serial_generic_unthrottle, -}; - -static int __init samba_init(void) -{ - int retval; - - retval = usb_serial_register(&samba_device); - if (retval) - return retval; - - retval = usb_register(&samba_driver); - if (retval) { - usb_serial_deregister(&samba_device); - return retval; - } - - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ": " - DRIVER_DESC "\n"); - return 0; -} - -static void __exit samba_exit(void) -{ - usb_deregister(&samba_driver); - usb_serial_deregister(&samba_device); -} - -module_init(samba_init); -module_exit(samba_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable verbose debugging messages"); -- cgit v1.2.3 From 3e112662129b48bf8571ee5f7c49a4dbb3b70f04 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 8 Apr 2011 13:22:09 +0900 Subject: ARM: S5P: Add usb ehci device This patch adds usb ehci device definition for samsung s5p cpus. Signed-off-by: Joonyoung Shim Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-exynos4/include/mach/map.h | 3 ++ arch/arm/plat-s5p/Kconfig | 5 ++++ arch/arm/plat-s5p/Makefile | 1 + arch/arm/plat-s5p/dev-ehci.c | 50 +++++++++++++++++++++++++++++++ arch/arm/plat-s5p/include/plat/ehci.h | 21 +++++++++++++ arch/arm/plat-samsung/include/plat/devs.h | 2 ++ 6 files changed, 82 insertions(+) create mode 100644 arch/arm/plat-s5p/dev-ehci.c create mode 100644 arch/arm/plat-s5p/include/plat/ehci.h diff --git a/arch/arm/mach-exynos4/include/mach/map.h b/arch/arm/mach-exynos4/include/mach/map.h index 6330b73b9ea..213c2a27d7c 100644 --- a/arch/arm/mach-exynos4/include/mach/map.h +++ b/arch/arm/mach-exynos4/include/mach/map.h @@ -101,6 +101,8 @@ #define EXYNOS4_PA_SROMC 0x12570000 +#define EXYNOS4_PA_EHCI 0x12580000 + #define EXYNOS4_PA_UART 0x13800000 #define EXYNOS4_PA_IIC(x) (0x13860000 + ((x) * 0x10000)) @@ -143,6 +145,7 @@ #define S5P_PA_SROMC EXYNOS4_PA_SROMC #define S5P_PA_SYSCON EXYNOS4_PA_SYSCON #define S5P_PA_TIMER EXYNOS4_PA_TIMER +#define S5P_PA_EHCI EXYNOS4_PA_EHCI #define SAMSUNG_PA_KEYPAD EXYNOS4_PA_KEYPAD diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig index 84922971658..6751bcf7b88 100644 --- a/arch/arm/plat-s5p/Kconfig +++ b/arch/arm/plat-s5p/Kconfig @@ -85,6 +85,11 @@ config S5P_DEV_CSIS1 help Compile in platform device definitions for MIPI-CSIS channel 1 +config S5P_DEV_USB_EHCI + bool + help + Compile in platform device definition for USB EHCI + config S5P_SETUP_MIPIPHY bool help diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile index 42afff7f60b..e234cc4d49a 100644 --- a/arch/arm/plat-s5p/Makefile +++ b/arch/arm/plat-s5p/Makefile @@ -33,4 +33,5 @@ obj-$(CONFIG_S5P_DEV_FIMC3) += dev-fimc3.o obj-$(CONFIG_S5P_DEV_ONENAND) += dev-onenand.o obj-$(CONFIG_S5P_DEV_CSIS0) += dev-csis0.o obj-$(CONFIG_S5P_DEV_CSIS1) += dev-csis1.o +obj-$(CONFIG_S5P_DEV_USB_EHCI) += dev-ehci.o obj-$(CONFIG_S5P_SETUP_MIPIPHY) += setup-mipiphy.o diff --git a/arch/arm/plat-s5p/dev-ehci.c b/arch/arm/plat-s5p/dev-ehci.c new file mode 100644 index 00000000000..a610e5c6096 --- /dev/null +++ b/arch/arm/plat-s5p/dev-ehci.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +/* USB EHCI Host Controller registration */ +static struct resource s5p_ehci_resource[] = { + [0] = { + .start = S5P_PA_EHCI, + .end = S5P_PA_EHCI + SZ_256 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_USB_HOST, + .end = IRQ_USB_HOST, + .flags = IORESOURCE_IRQ, + } +}; + +static u64 s5p_device_ehci_dmamask = 0xffffffffUL; + +struct platform_device s5p_device_ehci = { + .name = "s5p-ehci", + .id = -1, + .num_resources = ARRAY_SIZE(s5p_ehci_resource), + .resource = s5p_ehci_resource, + .dev = { + .dma_mask = &s5p_device_ehci_dmamask, + .coherent_dma_mask = 0xffffffffUL + } +}; + +void __init s5p_ehci_set_platdata(struct s5p_ehci_platdata *pd) +{ + s3c_set_platdata(pd, sizeof(struct s5p_ehci_platdata), + &s5p_device_ehci); +} diff --git a/arch/arm/plat-s5p/include/plat/ehci.h b/arch/arm/plat-s5p/include/plat/ehci.h new file mode 100644 index 00000000000..6ae6810c756 --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/ehci.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __PLAT_S5P_EHCI_H +#define __PLAT_S5P_EHCI_H + +struct s5p_ehci_platdata { + int (*phy_init)(struct platform_device *pdev, int type); + int (*phy_exit)(struct platform_device *pdev, int type); +}; + +extern void s5p_ehci_set_platdata(struct s5p_ehci_platdata *pd); + +#endif /* __PLAT_S5P_EHCI_H */ diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h index f0da6b70fba..3f38debbb10 100644 --- a/arch/arm/plat-samsung/include/plat/devs.h +++ b/arch/arm/plat-samsung/include/plat/devs.h @@ -142,6 +142,8 @@ extern struct platform_device s5p_device_fimc3; extern struct platform_device s5p_device_mipi_csis0; extern struct platform_device s5p_device_mipi_csis1; +extern struct platform_device s5p_device_ehci; + extern struct platform_device exynos4_device_sysmmu; /* s3c2440 specific devices */ -- cgit v1.2.3 From 8f1d169f999fea892c3fcbf5a79ae8525a477572 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 8 Apr 2011 13:22:10 +0900 Subject: ARM: EXYNOS4: Add usb host phy control EXYNOS4 has 2 phys for usb host and usb device. This patch supports to control usb host phy of EXYNOS4. Signed-off-by: Joonyoung Shim Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-exynos4/Makefile | 2 + arch/arm/mach-exynos4/cpu.c | 7 +- arch/arm/mach-exynos4/include/mach/map.h | 1 + arch/arm/mach-exynos4/include/mach/regs-pmu.h | 3 + arch/arm/mach-exynos4/include/mach/regs-usb-phy.h | 64 ++++++++++ arch/arm/mach-exynos4/usb-phy.c | 136 ++++++++++++++++++++++ arch/arm/plat-s5p/dev-ehci.c | 9 +- arch/arm/plat-s5p/include/plat/map-s5p.h | 2 +- arch/arm/plat-s5p/include/plat/usb-phy.h | 22 ++++ 9 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 arch/arm/mach-exynos4/include/mach/regs-usb-phy.h create mode 100644 arch/arm/mach-exynos4/usb-phy.c create mode 100644 arch/arm/plat-s5p/include/plat/usb-phy.h diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile index 9be104f63c0..777897551e4 100644 --- a/arch/arm/mach-exynos4/Makefile +++ b/arch/arm/mach-exynos4/Makefile @@ -54,3 +54,5 @@ obj-$(CONFIG_EXYNOS4_SETUP_I2C7) += setup-i2c7.o obj-$(CONFIG_EXYNOS4_SETUP_KEYPAD) += setup-keypad.o obj-$(CONFIG_EXYNOS4_SETUP_SDHCI) += setup-sdhci.o obj-$(CONFIG_EXYNOS4_SETUP_SDHCI_GPIO) += setup-sdhci-gpio.o + +obj-$(CONFIG_USB_SUPPORT) += usb-phy.o diff --git a/arch/arm/mach-exynos4/cpu.c b/arch/arm/mach-exynos4/cpu.c index 79301139194..08813a6f66b 100644 --- a/arch/arm/mach-exynos4/cpu.c +++ b/arch/arm/mach-exynos4/cpu.c @@ -97,7 +97,12 @@ static struct map_desc exynos4_iodesc[] __initdata = { .pfn = __phys_to_pfn(EXYNOS4_PA_SROMC), .length = SZ_4K, .type = MT_DEVICE, - }, + }, { + .virtual = (unsigned long)S5P_VA_USB_HSPHY, + .pfn = __phys_to_pfn(EXYNOS4_PA_HSPHY), + .length = SZ_4K, + .type = MT_DEVICE, + } }; static void exynos4_idle(void) diff --git a/arch/arm/mach-exynos4/include/mach/map.h b/arch/arm/mach-exynos4/include/mach/map.h index 213c2a27d7c..0009e77a05f 100644 --- a/arch/arm/mach-exynos4/include/mach/map.h +++ b/arch/arm/mach-exynos4/include/mach/map.h @@ -102,6 +102,7 @@ #define EXYNOS4_PA_SROMC 0x12570000 #define EXYNOS4_PA_EHCI 0x12580000 +#define EXYNOS4_PA_HSPHY 0x125B0000 #define EXYNOS4_PA_UART 0x13800000 diff --git a/arch/arm/mach-exynos4/include/mach/regs-pmu.h b/arch/arm/mach-exynos4/include/mach/regs-pmu.h index 62b0014d05e..a9643371f8e 100644 --- a/arch/arm/mach-exynos4/include/mach/regs-pmu.h +++ b/arch/arm/mach-exynos4/include/mach/regs-pmu.h @@ -33,6 +33,9 @@ #define S5P_EINT_WAKEUP_MASK S5P_PMUREG(0x0604) #define S5P_WAKEUP_MASK S5P_PMUREG(0x0608) +#define S5P_USBHOST_PHY_CONTROL S5P_PMUREG(0x0708) +#define S5P_USBHOST_PHY_ENABLE (1 << 0) + #define S5P_MIPI_DPHY_CONTROL(n) S5P_PMUREG(0x0710 + (n) * 4) #define S5P_MIPI_DPHY_ENABLE (1 << 0) #define S5P_MIPI_DPHY_SRESETN (1 << 1) diff --git a/arch/arm/mach-exynos4/include/mach/regs-usb-phy.h b/arch/arm/mach-exynos4/include/mach/regs-usb-phy.h new file mode 100644 index 00000000000..703118d5173 --- /dev/null +++ b/arch/arm/mach-exynos4/include/mach/regs-usb-phy.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __PLAT_S5P_REGS_USB_PHY_H +#define __PLAT_S5P_REGS_USB_PHY_H + +#define EXYNOS4_HSOTG_PHYREG(x) ((x) + S5P_VA_USB_HSPHY) + +#define EXYNOS4_PHYPWR EXYNOS4_HSOTG_PHYREG(0x00) +#define PHY1_HSIC_NORMAL_MASK (0xf << 9) +#define PHY1_HSIC1_SLEEP (1 << 12) +#define PHY1_HSIC1_FORCE_SUSPEND (1 << 11) +#define PHY1_HSIC0_SLEEP (1 << 10) +#define PHY1_HSIC0_FORCE_SUSPEND (1 << 9) + +#define PHY1_STD_NORMAL_MASK (0x7 << 6) +#define PHY1_STD_SLEEP (1 << 8) +#define PHY1_STD_ANALOG_POWERDOWN (1 << 7) +#define PHY1_STD_FORCE_SUSPEND (1 << 6) + +#define PHY0_NORMAL_MASK (0x39 << 0) +#define PHY0_SLEEP (1 << 5) +#define PHY0_OTG_DISABLE (1 << 4) +#define PHY0_ANALOG_POWERDOWN (1 << 3) +#define PHY0_FORCE_SUSPEND (1 << 0) + +#define EXYNOS4_PHYCLK EXYNOS4_HSOTG_PHYREG(0x04) +#define PHY1_COMMON_ON_N (1 << 7) +#define PHY0_COMMON_ON_N (1 << 4) +#define PHY0_ID_PULLUP (1 << 2) +#define CLKSEL_MASK (0x3 << 0) +#define CLKSEL_SHIFT (0) +#define CLKSEL_48M (0x0 << 0) +#define CLKSEL_12M (0x2 << 0) +#define CLKSEL_24M (0x3 << 0) + +#define EXYNOS4_RSTCON EXYNOS4_HSOTG_PHYREG(0x08) +#define HOST_LINK_PORT_SWRST_MASK (0xf << 6) +#define HOST_LINK_PORT2_SWRST (1 << 9) +#define HOST_LINK_PORT1_SWRST (1 << 8) +#define HOST_LINK_PORT0_SWRST (1 << 7) +#define HOST_LINK_ALL_SWRST (1 << 6) + +#define PHY1_SWRST_MASK (0x7 << 3) +#define PHY1_HSIC_SWRST (1 << 5) +#define PHY1_STD_SWRST (1 << 4) +#define PHY1_ALL_SWRST (1 << 3) + +#define PHY0_SWRST_MASK (0x7 << 0) +#define PHY0_PHYLINK_SWRST (1 << 2) +#define PHY0_HLINK_SWRST (1 << 1) +#define PHY0_SWRST (1 << 0) + +#define EXYNOS4_PHY1CON EXYNOS4_HSOTG_PHYREG(0x34) +#define FPENABLEN (1 << 0) + +#endif /* __PLAT_S5P_REGS_USB_PHY_H */ diff --git a/arch/arm/mach-exynos4/usb-phy.c b/arch/arm/mach-exynos4/usb-phy.c new file mode 100644 index 00000000000..0883c1b824b --- /dev/null +++ b/arch/arm/mach-exynos4/usb-phy.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int exynos4_usb_phy1_init(struct platform_device *pdev) +{ + struct clk *otg_clk; + struct clk *xusbxti_clk; + u32 phyclk; + u32 rstcon; + int err; + + otg_clk = clk_get(&pdev->dev, "otg"); + if (IS_ERR(otg_clk)) { + dev_err(&pdev->dev, "Failed to get otg clock\n"); + return PTR_ERR(otg_clk); + } + + err = clk_enable(otg_clk); + if (err) { + clk_put(otg_clk); + return err; + } + + writel(readl(S5P_USBHOST_PHY_CONTROL) | S5P_USBHOST_PHY_ENABLE, + S5P_USBHOST_PHY_CONTROL); + + /* set clock frequency for PLL */ + phyclk = readl(EXYNOS4_PHYCLK) & ~CLKSEL_MASK; + + xusbxti_clk = clk_get(&pdev->dev, "xusbxti"); + if (xusbxti_clk && !IS_ERR(xusbxti_clk)) { + switch (clk_get_rate(xusbxti_clk)) { + case 12 * MHZ: + phyclk |= CLKSEL_12M; + break; + case 24 * MHZ: + phyclk |= CLKSEL_24M; + break; + default: + case 48 * MHZ: + /* default reference clock */ + break; + } + clk_put(xusbxti_clk); + } + + writel(phyclk, EXYNOS4_PHYCLK); + + /* floating prevention logic: disable */ + writel((readl(EXYNOS4_PHY1CON) | FPENABLEN), EXYNOS4_PHY1CON); + + /* set to normal HSIC 0 and 1 of PHY1 */ + writel((readl(EXYNOS4_PHYPWR) & ~PHY1_HSIC_NORMAL_MASK), + EXYNOS4_PHYPWR); + + /* set to normal standard USB of PHY1 */ + writel((readl(EXYNOS4_PHYPWR) & ~PHY1_STD_NORMAL_MASK), EXYNOS4_PHYPWR); + + /* reset all ports of both PHY and Link */ + rstcon = readl(EXYNOS4_RSTCON) | HOST_LINK_PORT_SWRST_MASK | + PHY1_SWRST_MASK; + writel(rstcon, EXYNOS4_RSTCON); + udelay(10); + + rstcon &= ~(HOST_LINK_PORT_SWRST_MASK | PHY1_SWRST_MASK); + writel(rstcon, EXYNOS4_RSTCON); + udelay(50); + + clk_disable(otg_clk); + clk_put(otg_clk); + + return 0; +} + +static int exynos4_usb_phy1_exit(struct platform_device *pdev) +{ + struct clk *otg_clk; + int err; + + otg_clk = clk_get(&pdev->dev, "otg"); + if (IS_ERR(otg_clk)) { + dev_err(&pdev->dev, "Failed to get otg clock\n"); + return PTR_ERR(otg_clk); + } + + err = clk_enable(otg_clk); + if (err) { + clk_put(otg_clk); + return err; + } + + writel((readl(EXYNOS4_PHYPWR) | PHY1_STD_ANALOG_POWERDOWN), + EXYNOS4_PHYPWR); + + writel(readl(S5P_USBHOST_PHY_CONTROL) & ~S5P_USBHOST_PHY_ENABLE, + S5P_USBHOST_PHY_CONTROL); + + clk_disable(otg_clk); + clk_put(otg_clk); + + return 0; +} + +int s5p_usb_phy_init(struct platform_device *pdev, int type) +{ + if (type == S5P_USB_PHY_HOST) + return exynos4_usb_phy1_init(pdev); + + return -EINVAL; +} + +int s5p_usb_phy_exit(struct platform_device *pdev, int type) +{ + if (type == S5P_USB_PHY_HOST) + return exynos4_usb_phy1_exit(pdev); + + return -EINVAL; +} diff --git a/arch/arm/plat-s5p/dev-ehci.c b/arch/arm/plat-s5p/dev-ehci.c index a610e5c6096..94080fff9e9 100644 --- a/arch/arm/plat-s5p/dev-ehci.c +++ b/arch/arm/plat-s5p/dev-ehci.c @@ -45,6 +45,13 @@ struct platform_device s5p_device_ehci = { void __init s5p_ehci_set_platdata(struct s5p_ehci_platdata *pd) { - s3c_set_platdata(pd, sizeof(struct s5p_ehci_platdata), + struct s5p_ehci_platdata *npd; + + npd = s3c_set_platdata(pd, sizeof(struct s5p_ehci_platdata), &s5p_device_ehci); + + if (!npd->phy_init) + npd->phy_init = s5p_usb_phy_init; + if (!npd->phy_exit) + npd->phy_exit = s5p_usb_phy_exit; } diff --git a/arch/arm/plat-s5p/include/plat/map-s5p.h b/arch/arm/plat-s5p/include/plat/map-s5p.h index d973d39666a..a6c3d327ce7 100644 --- a/arch/arm/plat-s5p/include/plat/map-s5p.h +++ b/arch/arm/plat-s5p/include/plat/map-s5p.h @@ -39,7 +39,7 @@ #define S5P_VA_TWD S5P_VA_COREPERI(0x600) #define S5P_VA_GIC_DIST S5P_VA_COREPERI(0x1000) -#define S3C_VA_USB_HSPHY S3C_ADDR(0x02900000) +#define S5P_VA_USB_HSPHY S3C_ADDR(0x02900000) #define VA_VIC(x) (S3C_VA_IRQ + ((x) * 0x10000)) #define VA_VIC0 VA_VIC(0) diff --git a/arch/arm/plat-s5p/include/plat/usb-phy.h b/arch/arm/plat-s5p/include/plat/usb-phy.h new file mode 100644 index 00000000000..6dd6bcfca3c --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/usb-phy.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __PLAT_S5P_USB_PHY_H +#define __PLAT_S5P_USB_PHY_H + +enum s5p_usb_phy_type { + S5P_USB_PHY_DEVICE, + S5P_USB_PHY_HOST, +}; + +extern int s5p_usb_phy_init(struct platform_device *pdev, int type); +extern int s5p_usb_phy_exit(struct platform_device *pdev, int type); + +#endif /* __PLAT_S5P_REGS_USB_PHY_H */ -- cgit v1.2.3 From 1bcc5aa87f043d34522d783154d08173b435fb46 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 8 Apr 2011 14:08:50 +0900 Subject: USB: Add initial S5P EHCI driver This patch adds host USB high speed driver for samsung S5P series. This is initial driver and we need additional implementation to support some functions like power management. Signed-off-by: Jingoo Han Signed-off-by: Joonyoung Shim Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 1 + drivers/usb/host/Kconfig | 6 ++ drivers/usb/host/ehci-hcd.c | 5 ++ drivers/usb/host/ehci-s5p.c | 201 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 drivers/usb/host/ehci-s5p.c diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index d299906e4f0..cf7614d29f0 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -65,6 +65,7 @@ config USB_ARCH_HAS_EHCI default y if ARCH_CNS3XXX default y if ARCH_VT8500 default y if PLAT_SPEAR + default y if PLAT_S5P default y if ARCH_MSM default PCI diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 5f518ded195..ade009081fe 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -188,6 +188,12 @@ config USB_EHCI_SH Enables support for the on-chip EHCI controller on the SuperH. If you use the PCI EHCI controller, this option is not necessary. +config USB_EHCI_S5P + boolean "S5P EHCI support" + depends on USB_EHCI_HCD && PLAT_S5P + help + Enable support for the S5P SOC's on-chip EHCI controller. + config USB_W90X900_EHCI bool "W90X900(W90P910) EHCI support" depends on USB_EHCI_HCD && ARCH_W90X900 diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 78561d112c0..6b20b3b12d6 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1265,6 +1265,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER tegra_ehci_driver #endif +#ifdef CONFIG_USB_EHCI_S5P +#include "ehci-s5p.c" +#define PLATFORM_DRIVER s5p_ehci_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c new file mode 100644 index 00000000000..0c18f280bf4 --- /dev/null +++ b/drivers/usb/host/ehci-s5p.c @@ -0,0 +1,201 @@ +/* + * SAMSUNG S5P USB HOST EHCI Controller + * + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Author: Jingoo Han + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +struct s5p_ehci_hcd { + struct device *dev; + struct usb_hcd *hcd; + struct clk *clk; +}; + +static const struct hc_driver s5p_ehci_hc_driver = { + .description = hcd_name, + .product_desc = "S5P EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + .reset = ehci_init, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + .get_frame_number = ehci_get_frame, + + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int s5p_ehci_probe(struct platform_device *pdev) +{ + struct s5p_ehci_platdata *pdata; + struct s5p_ehci_hcd *s5p_ehci; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct resource *res; + int irq; + int err; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data defined\n"); + return -EINVAL; + } + + s5p_ehci = kzalloc(sizeof(struct s5p_ehci_hcd), GFP_KERNEL); + if (!s5p_ehci) + return -ENOMEM; + + s5p_ehci->dev = &pdev->dev; + + hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) { + dev_err(&pdev->dev, "Unable to create HCD\n"); + err = -ENOMEM; + goto fail_hcd; + } + + s5p_ehci->clk = clk_get(&pdev->dev, "usbhost"); + + if (IS_ERR(s5p_ehci->clk)) { + dev_err(&pdev->dev, "Failed to get usbhost clock\n"); + err = PTR_ERR(s5p_ehci->clk); + goto fail_clk; + } + + err = clk_enable(s5p_ehci->clk); + if (err) + goto fail_clken; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get I/O memory\n"); + err = -ENXIO; + goto fail_io; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + hcd->regs = ioremap(res->start, resource_size(res)); + if (!hcd->regs) { + dev_err(&pdev->dev, "Failed to remap I/O memory\n"); + err = -ENOMEM; + goto fail_io; + } + + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(&pdev->dev, "Failed to get IRQ\n"); + err = -ENODEV; + goto fail; + } + + if (pdata->phy_init) + pdata->phy_init(pdev, S5P_USB_PHY_HOST); + + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); + + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = readl(&ehci->caps->hcs_params); + + err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (err) { + dev_err(&pdev->dev, "Failed to add USB HCD\n"); + goto fail; + } + + platform_set_drvdata(pdev, s5p_ehci); + + return 0; + +fail: + iounmap(hcd->regs); +fail_io: + clk_disable(s5p_ehci->clk); +fail_clken: + clk_put(s5p_ehci->clk); +fail_clk: + usb_put_hcd(hcd); +fail_hcd: + kfree(s5p_ehci); + return err; +} + +static int s5p_ehci_remove(struct platform_device *pdev) +{ + struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; + struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev); + struct usb_hcd *hcd = s5p_ehci->hcd; + + usb_remove_hcd(hcd); + + if (pdata && pdata->phy_exit) + pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + + iounmap(hcd->regs); + + clk_disable(s5p_ehci->clk); + clk_put(s5p_ehci->clk); + + usb_put_hcd(hcd); + kfree(s5p_ehci); + + return 0; +} + +static void s5p_ehci_shutdown(struct platform_device *pdev) +{ + struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev); + struct usb_hcd *hcd = s5p_ehci->hcd; + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + +static struct platform_driver s5p_ehci_driver = { + .probe = s5p_ehci_probe, + .remove = s5p_ehci_remove, + .shutdown = s5p_ehci_shutdown, + .driver = { + .name = "s5p-ehci", + .owner = THIS_MODULE, + } +}; + +MODULE_ALIAS("platform:s5p-ehci"); -- cgit v1.2.3 From 01da92f7f6436c6c29c11490c7fcdb20fb6c46b8 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 8 Apr 2011 13:22:11 +0900 Subject: ARM: EXYNOS4: Add usb ehci device to the NURI board This patch is to support usb ehci device to the NURI board. Signed-off-by: Joonyoung Shim Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-exynos4/Kconfig | 1 + arch/arm/mach-exynos4/mach-nuri.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig index e849f67be47..805196207ce 100644 --- a/arch/arm/mach-exynos4/Kconfig +++ b/arch/arm/mach-exynos4/Kconfig @@ -170,6 +170,7 @@ config MACH_NURI select S3C_DEV_HSMMC3 select S3C_DEV_I2C1 select S3C_DEV_I2C5 + select S5P_DEV_USB_EHCI select EXYNOS4_SETUP_I2C1 select EXYNOS4_SETUP_I2C5 select EXYNOS4_SETUP_SDHCI diff --git a/arch/arm/mach-exynos4/mach-nuri.c b/arch/arm/mach-exynos4/mach-nuri.c index b79ad010d19..bb5d12f43af 100644 --- a/arch/arm/mach-exynos4/mach-nuri.c +++ b/arch/arm/mach-exynos4/mach-nuri.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include @@ -262,6 +264,16 @@ static struct i2c_board_info i2c5_devs[] __initdata = { /* max8997, To be updated */ }; +/* USB EHCI */ +static struct s5p_ehci_platdata nuri_ehci_pdata; + +static void __init nuri_ehci_init(void) +{ + struct s5p_ehci_platdata *pdata = &nuri_ehci_pdata; + + s5p_ehci_set_platdata(pdata); +} + static struct platform_device *nuri_devices[] __initdata = { /* Samsung Platform Devices */ &emmc_fixed_voltage, @@ -270,6 +282,7 @@ static struct platform_device *nuri_devices[] __initdata = { &s3c_device_hsmmc3, &s3c_device_wdt, &s3c_device_timer[0], + &s5p_device_ehci, /* NURI Devices */ &nuri_gpio_keys, @@ -291,6 +304,9 @@ static void __init nuri_machine_init(void) i2c_register_board_info(1, i2c1_devs, ARRAY_SIZE(i2c1_devs)); i2c_register_board_info(5, i2c5_devs, ARRAY_SIZE(i2c5_devs)); + nuri_ehci_init(); + clk_xusbxti.rate = 24000000; + /* Last */ platform_add_devices(nuri_devices, ARRAY_SIZE(nuri_devices)); } -- cgit v1.2.3 From 502fa84195f47a79d7220470ebaa85a773659755 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 13 Apr 2011 10:54:22 +0200 Subject: USB: ehci: add bus glue for the Atheros AR71XX/AR724X/AR91XX SoCs The Atheros AR71XX/AR91XX SoCs have a built-in EHCI controller. This patch adds the necessary glue code to make the generic EHCI driver usable for them. Signed-off-by: Gabor Juhos Signed-off-by: Imre Kaloz Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- arch/mips/ath79/Kconfig | 3 + drivers/usb/host/Kconfig | 9 ++ drivers/usb/host/ehci-ath79.c | 200 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci-hcd.c | 5 ++ 4 files changed, 217 insertions(+) create mode 100644 drivers/usb/host/ehci-ath79.c diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig index b05828260f7..649a2a3bde7 100644 --- a/arch/mips/ath79/Kconfig +++ b/arch/mips/ath79/Kconfig @@ -26,12 +26,15 @@ config ATH79_MACH_PB44 endmenu config SOC_AR71XX + select USB_ARCH_HAS_EHCI def_bool n config SOC_AR724X + select USB_ARCH_HAS_EHCI def_bool n config SOC_AR913X + select USB_ARCH_HAS_EHCI def_bool n config ATH79_DEV_AR913X_WMAC diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index ade009081fe..da3757cec7a 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -208,6 +208,15 @@ config USB_CNS3XXX_EHCI It is needed for high-speed (480Mbit/sec) USB 2.0 device support. +config USB_EHCI_ATH79 + bool "EHCI support for AR7XXX/AR9XXX SoCs" + depends on USB_EHCI_HCD && (SOC_AR71XX || SOC_AR724X || SOC_AR913X) + select USB_EHCI_ROOT_HUB_TT + default y + ---help--- + Enables support for the built-in EHCI controller present + on the Atheros AR7XXX/AR9XXX SoCs. + config USB_OXU210HP_HCD tristate "OXU210HP HCD support" depends on USB diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c new file mode 100644 index 00000000000..74325b87bd7 --- /dev/null +++ b/drivers/usb/host/ehci-ath79.c @@ -0,0 +1,200 @@ +/* + * Bus Glue for Atheros AR7XXX/AR9XXX built-in EHCI controller. + * + * Copyright (C) 2008-2011 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * Parts of this file are based on Atheros' 2.6.15 BSP + * Copyright (C) 2007 Atheros Communications, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include + +enum { + EHCI_ATH79_IP_V1 = 0, + EHCI_ATH79_IP_V2, +}; + +static const struct platform_device_id ehci_ath79_id_table[] = { + { + .name = "ar71xx-ehci", + .driver_data = EHCI_ATH79_IP_V1, + }, + { + .name = "ar724x-ehci", + .driver_data = EHCI_ATH79_IP_V2, + }, + { + .name = "ar913x-ehci", + .driver_data = EHCI_ATH79_IP_V2, + }, + { + /* terminating entry */ + }, +}; + +MODULE_DEVICE_TABLE(platform, ehci_ath79_id_table); + +static int ehci_ath79_init(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct platform_device *pdev = to_platform_device(hcd->self.controller); + const struct platform_device_id *id; + int ret; + + id = platform_get_device_id(pdev); + if (!id) { + dev_err(hcd->self.controller, "missing device id\n"); + return -EINVAL; + } + + switch (id->driver_data) { + case EHCI_ATH79_IP_V1: + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + break; + + case EHCI_ATH79_IP_V2: + hcd->has_tt = 1; + + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + break; + + default: + BUG(); + } + + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + ehci->sbrn = 0x20; + + ehci_reset(ehci); + + ret = ehci_init(hcd); + if (ret) + return ret; + + ehci_port_power(ehci, 0); + + return 0; +} + +static const struct hc_driver ehci_ath79_hc_driver = { + .description = hcd_name, + .product_desc = "Atheros built-in EHCI controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + .reset = ehci_ath79_init, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + .get_frame_number = ehci_get_frame, + + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int ehci_ath79_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct resource *res; + int irq; + int ret; + + if (usb_disabled()) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_dbg(&pdev->dev, "no IRQ specified\n"); + return -ENODEV; + } + irq = res->start; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_dbg(&pdev->dev, "no base address specified\n"); + return -ENODEV; + } + + hcd = usb_create_hcd(&ehci_ath79_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + ret = -EBUSY; + goto err_put_hcd; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + ret = -EFAULT; + goto err_release_region; + } + + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (ret) + goto err_iounmap; + + return 0; + +err_iounmap: + iounmap(hcd->regs); + +err_release_region: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_put_hcd: + usb_put_hcd(hcd); + return ret; +} + +static int ehci_ath79_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + return 0; +} + +static struct platform_driver ehci_ath79_driver = { + .probe = ehci_ath79_probe, + .remove = ehci_ath79_remove, + .id_table = ehci_ath79_id_table, + .driver = { + .owner = THIS_MODULE, + .name = "ath79-ehci", + } +}; + +MODULE_ALIAS(PLATFORM_MODULE_PREFIX "ath79-ehci"); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 6b20b3b12d6..83b7d5f02a1 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1270,6 +1270,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER s5p_ehci_driver #endif +#ifdef CONFIG_USB_EHCI_ATH79 +#include "ehci-ath79.c" +#define PLATFORM_DRIVER ehci_ath79_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) -- cgit v1.2.3 From 2f7ac6c199978d0a0e407a12534201aa675a6482 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 13 Apr 2011 10:54:23 +0200 Subject: USB: ehci: add workaround for Synopsys HC bug A Synopsys USB core used in various SoCs has a bug which might cause that the host controller not issuing ping. When software uses the Doorbell mechanism to remove queue heads, the host controller still has references to the removed queue head even after indicating an Interrupt on Async Advance. This happens if the last executed queue head's Next Link queue head is removed. Consequences of the defect: The Host controller fetches the removed queue head, using memory that would otherwise be deallocated.This results in incorrect transactions on both the USB and system memory. This may result in undefined behavior. Workarounds: 1) If no queue head is active (no Status field's Active bit is set) after removing the queue heads, the software can write one of the valid queue head addresses to the ASYNCLISTADDR register and deallocate the removed queue head's memory after 2 microframes. If one or more of the queue heads is active (the Active bit is set in the Status field) after removing the queue heads, the software can delay memory deallocation after time X, where X is the time required for the Host Controller to go through all the queue heads once. X varies with the number of queue heads and the time required to process periodic transactions: if more periodic transactions must be performed, the Host Controller has less time to process asynchronous transaction processing. 2) Do not use the Doorbell mechanism to remove the queue heads. Disable the Asynchronous Schedule Enable bit instead. The bug has been discussed on the linux-usb-devel mailing-list four years ago, the original thread can be found here: http://www.mail-archive.com/linux-usb-devel@lists.sourceforge.net/msg45345.html This patch implements the first workaround as suggested by David Brownell. The built-in USB host controller of the Atheros AR7130/AR7141/AR7161 SoCs requires this to work properly. Signed-off-by: Gabor Juhos Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-ath79.c | 2 ++ drivers/usb/host/ehci-q.c | 4 ++++ drivers/usb/host/ehci.h | 1 + 3 files changed, 7 insertions(+) diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c index 74325b87bd7..7ea23b50f5d 100644 --- a/drivers/usb/host/ehci-ath79.c +++ b/drivers/usb/host/ehci-ath79.c @@ -54,6 +54,8 @@ static int ehci_ath79_init(struct usb_hcd *hcd) switch (id->driver_data) { case EHCI_ATH79_IP_V1: + ehci->has_synopsys_hc_bug = 1; + ehci->caps = hcd->regs; ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 98ded66e8d3..6582aeab623 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1183,6 +1183,10 @@ static void end_unlink_async (struct ehci_hcd *ehci) ehci->reclaim = NULL; start_unlink_async (ehci, next); } + + if (ehci->has_synopsys_hc_bug) + ehci_writel(ehci, (u32) ehci->async->qh_dma, + &ehci->regs->async_next); } /* makes sure the async qh will become idle */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 333ddc15691..168f1a88c4d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -134,6 +134,7 @@ struct ehci_hcd { /* one per controller */ unsigned amd_pll_fix:1; unsigned fs_i_thresh:1; /* Intel iso scheduling */ unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ + unsigned has_synopsys_hc_bug:1; /* Synopsys HC */ /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) -- cgit v1.2.3 From 90e6ca5cda8a38b7bb53660e67eff0845c0abe3f Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 13 Apr 2011 10:54:24 +0200 Subject: USB: ohci: add bus glue for the Atheros AR71XX/AR7240 SoCs The Atheros AR71XX/AR7240 SoCs have a built-in OHCI controller. This patch adds the necessary glue code to make the generic OHCI driver usable for them. Signed-off-by: Gabor Juhos Signed-off-by: Imre Kaloz Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- arch/mips/ath79/Kconfig | 2 + drivers/usb/host/Kconfig | 8 +++ drivers/usb/host/ohci-ath79.c | 151 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ohci-hcd.c | 5 ++ 4 files changed, 166 insertions(+) create mode 100644 drivers/usb/host/ohci-ath79.c diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig index 649a2a3bde7..47707410582 100644 --- a/arch/mips/ath79/Kconfig +++ b/arch/mips/ath79/Kconfig @@ -27,10 +27,12 @@ endmenu config SOC_AR71XX select USB_ARCH_HAS_EHCI + select USB_ARCH_HAS_OHCI def_bool n config SOC_AR724X select USB_ARCH_HAS_EHCI + select USB_ARCH_HAS_OHCI def_bool n config SOC_AR913X diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index da3757cec7a..fe4beca0000 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -302,6 +302,14 @@ config USB_OHCI_HCD_OMAP3 Enables support for the on-chip OHCI controller on OMAP3 and later chips. +config USB_OHCI_ATH79 + bool "USB OHCI support for the Atheros AR71XX/AR7240 SoCs" + depends on USB_OHCI_HCD && (SOC_AR71XX || SOC_AR724X) + default y + help + Enables support for the built-in OHCI controller present on the + Atheros AR71XX/AR7240 SoCs. + config USB_OHCI_HCD_PPC_SOC bool "OHCI support for on-chip PPC USB controller" depends on USB_OHCI_HCD && (STB03xxx || PPC_MPC52xx) diff --git a/drivers/usb/host/ohci-ath79.c b/drivers/usb/host/ohci-ath79.c new file mode 100644 index 00000000000..ffea3e7cb0a --- /dev/null +++ b/drivers/usb/host/ohci-ath79.c @@ -0,0 +1,151 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * Bus Glue for Atheros AR71XX/AR724X built-in OHCI controller. + * + * Copyright (C) 2008-2011 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * Parts of this file are based on Atheros' 2.6.15 BSP + * Copyright (C) 2007 Atheros Communications, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include + +static int __devinit ohci_ath79_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + ret = ohci_init(ohci); + if (ret < 0) + return ret; + + ret = ohci_run(ohci); + if (ret < 0) + goto err; + + return 0; + +err: + ohci_stop(hcd); + return ret; +} + +static const struct hc_driver ohci_ath79_hc_driver = { + .description = hcd_name, + .product_desc = "Atheros built-in OHCI controller", + .hcd_priv_size = sizeof(struct ohci_hcd), + + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + .start = ohci_ath79_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + .start_port_reset = ohci_start_port_reset, +}; + +static int ohci_ath79_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct resource *res; + int irq; + int ret; + + if (usb_disabled()) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_dbg(&pdev->dev, "no IRQ specified\n"); + return -ENODEV; + } + irq = res->start; + + hcd = usb_create_hcd(&ohci_ath79_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_dbg(&pdev->dev, "no base address specified\n"); + ret = -ENODEV; + goto err_put_hcd; + } + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + ret = -EBUSY; + goto err_put_hcd; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + ret = -EFAULT; + goto err_release_region; + } + + ohci_hcd_init(hcd_to_ohci(hcd)); + + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + if (ret) + goto err_stop_hcd; + + return 0; + +err_stop_hcd: + iounmap(hcd->regs); +err_release_region: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_put_hcd: + usb_put_hcd(hcd); + return ret; +} + +static int ohci_ath79_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + return 0; +} + +static struct platform_driver ohci_hcd_ath79_driver = { + .probe = ohci_ath79_probe, + .remove = ohci_ath79_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "ath79-ohci", + .owner = THIS_MODULE, + }, +}; + +MODULE_ALIAS(PLATFORM_MODULE_PREFIX "ath79-ohci"); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index d5572351486..8c8dc6559ac 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1105,6 +1105,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ohci_hcd_cns3xxx_driver #endif +#ifdef CONFIG_USB_OHCI_ATH79 +#include "ohci-ath79.c" +#define PLATFORM_DRIVER ohci_hcd_ath79_driver +#endif + #if !defined(PCI_DRIVER) && \ !defined(PLATFORM_DRIVER) && \ !defined(OMAP1_PLATFORM_DRIVER) && \ -- cgit v1.2.3 From db8fa2852ed5b46c05148db87be135300f17b8ce Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Thu, 14 Apr 2011 00:37:00 +0200 Subject: usb: gadget: storage_common: use kstrto*() This commit replaces the usage of strict_strtoul() (which became deprecated after commit 33ee3b2e) with kstrtouint(). Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/storage_common.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 3179b8bb6ce..86fcebd89ab 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -704,10 +704,11 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, ssize_t rc = count; struct fsg_lun *curlun = fsg_lun_from_dev(dev); struct rw_semaphore *filesem = dev_get_drvdata(dev); - unsigned long ro; + unsigned ro; - if (strict_strtoul(buf, 2, &ro)) - return -EINVAL; + rc = kstrtouint(buf, 2, &ro); + if (rc) + return rc; /* * Allow the write-enable status to change only while the @@ -731,10 +732,12 @@ static ssize_t fsg_store_nofua(struct device *dev, const char *buf, size_t count) { struct fsg_lun *curlun = fsg_lun_from_dev(dev); - unsigned long nofua; + unsigned nofua; + int ret; - if (strict_strtoul(buf, 2, &nofua)) - return -EINVAL; + ret = kstrtouint(buf, 2, &nofua); + if (ret) + return ret; /* Sync data when switching from async mode to sync */ if (!nofua && curlun->nofua) -- cgit v1.2.3 From 98346f7db014614a4814eb60639f651f8bbc591d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 14 Apr 2011 13:42:46 -0700 Subject: Revert "usb: usb_storage: do not align length of request for CBW to maxp size" This reverts commit 806e8f8fcc27e1753947bd9f059ba2316cf8f92a. To quote Alan Stern: The necessity for this patch has been under discussion. It turns out the UDC that Mian has been working on and Felipe's UDC have contradictory requirements. Mian's UDC driver wants a bulk-OUT transfer length to be shorter than the maxpacket size if a short packet is expected, whereas Felipe's UDC hardware always needs bulk-OUT transfer lengths to be evenly divisible by the maxpacket size. Mian has agreed to go back over the driver to resolve this conflict. This means we probably will not want this patch after all. (In fact, we may ultimately decide to change the gadget framework to require that bulk-OUT transfer lengths _always_ be divisible by the maxpacket size -- only the g_file_storage and g_mass_storage gadgets would need to be changed.) Cc: Mian Yousaf Kaukab Cc: Michal Nazarewicz Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 24 ++++++++++++++++++++---- drivers/usb/gadget/file_storage.c | 28 ++++++++++++++++++++++------ drivers/usb/gadget/storage_common.c | 7 +++++++ 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 7d95a2cf58a..01ae27b60d4 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -474,6 +474,20 @@ static int exception_in_progress(struct fsg_common *common) return common->state > FSG_STATE_IDLE; } +/* Make bulk-out requests be divisible by the maxpacket size */ +static void set_bulk_out_req_length(struct fsg_common *common, + struct fsg_buffhd *bh, unsigned int length) +{ + unsigned int rem; + + bh->bulk_out_intended_length = length; + rem = length % common->bulk_out_maxpacket; + if (rem > 0) + length += common->bulk_out_maxpacket - rem; + bh->outreq->length = length; +} + + /*-------------------------------------------------------------------------*/ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) @@ -572,9 +586,9 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) struct fsg_buffhd *bh = req->context; dump_msg(common, "bulk-out", req->buf, req->actual); - if (req->status || req->actual != req->length) + if (req->status || req->actual != bh->bulk_out_intended_length) DBG(common, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, req->length); + req->status, req->actual, bh->bulk_out_intended_length); if (req->status == -ECONNRESET) /* Request was cancelled */ usb_ep_fifo_flush(ep); @@ -966,6 +980,7 @@ static int do_write(struct fsg_common *common) * the bulk-out maxpacket size */ bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; bh->outreq->short_not_ok = 1; if (!start_out_transfer(common, bh)) /* Dunno what to do if common->fsg is NULL */ @@ -1611,6 +1626,7 @@ static int throw_away_data(struct fsg_common *common) * the bulk-out maxpacket size. */ bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; bh->outreq->short_not_ok = 1; if (!start_out_transfer(common, bh)) /* Dunno what to do if common->fsg is NULL */ @@ -2279,8 +2295,8 @@ static int get_next_command(struct fsg_common *common) } /* Queue a request to read a Bulk-only CBW */ - bh->outreq->length = USB_BULK_CB_WRAP_LEN; - bh->outreq->short_not_ok = 0; + set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN); + bh->outreq->short_not_ok = 1; if (!start_out_transfer(common, bh)) /* Don't know what to do if common->fsg is NULL */ return -EIO; diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index aebfb81f3ba..fcfc77c7ad7 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -497,6 +497,19 @@ static int exception_in_progress(struct fsg_dev *fsg) return (fsg->state > FSG_STATE_IDLE); } +/* Make bulk-out requests be divisible by the maxpacket size */ +static void set_bulk_out_req_length(struct fsg_dev *fsg, + struct fsg_buffhd *bh, unsigned int length) +{ + unsigned int rem; + + bh->bulk_out_intended_length = length; + rem = length % fsg->bulk_out_maxpacket; + if (rem > 0) + length += fsg->bulk_out_maxpacket - rem; + bh->outreq->length = length; +} + static struct fsg_dev *the_fsg; static struct usb_gadget_driver fsg_driver; @@ -717,9 +730,10 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) struct fsg_buffhd *bh = req->context; dump_msg(fsg, "bulk-out", req->buf, req->actual); - if (req->status || req->actual != req->length) + if (req->status || req->actual != bh->bulk_out_intended_length) DBG(fsg, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, req->length); + req->status, req->actual, + bh->bulk_out_intended_length); if (req->status == -ECONNRESET) // Request was cancelled usb_ep_fifo_flush(ep); @@ -1335,7 +1349,8 @@ static int do_write(struct fsg_dev *fsg) /* amount is always divisible by 512, hence by * the bulk-out maxpacket size */ - bh->outreq->length = amount; + bh->outreq->length = bh->bulk_out_intended_length = + amount; bh->outreq->short_not_ok = 1; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); @@ -1964,7 +1979,8 @@ static int throw_away_data(struct fsg_dev *fsg) /* amount is always divisible by 512, hence by * the bulk-out maxpacket size */ - bh->outreq->length = amount; + bh->outreq->length = bh->bulk_out_intended_length = + amount; bh->outreq->short_not_ok = 1; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); @@ -2643,8 +2659,8 @@ static int get_next_command(struct fsg_dev *fsg) } /* Queue a request to read a Bulk-only CBW */ - bh->outreq->length = USB_BULK_CB_WRAP_LEN; - bh->outreq->short_not_ok = 0; + set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); + bh->outreq->short_not_ok = 1; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 86fcebd89ab..109635a8488 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -286,6 +286,13 @@ struct fsg_buffhd { enum fsg_buffer_state state; struct fsg_buffhd *next; + /* + * The NetChip 2280 is faster, and handles some protocol faults + * better, if we don't submit any short bulk-out read requests. + * So we will record the intended request length here. + */ + unsigned int bulk_out_intended_length; + struct usb_request *inreq; int inreq_busy; struct usb_request *outreq; -- cgit v1.2.3 From 4eff0b40a7174896b860312910e0db51f2dcc567 Mon Sep 17 00:00:00 2001 From: Craig Shelley Date: Sun, 20 Mar 2011 13:51:13 +0000 Subject: USB: CP210x Add 4 Device IDs for AC-Services Devices This patch adds 4 device IDs for CP2102 based devices manufactured by AC-Services. See http://www.ac-services.eu for further info. Signed-off-by: Craig Shelley Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 0f11afdda13..ebeccb75558 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -112,6 +112,10 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */ { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ { USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */ + { USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */ + { USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */ + { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */ + { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */ -- cgit v1.2.3 From 3938a0b32dc12229e76735679b37095bc2bc1578 Mon Sep 17 00:00:00 2001 From: Elizabeth Jennifer Myers Date: Sat, 16 Apr 2011 14:49:51 -0400 Subject: USB: moto_modem: Add USB identifier for the Motorola VE240. Tested on my phone, the ttyUSB device is created and is fully functional. Signed-off-by: Elizabeth Jennifer Myers Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/moto_modem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/moto_modem.c b/drivers/usb/serial/moto_modem.c index 653465f61d4..e2bfecc4640 100644 --- a/drivers/usb/serial/moto_modem.c +++ b/drivers/usb/serial/moto_modem.c @@ -25,6 +25,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x05c6, 0x3197) }, /* unknown Motorola phone */ { USB_DEVICE(0x0c44, 0x0022) }, /* unknown Mororola phone */ { USB_DEVICE(0x22b8, 0x2a64) }, /* Motorola KRZR K1m */ + { USB_DEVICE(0x22b8, 0x2c84) }, /* Motorola VE240 phone */ { USB_DEVICE(0x22b8, 0x2c64) }, /* Motorola V950 phone */ { }, }; -- cgit v1.2.3 From 37909fe588c9e09ab57cd267e98678a17ceda64a Mon Sep 17 00:00:00 2001 From: Benedek László Date: Wed, 20 Apr 2011 03:22:21 +0200 Subject: USB: serial: ftdi_sio: adding support for TavIR STK500 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding support for the TavIR STK500 (id 0403:FA33) Atmel AVR programmer device based on FTDI FT232RL. Signed-off-by: Benedek László Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio_ids.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 4de6ef0ae52..e8dbde55f6c 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -566,6 +566,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_IBS_APP70_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) }, /* * ELV devices: */ diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index efffc23723b..1d946cd238b 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -491,6 +491,11 @@ /* www.canusb.com Lawicel CANUSB device (FTDI_VID) */ #define FTDI_CANUSB_PID 0xFFA8 /* Product Id */ +/* + * TavIR AVR product ids (FTDI_VID) + */ +#define FTDI_TAVIR_STK500_PID 0xFA33 /* STK500 AVR programmer */ + /********************************/ -- cgit v1.2.3 From 7701846fd52f86dffe50715e0e63154088b7c982 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 26 Apr 2011 19:08:36 +0200 Subject: USB: gadget: g_multi: fixed vendor and product ID in inf files Commit 1c6529e92b "USB: gadget: g_multi: fixed vendor and product ID" replaced g_multi's vendor and product ID with proper ID's from Linux Foundation. This commit now updates INF files in the Documentation/usb directory which were omitted in the original commit. Signed-off-by: Michal Nazarewicz Cc: stable Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/linux-cdc-acm.inf | 4 ++-- Documentation/usb/linux.inf | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/usb/linux-cdc-acm.inf b/Documentation/usb/linux-cdc-acm.inf index 612e7220fb2..37a02ce5484 100644 --- a/Documentation/usb/linux-cdc-acm.inf +++ b/Documentation/usb/linux-cdc-acm.inf @@ -90,10 +90,10 @@ ServiceBinary=%12%\USBSER.sys [SourceDisksFiles] [SourceDisksNames] [DeviceList] -%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_0525&PID_A4AB&MI_02 +%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_1D6B&PID_0104&MI_02 [DeviceList.NTamd64] -%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_0525&PID_A4AB&MI_02 +%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_1D6B&PID_0104&MI_02 ;------------------------------------------------------------------------------ diff --git a/Documentation/usb/linux.inf b/Documentation/usb/linux.inf index 4dee9585122..4ffa715b0ae 100644 --- a/Documentation/usb/linux.inf +++ b/Documentation/usb/linux.inf @@ -18,15 +18,15 @@ DriverVer = 06/21/2006,6.0.6000.16384 ; Decoration for x86 architecture [LinuxDevices.NTx86] -%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00 +%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_1d6b&PID_0104&MI_00 ; Decoration for x64 architecture [LinuxDevices.NTamd64] -%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00 +%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_1d6b&PID_0104&MI_00 ; Decoration for ia64 architecture [LinuxDevices.NTia64] -%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00 +%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_1d6b&PID_0104&MI_00 ;@@@ This is the common setting for setup [ControlFlags] -- cgit v1.2.3 From b4026c4584cd70858d4d3450abfb1cd0714d4f32 Mon Sep 17 00:00:00 2001 From: Hermann Kneissel Date: Fri, 29 Apr 2011 08:58:43 +0200 Subject: USB: gamin_gps: Fix for data transfer problems in native mode This patch fixes a problem where data received from the gps is sometimes transferred incompletely to the serial port. If used in native mode now all data received via the bulk queue will be forwarded to the serial port. Signed-off-by: Hermann Kneissel Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/garmin_gps.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 26710b18991..b0a7a9e909a 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1,7 +1,7 @@ /* * Garmin GPS driver * - * Copyright (C) 2006-2009 Hermann Kneissel herkne@users.sourceforge.net + * Copyright (C) 2006-2011 Hermann Kneissel herkne@gmx.de * * The latest version of the driver can be found at * http://sourceforge.net/projects/garmin-gps/ @@ -51,7 +51,7 @@ static int debug; */ #define VERSION_MAJOR 0 -#define VERSION_MINOR 33 +#define VERSION_MINOR 36 #define _STR(s) #s #define _DRIVER_VERSION(a, b) "v" _STR(a) "." _STR(b) @@ -410,6 +410,7 @@ static int gsp_send_ack(struct garmin_data *garmin_data_p, __u8 pkt_id) */ static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count) { + unsigned long flags; const __u8 *recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET; __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer; @@ -458,7 +459,9 @@ static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count) /* if this was an abort-transfer command, flush all queued data. */ if (isAbortTrfCmnd(garmin_data_p->inbuffer)) { + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_DROP_DATA; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); pkt_clear(garmin_data_p); } @@ -943,7 +946,7 @@ static int garmin_open(struct tty_struct *tty, struct usb_serial_port *port) spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->mode = initial_mode; garmin_data_p->count = 0; - garmin_data_p->flags = 0; + garmin_data_p->flags &= FLAGS_SESSION_REPLY1_SEEN; spin_unlock_irqrestore(&garmin_data_p->lock, flags); /* shutdown any bulk reads that might be going on */ @@ -1178,7 +1181,8 @@ static int garmin_write_room(struct tty_struct *tty) static void garmin_read_process(struct garmin_data *garmin_data_p, - unsigned char *data, unsigned data_length) + unsigned char *data, unsigned data_length, + int bulk_data) { unsigned long flags; @@ -1193,7 +1197,8 @@ static void garmin_read_process(struct garmin_data *garmin_data_p, send it directly to the tty port */ if (garmin_data_p->flags & FLAGS_QUEUING) { pkt_add(garmin_data_p, data, data_length); - } else if (getLayerId(data) == GARMIN_LAYERID_APPL) { + } else if (bulk_data || + getLayerId(data) == GARMIN_LAYERID_APPL) { spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= APP_RESP_SEEN; @@ -1237,7 +1242,7 @@ static void garmin_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - garmin_read_process(garmin_data_p, data, urb->actual_length); + garmin_read_process(garmin_data_p, data, urb->actual_length, 1); if (urb->actual_length == 0 && 0 != (garmin_data_p->flags & FLAGS_BULK_IN_RESTART)) { @@ -1346,7 +1351,7 @@ static void garmin_read_int_callback(struct urb *urb) __func__, garmin_data_p->serial_num); } - garmin_read_process(garmin_data_p, data, urb->actual_length); + garmin_read_process(garmin_data_p, data, urb->actual_length, 0); port->interrupt_in_urb->dev = port->serial->dev; retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -1461,6 +1466,7 @@ static int garmin_attach(struct usb_serial *serial) garmin_data_p->timer.function = timeout_handler; garmin_data_p->port = port; garmin_data_p->state = 0; + garmin_data_p->flags = 0; garmin_data_p->count = 0; usb_set_serial_port_data(port, garmin_data_p); -- cgit v1.2.3 From d771d8aa37805f5bf75808b12b737f39ee97f5d4 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 29 Apr 2011 14:12:21 +0200 Subject: USB: cdc-wdm: reset handling according to new requirements This patch - ensures no IO takes place during resets - reports resets to user space Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-wdm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index a97c018dd41..2b9ff518b50 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -542,6 +542,8 @@ static int wdm_open(struct inode *inode, struct file *file) mutex_lock(&desc->lock); if (!desc->count++) { + desc->werr = 0; + desc->rerr = 0; rv = usb_submit_urb(desc->validity, GFP_KERNEL); if (rv < 0) { desc->count--; @@ -853,6 +855,18 @@ static int wdm_pre_reset(struct usb_interface *intf) struct wdm_device *desc = usb_get_intfdata(intf); mutex_lock(&desc->lock); + kill_urbs(desc); + + /* + * we notify everybody using poll of + * an exceptional situation + * must be done before recovery lest a spontaneous + * message from the device is lost + */ + spin_lock_irq(&desc->iuspin); + desc->rerr = -EINTR; + spin_unlock_irq(&desc->iuspin); + wake_up_all(&desc->wait); return 0; } -- cgit v1.2.3 From 15b2f3204a5c878c32939094775fb7349f707263 Mon Sep 17 00:00:00 2001 From: "Marius B. Kotsbak" Date: Mon, 21 Mar 2011 23:27:21 +0100 Subject: Bind only modem AT command endpoint to option module. Network interface is handled by upcoming gt_b3730 module. Removed "GT-B3710" from comment, it is another modem with another USB ID. Signed-off-by: Marius B. Kotsbak Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index d77ff043589..4001f7630a5 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -972,7 +972,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100) }, { USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */ { USB_DEVICE(ONDA_VENDOR_ID, ONDA_MT825UP) }, /* ONDA MT825UP modem */ - { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730/GT-B3710 LTE USB modem.*/ + { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730 LTE USB modem.*/ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); @@ -1109,6 +1109,12 @@ static int option_probe(struct usb_serial *serial, serial->interface->cur_altsetting->desc.bInterfaceNumber == 1) return -ENODEV; + /* Don't bind network interface on Samsung GT-B3730, it is handled by a separate module */ + if (serial->dev->descriptor.idVendor == SAMSUNG_VENDOR_ID && + serial->dev->descriptor.idProduct == SAMSUNG_PRODUCT_GT_B3730 && + serial->interface->cur_altsetting->desc.bInterfaceClass != USB_CLASS_CDC_DATA) + return -ENODEV; + data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) -- cgit v1.2.3 From 6498d9db6d2dad4cf5deb2dd09e0816904f41ca5 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 28 Apr 2011 10:45:24 -0400 Subject: USB: documentation update for the pre_reset method This patch (as1459) updates the documentation for the pre_reset method in struct usb_driver. When a driver is notified of an impending reset, it must cancel all outstanding I/O and not start any new I/O until it has been notified that the reset is complete. As far as I know, most existing drivers that implement pre_reset do this now. The major exceptions appear to be the SpeedTouch and CDC-WDM drivers. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/callbacks.txt | 8 +++++--- include/linux/usb.h | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Documentation/usb/callbacks.txt b/Documentation/usb/callbacks.txt index bfb36b34b79..9e85846bdb9 100644 --- a/Documentation/usb/callbacks.txt +++ b/Documentation/usb/callbacks.txt @@ -95,9 +95,11 @@ pre_reset int (*pre_reset)(struct usb_interface *intf); -Another driver or user space is triggering a reset on the device which -contains the interface passed as an argument. Cease IO and save any -device state you need to restore. +A driver or user space is triggering a reset on the device which +contains the interface passed as an argument. Cease IO, wait for all +outstanding URBs to complete, and save any device state you need to +restore. No more URBs may be submitted until the post_reset method +is called. If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if you are in atomic context. diff --git a/include/linux/usb.h b/include/linux/usb.h index 65f78ca5d88..73c7df48960 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -806,8 +806,10 @@ struct usbdrv_wrap { * @resume: Called when the device is being resumed by the system. * @reset_resume: Called when the suspended device has been reset instead * of being resumed. - * @pre_reset: Called by usb_reset_device() when the device - * is about to be reset. + * @pre_reset: Called by usb_reset_device() when the device is about to be + * reset. This routine must not return until the driver has no active + * URBs for the device, and no more URBs may be submitted until the + * post_reset method is called. * @post_reset: Called by usb_reset_device() after the device * has been reset * @id_table: USB drivers use ID table to support hotplugging. -- cgit v1.2.3 From c89d2613b8e13c51a6efe23196fdc509984687e7 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 14 Apr 2011 13:57:06 +0900 Subject: usb: renesas_usbhs: fixup cast warning This patch fixup cast warning on 64 bit compiler Signed-off-by: Kuninori Morimoto Reported-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/pipe.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 4a60dcef967..1cca9b7fb26 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -93,8 +93,7 @@ void usbhs_pipe_init(struct usbhs_priv *priv); int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe); -#define usbhs_pipe_number(p) (((u32)(p) - (u32)(p)->priv->pipe_info.pipe) / \ - sizeof(struct usbhs_pipe)) +#define usbhs_pipe_number(p) (int)((p) - (p)->priv->pipe_info.pipe) /* * dcp control -- cgit v1.2.3 From 25a73c6ce7f3b7b667a11a116d9607d6d5bf5cab Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 15 Apr 2011 14:19:46 +0900 Subject: usb: renesas_usbhs: Driver is depend on SuperH Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig index 481490e5500..b2e64918884 100644 --- a/drivers/usb/renesas_usbhs/Kconfig +++ b/drivers/usb/renesas_usbhs/Kconfig @@ -4,6 +4,7 @@ config USB_RENESAS_USBHS tristate 'Renesas USBHS controller' + depends on SUPERH || ARCH_SHMOBILE default n help Renesas USBHS is a discrete USB host and peripheral controller chip -- cgit v1.2.3 From 1744020ceb86c83d8116d28c7bdd221d071ca213 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Wed, 23 Mar 2011 21:36:17 +0100 Subject: USB: dbgp gadget: fix return value of dbgp_setup Current code returns 0 even if it can't handle the request. This leads to timeouts when an unhandled request is sent: Bus 001 Device 003: ID 0525:c0de Netchip Technology, Inc. Device Descriptor: [..] can't get device qualifier: Connection timed out [..] change the code to return EOPNOTSUPP in such cases. Signed-off-by: Sven Schnelle Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/dbgp.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index e5ac8a316fe..2932b48da0f 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -350,9 +350,9 @@ static int dbgp_setup(struct usb_gadget *gadget, u8 request = ctrl->bRequest; u16 value = le16_to_cpu(ctrl->wValue); u16 length = le16_to_cpu(ctrl->wLength); - int err = 0; - void *data; - u16 len; + int err = -EOPNOTSUPP; + void *data = NULL; + u16 len = 0; gadget->ep0->driver_data = gadget; @@ -371,10 +371,9 @@ static int dbgp_setup(struct usb_gadget *gadget, default: goto fail; } + err = 0; } else if (request == USB_REQ_SET_FEATURE && value == USB_DEVICE_DEBUG_MODE) { - len = 0; - data = NULL; dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n"); #ifdef CONFIG_USB_G_DBGP_PRINTK err = dbgp_enable_ep(); -- cgit v1.2.3 From 83b720199393fe2be38159354ef5a07a1b861e61 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Wed, 23 Mar 2011 21:24:55 +0100 Subject: USB: dbgp gadget: set MaxpacketSize0 The current code doesn't set it, so linux complains about it when connected, and ignores the device: [104611.068082] usb 1-5: new high speed USB device using ehci_hcd and address 127 [104611.088368] usb 1-5: Invalid ep0 maxpacket: 0 Signed-off-by: Sven Schnelle Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/dbgp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index 2932b48da0f..795525c3247 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -312,6 +312,7 @@ static int __init dbgp_bind(struct usb_gadget *gadget) dbgp.req->length = DBGP_REQ_EP0_LEN; gadget->ep0->driver_data = gadget; + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; #ifdef CONFIG_USB_G_DBGP_SERIAL dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL); -- cgit v1.2.3 From a8779ee94e6114bf071ef3ca6c8c9cb270d179ed Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Wed, 23 Mar 2011 15:24:56 +0100 Subject: USB: dbpg gadget: dont mask out direction bit Stripping the direction bit off will produce an invalid descriptor. Signed-off-by: Sven Schnelle Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/dbgp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index 795525c3247..dbe92ee8847 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -261,8 +261,8 @@ static int __init dbgp_configure_endpoints(struct usb_gadget *gadget) o_desc.wMaxPacketSize = __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE); - dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress & 0x7f; - dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress & 0x7f; + dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress; + dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress; #ifdef CONFIG_USB_G_DBGP_SERIAL dbgp.serial->in = dbgp.i_ep; -- cgit v1.2.3 From a0c25c20502e0c4a467f5d7e27b2a599cfe57abe Mon Sep 17 00:00:00 2001 From: Jonas Andersson Date: Mon, 21 Mar 2011 14:54:09 +0100 Subject: USB: g_printer required set interface request g_printer reqiured "set interface" request from host. Not all hosts send this request. This patch enable the interface when it get "set configuration" request from host. Signed-off-by: Jonas Andersson Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/printer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index c3f2bd42bd5..271ef94668e 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -1189,6 +1189,8 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) else if (gadget->a_alt_hnp_support) DBG(dev, "HNP needs a different root port\n"); value = printer_set_config(dev, wValue); + if (!value) + value = set_interface(dev, PRINTER_INTERFACE); break; case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != USB_DIR_IN) -- cgit v1.2.3 From 45e13e6e0aa954af2d4779a07262f4c12d6f032f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Apr 2011 14:09:58 +0900 Subject: usb: renesas_usbhs: modify fifo clear timing Pipe buffer should be cleaned before using it, but should NOT be cleaned in pipe "prepare" function. Because the pipe might be working in such timing. This patch fixup this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/pipe.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index b7a9137f599..4852c073e2f 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -369,15 +369,7 @@ static int usbhsp_fifo_select(struct usbhs_pipe *pipe, int write) int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe) { - int ret; - - ret = usbhsp_fifo_select(pipe, 1); - if (ret < 0) - return ret; - - usbhsp_fifo_clear(pipe); - - return ret; + return usbhsp_fifo_select(pipe, 1); } int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len) @@ -392,7 +384,7 @@ int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len) if (ret < 0) return ret; - ret = usbhs_fifo_prepare_write(pipe); + ret = usbhsp_fifo_select(pipe, 1); if (ret < 0) return ret; @@ -750,6 +742,8 @@ void usbhs_pipe_init(struct usbhs_priv *priv) usbhsp_flags_init(pipe); pipe->mod_private = NULL; + + usbhsp_fifo_clear(pipe); } } -- cgit v1.2.3 From 409ba9e7c023bdbfd2ecab960532523124de5c81 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 26 Apr 2011 09:21:35 +0900 Subject: usb: renesas_usbhs: don't re-allocation pipe buffer Because pipe buffer allocation is very picky and difficult, current renesas_usbhs driver is not caring pipe re-allocation. In this situation, driver will create new pipe without caring old pipe if "usbhsg_ep_enable" is called after "usbhsg_ep_disable" on current driver. This mean the limited pipe and buffer will be used as waste. But it is possible to re-use same buffer to same pipe. By this patch, driver will initialize pipe when it detected new connection or new gadget, and doesn't try re-allocation for same pipe in above case. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/mod_gadget.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 9a5ac02077b..8c721d86bac 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -787,12 +787,18 @@ static int usbhsg_pipe_disable(struct usbhsg_uep *uep) usbhsg_queue_pop(uep, ureq, -ECONNRESET); } - uep->pipe->mod_private = NULL; - uep->pipe = NULL; - return 0; } +static void usbhsg_uep_init(struct usbhsg_gpriv *gpriv) +{ + int i; + struct usbhsg_uep *uep; + + usbhsg_for_each_uep_with_dcp(uep, gpriv, i) + uep->pipe = NULL; +} + /* * * usb_ep_ops @@ -809,6 +815,13 @@ static int usbhsg_ep_enable(struct usb_ep *ep, unsigned long flags; int ret = -EIO; + /* + * if it already have pipe, + * nothing to do + */ + if (uep->pipe) + return 0; + /******************** spin lock ********************/ spin_lock_irqsave(lock, flags); @@ -1045,6 +1058,7 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) * pipe initialize and enable DCP */ usbhs_pipe_init(priv); + usbhsg_uep_init(gpriv); usbhsg_dcp_enable(dcp); /* -- cgit v1.2.3 From cb96632c185f13f746d009ec1125539e0b5cd899 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Apr 2011 14:10:08 +0900 Subject: usb: renesas_usbhs: modify spinlock method Current renesas_usbhs driver was using spin_trylock to avoid dead lock / nest lock. But acording to CONFIG_DEBUG_SPINLOCK, it is BUG under UP environment. This patch add usbhsg_trylock to avoid this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/mod_gadget.c | 97 +++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 8c721d86bac..d027c80ab35 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -118,6 +118,35 @@ struct usbhsg_recip_handle { #define usbhsg_status_clr(gp, b) (gp->status &= ~b) #define usbhsg_status_has(gp, b) (gp->status & b) +/* + * usbhsg_trylock + * + * This driver don't use spin_try_lock + * to avoid warning of CONFIG_DEBUG_SPINLOCK + */ +static spinlock_t *usbhsg_trylock(struct usbhsg_gpriv *gpriv, + unsigned long *flags) +{ + spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + + /* check spin lock status + * to avoid deadlock/nest */ + if (spin_is_locked(lock)) + return NULL; + + spin_lock_irqsave(lock, *flags); + + return lock; +} + +static void usbhsg_unlock(spinlock_t *lock, unsigned long *flags) +{ + if (!lock) + return; + + spin_unlock_irqrestore(lock, *flags); +} + /* * list push/pop */ @@ -159,9 +188,8 @@ static int __usbhsg_queue_handler(struct usbhsg_uep *uep, int prepare) struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); struct device *dev = usbhsg_gpriv_to_dev(gpriv); struct usbhsg_request *ureq; - spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + spinlock_t *lock; unsigned long flags; - int is_locked; int ret = 0; if (!uep->handler) { @@ -179,7 +207,7 @@ static int __usbhsg_queue_handler(struct usbhsg_uep *uep, int prepare) * - usb_request :: complete * * But the caller of this function need not care about spinlock. - * This function is using spin_trylock_irqsave for it. + * This function is using usbhsg_trylock for it. * if "is_locked" is 1, this mean this function lock it. * but if it is 0, this mean it is already under spin lock. * see also @@ -188,7 +216,8 @@ static int __usbhsg_queue_handler(struct usbhsg_uep *uep, int prepare) */ /****************** spin try lock *******************/ - is_locked = spin_trylock_irqsave(lock, flags); + lock = usbhsg_trylock(gpriv, &flags); + ureq = usbhsg_queue_get(uep); if (ureq) { if (prepare) @@ -196,8 +225,7 @@ static int __usbhsg_queue_handler(struct usbhsg_uep *uep, int prepare) else ret = uep->handler->try_run(uep, ureq); } - if (is_locked) - spin_unlock_irqrestore(lock, flags); + usbhsg_unlock(lock, &flags); /******************** spin unlock ******************/ return ret; @@ -228,7 +256,7 @@ static void usbhsg_queue_pop(struct usbhsg_uep *uep, * It mean "usb_ep_ops :: queue" which is using spinlock is called * under spinlock. * - * To avoid dead-lock, this driver is using spin_trylock. + * To avoid dead-lock, this driver is using usbhsg_trylock. * CAUTION [*endpoint queue*] * CAUTION [*queue handler*] */ @@ -811,7 +839,7 @@ static int usbhsg_ep_enable(struct usb_ep *ep, struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); struct usbhs_pipe *pipe; - spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + spinlock_t *lock; unsigned long flags; int ret = -EIO; @@ -823,7 +851,7 @@ static int usbhsg_ep_enable(struct usb_ep *ep, return 0; /******************** spin lock ********************/ - spin_lock_irqsave(lock, flags); + lock = usbhsg_trylock(gpriv, &flags); pipe = usbhs_pipe_malloc(priv, desc); if (pipe) { @@ -838,7 +866,8 @@ static int usbhsg_ep_enable(struct usb_ep *ep, ret = 0; } - spin_unlock_irqrestore(lock, flags); + + usbhsg_unlock(lock, &flags); /******************** spin unlock ******************/ return ret; @@ -848,14 +877,16 @@ static int usbhsg_ep_disable(struct usb_ep *ep) { struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); - spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + spinlock_t *lock; unsigned long flags; int ret; /******************** spin lock ********************/ - spin_lock_irqsave(lock, flags); + lock = usbhsg_trylock(gpriv, &flags); + ret = usbhsg_pipe_disable(uep); - spin_unlock_irqrestore(lock, flags); + + usbhsg_unlock(lock, &flags); /******************** spin unlock ******************/ return ret; @@ -890,10 +921,9 @@ static int usbhsg_ep_queue(struct usb_ep *ep, struct usb_request *req, struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); - spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + spinlock_t *lock; unsigned long flags; int ret = 0; - int is_locked; /* * CAUTION [*endpoint queue*] @@ -904,7 +934,7 @@ static int usbhsg_ep_queue(struct usb_ep *ep, struct usb_request *req, * it is already under spinlock on this driver. * but it is called frm usb driver, this function should call spinlock. * - * This function is using spin_trylock_irqsave to solve this issue. + * This function is using usbshg_trylock to solve this issue. * if "is_locked" is 1, this mean this function lock it. * but if it is 0, this mean it is already under spin lock. * see also @@ -913,7 +943,7 @@ static int usbhsg_ep_queue(struct usb_ep *ep, struct usb_request *req, */ /******************** spin lock ********************/ - is_locked = spin_trylock_irqsave(lock, flags); + lock = usbhsg_trylock(gpriv, &flags); /* param check */ if (usbhsg_is_not_connected(gpriv) || @@ -923,8 +953,7 @@ static int usbhsg_ep_queue(struct usb_ep *ep, struct usb_request *req, else usbhsg_queue_push(uep, ureq); - if (is_locked) - spin_unlock_irqrestore(lock, flags); + usbhsg_unlock(lock, &flags); /******************** spin unlock ******************/ usbhsg_queue_prepare(uep); @@ -937,9 +966,8 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); - spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + spinlock_t *lock; unsigned long flags; - int is_locked; /* * see @@ -949,12 +977,11 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) */ /******************** spin lock ********************/ - is_locked = spin_trylock_irqsave(lock, flags); + lock = usbhsg_trylock(gpriv, &flags); usbhsg_queue_pop(uep, ureq, -ECONNRESET); - if (is_locked) - spin_unlock_irqrestore(lock, flags); + usbhsg_unlock(lock, &flags); /******************** spin unlock ******************/ return 0; @@ -966,10 +993,9 @@ static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge) struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); struct device *dev = usbhsg_gpriv_to_dev(gpriv); - spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + spinlock_t *lock; unsigned long flags; int ret = -EAGAIN; - int is_locked; /* * see @@ -979,7 +1005,7 @@ static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge) */ /******************** spin lock ********************/ - is_locked = spin_trylock_irqsave(lock, flags); + lock = usbhsg_trylock(gpriv, &flags); if (!usbhsg_queue_get(uep)) { dev_dbg(dev, "set halt %d (pipe %d)\n", @@ -998,8 +1024,7 @@ static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge) ret = 0; } - if (is_locked) - spin_unlock_irqrestore(lock, flags); + usbhsg_unlock(lock, &flags); /******************** spin unlock ******************/ return ret; @@ -1038,11 +1063,11 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); struct usbhs_mod *mod = usbhs_mod_get_current(priv); struct device *dev = usbhs_priv_to_dev(priv); - spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + spinlock_t *lock; unsigned long flags; /******************** spin lock ********************/ - spin_lock_irqsave(lock, flags); + lock = usbhsg_trylock(gpriv, &flags); /* * enable interrupt and systems if ready @@ -1083,7 +1108,7 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) usbhs_irq_callback_update(priv, mod); usbhsg_try_start_unlock: - spin_unlock_irqrestore(lock, flags); + usbhsg_unlock(lock, &flags); /******************** spin unlock ********************/ return 0; @@ -1095,11 +1120,11 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) struct usbhs_mod *mod = usbhs_mod_get_current(priv); struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); struct device *dev = usbhs_priv_to_dev(priv); - spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + spinlock_t *lock; unsigned long flags; /******************** spin lock ********************/ - spin_lock_irqsave(lock, flags); + lock = usbhsg_trylock(gpriv, &flags); /* * disable interrupt and systems if 1st try @@ -1127,7 +1152,7 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) usbhs_sys_function_ctrl(priv, 0); usbhs_sys_usb_ctrl(priv, 0); - spin_unlock_irqrestore(lock, flags); + usbhsg_unlock(lock, &flags); /******************** spin unlock ********************/ if (gpriv->driver && @@ -1139,7 +1164,7 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) return 0; usbhsg_try_stop_unlock: - spin_unlock_irqrestore(lock, flags); + usbhsg_unlock(lock, &flags); return 0; } -- cgit v1.2.3 From 9a28b7bd4f1fb388a15b12fb425a589ba6188425 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Apr 2011 14:10:12 +0900 Subject: usb: renesas_usbhs: return error if wrong recip request There are some USB Host which doesn't notice disconnection at once. And it might try some request after reconnection with old settings. Current renesas_usbhs will crash in such case. This patch prevent this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/mod_gadget.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index d027c80ab35..206cfabc928 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -583,6 +583,10 @@ static int usbhsg_recip_run_handle(struct usbhs_priv *priv, char *msg; uep = usbhsg_gpriv_to_nth_uep(gpriv, nth); + if (!usbhsg_uep_to_pipe(uep)) { + dev_err(dev, "wrong recip request\n"); + return -EINVAL; + } switch (recip) { case USB_RECIP_DEVICE: -- cgit v1.2.3 From af32fe511374f17feb137d7fbfe2f4c73a8f531c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Apr 2011 14:10:16 +0900 Subject: usb: renesas_usbhs: remove callback when module removed. The callback function which is called from platform must be removed if module removed. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 4 ++++ include/linux/usb/renesas_usbhs.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index d9ad60d1c15..fda586d1f7d 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -352,9 +352,13 @@ probe_end_kfree: static int __devexit usbhs_remove(struct platform_device *pdev) { struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev); + struct renesas_usbhs_platform_info *info = pdev->dev.platform_data; + struct renesas_usbhs_driver_callback *dfunc = &info->driver_callback; dev_dbg(&pdev->dev, "usb remove\n"); + dfunc->notify_hotplug = NULL; + pm_runtime_disable(&pdev->dev); usbhsc_bus_ctrl(priv, 0); diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 565bca3aa44..66bbdd12d15 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -143,7 +143,7 @@ struct renesas_usbhs_platform_info { ({ \ struct renesas_usbhs_driver_callback *dc; \ dc = &(renesas_usbhs_get_info(pdev)->driver_callback); \ - if (dc) \ + if (dc && dc->notify_hotplug) \ dc->notify_hotplug(pdev); \ }) #endif /* RENESAS_USB_H */ -- cgit v1.2.3 From f429ea3f3488e1a0683f6f9ce76b420b5ebd2d63 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Apr 2011 14:10:24 +0900 Subject: usb: renesas_usbhs: add error reason for usbhs_pipe_malloc Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/pipe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 4852c073e2f..5897ddad05f 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -758,8 +758,11 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, u16 pipecfg, pipebuf, pipemaxp; pipe = usbhsp_get_pipe(priv, usb_endpoint_type(desc)); - if (!pipe) + if (!pipe) { + dev_err(dev, "can't get pipe (%s)\n", + usbhsp_pipe_name[usb_endpoint_type(desc)]); return NULL; + } usbhs_fifo_disable(pipe); -- cgit v1.2.3 From 206dcc2cfe15b6174f15293ae15a097ee03eb386 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 28 Apr 2011 16:40:54 +0900 Subject: usb: renesas_usbhs: move pdev_to_priv to global usbhs_pdev_to_priv function will be used in other files. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 14 +++++++------- drivers/usb/renesas_usbhs/common.h | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index fda586d1f7d..53be4443b5e 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -56,6 +56,11 @@ void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data) usbhs_write(priv, reg, val); } +struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev) +{ + return dev_get_drvdata(&pdev->dev); +} + /* * syscfg functions */ @@ -113,11 +118,6 @@ int usbhs_frame_get_num(struct usbhs_priv *priv) /* * local functions */ -static struct usbhs_priv *usbhsc_pdev_to_priv(struct platform_device *pdev) -{ - return dev_get_drvdata(&pdev->dev); -} - static void usbhsc_bus_ctrl(struct usbhs_priv *priv, int enable) { int wait = usbhs_get_dparam(priv, buswait_bwait); @@ -210,7 +210,7 @@ static void usbhsc_notify_hotplug(struct work_struct *work) static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) { - struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev); + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); /* * This functions will be called in interrupt. @@ -351,7 +351,7 @@ probe_end_kfree: static int __devexit usbhs_remove(struct platform_device *pdev) { - struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev); + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); struct renesas_usbhs_platform_info *info = pdev->dev.platform_data; struct renesas_usbhs_driver_callback *dfunc = &info->driver_callback; diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index f1a2b62f93f..f3b907d2608 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -217,6 +217,7 @@ int usbhs_frame_get_num(struct usbhs_priv *priv); /* * data */ +struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev); #define usbhs_get_dparam(priv, param) (priv->dparam->param) #define usbhs_priv_to_pdev(priv) (priv->pdev) #define usbhs_priv_to_dev(priv) (&priv->pdev->dev) -- cgit v1.2.3 From 6e267da8f10b1a6551b6c4dee3779f6f56e2644d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 28 Apr 2011 16:41:02 +0900 Subject: usb: renesas_usbhs: add power control function Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 42 ++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 53be4443b5e..db13cef9eff 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -148,7 +148,31 @@ static u32 usbhsc_default_pipe_type[] = { }; /* - * driver callback functions + * power control + */ +static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) +{ + struct device *dev = usbhs_priv_to_dev(priv); + + if (enable) { + /* enable PM */ + pm_runtime_get_sync(dev); + + /* USB on */ + usbhs_sys_clock_ctrl(priv, enable); + usbhsc_bus_ctrl(priv, enable); + } else { + /* USB off */ + usbhsc_bus_ctrl(priv, enable); + usbhs_sys_clock_ctrl(priv, enable); + + /* disable PM */ + pm_runtime_put_sync(dev); + } +} + +/* + * notify hotplug */ static void usbhsc_notify_hotplug(struct work_struct *work) { @@ -178,12 +202,8 @@ static void usbhsc_notify_hotplug(struct work_struct *work) dev_dbg(&pdev->dev, "%s enable\n", __func__); - /* enable PM */ - pm_runtime_get_sync(&pdev->dev); - - /* USB on */ - usbhs_sys_clock_ctrl(priv, enable); - usbhsc_bus_ctrl(priv, enable); + /* power on */ + usbhsc_power_ctrl(priv, enable); /* module start */ usbhs_mod_call(priv, start, priv); @@ -194,12 +214,8 @@ static void usbhsc_notify_hotplug(struct work_struct *work) /* module stop */ usbhs_mod_call(priv, stop, priv); - /* USB off */ - usbhsc_bus_ctrl(priv, enable); - usbhs_sys_clock_ctrl(priv, enable); - - /* disable PM */ - pm_runtime_put_sync(&pdev->dev); + /* power off */ + usbhsc_power_ctrl(priv, enable); usbhs_mod_change(priv, -1); -- cgit v1.2.3 From 5ea68d541df200d10a373c06f945f98225c2486b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 28 Apr 2011 16:41:07 +0900 Subject: usb: renesas_usbhs: prevent NULL pointer crash usbhs_status_get_each_irq/usbhs_irq_callback_update might be called with mod == NULL Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/mod.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 73604a1d684..d0f5f67e074 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -179,15 +179,17 @@ static void usbhs_status_get_each_irq(struct usbhs_priv *priv, state->intsts0 = usbhs_read(priv, INTSTS0); state->intsts1 = usbhs_read(priv, INTSTS1); - state->brdysts = usbhs_read(priv, BRDYSTS); - state->nrdysts = usbhs_read(priv, NRDYSTS); - state->bempsts = usbhs_read(priv, BEMPSTS); - state->dvstctr = usbhs_read(priv, DVSTCTR); /* mask */ - state->bempsts &= mod->irq_bempsts; - state->brdysts &= mod->irq_brdysts; + if (mod) { + state->brdysts = usbhs_read(priv, BRDYSTS); + state->nrdysts = usbhs_read(priv, NRDYSTS); + state->bempsts = usbhs_read(priv, BEMPSTS); + + state->bempsts &= mod->irq_bempsts; + state->brdysts &= mod->irq_brdysts; + } } /* @@ -259,17 +261,19 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) * but "mod->irq_dev_state" will be called. */ - if (mod->irq_ctrl_stage) - intenb0 |= CTRE; + if (mod) { + if (mod->irq_ctrl_stage) + intenb0 |= CTRE; - if (mod->irq_empty && mod->irq_bempsts) { - usbhs_write(priv, BEMPENB, mod->irq_bempsts); - intenb0 |= BEMPE; - } + if (mod->irq_empty && mod->irq_bempsts) { + usbhs_write(priv, BEMPENB, mod->irq_bempsts); + intenb0 |= BEMPE; + } - if (mod->irq_ready && mod->irq_brdysts) { - usbhs_write(priv, BRDYENB, mod->irq_brdysts); - intenb0 |= BRDYE; + if (mod->irq_ready && mod->irq_brdysts) { + usbhs_write(priv, BRDYENB, mod->irq_brdysts); + intenb0 |= BRDYE; + } } usbhs_write(priv, INTENB0, intenb0); -- cgit v1.2.3 From bc57381e634782009b1cb2e86b18013699ada576 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 28 Apr 2011 16:41:14 +0900 Subject: usb: renesas_usbhs: use delayed_work instead of work_struct This delay is used to overjump debounce. And, this patch also move usbhsc_drvcllbck_notify_hotplug to global, because it will be called from other files. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 9 +++++---- drivers/usb/renesas_usbhs/common.h | 3 ++- include/linux/usb/renesas_usbhs.h | 7 +++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index db13cef9eff..9a75a45687b 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -178,7 +178,7 @@ static void usbhsc_notify_hotplug(struct work_struct *work) { struct usbhs_priv *priv = container_of(work, struct usbhs_priv, - notify_hotplug_work); + notify_hotplug_work.work); struct platform_device *pdev = usbhs_priv_to_pdev(priv); struct usbhs_mod *mod = usbhs_mod_get_current(priv); int id; @@ -224,16 +224,17 @@ static void usbhsc_notify_hotplug(struct work_struct *work) } } -static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) +int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + int delay = usbhs_get_dparam(priv, detection_delay); /* * This functions will be called in interrupt. * To make sure safety context, * use workqueue for usbhs_notify_hotplug */ - schedule_work(&priv->notify_hotplug_work); + schedule_delayed_work(&priv->notify_hotplug_work, delay); return 0; } @@ -300,7 +301,7 @@ static int __devinit usbhs_probe(struct platform_device *pdev) */ priv->irq = irq; priv->pdev = pdev; - INIT_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug); + INIT_DELAYED_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug); spin_lock_init(usbhs_priv_to_lock(priv)); /* call pipe and module init */ diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index f3b907d2608..0157eb805cf 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -177,7 +177,7 @@ struct usbhs_priv { struct renesas_usbhs_platform_callback *pfunc; struct renesas_usbhs_driver_param *dparam; - struct work_struct notify_hotplug_work; + struct delayed_work notify_hotplug_work; struct platform_device *pdev; spinlock_t lock; @@ -200,6 +200,7 @@ u16 usbhs_read(struct usbhs_priv *priv, u32 reg); void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data); void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data); +int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev); /* * sysconfig */ diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 66bbdd12d15..3a7f1d982dd 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -103,6 +103,13 @@ struct renesas_usbhs_driver_param { * for BUSWAIT :: BWAIT * */ int buswait_bwait; + + /* + * option: + * + * delay time from notify_hotplug callback + */ + int detection_delay; }; /* -- cgit v1.2.3 From b002ff6e268b6024d6927a1ce330a14ca162b6ab Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 28 Apr 2011 16:41:20 +0900 Subject: usb: renesas_usbhs: add autonomy mode Current renesas_usbhs was designed to save power when USB is not connected. And it assumed platform uses callback to notify connection/disconnection by external interrupt. But some SuperH / platform board doesn't have such feature. This patch adds autonomy mode which detect USB connection/disconnection by internal interrupt. But power will be always ON when autonomy mode is selected. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 36 ++++++++++++++++++++++------ drivers/usb/renesas_usbhs/common.h | 3 +++ drivers/usb/renesas_usbhs/mod.c | 48 ++++++++++++++++++++++++++++++++++++++ drivers/usb/renesas_usbhs/mod.h | 15 ++++++++++++ 4 files changed, 95 insertions(+), 7 deletions(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 9a75a45687b..34e68e0205c 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -21,6 +21,14 @@ #include #include "./common.h" +#define USBHSF_RUNTIME_PWCTRL (1 << 0) + +/* status */ +#define usbhsc_flags_init(p) do {(p)->flags = 0; } while (0) +#define usbhsc_flags_set(p, b) ((p)->flags |= (b)) +#define usbhsc_flags_clr(p, b) ((p)->flags &= ~(b)) +#define usbhsc_flags_has(p, b) ((p)->flags & (b)) + /* * platform call back * @@ -203,7 +211,8 @@ static void usbhsc_notify_hotplug(struct work_struct *work) dev_dbg(&pdev->dev, "%s enable\n", __func__); /* power on */ - usbhsc_power_ctrl(priv, enable); + if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + usbhsc_power_ctrl(priv, enable); /* module start */ usbhs_mod_call(priv, start, priv); @@ -215,7 +224,8 @@ static void usbhsc_notify_hotplug(struct work_struct *work) usbhs_mod_call(priv, stop, priv); /* power off */ - usbhsc_power_ctrl(priv, enable); + if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + usbhsc_power_ctrl(priv, enable); usbhs_mod_change(priv, -1); @@ -252,8 +262,7 @@ static int __devinit usbhs_probe(struct platform_device *pdev) /* check platform information */ if (!info || - !info->platform_callback.get_id || - !info->platform_callback.get_vbus) { + !info->platform_callback.get_id) { dev_err(&pdev->dev, "no platform information\n"); return -EINVAL; } @@ -296,6 +305,11 @@ static int __devinit usbhs_probe(struct platform_device *pdev) priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type); } + /* FIXME */ + /* runtime power control ? */ + if (priv->pfunc->get_vbus) + usbhsc_flags_set(priv, USBHSF_RUNTIME_PWCTRL); + /* * priv settings */ @@ -338,10 +352,16 @@ static int __devinit usbhs_probe(struct platform_device *pdev) /* reset phy for connection */ usbhs_platform_call(priv, phy_reset, pdev); + /* power control */ + pm_runtime_enable(&pdev->dev); + if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) { + usbhsc_power_ctrl(priv, 1); + usbhs_mod_autonomy_mode(priv); + } + /* * manual call notify_hotplug for cold plug */ - pm_runtime_enable(&pdev->dev); ret = usbhsc_drvcllbck_notify_hotplug(pdev); if (ret < 0) goto probe_end_call_remove; @@ -376,9 +396,11 @@ static int __devexit usbhs_remove(struct platform_device *pdev) dfunc->notify_hotplug = NULL; - pm_runtime_disable(&pdev->dev); + /* power off */ + if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + usbhsc_power_ctrl(priv, 0); - usbhsc_bus_ctrl(priv, 0); + pm_runtime_disable(&pdev->dev); usbhs_platform_call(priv, hardware_exit, pdev); usbhs_pipe_remove(priv); diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 0157eb805cf..0aadcb40276 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -105,6 +105,7 @@ struct usbhs_priv; #define SACKE (1 << 4) /* Setup Transaction ACK Interrupt Enable */ /* INTSTS0 */ +#define VBINT (1 << 15) /* VBUS0_0 and VBUS1_0 Interrupt Status */ #define DVST (1 << 12) /* Device State Transition Interrupt Status */ #define CTRT (1 << 11) /* Control Stage Interrupt Status */ #define BEMP (1 << 10) /* Buffer Empty Interrupt Status */ @@ -182,6 +183,8 @@ struct usbhs_priv { spinlock_t lock; + u32 flags; + /* * module control */ diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index d0f5f67e074..a577f8f4064 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -20,6 +20,48 @@ #include "./mod.h" #define usbhs_priv_to_modinfo(priv) (&priv->mod_info) +#define usbhs_mod_info_call(priv, func, param...) \ +({ \ + struct usbhs_mod_info *info; \ + info = usbhs_priv_to_modinfo(priv); \ + !info->func ? 0 : \ + info->func(param); \ +}) + +/* + * autonomy + * + * these functions are used if platform doesn't have external phy. + * -> there is no "notify_hotplug" callback from platform + * -> call "notify_hotplug" by itself + * -> use own interrupt to connect/disconnect + * -> it mean module clock is always ON + * ~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +static int usbhsm_autonomy_get_vbus(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + + return VBSTS & usbhs_read(priv, INTSTS0); +} + +static int usbhsm_autonomy_irq_vbus(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct platform_device *pdev = usbhs_priv_to_pdev(priv); + + return usbhsc_drvcllbck_notify_hotplug(pdev); +} + +void usbhs_mod_autonomy_mode(struct usbhs_priv *priv) +{ + struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); + + info->irq_vbus = usbhsm_autonomy_irq_vbus; + priv->pfunc->get_vbus = usbhsm_autonomy_get_vbus; + + usbhs_irq_callback_update(priv, NULL); +} /* * host / gadget functions @@ -227,6 +269,9 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) * see also * usbhs_irq_setting_update */ + if (irq_state.intsts0 & VBINT) + usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state); + if (irq_state.intsts0 & DVST) usbhs_mod_call(priv, irq_dev_state, priv, &irq_state); @@ -245,6 +290,7 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) { u16 intenb0 = 0; + struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); usbhs_write(priv, INTENB0, 0); @@ -260,6 +306,8 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) * it don't enable DVSE (intenb0) here * but "mod->irq_dev_state" will be called. */ + if (info->irq_vbus) + intenb0 |= VBSE; if (mod) { if (mod->irq_ctrl_stage) diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h index 8644191e164..5c845a28a21 100644 --- a/drivers/usb/renesas_usbhs/mod.h +++ b/drivers/usb/renesas_usbhs/mod.h @@ -68,6 +68,19 @@ struct usbhs_mod { struct usbhs_mod_info { struct usbhs_mod *mod[USBHS_MAX]; struct usbhs_mod *curt; /* current mod */ + + /* + * INTSTS0 :: VBINT + * + * This function will be used as autonomy mode + * when platform cannot call notify_hotplug. + * + * This callback cannot be member of "struct usbhs_mod" + * because it will be used even though + * host/gadget has not been selected. + */ + int (*irq_vbus)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); }; /* @@ -81,6 +94,8 @@ int usbhs_mod_change(struct usbhs_priv *priv, int id); int usbhs_mod_probe(struct usbhs_priv *priv); void usbhs_mod_remove(struct usbhs_priv *priv); +void usbhs_mod_autonomy_mode(struct usbhs_priv *priv); + /* * status functions */ -- cgit v1.2.3 From 1d15ee4cd7c9ddacfb4b517131b257d8c0d74d42 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 14 Apr 2011 11:22:32 +0200 Subject: usb/hcd: don't return 0 on error in usb_add_hcd() If USB type detections fails, we run into default and return 0. Signed-off-by: Sebastian Andrzej Siewior Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 77a7faec8d7..10936ba9c42 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2407,6 +2407,7 @@ int usb_add_hcd(struct usb_hcd *hcd, rhdev->speed = USB_SPEED_SUPER; break; default: + retval = -EINVAL; goto err_set_rh_speed; } -- cgit v1.2.3 From 865835fa441fcabc65251f14280df3055fe82d0f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 15 Apr 2011 20:37:06 +0200 Subject: usb/dummy_hcd: don't probe for udc if hcd failed the_controller is allocated in dummy_hcd_probe() and is NULL if the allocation failed. The probe function of the udc driver is dereferencing this pointer and fault. Alan Stern suggested to abort the dummy_hcd driver probing so the module is not loaded. The is abort-on-error has been also added to the udc driver. Signed-off-by: Sebastian Andrzej Siewior Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/dummy_hcd.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 3214ca375d6..61ff927928a 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -892,10 +892,11 @@ static int dummy_udc_probe (struct platform_device *pdev) return rc; } - platform_set_drvdata (pdev, dum); rc = device_create_file (&dum->gadget.dev, &dev_attr_function); if (rc < 0) device_unregister (&dum->gadget.dev); + else + platform_set_drvdata(pdev, dum); return rc; } @@ -1995,11 +1996,29 @@ static int __init init (void) retval = platform_device_add(the_hcd_pdev); if (retval < 0) goto err_add_hcd; + if (!the_controller) { + /* + * The hcd was added successfully but its probe function failed + * for some reason. + */ + retval = -EINVAL; + goto err_add_udc; + } retval = platform_device_add(the_udc_pdev); if (retval < 0) goto err_add_udc; + if (!platform_get_drvdata(the_udc_pdev)) { + /* + * The udc was added successfully but its probe function failed + * for some reason. + */ + retval = -EINVAL; + goto err_probe_udc; + } return retval; +err_probe_udc: + platform_device_del(the_udc_pdev); err_add_udc: platform_device_del(the_hcd_pdev); err_add_hcd: -- cgit v1.2.3 From fd4477b09e13f60c7d9e497311ea6410a94aaae8 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Thu, 14 Apr 2011 11:55:43 +0200 Subject: usb: gadget: storage_common: use kstrto*() [bug fix] This commit fixes an embarrassing bug in the "storage_common: use kstrto*()" patch which caused fsg_store_ro() to return zero instead of the length of the consumed buffer. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/storage_common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 109635a8488..1fa4f705b0b 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -708,7 +708,7 @@ static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - ssize_t rc = count; + ssize_t rc; struct fsg_lun *curlun = fsg_lun_from_dev(dev); struct rw_semaphore *filesem = dev_get_drvdata(dev); unsigned ro; @@ -729,6 +729,7 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, curlun->ro = ro; curlun->initially_ro = ro; LDBG(curlun, "read-only status set to %d\n", curlun->ro); + rc = count; } up_read(filesem); return rc; -- cgit v1.2.3 From e7f84331c21408fecc872aaabc41c7b97fe15ae4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 14 Apr 2011 21:09:37 +0900 Subject: USB: ohci-s3c2410: use __devinit and __devexit macros for probe and remove The __devinit and __devexit macros were added to probe and remove functions. The macros move the probe and remove functions to the devinit and devexit sections. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-s3c2410.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index a68af2dd55c..16ec7da9237 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -473,12 +473,12 @@ static const struct hc_driver ohci_s3c2410_hc_driver = { /* device driver */ -static int ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev) +static int __devinit ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev) { return usb_hcd_s3c2410_probe(&ohci_s3c2410_hc_driver, pdev); } -static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev) +static int __devexit ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); @@ -488,7 +488,7 @@ static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev) static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_drv_probe, - .remove = ohci_hcd_s3c2410_drv_remove, + .remove = __devexit_p(ohci_hcd_s3c2410_drv_remove), .shutdown = usb_hcd_platform_shutdown, /*.suspend = ohci_hcd_s3c2410_drv_suspend, */ /*.resume = ohci_hcd_s3c2410_drv_resume, */ -- cgit v1.2.3 From 3c86c07baaa22e1ebae1922b5285f79a39e93d83 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 14 Apr 2011 21:09:16 +0900 Subject: USB: ohci-s3c2410: use resource_size() This patch uses the resource_size help function instead of manually calculating the resource size. It can reduce the chance of introducing off-by-one errors. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-s3c2410.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index 16ec7da9237..5837d218050 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -353,7 +353,7 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver, return -ENOMEM; hcd->rsrc_start = dev->resource[0].start; - hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; + hcd->rsrc_len = resource_size(&dev->resource[0]); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { dev_err(&dev->dev, "request_mem_region failed\n"); -- cgit v1.2.3 From 1f594b64a4f74ece0b7166ca4db05a71a64bd685 Mon Sep 17 00:00:00 2001 From: Jim Lin Date: Sun, 17 Apr 2011 11:58:25 +0300 Subject: USB: ehci: tegra: fix USB1 port reset issue Tegra USB1 port needs to issue Port Reset twice internally, otherwise it fails to enumerate devices attached to it Signed-off-by: Jim Lin Signed-off-by: Olof Johansson [ squash two patches into one and minor style cleanups ] Signed-off-by: Mike Rapoport Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-tegra.c | 72 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index a516af28c29..7359bcbe417 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -58,6 +58,71 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd) clk_disable(tegra->emc_clk); } +static int tegra_ehci_internal_port_reset( + struct ehci_hcd *ehci, + u32 __iomem *portsc_reg +) +{ + u32 temp; + unsigned long flags; + int retval = 0; + int i, tries; + u32 saved_usbintr; + + spin_lock_irqsave(&ehci->lock, flags); + saved_usbintr = ehci_readl(ehci, &ehci->regs->intr_enable); + /* disable USB interrupt */ + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + spin_unlock_irqrestore(&ehci->lock, flags); + + /* + * Here we have to do Port Reset at most twice for + * Port Enable bit to be set. + */ + for (i = 0; i < 2; i++) { + temp = ehci_readl(ehci, portsc_reg); + temp |= PORT_RESET; + ehci_writel(ehci, temp, portsc_reg); + mdelay(10); + temp &= ~PORT_RESET; + ehci_writel(ehci, temp, portsc_reg); + mdelay(1); + tries = 100; + do { + mdelay(1); + /* + * Up to this point, Port Enable bit is + * expected to be set after 2 ms waiting. + * USB1 usually takes extra 45 ms, for safety, + * we take 100 ms as timeout. + */ + temp = ehci_readl(ehci, portsc_reg); + } while (!(temp & PORT_PE) && tries--); + if (temp & PORT_PE) + break; + } + if (i == 2) + retval = -ETIMEDOUT; + + /* + * Clear Connect Status Change bit if it's set. + * We can't clear PORT_PEC. It will also cause PORT_PE to be cleared. + */ + if (temp & PORT_CSC) + ehci_writel(ehci, PORT_CSC, portsc_reg); + + /* + * Write to clear any interrupt status bits that might be set + * during port reset. + */ + temp = ehci_readl(ehci, &ehci->regs->status); + ehci_writel(ehci, temp, &ehci->regs->status); + + /* restore original interrupt enable bits */ + ehci_writel(ehci, saved_usbintr, &ehci->regs->intr_enable); + return retval; +} + static int tegra_ehci_hub_control( struct usb_hcd *hcd, u16 typeReq, @@ -121,6 +186,13 @@ static int tegra_ehci_hub_control( goto done; } + /* For USB1 port we need to issue Port Reset twice internally */ + if (tegra->phy->instance == 0 && + (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) { + spin_unlock_irqrestore(&ehci->lock, flags); + return tegra_ehci_internal_port_reset(ehci, status_reg); + } + /* * Tegra host controller will time the resume operation to clear the bit * when the port control state switches to HS or FS Idle. This behavior -- cgit v1.2.3 From 869410f82cbbb1464772046d87de8d18a916e706 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 14 Apr 2011 11:21:04 -0400 Subject: USB: add queued-unlinks test case to usbtest driver This patch (as1452b) adds a new test case to the usbtest driver. Test 24 exercises the unlink-from-queue pathways in the host. It queues a user-specified number of bulk-OUT URBs of user-specified size, unlinks the fourth- and second-from-last URBs in the queue, and then waits to see if all the URBs complete in the expected way (except of course that the unlinked URBs might complete normally, if they weren't unlinked soon enough). This new test has confirmed the existence of a bug in the ehci-hcd driver, to be fixed by a separate patch. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 120 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 2 deletions(-) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 388cc128072..58a5685fb7d 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1195,6 +1195,104 @@ static int unlink_simple(struct usbtest_dev *dev, int pipe, int len) /*-------------------------------------------------------------------------*/ +struct queued_ctx { + struct completion complete; + atomic_t pending; + unsigned num; + int status; + struct urb **urbs; +}; + +static void unlink_queued_callback(struct urb *urb) +{ + int status = urb->status; + struct queued_ctx *ctx = urb->context; + + if (ctx->status) + goto done; + if (urb == ctx->urbs[ctx->num - 4] || urb == ctx->urbs[ctx->num - 2]) { + if (status == -ECONNRESET) + goto done; + /* What error should we report if the URB completed normally? */ + } + if (status != 0) + ctx->status = status; + + done: + if (atomic_dec_and_test(&ctx->pending)) + complete(&ctx->complete); +} + +static int unlink_queued(struct usbtest_dev *dev, int pipe, unsigned num, + unsigned size) +{ + struct queued_ctx ctx; + struct usb_device *udev = testdev_to_usbdev(dev); + void *buf; + dma_addr_t buf_dma; + int i; + int retval = -ENOMEM; + + init_completion(&ctx.complete); + atomic_set(&ctx.pending, 1); /* One more than the actual value */ + ctx.num = num; + ctx.status = 0; + + buf = usb_alloc_coherent(udev, size, GFP_KERNEL, &buf_dma); + if (!buf) + return retval; + memset(buf, 0, size); + + /* Allocate and init the urbs we'll queue */ + ctx.urbs = kcalloc(num, sizeof(struct urb *), GFP_KERNEL); + if (!ctx.urbs) + goto free_buf; + for (i = 0; i < num; i++) { + ctx.urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!ctx.urbs[i]) + goto free_urbs; + usb_fill_bulk_urb(ctx.urbs[i], udev, pipe, buf, size, + unlink_queued_callback, &ctx); + ctx.urbs[i]->transfer_dma = buf_dma; + ctx.urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + } + + /* Submit all the URBs and then unlink URBs num - 4 and num - 2. */ + for (i = 0; i < num; i++) { + atomic_inc(&ctx.pending); + retval = usb_submit_urb(ctx.urbs[i], GFP_KERNEL); + if (retval != 0) { + dev_err(&dev->intf->dev, "submit urbs[%d] fail %d\n", + i, retval); + atomic_dec(&ctx.pending); + ctx.status = retval; + break; + } + } + if (i == num) { + usb_unlink_urb(ctx.urbs[num - 4]); + usb_unlink_urb(ctx.urbs[num - 2]); + } else { + while (--i >= 0) + usb_unlink_urb(ctx.urbs[i]); + } + + if (atomic_dec_and_test(&ctx.pending)) /* The extra count */ + complete(&ctx.complete); + wait_for_completion(&ctx.complete); + retval = ctx.status; + + free_urbs: + for (i = 0; i < num; i++) + usb_free_urb(ctx.urbs[i]); + kfree(ctx.urbs); + free_buf: + usb_free_coherent(udev, size, buf, buf_dma); + return retval; +} + +/*-------------------------------------------------------------------------*/ + static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb) { int retval; @@ -1970,8 +2068,6 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev->in_iso_pipe, dev->iso_in, 0); break; - /* FIXME unlink from queue (ring with N urbs) */ - /* FIXME scatterlist cancel (needs helper thread) */ /* Tests for bulk I/O using DMA mapping by core and odd address */ @@ -2064,6 +2160,26 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev->in_iso_pipe, dev->iso_in, 1); break; + /* unlink URBs from a bulk-OUT queue */ + case 24: + if (dev->out_pipe == 0 || !param->length || param->sglen < 4) + break; + retval = 0; + dev_info(&intf->dev, "TEST 17: unlink from %d queues of " + "%d %d-byte writes\n", + param->iterations, param->sglen, param->length); + for (i = param->iterations; retval == 0 && i > 0; --i) { + retval = unlink_queued(dev, dev->out_pipe, + param->sglen, param->length); + if (retval) { + dev_err(&intf->dev, + "unlink queued writes failed %d, " + "iterations left %d\n", retval, i); + break; + } + } + break; + } do_gettimeofday(¶m->duration); param->duration.tv_sec -= start.tv_sec; -- cgit v1.2.3 From 643de6240b0b59c420ad71dfeeb3125a3607af92 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Thu, 14 Apr 2011 17:47:09 +0200 Subject: usb: core: Change usb_create_sysfs_intf_files()' return type to void The usb_create_sysfs_intf_files() function always returned zero even if it failed to create sysfs fails. Since this is a desired behaviour there is no need to return return code at all. This commit changes function's return type (form int) to void. Signed-off-by: Michal Nazarewicz Cc: Sergey Senozhatsky Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/sysfs.c | 13 +++++-------- drivers/usb/core/usb.c | 3 +-- drivers/usb/core/usb.h | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 6781c369ce2..cf05b97693e 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -842,22 +842,19 @@ const struct attribute_group *usb_interface_groups[] = { NULL }; -int usb_create_sysfs_intf_files(struct usb_interface *intf) +void usb_create_sysfs_intf_files(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_interface *alt = intf->cur_altsetting; - int retval; if (intf->sysfs_files_created || intf->unregistering) - return 0; + return; - if (alt->string == NULL && - !(udev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS)) + if (!alt->string && !(udev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS)) alt->string = usb_cache_string(udev, alt->desc.iInterface); - if (alt->string) - retval = device_create_file(&intf->dev, &dev_attr_interface); + if (alt->string && device_create_file(&intf->dev, &dev_attr_interface)) + ; /* We don't actually care if the function fails. */ intf->sysfs_files_created = 1; - return 0; } void usb_remove_sysfs_intf_files(struct usb_interface *intf) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index d9d4b169404..8706fc97e60 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -953,8 +953,7 @@ static int usb_bus_notify(struct notifier_block *nb, unsigned long action, if (dev->type == &usb_device_type) (void) usb_create_sysfs_dev_files(to_usb_device(dev)); else if (dev->type == &usb_if_device_type) - (void) usb_create_sysfs_intf_files( - to_usb_interface(dev)); + usb_create_sysfs_intf_files(to_usb_interface(dev)); break; case BUS_NOTIFY_DEL_DEVICE: diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index d450b742137..d44d4b7bbf1 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -4,7 +4,7 @@ extern int usb_create_sysfs_dev_files(struct usb_device *dev); extern void usb_remove_sysfs_dev_files(struct usb_device *dev); -extern int usb_create_sysfs_intf_files(struct usb_interface *intf); +extern void usb_create_sysfs_intf_files(struct usb_interface *intf); extern void usb_remove_sysfs_intf_files(struct usb_interface *intf); extern int usb_create_ep_devs(struct device *parent, struct usb_host_endpoint *endpoint, -- cgit v1.2.3 From 7fc2a61638ef78cdf8d65d5934782963a6e0fc66 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 25 Apr 2011 16:54:28 +0100 Subject: xhci-hcd: Include in xhci-pci.c Commit b02d0ed677acb3465e7600366f2353413bf24074 ('xhci: Change hcd_priv into a pointer') added calls to kzalloc() and kfree() in xhci-pci.c. On most architectures is indirectly included, but on some it is not. Signed-off-by: Ben Hutchings Cc: Sarah Sharp , Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index a10494c2f3c..cbc4d491e62 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -21,6 +21,7 @@ */ #include +#include #include "xhci.h" -- cgit v1.2.3 From 14b76ed9e613965e02603c31febd85c96e32e094 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 14 Apr 2011 15:45:42 +0200 Subject: usb/usbtest: print super on super speed Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 58a5685fb7d..7e74741ddf7 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -2308,6 +2308,9 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) case USB_SPEED_HIGH: tmp = "high"; break; + case USB_SPEED_SUPER: + tmp = "super"; + break; default: tmp = "unknown"; break; -- cgit v1.2.3 From 67e7d64bcece93c84fd6e3832e7c6d91e57c06dc Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 14 Apr 2011 16:17:21 +0200 Subject: usb/usbtest: fix test10 on superpseed dummy_hcd + g_zero: |./testusb -a -s 1024 -v 1024 -t 10 | usbtest 2-1:3.0: subtest 14 error, status 0 with patch: ./testusb -a -t 10 |unknown speed /proc/bus/usb/001/002 | dummy_udc dummy_udc: disabled ep-a | dummy_udc dummy_udc: disabled ep-b | dummy_udc dummy_udc: enabled ep-a (ep1in-bulk) maxpacket 1024 | dummy_udc dummy_udc: enabled ep-b (ep2out-bulk) maxpacket 1024 | zero gadget: source/sink enabled | usbtest 1-1:3.0: TEST 10: queue 32 control calls, 1000 times | dummy_hcd dummy_hcd: timer fired with no URBs pending? |/proc/bus/usb/001/002 test 10, 0.022370 secs Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 7e74741ddf7..7e0bb2af8e6 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1030,6 +1030,8 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param) req.wValue = cpu_to_le16((USB_DT_DEVICE << 8) | 0); /* device descriptor size == 18 bytes */ len = udev->descriptor.bMaxPacketSize0; + if (udev->speed == USB_SPEED_SUPER) + len = 512; switch (len) { case 8: len = 24; -- cgit v1.2.3 From 3dacdf11f1f82b98d301d5e1d42cdaea9a39968a Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Fri, 15 Apr 2011 16:18:38 +0200 Subject: usb: factor out state_string() on otg drivers Provide common otg_state_string() and use it in drivers. Signed-off-by: Anatolij Gustschin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/isp1301_omap.c | 26 +++----------------------- drivers/usb/otg/langwell_otg.c | 40 +++------------------------------------- drivers/usb/otg/otg.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/usb/otg.h | 1 + 4 files changed, 42 insertions(+), 60 deletions(-) diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index e25700f44b6..8c282258e1b 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -234,29 +234,9 @@ isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits) /*-------------------------------------------------------------------------*/ -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"; - } -} - static inline const char *state_name(struct isp1301 *isp) { - return state_string(isp->otg.state); + return otg_state_string(isp->otg.state); } /*-------------------------------------------------------------------------*/ @@ -501,7 +481,7 @@ static void check_state(struct isp1301 *isp, const char *tag) if (isp->otg.state == state && !extra) return; pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag, - state_string(state), fsm, state_name(isp), + otg_state_string(state), fsm, state_name(isp), omap_readl(OTG_CTRL)); } @@ -1095,7 +1075,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat) if (state != isp->otg.state) pr_debug(" isp, %s -> %s\n", - state_string(state), state_name(isp)); + otg_state_string(state), state_name(isp)); #ifdef CONFIG_USB_OTG /* update the OTG controller state to match the isp1301; may diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c index e973ff19c55..f08f784086f 100644 --- a/drivers/usb/otg/langwell_otg.c +++ b/drivers/usb/otg/langwell_otg.c @@ -82,40 +82,6 @@ static struct pci_driver otg_pci_driver = { .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) @@ -968,7 +934,7 @@ static void langwell_otg_work(struct work_struct *work) pdev = to_pci_dev(lnw->dev); dev_dbg(lnw->dev, "%s: old state = %s\n", __func__, - state_string(iotg->otg.state)); + otg_state_string(iotg->otg.state)); switch (iotg->otg.state) { case OTG_STATE_UNDEFINED: @@ -1703,7 +1669,7 @@ static void langwell_otg_work(struct work_struct *work) } dev_dbg(lnw->dev, "%s: new state = %s\n", __func__, - state_string(iotg->otg.state)); + otg_state_string(iotg->otg.state)); } static ssize_t @@ -1789,7 +1755,7 @@ show_hsm(struct device *_dev, struct device_attribute *attr, char *buf) "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), + otg_state_string(iotg->otg.state), iotg->hsm.a_bus_resume, iotg->hsm.a_bus_suspend, iotg->hsm.a_conn, diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 0a43a7db750..fb7adeff9ff 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -64,3 +64,38 @@ int otg_set_transceiver(struct otg_transceiver *x) return 0; } EXPORT_SYMBOL(otg_set_transceiver); + +const char *otg_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"; + } +} +EXPORT_SYMBOL(otg_state_string); diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 6e40718f5ab..bc84858b3a4 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -246,5 +246,6 @@ otg_unregister_notifier(struct otg_transceiver *otg, struct notifier_block *nb) /* for OTG controller drivers (and maybe other stuff) */ extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num); +extern const char *otg_state_string(enum usb_otg_state state); #endif /* __LINUX_USB_OTG_H */ -- cgit v1.2.3 From 28ccd2962c66556d7037b2d9f1c11cdcd3b805d5 Mon Sep 17 00:00:00 2001 From: Matt Evans Date: Tue, 29 Mar 2011 13:40:46 +1100 Subject: xhci: Make xHCI driver endian-safe This patch changes the struct members defining access to xHCI device-visible memory to use __le32/__le64 where appropriate, and then adds swaps where required. Checked with sparse that all accesses are correct. MMIO accesses use readl/writel so already are performed LE, but prototypes now reflect this with __le*. There were a couple of (debug) instances of DMA pointers being truncated to 32bits which have been fixed too. Signed-off-by: Matt Evans Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-dbg.c | 51 +++++---- drivers/usb/host/xhci-hub.c | 18 +-- drivers/usb/host/xhci-mem.c | 122 ++++++++++---------- drivers/usb/host/xhci-ring.c | 267 +++++++++++++++++++++++-------------------- drivers/usb/host/xhci.c | 109 +++++++++--------- drivers/usb/host/xhci.h | 134 +++++++++++----------- 6 files changed, 360 insertions(+), 341 deletions(-) diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 0231814a97a..2e0486178db 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -147,7 +147,7 @@ static void xhci_print_op_regs(struct xhci_hcd *xhci) static void xhci_print_ports(struct xhci_hcd *xhci) { - u32 __iomem *addr; + __le32 __iomem *addr; int i, j; int ports; char *names[NUM_PORT_REGS] = { @@ -253,27 +253,27 @@ void xhci_print_trb_offsets(struct xhci_hcd *xhci, union xhci_trb *trb) void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb) { u64 address; - u32 type = xhci_readl(xhci, &trb->link.control) & TRB_TYPE_BITMASK; + u32 type = le32_to_cpu(trb->link.control) & TRB_TYPE_BITMASK; switch (type) { case TRB_TYPE(TRB_LINK): xhci_dbg(xhci, "Link TRB:\n"); xhci_print_trb_offsets(xhci, trb); - address = trb->link.segment_ptr; + address = le64_to_cpu(trb->link.segment_ptr); xhci_dbg(xhci, "Next ring segment DMA address = 0x%llx\n", address); xhci_dbg(xhci, "Interrupter target = 0x%x\n", - GET_INTR_TARGET(trb->link.intr_target)); + GET_INTR_TARGET(le32_to_cpu(trb->link.intr_target))); xhci_dbg(xhci, "Cycle bit = %u\n", - (unsigned int) (trb->link.control & TRB_CYCLE)); + (unsigned int) (le32_to_cpu(trb->link.control) & TRB_CYCLE)); xhci_dbg(xhci, "Toggle cycle bit = %u\n", - (unsigned int) (trb->link.control & LINK_TOGGLE)); + (unsigned int) (le32_to_cpu(trb->link.control) & LINK_TOGGLE)); xhci_dbg(xhci, "No Snoop bit = %u\n", - (unsigned int) (trb->link.control & TRB_NO_SNOOP)); + (unsigned int) (le32_to_cpu(trb->link.control) & TRB_NO_SNOOP)); break; case TRB_TYPE(TRB_TRANSFER): - address = trb->trans_event.buffer; + address = le64_to_cpu(trb->trans_event.buffer); /* * FIXME: look at flags to figure out if it's an address or if * the data is directly in the buffer field. @@ -281,11 +281,12 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb) xhci_dbg(xhci, "DMA address or buffer contents= %llu\n", address); break; case TRB_TYPE(TRB_COMPLETION): - address = trb->event_cmd.cmd_trb; + address = le64_to_cpu(trb->event_cmd.cmd_trb); xhci_dbg(xhci, "Command TRB pointer = %llu\n", address); xhci_dbg(xhci, "Completion status = %u\n", - (unsigned int) GET_COMP_CODE(trb->event_cmd.status)); - xhci_dbg(xhci, "Flags = 0x%x\n", (unsigned int) trb->event_cmd.flags); + (unsigned int) GET_COMP_CODE(le32_to_cpu(trb->event_cmd.status))); + xhci_dbg(xhci, "Flags = 0x%x\n", + (unsigned int) le32_to_cpu(trb->event_cmd.flags)); break; default: xhci_dbg(xhci, "Unknown TRB with TRB type ID %u\n", @@ -311,16 +312,16 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb) void xhci_debug_segment(struct xhci_hcd *xhci, struct xhci_segment *seg) { int i; - u32 addr = (u32) seg->dma; + u64 addr = seg->dma; union xhci_trb *trb = seg->trbs; for (i = 0; i < TRBS_PER_SEGMENT; ++i) { trb = &seg->trbs[i]; - xhci_dbg(xhci, "@%08x %08x %08x %08x %08x\n", addr, - lower_32_bits(trb->link.segment_ptr), - upper_32_bits(trb->link.segment_ptr), - (unsigned int) trb->link.intr_target, - (unsigned int) trb->link.control); + xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n", addr, + (u32)lower_32_bits(le64_to_cpu(trb->link.segment_ptr)), + (u32)upper_32_bits(le64_to_cpu(trb->link.segment_ptr)), + (unsigned int) le32_to_cpu(trb->link.intr_target), + (unsigned int) le32_to_cpu(trb->link.control)); addr += sizeof(*trb); } } @@ -391,18 +392,18 @@ void xhci_dbg_ep_rings(struct xhci_hcd *xhci, void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) { - u32 addr = (u32) erst->erst_dma_addr; + u64 addr = erst->erst_dma_addr; int i; struct xhci_erst_entry *entry; for (i = 0; i < erst->num_entries; ++i) { entry = &erst->entries[i]; - xhci_dbg(xhci, "@%08x %08x %08x %08x %08x\n", - (unsigned int) addr, - lower_32_bits(entry->seg_addr), - upper_32_bits(entry->seg_addr), - (unsigned int) entry->seg_size, - (unsigned int) entry->rsvd); + xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n", + addr, + lower_32_bits(le64_to_cpu(entry->seg_addr)), + upper_32_bits(le64_to_cpu(entry->seg_addr)), + (unsigned int) le32_to_cpu(entry->seg_size), + (unsigned int) le32_to_cpu(entry->rsvd)); addr += sizeof(*entry); } } @@ -436,7 +437,7 @@ char *xhci_get_slot_state(struct xhci_hcd *xhci, { struct xhci_slot_ctx *slot_ctx = xhci_get_slot_ctx(xhci, ctx); - switch (GET_SLOT_STATE(slot_ctx->dev_state)) { + switch (GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state))) { case 0: return "enabled/disabled"; case 1: diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index a78f2ebd11b..ae1d24cb930 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -50,7 +50,7 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, temp |= 0x0008; /* Bits 6:5 - no TTs in root ports */ /* Bit 7 - no port indicators */ - desc->wHubCharacteristics = (__force __u16) cpu_to_le16(temp); + desc->wHubCharacteristics = cpu_to_le16(temp); } /* Fill in the USB 2.0 roothub descriptor */ @@ -314,7 +314,7 @@ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id) } static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, - u16 wIndex, u32 __iomem *addr, u32 port_status) + u16 wIndex, __le32 __iomem *addr, u32 port_status) { /* Don't allow the USB core to disable SuperSpeed ports. */ if (hcd->speed == HCD_USB3) { @@ -331,7 +331,7 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, } static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, - u16 wIndex, u32 __iomem *addr, u32 port_status) + u16 wIndex, __le32 __iomem *addr, u32 port_status) { char *port_change_bit; u32 status; @@ -376,7 +376,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, unsigned long flags; u32 temp, temp1, status; int retval = 0; - u32 __iomem **port_array; + __le32 __iomem **port_array; int slot_id; struct xhci_bus_state *bus_state; @@ -664,7 +664,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) int i, retval; struct xhci_hcd *xhci = hcd_to_xhci(hcd); int ports; - u32 __iomem **port_array; + __le32 __iomem **port_array; struct xhci_bus_state *bus_state; if (hcd->speed == HCD_USB3) { @@ -709,7 +709,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports, port_index; - u32 __iomem **port_array; + __le32 __iomem **port_array; struct xhci_bus_state *bus_state; unsigned long flags; @@ -779,7 +779,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) if (DEV_HIGHSPEED(t1)) { /* enable remote wake up for USB 2.0 */ - u32 __iomem *addr; + __le32 __iomem *addr; u32 tmp; /* Add one to the port status register address to get @@ -801,7 +801,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports, port_index; - u32 __iomem **port_array; + __le32 __iomem **port_array; struct xhci_bus_state *bus_state; u32 temp; unsigned long flags; @@ -875,7 +875,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) if (DEV_HIGHSPEED(temp)) { /* disable remote wake up for USB 2.0 */ - u32 __iomem *addr; + __le32 __iomem *addr; u32 tmp; /* Add one to the port status register address to get diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 627f3438028..500ec7a9eb8 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -89,16 +89,17 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, return; prev->next = next; if (link_trbs) { - prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = next->dma; + prev->trbs[TRBS_PER_SEGMENT-1].link. + segment_ptr = cpu_to_le64(next->dma); /* Set the last TRB in the segment to have a TRB type ID of Link TRB */ - val = prev->trbs[TRBS_PER_SEGMENT-1].link.control; + val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control); val &= ~TRB_TYPE_BITMASK; val |= TRB_TYPE(TRB_LINK); /* Always set the chain bit with 0.95 hardware */ if (xhci_link_trb_quirk(xhci)) val |= TRB_CHAIN; - prev->trbs[TRBS_PER_SEGMENT-1].link.control = val; + prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); } xhci_dbg(xhci, "Linking segment 0x%llx to segment 0x%llx (DMA)\n", (unsigned long long)prev->dma, @@ -186,7 +187,8 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, if (link_trbs) { /* See section 4.9.2.1 and 6.4.4.1 */ - prev->trbs[TRBS_PER_SEGMENT-1].link.control |= (LINK_TOGGLE); + prev->trbs[TRBS_PER_SEGMENT-1].link. + control |= cpu_to_le32(LINK_TOGGLE); xhci_dbg(xhci, "Wrote link toggle flag to" " segment %p (virtual), 0x%llx (DMA)\n", prev, (unsigned long long)prev->dma); @@ -548,7 +550,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, addr = cur_ring->first_seg->dma | SCT_FOR_CTX(SCT_PRI_TR) | cur_ring->cycle_state; - stream_info->stream_ctx_array[cur_stream].stream_ring = addr; + stream_info->stream_ctx_array[cur_stream]. + stream_ring = cpu_to_le64(addr); xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n", cur_stream, (unsigned long long) addr); @@ -614,10 +617,10 @@ void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci, max_primary_streams = fls(stream_info->num_stream_ctxs) - 2; xhci_dbg(xhci, "Setting number of stream ctx array entries to %u\n", 1 << (max_primary_streams + 1)); - ep_ctx->ep_info &= ~EP_MAXPSTREAMS_MASK; - ep_ctx->ep_info |= EP_MAXPSTREAMS(max_primary_streams); - ep_ctx->ep_info |= EP_HAS_LSA; - ep_ctx->deq = stream_info->ctx_array_dma; + ep_ctx->ep_info &= cpu_to_le32(~EP_MAXPSTREAMS_MASK); + ep_ctx->ep_info |= cpu_to_le32(EP_MAXPSTREAMS(max_primary_streams) + | EP_HAS_LSA); + ep_ctx->deq = cpu_to_le64(stream_info->ctx_array_dma); } /* @@ -630,10 +633,9 @@ void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) { dma_addr_t addr; - ep_ctx->ep_info &= ~EP_MAXPSTREAMS_MASK; - ep_ctx->ep_info &= ~EP_HAS_LSA; + ep_ctx->ep_info &= cpu_to_le32(~(EP_MAXPSTREAMS_MASK | EP_HAS_LSA)); addr = xhci_trb_virt_to_dma(ep->ring->deq_seg, ep->ring->dequeue); - ep_ctx->deq = addr | ep->ring->cycle_state; + ep_ctx->deq = cpu_to_le64(addr | ep->ring->cycle_state); } /* Frees all stream contexts associated with the endpoint, @@ -781,11 +783,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, dev->udev = udev; /* Point to output device context in dcbaa. */ - xhci->dcbaa->dev_context_ptrs[slot_id] = dev->out_ctx->dma; + xhci->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(dev->out_ctx->dma); xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n", - slot_id, - &xhci->dcbaa->dev_context_ptrs[slot_id], - (unsigned long long) xhci->dcbaa->dev_context_ptrs[slot_id]); + slot_id, + &xhci->dcbaa->dev_context_ptrs[slot_id], + (unsigned long long) le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id])); return 1; fail: @@ -810,8 +812,9 @@ void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci, * configured device has reset, so all control transfers should have * been completed or cancelled before the reset. */ - ep0_ctx->deq = xhci_trb_virt_to_dma(ep_ring->enq_seg, ep_ring->enqueue); - ep0_ctx->deq |= ep_ring->cycle_state; + ep0_ctx->deq = cpu_to_le64(xhci_trb_virt_to_dma(ep_ring->enq_seg, + ep_ring->enqueue) + | ep_ring->cycle_state); } /* @@ -885,24 +888,22 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud slot_ctx = xhci_get_slot_ctx(xhci, dev->in_ctx); /* 2) New slot context and endpoint 0 context are valid*/ - ctrl_ctx->add_flags = SLOT_FLAG | EP0_FLAG; + ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); /* 3) Only the control endpoint is valid - one endpoint context */ - slot_ctx->dev_info |= LAST_CTX(1); - - slot_ctx->dev_info |= (u32) udev->route; + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | (u32) udev->route); switch (udev->speed) { case USB_SPEED_SUPER: - slot_ctx->dev_info |= (u32) SLOT_SPEED_SS; + slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_SS); break; case USB_SPEED_HIGH: - slot_ctx->dev_info |= (u32) SLOT_SPEED_HS; + slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_HS); break; case USB_SPEED_FULL: - slot_ctx->dev_info |= (u32) SLOT_SPEED_FS; + slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_FS); break; case USB_SPEED_LOW: - slot_ctx->dev_info |= (u32) SLOT_SPEED_LS; + slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_LS); break; case USB_SPEED_WIRELESS: xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n"); @@ -916,7 +917,7 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud port_num = xhci_find_real_port_number(xhci, udev); if (!port_num) return -EINVAL; - slot_ctx->dev_info2 |= (u32) ROOT_HUB_PORT(port_num); + slot_ctx->dev_info2 |= cpu_to_le32((u32) ROOT_HUB_PORT(port_num)); /* Set the port number in the virtual_device to the faked port number */ for (top_dev = udev; top_dev->parent && top_dev->parent->parent; top_dev = top_dev->parent) @@ -927,31 +928,31 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud /* Is this a LS/FS device under an external HS hub? */ if (udev->tt && udev->tt->hub->parent) { - slot_ctx->tt_info = udev->tt->hub->slot_id; - slot_ctx->tt_info |= udev->ttport << 8; + slot_ctx->tt_info = cpu_to_le32(udev->tt->hub->slot_id | + (udev->ttport << 8)); if (udev->tt->multi) - slot_ctx->dev_info |= DEV_MTT; + slot_ctx->dev_info |= cpu_to_le32(DEV_MTT); } xhci_dbg(xhci, "udev->tt = %p\n", udev->tt); xhci_dbg(xhci, "udev->ttport = 0x%x\n", udev->ttport); /* Step 4 - ring already allocated */ /* Step 5 */ - ep0_ctx->ep_info2 = EP_TYPE(CTRL_EP); + ep0_ctx->ep_info2 = cpu_to_le32(EP_TYPE(CTRL_EP)); /* * XXX: Not sure about wireless USB devices. */ switch (udev->speed) { case USB_SPEED_SUPER: - ep0_ctx->ep_info2 |= MAX_PACKET(512); + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(512)); break; case USB_SPEED_HIGH: /* USB core guesses at a 64-byte max packet first for FS devices */ case USB_SPEED_FULL: - ep0_ctx->ep_info2 |= MAX_PACKET(64); + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(64)); break; case USB_SPEED_LOW: - ep0_ctx->ep_info2 |= MAX_PACKET(8); + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(8)); break; case USB_SPEED_WIRELESS: xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n"); @@ -962,12 +963,10 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud BUG(); } /* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */ - ep0_ctx->ep_info2 |= MAX_BURST(0); - ep0_ctx->ep_info2 |= ERROR_COUNT(3); + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(0) | ERROR_COUNT(3)); - ep0_ctx->deq = - dev->eps[0].ring->first_seg->dma; - ep0_ctx->deq |= dev->eps[0].ring->cycle_state; + ep0_ctx->deq = cpu_to_le64(dev->eps[0].ring->first_seg->dma | + dev->eps[0].ring->cycle_state); /* Steps 7 and 8 were done in xhci_alloc_virt_device() */ @@ -1133,8 +1132,8 @@ static u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci, if (udev->speed == USB_SPEED_SUPER) return ep->ss_ep_comp.wBytesPerInterval; - max_packet = GET_MAX_PACKET(ep->desc.wMaxPacketSize); - max_burst = (ep->desc.wMaxPacketSize & 0x1800) >> 11; + max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize)); + max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) & 0x1800) >> 11; /* A 0 in max burst means 1 transfer per ESIT */ return max_packet * (max_burst + 1); } @@ -1183,10 +1182,10 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, } virt_dev->eps[ep_index].skip = false; ep_ring = virt_dev->eps[ep_index].new_ring; - ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state; + ep_ctx->deq = cpu_to_le64(ep_ring->first_seg->dma | ep_ring->cycle_state); - ep_ctx->ep_info = xhci_get_endpoint_interval(udev, ep); - ep_ctx->ep_info |= EP_MULT(xhci_get_endpoint_mult(udev, ep)); + ep_ctx->ep_info = cpu_to_le32(xhci_get_endpoint_interval(udev, ep) + | EP_MULT(xhci_get_endpoint_mult(udev, ep))); /* FIXME dig Mult and streams info out of ep companion desc */ @@ -1194,22 +1193,22 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, * error count = 0 means infinite retries. */ if (!usb_endpoint_xfer_isoc(&ep->desc)) - ep_ctx->ep_info2 = ERROR_COUNT(3); + ep_ctx->ep_info2 = cpu_to_le32(ERROR_COUNT(3)); else - ep_ctx->ep_info2 = ERROR_COUNT(1); + ep_ctx->ep_info2 = cpu_to_le32(ERROR_COUNT(1)); - ep_ctx->ep_info2 |= xhci_get_endpoint_type(udev, ep); + ep_ctx->ep_info2 |= cpu_to_le32(xhci_get_endpoint_type(udev, ep)); /* Set the max packet size and max burst */ switch (udev->speed) { case USB_SPEED_SUPER: - max_packet = ep->desc.wMaxPacketSize; - ep_ctx->ep_info2 |= MAX_PACKET(max_packet); + max_packet = le16_to_cpu(ep->desc.wMaxPacketSize); + ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet)); /* dig out max burst from ep companion desc */ max_packet = ep->ss_ep_comp.bMaxBurst; if (!max_packet) xhci_warn(xhci, "WARN no SS endpoint bMaxBurst\n"); - ep_ctx->ep_info2 |= MAX_BURST(max_packet); + ep_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(max_packet)); break; case USB_SPEED_HIGH: /* bits 11:12 specify the number of additional transaction @@ -1217,20 +1216,21 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, */ if (usb_endpoint_xfer_isoc(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) { - max_burst = (ep->desc.wMaxPacketSize & 0x1800) >> 11; - ep_ctx->ep_info2 |= MAX_BURST(max_burst); + max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) + & 0x1800) >> 11; + ep_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(max_burst)); } /* Fall through */ case USB_SPEED_FULL: case USB_SPEED_LOW: - max_packet = GET_MAX_PACKET(ep->desc.wMaxPacketSize); - ep_ctx->ep_info2 |= MAX_PACKET(max_packet); + max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize)); + ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet)); break; default: BUG(); } max_esit_payload = xhci_get_max_esit_payload(xhci, udev, ep); - ep_ctx->tx_info = MAX_ESIT_PAYLOAD_FOR_EP(max_esit_payload); + ep_ctx->tx_info = cpu_to_le32(MAX_ESIT_PAYLOAD_FOR_EP(max_esit_payload)); /* * XXX no idea how to calculate the average TRB buffer length for bulk @@ -1247,7 +1247,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, * use Event Data TRBs, and we don't chain in a link TRB on short * transfers, we're basically dividing by 1. */ - ep_ctx->tx_info |= AVG_TRB_LENGTH_FOR_EP(max_esit_payload); + ep_ctx->tx_info |= cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(max_esit_payload)); /* FIXME Debug endpoint context */ return 0; @@ -1347,7 +1347,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) if (!xhci->scratchpad->sp_dma_buffers) goto fail_sp4; - xhci->dcbaa->dev_context_ptrs[0] = xhci->scratchpad->sp_dma; + xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma); for (i = 0; i < num_sp; i++) { dma_addr_t dma; void *buf = pci_alloc_consistent(to_pci_dev(dev), @@ -1724,7 +1724,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) } static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, - u32 __iomem *addr, u8 major_revision) + __le32 __iomem *addr, u8 major_revision) { u32 temp, port_offset, port_count; int i; @@ -1789,7 +1789,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, */ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) { - u32 __iomem *addr; + __le32 __iomem *addr; u32 offset; unsigned int num_ports; int i, port_index; @@ -2042,8 +2042,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* set ring base address and size for each segment table entry */ for (val = 0, seg = xhci->event_ring->first_seg; val < ERST_NUM_SEGS; val++) { struct xhci_erst_entry *entry = &xhci->erst.entries[val]; - entry->seg_addr = seg->dma; - entry->seg_size = TRBS_PER_SEGMENT; + entry->seg_addr = cpu_to_le64(seg->dma); + entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); entry->rsvd = 0; seg = seg->next; } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 7437386a9a5..9b1eeb04ce6 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -100,7 +100,7 @@ static bool last_trb_on_last_seg(struct xhci_hcd *xhci, struct xhci_ring *ring, return (trb == &seg->trbs[TRBS_PER_SEGMENT]) && (seg->next == xhci->event_ring->first_seg); else - return trb->link.control & LINK_TOGGLE; + return le32_to_cpu(trb->link.control) & LINK_TOGGLE; } /* Is this TRB a link TRB or was the last TRB the last TRB in this event ring @@ -113,13 +113,15 @@ static int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, if (ring == xhci->event_ring) return trb == &seg->trbs[TRBS_PER_SEGMENT]; else - return (trb->link.control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK); + return (le32_to_cpu(trb->link.control) & TRB_TYPE_BITMASK) + == TRB_TYPE(TRB_LINK); } static int enqueue_is_link_trb(struct xhci_ring *ring) { struct xhci_link_trb *link = &ring->enqueue->link; - return ((link->control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)); + return ((le32_to_cpu(link->control) & TRB_TYPE_BITMASK) == + TRB_TYPE(TRB_LINK)); } /* Updates trb to point to the next TRB in the ring, and updates seg if the next @@ -197,7 +199,7 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, union xhci_trb *next; unsigned long long addr; - chain = ring->enqueue->generic.field[3] & TRB_CHAIN; + chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN; next = ++(ring->enqueue); ring->enq_updates++; @@ -223,12 +225,14 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, * (which may mean the chain bit is cleared). */ if (!xhci_link_trb_quirk(xhci)) { - next->link.control &= ~TRB_CHAIN; - next->link.control |= chain; + next->link.control &= + cpu_to_le32(~TRB_CHAIN); + next->link.control |= + cpu_to_le32(chain); } /* Give this link TRB to the hardware */ wmb(); - next->link.control ^= TRB_CYCLE; + next->link.control ^= cpu_to_le32(TRB_CYCLE); } /* Toggle the cycle bit after the last ring segment. */ if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) { @@ -319,7 +323,7 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int ep_index, unsigned int stream_id) { - __u32 __iomem *db_addr = &xhci->dba->doorbell[slot_id]; + __le32 __iomem *db_addr = &xhci->dba->doorbell[slot_id]; struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; unsigned int ep_state = ep->ep_state; @@ -380,7 +384,7 @@ static struct xhci_segment *find_trb_seg( while (cur_seg->trbs > trb || &cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) { generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic; - if (generic_trb->field[3] & LINK_TOGGLE) + if (le32_to_cpu(generic_trb->field[3]) & LINK_TOGGLE) *cycle_state ^= 0x1; cur_seg = cur_seg->next; if (cur_seg == start_seg) @@ -447,6 +451,10 @@ static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci, * any link TRBs with the toggle cycle bit set. * - Finally we move the dequeue state one TRB further, toggling the cycle bit * if we've moved it past a link TRB with the toggle cycle bit set. + * + * Some of the uses of xhci_generic_trb are grotty, but if they're done + * with correct __le32 accesses they should work fine. Only users of this are + * in here. */ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, @@ -480,7 +488,7 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, /* Dig out the cycle state saved by the xHC during the stop ep cmd */ xhci_dbg(xhci, "Finding endpoint context\n"); ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); - state->new_cycle_state = 0x1 & ep_ctx->deq; + state->new_cycle_state = 0x1 & le64_to_cpu(ep_ctx->deq); state->new_deq_ptr = cur_td->last_trb; xhci_dbg(xhci, "Finding segment containing last TRB in TD.\n"); @@ -493,8 +501,8 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, } trb = &state->new_deq_ptr->generic; - if ((trb->field[3] & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK) && - (trb->field[3] & LINK_TOGGLE)) + if ((le32_to_cpu(trb->field[3]) & TRB_TYPE_BITMASK) == + TRB_TYPE(TRB_LINK) && (le32_to_cpu(trb->field[3]) & LINK_TOGGLE)) state->new_cycle_state ^= 0x1; next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr); @@ -529,12 +537,12 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, for (cur_seg = cur_td->start_seg, cur_trb = cur_td->first_trb; true; next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { - if ((cur_trb->generic.field[3] & TRB_TYPE_BITMASK) == - TRB_TYPE(TRB_LINK)) { + if ((le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK) + == TRB_TYPE(TRB_LINK)) { /* Unchain any chained Link TRBs, but * leave the pointers intact. */ - cur_trb->generic.field[3] &= ~TRB_CHAIN; + cur_trb->generic.field[3] &= cpu_to_le32(~TRB_CHAIN); xhci_dbg(xhci, "Cancel (unchain) link TRB\n"); xhci_dbg(xhci, "Address = %p (0x%llx dma); " "in seg %p (0x%llx dma)\n", @@ -547,8 +555,9 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, cur_trb->generic.field[1] = 0; cur_trb->generic.field[2] = 0; /* Preserve only the cycle bit of this TRB */ - cur_trb->generic.field[3] &= TRB_CYCLE; - cur_trb->generic.field[3] |= TRB_TYPE(TRB_TR_NOOP); + cur_trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE); + cur_trb->generic.field[3] |= cpu_to_le32( + TRB_TYPE(TRB_TR_NOOP)); xhci_dbg(xhci, "Cancel TRB %p (0x%llx dma) " "in seg %p (0x%llx dma)\n", cur_trb, @@ -662,9 +671,9 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, struct xhci_dequeue_state deq_state; if (unlikely(TRB_TO_SUSPEND_PORT( - xhci->cmd_ring->dequeue->generic.field[3]))) { + le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])))) { slot_id = TRB_TO_SLOT_ID( - xhci->cmd_ring->dequeue->generic.field[3]); + le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])); virt_dev = xhci->devs[slot_id]; if (virt_dev) handle_cmd_in_cmd_wait_list(xhci, virt_dev, @@ -677,8 +686,8 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, } memset(&deq_state, 0, sizeof(deq_state)); - slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); - ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); + ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); ep = &xhci->devs[slot_id]->eps[ep_index]; if (list_empty(&ep->cancelled_td_list)) { @@ -910,9 +919,9 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, struct xhci_ep_ctx *ep_ctx; struct xhci_slot_ctx *slot_ctx; - slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); - ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); - stream_id = TRB_TO_STREAM_ID(trb->generic.field[2]); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); + ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); + stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2])); dev = xhci->devs[slot_id]; ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id); @@ -928,11 +937,11 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx); - if (GET_COMP_CODE(event->status) != COMP_SUCCESS) { + if (GET_COMP_CODE(le32_to_cpu(event->status)) != COMP_SUCCESS) { unsigned int ep_state; unsigned int slot_state; - switch (GET_COMP_CODE(event->status)) { + switch (GET_COMP_CODE(le32_to_cpu(event->status))) { case COMP_TRB_ERR: xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because " "of stream ID configuration\n"); @@ -940,9 +949,9 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, case COMP_CTX_STATE: xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due " "to incorrect slot or ep state.\n"); - ep_state = ep_ctx->ep_info; + ep_state = le32_to_cpu(ep_ctx->ep_info); ep_state &= EP_STATE_MASK; - slot_state = slot_ctx->dev_state; + slot_state = le32_to_cpu(slot_ctx->dev_state); slot_state = GET_SLOT_STATE(slot_state); xhci_dbg(xhci, "Slot state = %u, EP state = %u\n", slot_state, ep_state); @@ -954,7 +963,7 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, default: xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown " "completion code of %u.\n", - GET_COMP_CODE(event->status)); + GET_COMP_CODE(le32_to_cpu(event->status))); break; } /* OK what do we do now? The endpoint state is hosed, and we @@ -965,10 +974,10 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, */ } else { xhci_dbg(xhci, "Successful Set TR Deq Ptr cmd, deq = @%08llx\n", - ep_ctx->deq); + le64_to_cpu(ep_ctx->deq)); if (xhci_trb_virt_to_dma(dev->eps[ep_index].queued_deq_seg, - dev->eps[ep_index].queued_deq_ptr) == - (ep_ctx->deq & ~(EP_CTX_CYCLE_MASK))) { + dev->eps[ep_index].queued_deq_ptr) == + (le64_to_cpu(ep_ctx->deq) & ~(EP_CTX_CYCLE_MASK))) { /* Update the ring's dequeue segment and dequeue pointer * to reflect the new position. */ @@ -997,13 +1006,13 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci, int slot_id; unsigned int ep_index; - slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); - ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); + ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); /* This command will only fail if the endpoint wasn't halted, * but we don't care. */ xhci_dbg(xhci, "Ignoring reset ep completion code of %u\n", - (unsigned int) GET_COMP_CODE(event->status)); + (unsigned int) GET_COMP_CODE(le32_to_cpu(event->status))); /* HW with the reset endpoint quirk needs to have a configure endpoint * command complete before the endpoint can be used. Queue that here @@ -1040,8 +1049,7 @@ static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci, if (xhci->cmd_ring->dequeue != command->command_trb) return 0; - command->status = - GET_COMP_CODE(event->status); + command->status = GET_COMP_CODE(le32_to_cpu(event->status)); list_del(&command->cmd_list); if (command->completion) complete(command->completion); @@ -1053,7 +1061,7 @@ static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci, static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_event_cmd *event) { - int slot_id = TRB_TO_SLOT_ID(event->flags); + int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); u64 cmd_dma; dma_addr_t cmd_dequeue_dma; struct xhci_input_control_ctx *ctrl_ctx; @@ -1062,7 +1070,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_ring *ep_ring; unsigned int ep_state; - cmd_dma = event->cmd_trb; + cmd_dma = le64_to_cpu(event->cmd_trb); cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, xhci->cmd_ring->dequeue); /* Is the command ring deq ptr out of sync with the deq seg ptr? */ @@ -1075,9 +1083,10 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, xhci->error_bitmask |= 1 << 5; return; } - switch (xhci->cmd_ring->dequeue->generic.field[3] & TRB_TYPE_BITMASK) { + switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]) + & TRB_TYPE_BITMASK) { case TRB_TYPE(TRB_ENABLE_SLOT): - if (GET_COMP_CODE(event->status) == COMP_SUCCESS) + if (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_SUCCESS) xhci->slot_id = slot_id; else xhci->slot_id = 0; @@ -1102,7 +1111,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx); /* Input ctx add_flags are the endpoint index plus one */ - ep_index = xhci_last_valid_endpoint(ctrl_ctx->add_flags) - 1; + ep_index = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)) - 1; /* A usb_set_interface() call directly after clearing a halted * condition may race on this quirky hardware. Not worth * worrying about, since this is prototype hardware. Not sure @@ -1111,8 +1120,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, */ if (xhci->quirks & XHCI_RESET_EP_QUIRK && ep_index != (unsigned int) -1 && - ctrl_ctx->add_flags - SLOT_FLAG == - ctrl_ctx->drop_flags) { + le32_to_cpu(ctrl_ctx->add_flags) - SLOT_FLAG == + le32_to_cpu(ctrl_ctx->drop_flags)) { ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state; if (!(ep_state & EP_HALTED)) @@ -1129,18 +1138,18 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, bandwidth_change: xhci_dbg(xhci, "Completed config ep cmd\n"); xhci->devs[slot_id]->cmd_status = - GET_COMP_CODE(event->status); + GET_COMP_CODE(le32_to_cpu(event->status)); complete(&xhci->devs[slot_id]->cmd_completion); break; case TRB_TYPE(TRB_EVAL_CONTEXT): virt_dev = xhci->devs[slot_id]; if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event)) break; - xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(event->status); + xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status)); complete(&xhci->devs[slot_id]->cmd_completion); break; case TRB_TYPE(TRB_ADDR_DEV): - xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(event->status); + xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status)); complete(&xhci->addr_dev); break; case TRB_TYPE(TRB_STOP_RING): @@ -1157,7 +1166,7 @@ bandwidth_change: case TRB_TYPE(TRB_RESET_DEV): xhci_dbg(xhci, "Completed reset device command.\n"); slot_id = TRB_TO_SLOT_ID( - xhci->cmd_ring->dequeue->generic.field[3]); + le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])); virt_dev = xhci->devs[slot_id]; if (virt_dev) handle_cmd_in_cmd_wait_list(xhci, virt_dev, event); @@ -1171,8 +1180,8 @@ bandwidth_change: break; } xhci_dbg(xhci, "NEC firmware version %2x.%02x\n", - NEC_FW_MAJOR(event->status), - NEC_FW_MINOR(event->status)); + NEC_FW_MAJOR(le32_to_cpu(event->status)), + NEC_FW_MINOR(le32_to_cpu(event->status))); break; default: /* Skip over unknown commands on the event ring */ @@ -1187,7 +1196,7 @@ static void handle_vendor_event(struct xhci_hcd *xhci, { u32 trb_type; - trb_type = TRB_FIELD_TO_TYPE(event->generic.field[3]); + trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->generic.field[3])); xhci_dbg(xhci, "Vendor specific event TRB type = %u\n", trb_type); if (trb_type == TRB_NEC_CMD_COMP && (xhci->quirks & XHCI_NEC_HOST)) handle_cmd_completion(xhci, &event->event_cmd); @@ -1241,15 +1250,15 @@ static void handle_port_status(struct xhci_hcd *xhci, unsigned int faked_port_index; u8 major_revision; struct xhci_bus_state *bus_state; - u32 __iomem **port_array; + __le32 __iomem **port_array; bool bogus_port_status = false; /* Port status change events always have a successful completion code */ - if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) { + if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS) { xhci_warn(xhci, "WARN: xHC returned failed port status event\n"); xhci->error_bitmask |= 1 << 8; } - port_id = GET_PORT_ID(event->generic.field[0]); + port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0])); xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id); max_ports = HCS_MAX_PORTS(xhci->hcs_params1); @@ -1456,7 +1465,7 @@ static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci, * endpoint anyway. Check if a babble halted the * endpoint. */ - if ((ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_HALTED) + if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == EP_STATE_HALTED) return 1; return 0; @@ -1494,12 +1503,12 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td, struct urb_priv *urb_priv; u32 trb_comp_code; - slot_id = TRB_TO_SLOT_ID(event->flags); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); xdev = xhci->devs[slot_id]; - ep_index = TRB_TO_EP_ID(event->flags) - 1; - ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); + ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; + ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); - trb_comp_code = GET_COMP_CODE(event->transfer_len); + trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); if (skip) goto td_cleanup; @@ -1602,12 +1611,12 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_ep_ctx *ep_ctx; u32 trb_comp_code; - slot_id = TRB_TO_SLOT_ID(event->flags); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); xdev = xhci->devs[slot_id]; - ep_index = TRB_TO_EP_ID(event->flags) - 1; - ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); + ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; + ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); - trb_comp_code = GET_COMP_CODE(event->transfer_len); + trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); xhci_debug_trb(xhci, xhci->event_ring->dequeue); switch (trb_comp_code) { @@ -1646,7 +1655,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, event_trb != td->last_trb) td->urb->actual_length = td->urb->transfer_buffer_length - - TRB_LEN(event->transfer_len); + - TRB_LEN(le32_to_cpu(event->transfer_len)); else td->urb->actual_length = 0; @@ -1680,7 +1689,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, /* We didn't stop on a link TRB in the middle */ td->urb->actual_length = td->urb->transfer_buffer_length - - TRB_LEN(event->transfer_len); + TRB_LEN(le32_to_cpu(event->transfer_len)); xhci_dbg(xhci, "Waiting for status " "stage event\n"); return 0; @@ -1708,8 +1717,8 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, u32 trb_comp_code; bool skip_td = false; - ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); - trb_comp_code = GET_COMP_CODE(event->transfer_len); + ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); + trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); urb_priv = td->urb->hcpriv; idx = urb_priv->td_cnt; frame = &td->urb->iso_frame_desc[idx]; @@ -1752,15 +1761,14 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg; cur_trb != event_trb; next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { - if ((cur_trb->generic.field[3] & + if ((le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) && - (cur_trb->generic.field[3] & + (le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK)) - len += - TRB_LEN(cur_trb->generic.field[2]); + len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])); } - len += TRB_LEN(cur_trb->generic.field[2]) - - TRB_LEN(event->transfer_len); + len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) - + TRB_LEN(le32_to_cpu(event->transfer_len)); if (trb_comp_code != COMP_STOP_INVAL) { frame->actual_length = len; @@ -1815,8 +1823,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_segment *cur_seg; u32 trb_comp_code; - ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); - trb_comp_code = GET_COMP_CODE(event->transfer_len); + ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); + trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); switch (trb_comp_code) { case COMP_SUCCESS: @@ -1852,18 +1860,18 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, "%d bytes untransferred\n", td->urb->ep->desc.bEndpointAddress, td->urb->transfer_buffer_length, - TRB_LEN(event->transfer_len)); + TRB_LEN(le32_to_cpu(event->transfer_len))); /* Fast path - was this the last TRB in the TD for this URB? */ if (event_trb == td->last_trb) { - if (TRB_LEN(event->transfer_len) != 0) { + if (TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) { td->urb->actual_length = td->urb->transfer_buffer_length - - TRB_LEN(event->transfer_len); + TRB_LEN(le32_to_cpu(event->transfer_len)); if (td->urb->transfer_buffer_length < td->urb->actual_length) { xhci_warn(xhci, "HC gave bad length " "of %d bytes left\n", - TRB_LEN(event->transfer_len)); + TRB_LEN(le32_to_cpu(event->transfer_len))); td->urb->actual_length = 0; if (td->urb->transfer_flags & URB_SHORT_NOT_OK) *status = -EREMOTEIO; @@ -1894,20 +1902,20 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg; cur_trb != event_trb; next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { - if ((cur_trb->generic.field[3] & + if ((le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) && - (cur_trb->generic.field[3] & + (le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK)) td->urb->actual_length += - TRB_LEN(cur_trb->generic.field[2]); + TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])); } /* If the ring didn't stop on a Link or No-op TRB, add * in the actual bytes transferred from the Normal TRB */ if (trb_comp_code != COMP_STOP_INVAL) td->urb->actual_length += - TRB_LEN(cur_trb->generic.field[2]) - - TRB_LEN(event->transfer_len); + TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) - + TRB_LEN(le32_to_cpu(event->transfer_len)); } return finish_td(xhci, td, event_trb, event, ep, status, false); @@ -1937,7 +1945,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, u32 trb_comp_code; int ret = 0; - slot_id = TRB_TO_SLOT_ID(event->flags); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); xdev = xhci->devs[slot_id]; if (!xdev) { xhci_err(xhci, "ERROR Transfer event pointed to bad slot\n"); @@ -1945,20 +1953,21 @@ static int handle_tx_event(struct xhci_hcd *xhci, } /* Endpoint ID is 1 based, our index is zero based */ - ep_index = TRB_TO_EP_ID(event->flags) - 1; + ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; xhci_dbg(xhci, "%s - ep index = %d\n", __func__, ep_index); ep = &xdev->eps[ep_index]; - ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); + ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); if (!ep_ring || - (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) { + (le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == + EP_STATE_DISABLED) { xhci_err(xhci, "ERROR Transfer event for disabled endpoint " "or incorrect stream ring\n"); return -ENODEV; } - event_dma = event->buffer; - trb_comp_code = GET_COMP_CODE(event->transfer_len); + event_dma = le64_to_cpu(event->buffer); + trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); /* Look for common error cases */ switch (trb_comp_code) { /* Skip codes that require special handling depending on @@ -2011,14 +2020,16 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (!list_empty(&ep_ring->td_list)) xhci_dbg(xhci, "Underrun Event for slot %d ep %d " "still with TDs queued?\n", - TRB_TO_SLOT_ID(event->flags), ep_index); + TRB_TO_SLOT_ID(le32_to_cpu(event->flags)), + ep_index); goto cleanup; case COMP_OVERRUN: xhci_dbg(xhci, "overrun event on endpoint\n"); if (!list_empty(&ep_ring->td_list)) xhci_dbg(xhci, "Overrun Event for slot %d ep %d " "still with TDs queued?\n", - TRB_TO_SLOT_ID(event->flags), ep_index); + TRB_TO_SLOT_ID(le32_to_cpu(event->flags)), + ep_index); goto cleanup; case COMP_MISSED_INT: /* @@ -2047,9 +2058,11 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (list_empty(&ep_ring->td_list)) { xhci_warn(xhci, "WARN Event TRB for slot %d ep %d " "with no TDs queued?\n", - TRB_TO_SLOT_ID(event->flags), ep_index); + TRB_TO_SLOT_ID(le32_to_cpu(event->flags)), + ep_index); xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", - (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); + (unsigned int) (le32_to_cpu(event->flags) + & TRB_TYPE_BITMASK)>>10); xhci_print_trb_offsets(xhci, (union xhci_trb *) event); if (ep->skip) { ep->skip = false; @@ -2092,7 +2105,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, * corresponding TD has been cancelled. Just ignore * the TD. */ - if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK) + if ((le32_to_cpu(event_trb->generic.field[3]) + & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_TR_NOOP)) { xhci_dbg(xhci, "event_trb is a no-op TRB. Skip it\n"); @@ -2172,15 +2186,15 @@ static void xhci_handle_event(struct xhci_hcd *xhci) event = xhci->event_ring->dequeue; /* Does the HC or OS own the TRB? */ - if ((event->event_cmd.flags & TRB_CYCLE) != - xhci->event_ring->cycle_state) { + if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) != + xhci->event_ring->cycle_state) { xhci->error_bitmask |= 1 << 2; return; } xhci_dbg(xhci, "%s - OS owns TRB\n", __func__); /* FIXME: Handle more event types. */ - switch ((event->event_cmd.flags & TRB_TYPE_BITMASK)) { + switch ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK)) { case TRB_TYPE(TRB_COMPLETION): xhci_dbg(xhci, "%s - calling handle_cmd_completion\n", __func__); handle_cmd_completion(xhci, &event->event_cmd); @@ -2202,7 +2216,8 @@ static void xhci_handle_event(struct xhci_hcd *xhci) update_ptrs = 0; break; default: - if ((event->event_cmd.flags & TRB_TYPE_BITMASK) >= TRB_TYPE(48)) + if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >= + TRB_TYPE(48)) handle_vendor_event(xhci, event); else xhci->error_bitmask |= 1 << 3; @@ -2252,12 +2267,12 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) xhci_dbg(xhci, "op reg status = %08x\n", status); xhci_dbg(xhci, "Event ring dequeue ptr:\n"); xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n", - (unsigned long long) - xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb), - lower_32_bits(trb->link.segment_ptr), - upper_32_bits(trb->link.segment_ptr), - (unsigned int) trb->link.intr_target, - (unsigned int) trb->link.control); + (unsigned long long) + xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb), + lower_32_bits(le64_to_cpu(trb->link.segment_ptr)), + upper_32_bits(le64_to_cpu(trb->link.segment_ptr)), + (unsigned int) le32_to_cpu(trb->link.intr_target), + (unsigned int) le32_to_cpu(trb->link.control)); if (status & STS_FATAL) { xhci_warn(xhci, "WARNING: Host System Error\n"); @@ -2358,10 +2373,10 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, struct xhci_generic_trb *trb; trb = &ring->enqueue->generic; - trb->field[0] = field1; - trb->field[1] = field2; - trb->field[2] = field3; - trb->field[3] = field4; + trb->field[0] = cpu_to_le32(field1); + trb->field[1] = cpu_to_le32(field2); + trb->field[2] = cpu_to_le32(field3); + trb->field[3] = cpu_to_le32(field4); inc_enq(xhci, ring, consumer, more_trbs_coming); } @@ -2414,17 +2429,16 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, next = ring->enqueue; while (last_trb(xhci, ring, ring->enq_seg, next)) { - /* If we're not dealing with 0.95 hardware, * clear the chain bit. */ if (!xhci_link_trb_quirk(xhci)) - next->link.control &= ~TRB_CHAIN; + next->link.control &= cpu_to_le32(~TRB_CHAIN); else - next->link.control |= TRB_CHAIN; + next->link.control |= cpu_to_le32(TRB_CHAIN); wmb(); - next->link.control ^= (u32) TRB_CYCLE; + next->link.control ^= cpu_to_le32((u32) TRB_CYCLE); /* Toggle the cycle bit after the last ring segment. */ if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) { @@ -2467,8 +2481,8 @@ static int prepare_transfer(struct xhci_hcd *xhci, } ret = prepare_ring(xhci, ep_ring, - ep_ctx->ep_info & EP_STATE_MASK, - num_trbs, mem_flags); + le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK, + num_trbs, mem_flags); if (ret) return ret; @@ -2570,9 +2584,9 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id, */ wmb(); if (start_cycle) - start_trb->field[3] |= start_cycle; + start_trb->field[3] |= cpu_to_le32(start_cycle); else - start_trb->field[3] &= ~0x1; + start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE); xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id); } @@ -2590,7 +2604,7 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, int xhci_interval; int ep_interval; - xhci_interval = EP_INTERVAL_TO_UFRAMES(ep_ctx->ep_info); + xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx->ep_info)); ep_interval = urb->interval; /* Convert to microframes */ if (urb->dev->speed == USB_SPEED_LOW || @@ -2979,12 +2993,11 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (start_cycle == 0) field |= 0x1; queue_trb(xhci, ep_ring, false, true, - /* FIXME endianness is probably going to bite my ass here. */ - setup->bRequestType | setup->bRequest << 8 | setup->wValue << 16, - setup->wIndex | setup->wLength << 16, - TRB_LEN(8) | TRB_INTR_TARGET(0), - /* Immediate data in pointer */ - field); + setup->bRequestType | setup->bRequest << 8 | le16_to_cpu(setup->wValue) << 16, + le16_to_cpu(setup->wIndex) | le16_to_cpu(setup->wLength) << 16, + TRB_LEN(8) | TRB_INTR_TARGET(0), + /* Immediate data in pointer */ + field); /* If there's data, queue data TRBs */ field = 0; @@ -3211,8 +3224,8 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, /* Check the ring to guarantee there is enough room for the whole urb. * Do not insert any td of the urb to the ring if the check failed. */ - ret = prepare_ring(xhci, ep_ring, ep_ctx->ep_info & EP_STATE_MASK, - num_trbs, mem_flags); + ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK, + num_trbs, mem_flags); if (ret) return ret; @@ -3224,7 +3237,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, urb->dev->speed == USB_SPEED_FULL) urb->start_frame >>= 3; - xhci_interval = EP_INTERVAL_TO_UFRAMES(ep_ctx->ep_info); + xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx->ep_info)); ep_interval = urb->interval; /* Convert to microframes */ if (urb->dev->speed == USB_SPEED_LOW || diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 81b976e4588..e8ab1899c88 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -973,8 +973,8 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, out_ctx = xhci->devs[slot_id]->out_ctx; ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); - hw_max_packet_size = MAX_PACKET_DECODED(ep_ctx->ep_info2); - max_packet_size = urb->dev->ep0.desc.wMaxPacketSize; + hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); + max_packet_size = le16_to_cpu(urb->dev->ep0.desc.wMaxPacketSize); if (hw_max_packet_size != max_packet_size) { xhci_dbg(xhci, "Max Packet Size for ep 0 changed.\n"); xhci_dbg(xhci, "Max packet size in usb_device = %d\n", @@ -988,15 +988,15 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, xhci->devs[slot_id]->out_ctx, ep_index); in_ctx = xhci->devs[slot_id]->in_ctx; ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index); - ep_ctx->ep_info2 &= ~MAX_PACKET_MASK; - ep_ctx->ep_info2 |= MAX_PACKET(max_packet_size); + ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET_MASK); + ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size)); /* Set up the input context flags for the command */ /* FIXME: This won't work if a non-default control endpoint * changes max packet sizes. */ ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); - ctrl_ctx->add_flags = EP0_FLAG; + ctrl_ctx->add_flags = cpu_to_le32(EP0_FLAG); ctrl_ctx->drop_flags = 0; xhci_dbg(xhci, "Slot %d input context\n", slot_id); @@ -1010,7 +1010,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, /* Clean up the input context for later use by bandwidth * functions. */ - ctrl_ctx->add_flags = SLOT_FLAG; + ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG); } return ret; } @@ -1331,27 +1331,30 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, /* If the HC already knows the endpoint is disabled, * or the HCD has noted it is disabled, ignore this request */ - if ((ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED || - ctrl_ctx->drop_flags & xhci_get_endpoint_flag(&ep->desc)) { + if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == + EP_STATE_DISABLED || + le32_to_cpu(ctrl_ctx->drop_flags) & + xhci_get_endpoint_flag(&ep->desc)) { xhci_warn(xhci, "xHCI %s called with disabled ep %p\n", __func__, ep); return 0; } - ctrl_ctx->drop_flags |= drop_flag; - new_drop_flags = ctrl_ctx->drop_flags; + ctrl_ctx->drop_flags |= cpu_to_le32(drop_flag); + new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags); - ctrl_ctx->add_flags &= ~drop_flag; - new_add_flags = ctrl_ctx->add_flags; + ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag); + new_add_flags = le32_to_cpu(ctrl_ctx->add_flags); - last_ctx = xhci_last_valid_endpoint(ctrl_ctx->add_flags); + last_ctx = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)); slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); /* Update the last valid endpoint context, if we deleted the last one */ - if ((slot_ctx->dev_info & LAST_CTX_MASK) > LAST_CTX(last_ctx)) { - slot_ctx->dev_info &= ~LAST_CTX_MASK; - slot_ctx->dev_info |= LAST_CTX(last_ctx); + if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) > + LAST_CTX(last_ctx)) { + slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); } - new_slot_info = slot_ctx->dev_info; + new_slot_info = le32_to_cpu(slot_ctx->dev_info); xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep); @@ -1419,7 +1422,8 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, /* If the HCD has already noted the endpoint is enabled, * ignore this request. */ - if (ctrl_ctx->add_flags & xhci_get_endpoint_flag(&ep->desc)) { + if (le32_to_cpu(ctrl_ctx->add_flags) & + xhci_get_endpoint_flag(&ep->desc)) { xhci_warn(xhci, "xHCI %s called with enabled ep %p\n", __func__, ep); return 0; @@ -1437,8 +1441,8 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, return -ENOMEM; } - ctrl_ctx->add_flags |= added_ctxs; - new_add_flags = ctrl_ctx->add_flags; + ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs); + new_add_flags = le32_to_cpu(ctrl_ctx->add_flags); /* If xhci_endpoint_disable() was called for this endpoint, but the * xHC hasn't been notified yet through the check_bandwidth() call, @@ -1446,15 +1450,16 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, * descriptors. We must drop and re-add this endpoint, so we leave the * drop flags alone. */ - new_drop_flags = ctrl_ctx->drop_flags; + new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags); slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); /* Update the last valid endpoint context, if we just added one past */ - if ((slot_ctx->dev_info & LAST_CTX_MASK) < LAST_CTX(last_ctx)) { - slot_ctx->dev_info &= ~LAST_CTX_MASK; - slot_ctx->dev_info |= LAST_CTX(last_ctx); + if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) < + LAST_CTX(last_ctx)) { + slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); } - new_slot_info = slot_ctx->dev_info; + new_slot_info = le32_to_cpu(slot_ctx->dev_info); /* Store the usb_device pointer for later use */ ep->hcpriv = udev; @@ -1484,9 +1489,9 @@ static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *vir ctrl_ctx->drop_flags = 0; ctrl_ctx->add_flags = 0; slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); - slot_ctx->dev_info &= ~LAST_CTX_MASK; + slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); /* Endpoint 0 is always valid */ - slot_ctx->dev_info |= LAST_CTX(1); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1)); for (i = 1; i < 31; ++i) { ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, i); ep_ctx->ep_info = 0; @@ -1581,7 +1586,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, unsigned long flags; struct xhci_container_ctx *in_ctx; struct completion *cmd_completion; - int *cmd_status; + u32 *cmd_status; struct xhci_virt_device *virt_dev; spin_lock_irqsave(&xhci->lock, flags); @@ -1595,8 +1600,8 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, /* Enqueue pointer can be left pointing to the link TRB, * we must handle that */ - if ((command->command_trb->link.control & TRB_TYPE_BITMASK) - == TRB_TYPE(TRB_LINK)) + if ((le32_to_cpu(command->command_trb->link.control) + & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)) command->command_trb = xhci->cmd_ring->enq_seg->next->trbs; @@ -1672,14 +1677,13 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */ ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx); - ctrl_ctx->add_flags |= SLOT_FLAG; - ctrl_ctx->add_flags &= ~EP0_FLAG; - ctrl_ctx->drop_flags &= ~SLOT_FLAG; - ctrl_ctx->drop_flags &= ~EP0_FLAG; + ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); + ctrl_ctx->add_flags &= cpu_to_le32(~EP0_FLAG); + ctrl_ctx->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); xhci_dbg(xhci, "New Input Control Context:\n"); slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); xhci_dbg_ctx(xhci, virt_dev->in_ctx, - LAST_CTX_TO_EP_NUM(slot_ctx->dev_info)); + LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info))); ret = xhci_configure_endpoint(xhci, udev, NULL, false, false); @@ -1690,7 +1694,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) xhci_dbg(xhci, "Output context after successful config ep cmd:\n"); xhci_dbg_ctx(xhci, virt_dev->out_ctx, - LAST_CTX_TO_EP_NUM(slot_ctx->dev_info)); + LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info))); xhci_zero_in_ctx(xhci, virt_dev); /* Install new rings and free or cache any old rings */ @@ -1740,10 +1744,10 @@ static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci, { struct xhci_input_control_ctx *ctrl_ctx; ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); - ctrl_ctx->add_flags = add_flags; - ctrl_ctx->drop_flags = drop_flags; + ctrl_ctx->add_flags = cpu_to_le32(add_flags); + ctrl_ctx->drop_flags = cpu_to_le32(drop_flags); xhci_slot_copy(xhci, in_ctx, out_ctx); - ctrl_ctx->add_flags |= SLOT_FLAG; + ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); xhci_dbg(xhci, "Input Context:\n"); xhci_dbg_ctx(xhci, in_ctx, xhci_last_valid_endpoint(add_flags)); @@ -1772,7 +1776,7 @@ static void xhci_setup_input_ctx_for_quirk(struct xhci_hcd *xhci, deq_state->new_deq_ptr); return; } - ep_ctx->deq = addr | deq_state->new_cycle_state; + ep_ctx->deq = cpu_to_le64(addr | deq_state->new_cycle_state); added_ctxs = xhci_get_endpoint_flag_from_index(ep_index); xhci_setup_input_ctx_for_config_ep(xhci, xhci->devs[slot_id]->in_ctx, @@ -2327,8 +2331,8 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) /* Enqueue pointer can be left pointing to the link TRB, * we must handle that */ - if ((reset_device_cmd->command_trb->link.control & TRB_TYPE_BITMASK) - == TRB_TYPE(TRB_LINK)) + if ((le32_to_cpu(reset_device_cmd->command_trb->link.control) + & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)) reset_device_cmd->command_trb = xhci->cmd_ring->enq_seg->next->trbs; @@ -2609,10 +2613,10 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) temp_64 = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci_dbg(xhci, "Op regs DCBAA ptr = %#016llx\n", temp_64); xhci_dbg(xhci, "Slot ID %d dcbaa entry @%p = %#016llx\n", - udev->slot_id, - &xhci->dcbaa->dev_context_ptrs[udev->slot_id], - (unsigned long long) - xhci->dcbaa->dev_context_ptrs[udev->slot_id]); + udev->slot_id, + &xhci->dcbaa->dev_context_ptrs[udev->slot_id], + (unsigned long long) + le64_to_cpu(xhci->dcbaa->dev_context_ptrs[udev->slot_id])); xhci_dbg(xhci, "Output Context DMA address = %#08llx\n", (unsigned long long)virt_dev->out_ctx->dma); xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id); @@ -2626,7 +2630,8 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx); /* Use kernel assigned address for devices; store xHC assigned * address locally. */ - virt_dev->address = (slot_ctx->dev_state & DEV_ADDR_MASK) + 1; + virt_dev->address = (le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK) + + 1; /* Zero the input context control for later use */ ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx); ctrl_ctx->add_flags = 0; @@ -2670,16 +2675,16 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, spin_lock_irqsave(&xhci->lock, flags); xhci_slot_copy(xhci, config_cmd->in_ctx, vdev->out_ctx); ctrl_ctx = xhci_get_input_control_ctx(xhci, config_cmd->in_ctx); - ctrl_ctx->add_flags |= SLOT_FLAG; + ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); slot_ctx = xhci_get_slot_ctx(xhci, config_cmd->in_ctx); - slot_ctx->dev_info |= DEV_HUB; + slot_ctx->dev_info |= cpu_to_le32(DEV_HUB); if (tt->multi) - slot_ctx->dev_info |= DEV_MTT; + slot_ctx->dev_info |= cpu_to_le32(DEV_MTT); if (xhci->hci_version > 0x95) { xhci_dbg(xhci, "xHCI version %x needs hub " "TT think time and number of ports\n", (unsigned int) xhci->hci_version); - slot_ctx->dev_info2 |= XHCI_MAX_PORTS(hdev->maxchild); + slot_ctx->dev_info2 |= cpu_to_le32(XHCI_MAX_PORTS(hdev->maxchild)); /* Set TT think time - convert from ns to FS bit times. * 0 = 8 FS bit times, 1 = 16 FS bit times, * 2 = 24 FS bit times, 3 = 32 FS bit times. @@ -2687,7 +2692,7 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, think_time = tt->think_time; if (think_time != 0) think_time = (think_time / 666) - 1; - slot_ctx->tt_info |= TT_THINK_TIME(think_time); + slot_ctx->tt_info |= cpu_to_le32(TT_THINK_TIME(think_time)); } else { xhci_dbg(xhci, "xHCI version %x doesn't need hub " "TT think time or number of ports\n", diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ba1be6b7cc6..85e77980818 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -57,13 +57,13 @@ * @run_regs_off: RTSOFF - Runtime register space offset */ struct xhci_cap_regs { - u32 hc_capbase; - u32 hcs_params1; - u32 hcs_params2; - u32 hcs_params3; - u32 hcc_params; - u32 db_off; - u32 run_regs_off; + __le32 hc_capbase; + __le32 hcs_params1; + __le32 hcs_params2; + __le32 hcs_params3; + __le32 hcc_params; + __le32 db_off; + __le32 run_regs_off; /* Reserved up to (CAPLENGTH - 0x1C) */ }; @@ -155,26 +155,26 @@ struct xhci_cap_regs { * devices. */ struct xhci_op_regs { - u32 command; - u32 status; - u32 page_size; - u32 reserved1; - u32 reserved2; - u32 dev_notification; - u64 cmd_ring; + __le32 command; + __le32 status; + __le32 page_size; + __le32 reserved1; + __le32 reserved2; + __le32 dev_notification; + __le64 cmd_ring; /* rsvd: offset 0x20-2F */ - u32 reserved3[4]; - u64 dcbaa_ptr; - u32 config_reg; + __le32 reserved3[4]; + __le64 dcbaa_ptr; + __le32 config_reg; /* rsvd: offset 0x3C-3FF */ - u32 reserved4[241]; + __le32 reserved4[241]; /* port 1 registers, which serve as a base address for other ports */ - u32 port_status_base; - u32 port_power_base; - u32 port_link_base; - u32 reserved5; + __le32 port_status_base; + __le32 port_power_base; + __le32 port_link_base; + __le32 reserved5; /* registers for ports 2-255 */ - u32 reserved6[NUM_PORT_REGS*254]; + __le32 reserved6[NUM_PORT_REGS*254]; }; /* USBCMD - USB command - command bitmasks */ @@ -382,12 +382,12 @@ struct xhci_op_regs { * updates the dequeue pointer. */ struct xhci_intr_reg { - u32 irq_pending; - u32 irq_control; - u32 erst_size; - u32 rsvd; - u64 erst_base; - u64 erst_dequeue; + __le32 irq_pending; + __le32 irq_control; + __le32 erst_size; + __le32 rsvd; + __le64 erst_base; + __le64 erst_dequeue; }; /* irq_pending bitmasks */ @@ -432,8 +432,8 @@ struct xhci_intr_reg { * or larger accesses" */ struct xhci_run_regs { - u32 microframe_index; - u32 rsvd[7]; + __le32 microframe_index; + __le32 rsvd[7]; struct xhci_intr_reg ir_set[128]; }; @@ -447,7 +447,7 @@ struct xhci_run_regs { * Section 5.6 */ struct xhci_doorbell_array { - u32 doorbell[256]; + __le32 doorbell[256]; }; #define DB_VALUE(ep, stream) ((((ep) + 1) & 0xff) | ((stream) << 16)) @@ -504,12 +504,12 @@ struct xhci_container_ctx { * reserved at the end of the slot context for HC internal use. */ struct xhci_slot_ctx { - u32 dev_info; - u32 dev_info2; - u32 tt_info; - u32 dev_state; + __le32 dev_info; + __le32 dev_info2; + __le32 tt_info; + __le32 dev_state; /* offset 0x10 to 0x1f reserved for HC internal use */ - u32 reserved[4]; + __le32 reserved[4]; }; /* dev_info bitmasks */ @@ -580,12 +580,12 @@ struct xhci_slot_ctx { * reserved at the end of the endpoint context for HC internal use. */ struct xhci_ep_ctx { - u32 ep_info; - u32 ep_info2; - u64 deq; - u32 tx_info; + __le32 ep_info; + __le32 ep_info2; + __le64 deq; + __le32 tx_info; /* offset 0x14 - 0x1f reserved for HC internal use */ - u32 reserved[3]; + __le32 reserved[3]; }; /* ep_info bitmasks */ @@ -660,9 +660,9 @@ struct xhci_ep_ctx { * @add_context: set the bit of the endpoint context you want to enable */ struct xhci_input_control_ctx { - u32 drop_flags; - u32 add_flags; - u32 rsvd2[6]; + __le32 drop_flags; + __le32 add_flags; + __le32 rsvd2[6]; }; /* Represents everything that is needed to issue a command on the command ring. @@ -688,9 +688,9 @@ struct xhci_command { struct xhci_stream_ctx { /* 64-bit stream ring address, cycle state, and stream type */ - u64 stream_ring; + __le64 stream_ring; /* offset 0x14 - 0x1f reserved for HC internal use */ - u32 reserved[2]; + __le32 reserved[2]; }; /* Stream Context Types (section 6.4.1) - bits 3:1 of stream ctx deq ptr */ @@ -803,7 +803,7 @@ struct xhci_virt_device { */ struct xhci_device_context_array { /* 64-bit device addresses; we only write 32-bit addresses */ - u64 dev_context_ptrs[MAX_HC_SLOTS]; + __le64 dev_context_ptrs[MAX_HC_SLOTS]; /* private xHCD pointers */ dma_addr_t dma; }; @@ -816,10 +816,10 @@ struct xhci_device_context_array { struct xhci_transfer_event { /* 64-bit buffer address, or immediate data */ - u64 buffer; - u32 transfer_len; + __le64 buffer; + __le32 transfer_len; /* This field is interpreted differently based on the type of TRB */ - u32 flags; + __le32 flags; }; /** Transfer Event bit fields **/ @@ -898,9 +898,9 @@ struct xhci_transfer_event { struct xhci_link_trb { /* 64-bit segment pointer*/ - u64 segment_ptr; - u32 intr_target; - u32 control; + __le64 segment_ptr; + __le32 intr_target; + __le32 control; }; /* control bitfields */ @@ -909,9 +909,9 @@ struct xhci_link_trb { /* Command completion event TRB */ struct xhci_event_cmd { /* Pointer to command TRB, or the value passed by the event data trb */ - u64 cmd_trb; - u32 status; - u32 flags; + __le64 cmd_trb; + __le32 status; + __le32 flags; }; /* flags bitmasks */ @@ -970,7 +970,7 @@ struct xhci_event_cmd { #define TRB_SIA (1<<31) struct xhci_generic_trb { - u32 field[4]; + __le32 field[4]; }; union xhci_trb { @@ -1118,10 +1118,10 @@ struct xhci_ring { struct xhci_erst_entry { /* 64-bit event ring segment address */ - u64 seg_addr; - u32 seg_size; + __le64 seg_addr; + __le32 seg_size; /* Set to zero */ - u32 rsvd; + __le32 rsvd; }; struct xhci_erst { @@ -1286,10 +1286,10 @@ struct xhci_hcd { /* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */ u8 *port_array; /* Array of pointers to USB 3.0 PORTSC registers */ - u32 __iomem **usb3_ports; + __le32 __iomem **usb3_ports; unsigned int num_usb3_ports; /* Array of pointers to USB 2.0 PORTSC registers */ - u32 __iomem **usb2_ports; + __le32 __iomem **usb2_ports; unsigned int num_usb2_ports; }; @@ -1322,12 +1322,12 @@ static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci) /* TODO: copied from ehci.h - can be refactored? */ /* xHCI spec says all registers are little endian */ static inline unsigned int xhci_readl(const struct xhci_hcd *xhci, - __u32 __iomem *regs) + __le32 __iomem *regs) { return readl(regs); } static inline void xhci_writel(struct xhci_hcd *xhci, - const unsigned int val, __u32 __iomem *regs) + const unsigned int val, __le32 __iomem *regs) { xhci_dbg(xhci, "`MEM_WRITE_DWORD(3'b000, 32'h%p, 32'h%0x, 4'hf);\n", @@ -1345,7 +1345,7 @@ static inline void xhci_writel(struct xhci_hcd *xhci, * the high dword, and write order is irrelevant. */ static inline u64 xhci_read_64(const struct xhci_hcd *xhci, - __u64 __iomem *regs) + __le64 __iomem *regs) { __u32 __iomem *ptr = (__u32 __iomem *) regs; u64 val_lo = readl(ptr); @@ -1353,7 +1353,7 @@ static inline u64 xhci_read_64(const struct xhci_hcd *xhci, return val_lo + (val_hi << 32); } static inline void xhci_write_64(struct xhci_hcd *xhci, - const u64 val, __u64 __iomem *regs) + const u64 val, __le64 __iomem *regs) { __u32 __iomem *ptr = (__u32 __iomem *) regs; u32 val_lo = lower_32_bits(val); -- cgit v1.2.3 From 92a3da410aac6e14daaefe13c60368ca28e85830 Mon Sep 17 00:00:00 2001 From: Matt Evans Date: Tue, 29 Mar 2011 13:40:51 +1100 Subject: xhci: Add rmb() between reading event validity & event data access. On weakly-ordered systems, the reading of an event's content must occur after reading the event's validity. Signed-off-by: Matt Evans Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 9b1eeb04ce6..e0f05f5abe1 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2193,6 +2193,11 @@ static void xhci_handle_event(struct xhci_hcd *xhci) } xhci_dbg(xhci, "%s - OS owns TRB\n", __func__); + /* + * Barrier between reading the TRB_CYCLE (valid) flag above and any + * speculative reads of the event's flags/data below. + */ + rmb(); /* FIXME: Handle more event types. */ switch ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK)) { case TRB_TYPE(TRB_COMPLETION): -- cgit v1.2.3 From 7ed603ecf8b68ab81f4c83097d3063d43ec73bb8 Mon Sep 17 00:00:00 2001 From: Matt Evans Date: Tue, 29 Mar 2011 13:40:56 +1100 Subject: xhci: Add an assertion to check for virt_dev=0 bug. During a "plug-unplug" stress test on an NEC xHCI card, a null pointer dereference was observed. xhci_address_device() dereferenced a null virt_dev (possibly an erroneous udev->slot_id?); this patch adds a WARN_ON & message to aid debug if it can be recreated. Signed-off-by: Matt Evans Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index e8ab1899c88..5c0ae90df31 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2546,6 +2546,17 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) virt_dev = xhci->devs[udev->slot_id]; + if (WARN_ON(!virt_dev)) { + /* + * In plug/unplug torture test with an NEC controller, + * a zero-dereference was observed once due to virt_dev = 0. + * Print useful debug rather than crash if it is observed again! + */ + xhci_warn(xhci, "Virt dev invalid for slot_id 0x%x!\n", + udev->slot_id); + return -EINVAL; + } + slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); /* * If this is the first Set Address since device plug-in or -- cgit v1.2.3 From 9dee9a213cb90fdc13118ab221f65c9fa6944f7a Mon Sep 17 00:00:00 2001 From: Matt Evans Date: Tue, 29 Mar 2011 13:41:02 +1100 Subject: xhci: Remove recursive call to xhci_handle_event Make the caller loop while there are events to handle, instead. Signed-off-by: Matt Evans Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e0f05f5abe1..73f8db0ecc4 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2171,8 +2171,10 @@ cleanup: /* * This function handles all OS-owned events on the event ring. It may drop * xhci->lock between event processing (e.g. to pass up port status changes). + * Returns >0 for "possibly more events to process" (caller should call again), + * otherwise 0 if done. In future, <0 returns should indicate error code. */ -static void xhci_handle_event(struct xhci_hcd *xhci) +static int xhci_handle_event(struct xhci_hcd *xhci) { union xhci_trb *event; int update_ptrs = 1; @@ -2181,7 +2183,7 @@ static void xhci_handle_event(struct xhci_hcd *xhci) xhci_dbg(xhci, "In %s\n", __func__); if (!xhci->event_ring || !xhci->event_ring->dequeue) { xhci->error_bitmask |= 1 << 1; - return; + return 0; } event = xhci->event_ring->dequeue; @@ -2189,7 +2191,7 @@ static void xhci_handle_event(struct xhci_hcd *xhci) if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) != xhci->event_ring->cycle_state) { xhci->error_bitmask |= 1 << 2; - return; + return 0; } xhci_dbg(xhci, "%s - OS owns TRB\n", __func__); @@ -2233,15 +2235,17 @@ static void xhci_handle_event(struct xhci_hcd *xhci) if (xhci->xhc_state & XHCI_STATE_DYING) { xhci_dbg(xhci, "xHCI host dying, returning from " "event handler.\n"); - return; + return 0; } if (update_ptrs) /* Update SW event ring dequeue pointer */ inc_deq(xhci, xhci->event_ring, true); - /* Are there more items on the event ring? */ - xhci_handle_event(xhci); + /* Are there more items on the event ring? Caller will call us again to + * check. + */ + return 1; } /* @@ -2323,7 +2327,7 @@ hw_died: /* FIXME this should be a delayed service routine * that clears the EHB. */ - xhci_handle_event(xhci); + while (xhci_handle_event(xhci) > 0) {} temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); /* If necessary, update the HW's version of the event ring deq ptr. */ -- cgit v1.2.3 From 64b3c304bed25388fed48dbdc098dfcad7063d9c Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 11 Apr 2011 20:19:12 +0200 Subject: usb/ch9: use proper endianess for wBytesPerInterval while going through Tatyana's changes for the gadget framework I noticed that this type is not defined as __le16. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp --- drivers/usb/core/config.c | 2 +- drivers/usb/host/xhci-mem.c | 2 +- include/linux/usb/ch9.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 83126b03e7c..c962608b4b9 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -129,7 +129,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1); else max_tx = 999999; - if (desc->wBytesPerInterval > max_tx) { + if (le16_to_cpu(desc->wBytesPerInterval) > max_tx) { dev_warn(ddev, "%s endpoint with wBytesPerInterval of %d in " "config %d interface %d altsetting %d ep %d: " "setting to %d\n", diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 500ec7a9eb8..a4fc4d92938 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1130,7 +1130,7 @@ static u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci, return 0; if (udev->speed == USB_SPEED_SUPER) - return ep->ss_ep_comp.wBytesPerInterval; + return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize)); max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) & 0x1800) >> 11; diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index b72f305ce6b..0fd3fbdd828 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -579,7 +579,7 @@ struct usb_ss_ep_comp_descriptor { __u8 bMaxBurst; __u8 bmAttributes; - __u16 wBytesPerInterval; + __le16 wBytesPerInterval; } __attribute__ ((packed)); #define USB_DT_SS_EP_COMP_SIZE 6 -- cgit v1.2.3 From a11496ebf37534177d67222285e8debed7a39788 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Wed, 27 Apr 2011 18:07:29 +0800 Subject: xHCI: warm reset support This patch adds warm reset support to xhci hub control. It handles Set Port Feature(BH_PORT_RESET) and Clear Port Feature (C_BH_PORT_RESET) request from usbcore. Note warm reset is called BH reset some places in USB3.0 specification. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index ae1d24cb930..1ab7f2e2c05 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -341,6 +341,10 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, status = PORT_RC; port_change_bit = "reset"; break; + case USB_PORT_FEAT_C_BH_PORT_RESET: + status = PORT_WRC; + port_change_bit = "warm(BH) reset"; + break; case USB_PORT_FEAT_C_CONNECTION: status = PORT_CSC; port_change_bit = "connect"; @@ -557,6 +561,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); break; + case USB_PORT_FEAT_BH_PORT_RESET: + temp |= PORT_WR; + xhci_writel(xhci, temp, port_array[wIndex]); + + temp = xhci_readl(xhci, port_array[wIndex]); + break; default: goto error; } @@ -625,6 +635,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_SUSPEND: bus_state->port_c_suspend &= ~(1 << wIndex); case USB_PORT_FEAT_C_RESET: + case USB_PORT_FEAT_C_BH_PORT_RESET: case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_ENABLE: -- cgit v1.2.3 From 85387c0ea3e1cd85ad9d7215917ff5e71ca2aea3 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Wed, 27 Apr 2011 18:07:35 +0800 Subject: xHCI: Clear link state change support This patch adds support for Clear Port Feature(C_PORT_LINK_STATE) request from usbcore. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 1ab7f2e2c05..f3bafba4b5c 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -361,6 +361,10 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, status = PORT_PLC; port_change_bit = "suspend/resume"; break; + case USB_PORT_FEAT_C_PORT_LINK_STATE: + status = PORT_PLC; + port_change_bit = "link state"; + break; default: /* Should never happen */ return; @@ -639,6 +643,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_ENABLE: + case USB_PORT_FEAT_C_PORT_LINK_STATE: xhci_clear_port_change_bit(xhci, wValue, wIndex, port_array[wIndex], temp); break; -- cgit v1.2.3 From 2c44178032b046c4113c40d0d459a0d36e39b920 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Wed, 27 Apr 2011 18:07:39 +0800 Subject: xHCI: Set link state support This patch adds support for Set Port Feature(PORT_LINK_STATE) request. The most significant byte (bits 15..8) of the wIndex field specifies the U state the host software wants to put the link connected to the port into. This request is only valid when the PORT_ENABLE bit is set and the PORT_LINK_STATE should not be above value '5' (Rx.Detect). This request will be later used to replace the set/clear suspend USB3 protocol ports in hub driver. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index f3bafba4b5c..b8753544238 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -387,6 +387,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, __le32 __iomem **port_array; int slot_id; struct xhci_bus_state *bus_state; + u16 link_state = 0; if (hcd->speed == HCD_USB3) { ports = xhci->num_usb3_ports; @@ -497,6 +498,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, put_unaligned(cpu_to_le32(status), (__le32 *) buf); break; case SetPortFeature: + if (wValue == USB_PORT_FEAT_LINK_STATE) + link_state = (wIndex & 0xff00) >> 3; wIndex &= 0xff; if (!wIndex || wIndex > ports) goto error; @@ -545,6 +548,44 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_readl(xhci, port_array[wIndex]); bus_state->suspended_ports |= 1 << wIndex; break; + case USB_PORT_FEAT_LINK_STATE: + temp = xhci_readl(xhci, port_array[wIndex]); + /* Software should not attempt to set + * port link state above '5' (Rx.Detect) and the port + * must be enabled. + */ + if ((temp & PORT_PE) == 0 || + (link_state > USB_SS_PORT_LS_RX_DETECT)) { + xhci_warn(xhci, "Cannot set link state.\n"); + goto error; + } + + if (link_state == USB_SS_PORT_LS_U3) { + slot_id = xhci_find_slot_id_by_port(hcd, xhci, + wIndex + 1); + if (slot_id) { + /* unlock to execute stop endpoint + * commands */ + spin_unlock_irqrestore(&xhci->lock, + flags); + xhci_stop_device(xhci, slot_id, 1); + spin_lock_irqsave(&xhci->lock, flags); + } + } + + temp = xhci_port_state_to_neutral(temp); + temp &= ~PORT_PLS_MASK; + temp |= PORT_LINK_STROBE | link_state; + xhci_writel(xhci, temp, port_array[wIndex]); + + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(20); /* wait device to enter */ + spin_lock_irqsave(&xhci->lock, flags); + + temp = xhci_readl(xhci, port_array[wIndex]); + if (link_state == USB_SS_PORT_LS_U3) + bus_state->suspended_ports |= 1 << wIndex; + break; case USB_PORT_FEAT_POWER: /* * Turn on ports, even if there isn't per-port switching. -- cgit v1.2.3 From 0ed9a57e052a3d20df052a2ff12a3b42380867aa Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Wed, 27 Apr 2011 18:07:43 +0800 Subject: xHCI: report USB3.0 portstatus comply with USB3.0 specification USB3.0 specification has different wPortStatus and wPortChange definitions from USB2.0 specification. Since USB3 root hub and USB2 root hub are split now and USB3 hub only has USB3 protocol ports, we should modify the portstatus and portchange report of USB3 ports to comply with USB3.0 specification. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 52 ++++++++++++++++++++++++++++++++------------- drivers/usb/host/xhci-hub.c | 41 ++++++++++++++++++++++++----------- 2 files changed, 66 insertions(+), 27 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 93720bdc9ef..dcd78c15f8c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -379,15 +379,6 @@ static int hub_port_status(struct usb_hub *hub, int port1, *status = le16_to_cpu(hub->status->port.wPortStatus); *change = le16_to_cpu(hub->status->port.wPortChange); - if ((hub->hdev->parent != NULL) && - hub_is_superspeed(hub->hdev)) { - /* Translate the USB 3 port status */ - u16 tmp = *status & USB_SS_PORT_STAT_MASK; - if (*status & USB_SS_PORT_STAT_POWER) - tmp |= USB_PORT_STAT_POWER; - *status = tmp; - } - ret = 0; } mutex_unlock(&hub->status_mutex); @@ -2160,11 +2151,40 @@ static int hub_port_reset(struct usb_hub *hub, int port1, return status; } +/* Check if a port is power on */ +static int port_is_power_on(struct usb_hub *hub, unsigned portstatus) +{ + int ret = 0; + + if (hub_is_superspeed(hub->hdev)) { + if (portstatus & USB_SS_PORT_STAT_POWER) + ret = 1; + } else { + if (portstatus & USB_PORT_STAT_POWER) + ret = 1; + } + + return ret; +} + #ifdef CONFIG_PM -#define MASK_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | \ - USB_PORT_STAT_SUSPEND) -#define WANT_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION) +/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */ +static int port_is_suspended(struct usb_hub *hub, unsigned portstatus) +{ + int ret = 0; + + if (hub_is_superspeed(hub->hdev)) { + if ((portstatus & USB_PORT_STAT_LINK_STATE) + == USB_SS_PORT_LS_U3) + ret = 1; + } else { + if (portstatus & USB_PORT_STAT_SUSPEND) + ret = 1; + } + + return ret; +} /* Determine whether the device on a port is ready for a normal resume, * is ready for a reset-resume, or should be disconnected. @@ -2174,7 +2194,9 @@ static int check_port_resume_type(struct usb_device *udev, int status, unsigned portchange, unsigned portstatus) { /* Is the device still present? */ - if (status || (portstatus & MASK_BITS) != WANT_BITS) { + if (status || port_is_suspended(hub, portstatus) || + !port_is_power_on(hub, portstatus) || + !(portstatus & USB_PORT_STAT_CONNECTION)) { if (status >= 0) status = -ENODEV; } @@ -2439,7 +2461,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) /* Skip the initial Clear-Suspend step for a remote wakeup */ status = hub_port_status(hub, port1, &portstatus, &portchange); - if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND)) + if (status == 0 && !port_is_suspended(hub, portstatus)) goto SuspendCleared; // dev_dbg(hub->intfdev, "resume port %d\n", port1); @@ -3147,7 +3169,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, /* maybe switch power back on (e.g. root hub was reset) */ if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 - && !(portstatus & USB_PORT_STAT_POWER)) + && !port_is_power_on(hub, portstatus)) set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); if (portstatus & USB_PORT_STAT_ENABLE) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index b8753544238..4a3ca99fc64 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -431,9 +431,6 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp); - /* FIXME - should we return a port status value like the USB - * 3.0 external hubs do? - */ /* wPortChange bits */ if (temp & PORT_CSC) status |= USB_PORT_STAT_C_CONNECTION << 16; @@ -441,13 +438,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, status |= USB_PORT_STAT_C_ENABLE << 16; if ((temp & PORT_OCC)) status |= USB_PORT_STAT_C_OVERCURRENT << 16; - /* - * FIXME ignoring reset and USB 2.1/3.0 specific - * changes - */ - if ((temp & PORT_PLS_MASK) == XDEV_U3 - && (temp & PORT_POWER)) - status |= 1 << USB_PORT_FEAT_SUSPEND; + if ((temp & PORT_RC)) + status |= USB_PORT_STAT_C_RESET << 16; + /* USB3.0 only */ + if (hcd->speed == HCD_USB3) { + if ((temp & PORT_PLC)) + status |= USB_PORT_STAT_C_LINK_STATE << 16; + if ((temp & PORT_WRC)) + status |= USB_PORT_STAT_C_BH_RESET << 16; + } + + if (hcd->speed != HCD_USB3) { + if ((temp & PORT_PLS_MASK) == XDEV_U3 + && (temp & PORT_POWER)) + status |= USB_PORT_STAT_SUSPEND; + } if ((temp & PORT_PLS_MASK) == XDEV_RESUME) { if ((temp & PORT_RESET) || !(temp & PORT_PE)) goto error; @@ -490,8 +495,20 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, status |= USB_PORT_STAT_OVERCURRENT; if (temp & PORT_RESET) status |= USB_PORT_STAT_RESET; - if (temp & PORT_POWER) - status |= USB_PORT_STAT_POWER; + if (temp & PORT_POWER) { + if (hcd->speed == HCD_USB3) + status |= USB_SS_PORT_STAT_POWER; + else + status |= USB_PORT_STAT_POWER; + } + /* Port Link State */ + if (hcd->speed == HCD_USB3) { + /* resume state is a xHCI internal state. + * Do not report it to usb core. + */ + if ((temp & PORT_PLS_MASK) != XDEV_RESUME) + status |= (temp & PORT_PLS_MASK); + } if (bus_state->port_c_suspend & (1 << wIndex)) status |= 1 << USB_PORT_FEAT_C_SUSPEND; xhci_dbg(xhci, "Get port status returned 0x%x\n", status); -- cgit v1.2.3 From a7114230f6bd925f1c734d8ca1c32c93bf956aed Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Wed, 27 Apr 2011 18:07:50 +0800 Subject: usbcore: Refine USB3.0 device suspend and resume In the past, we use USB2.0 request to suspend and resume a USB3.0 device. Actually, USB3.0 hub does not support Set/Clear PORT_SUSPEND request, instead, it uses Set PORT_LINK_STATE request. This patch makes USB3.0 device suspend/resume comply with USB3.0 specification. This patch fixes the issue that USB3.0 device can not be suspended when connected to a USB3.0 external hub. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 33 ++++++++++++++++++------------ drivers/usb/host/xhci-hub.c | 49 +++++++++++++++++++-------------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index dcd78c15f8c..93035d862c6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2307,14 +2307,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) } /* see 7.1.7.6 */ - /* Clear PORT_POWER if it's a USB3.0 device connected to USB 3.0 - * external hub. - * FIXME: this is a temporary workaround to make the system able - * to suspend/resume. - */ - if ((hub->hdev->parent != NULL) && hub_is_superspeed(hub->hdev)) - status = clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_POWER); + if (hub_is_superspeed(hub->hdev)) + status = set_port_feature(hub->hdev, + port1 | (USB_SS_PORT_LS_U3 << 3), + USB_PORT_FEAT_LINK_STATE); else status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); @@ -2469,8 +2465,13 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) set_bit(port1, hub->busy_bits); /* see 7.1.7.7; affects power usage, but not budgeting */ - status = clear_port_feature(hub->hdev, - port1, USB_PORT_FEAT_SUSPEND); + if (hub_is_superspeed(hub->hdev)) + status = set_port_feature(hub->hdev, + port1 | (USB_SS_PORT_LS_U0 << 3), + USB_PORT_FEAT_LINK_STATE); + else + status = clear_port_feature(hub->hdev, + port1, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(hub->intfdev, "can't resume port %d, status %d\n", port1, status); @@ -2492,9 +2493,15 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) SuspendCleared: if (status == 0) { - if (portchange & USB_PORT_STAT_C_SUSPEND) - clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_SUSPEND); + if (hub_is_superspeed(hub->hdev)) { + if (portchange & USB_PORT_STAT_C_LINK_STATE) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + } else { + if (portchange & USB_PORT_STAT_C_SUSPEND) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_SUSPEND); + } } clear_bit(port1, hub->busy_bits); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 4a3ca99fc64..e3ddc6a95af 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -483,7 +483,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, && (temp & PORT_POWER) && (bus_state->suspended_ports & (1 << wIndex))) { bus_state->suspended_ports &= ~(1 << wIndex); - bus_state->port_c_suspend |= 1 << wIndex; + if (hcd->speed != HCD_USB3) + bus_state->port_c_suspend |= 1 << wIndex; } if (temp & PORT_CONNECT) { status |= USB_PORT_STAT_CONNECTION; @@ -656,35 +657,27 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (temp & XDEV_U3) { if ((temp & PORT_PE) == 0) goto error; - if (DEV_SUPERSPEED(temp)) { - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, - port_array[wIndex]); - xhci_readl(xhci, port_array[wIndex]); - } else { - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_RESUME; - xhci_writel(xhci, temp, - port_array[wIndex]); - spin_unlock_irqrestore(&xhci->lock, - flags); - msleep(20); - spin_lock_irqsave(&xhci->lock, flags); + temp = xhci_port_state_to_neutral(temp); + temp &= ~PORT_PLS_MASK; + temp |= PORT_LINK_STROBE | XDEV_RESUME; + xhci_writel(xhci, temp, + port_array[wIndex]); - temp = xhci_readl(xhci, - port_array[wIndex]); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, - port_array[wIndex]); - } - bus_state->port_c_suspend |= 1 << wIndex; + spin_unlock_irqrestore(&xhci->lock, + flags); + msleep(20); + spin_lock_irqsave(&xhci->lock, flags); + + temp = xhci_readl(xhci, + port_array[wIndex]); + temp = xhci_port_state_to_neutral(temp); + temp &= ~PORT_PLS_MASK; + temp |= PORT_LINK_STROBE | XDEV_U0; + xhci_writel(xhci, temp, + port_array[wIndex]); } + bus_state->port_c_suspend |= 1 << wIndex; slot_id = xhci_find_slot_id_by_port(hcd, xhci, wIndex + 1); @@ -755,7 +748,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) memset(buf, 0, retval); status = 0; - mask = PORT_CSC | PORT_PEC | PORT_OCC; + mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC; spin_lock_irqsave(&xhci->lock, flags); /* For each port, did anything change? If so, set that bit in buf. */ -- cgit v1.2.3 From 5e467f6ebab151b2f0166e17348e5b85ae3c87fa Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Wed, 27 Apr 2011 18:07:54 +0800 Subject: usbcore: warm reset USB3 port in SS.Inactive state Some USB3.0 devices go to SS.Inactive state when hot plug to USB3 ports. Warm reset the port to transition it to U0 state. This patch fixes the issue that Kingston USB3.0 flash drive can not be recognized when hot plug to USB3 port. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 93035d862c6..79a58c3a2e2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2151,6 +2151,42 @@ static int hub_port_reset(struct usb_hub *hub, int port1, return status; } +/* Warm reset a USB3 protocol port */ +static int hub_port_warm_reset(struct usb_hub *hub, int port) +{ + int ret; + u16 portstatus, portchange; + + if (!hub_is_superspeed(hub->hdev)) { + dev_err(hub->intfdev, "only USB3 hub support warm reset\n"); + return -EINVAL; + } + + /* Warm reset the port */ + ret = set_port_feature(hub->hdev, + port, USB_PORT_FEAT_BH_PORT_RESET); + if (ret) { + dev_err(hub->intfdev, "cannot warm reset port %d\n", port); + return ret; + } + + msleep(20); + ret = hub_port_status(hub, port, &portstatus, &portchange); + + if (portchange & USB_PORT_STAT_C_RESET) + clear_port_feature(hub->hdev, port, USB_PORT_FEAT_C_RESET); + + if (portchange & USB_PORT_STAT_C_BH_RESET) + clear_port_feature(hub->hdev, port, + USB_PORT_FEAT_C_BH_PORT_RESET); + + if (portchange & USB_PORT_STAT_C_LINK_STATE) + clear_port_feature(hub->hdev, port, + USB_PORT_FEAT_C_PORT_LINK_STATE); + + return ret; +} + /* Check if a port is power on */ static int port_is_power_on(struct usb_hub *hub, unsigned portstatus) { @@ -3519,6 +3555,16 @@ static void hub_events(void) USB_PORT_FEAT_C_PORT_CONFIG_ERROR); } + /* Warm reset a USB3 protocol port if it's in + * SS.Inactive state. + */ + if (hub_is_superspeed(hub->hdev) && + (portstatus & USB_PORT_STAT_LINK_STATE) + == USB_SS_PORT_LS_SS_INACTIVE) { + dev_dbg(hub_dev, "warm reset port %d\n", i); + hub_port_warm_reset(hub, i); + } + if (connect_change) hub_port_connect_change(hub, i, portstatus, portchange); -- cgit v1.2.3 From 00161f7d04eb1668fde5e22d3e5a17bf90356d2c Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 28 Apr 2011 12:23:23 -0700 Subject: xhci: Remove sparse warning about cmd_status. Sparse complains about the arguments to xhci_evaluate_context_result() and xhci_configure_endpoint_result(): CHECK drivers/usb/host/xhci.c drivers/usb/host/xhci.c:1647:53: warning: incorrect type in argument 3 (different signedness) drivers/usb/host/xhci.c:1647:53: expected int *cmd_status drivers/usb/host/xhci.c:1647:53: got unsigned int [usertype] *[assigned] cmd_status drivers/usb/host/xhci.c:1648:50: warning: incorrect type in argument 3 (different signedness) drivers/usb/host/xhci.c:1648:50: expected int *cmd_status drivers/usb/host/xhci.c:1648:50: got unsigned int [usertype] *[assigned] cmd_status The command status is taken from the command completion event TRB, and will always be a positive number. Change the signature of xhci_evaluate_context_result() and xhci_configure_endpoint_result() to take a u32 for cmd_status. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 5c0ae90df31..6864759c8d1 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1502,7 +1502,7 @@ static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *vir } static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, - struct usb_device *udev, int *cmd_status) + struct usb_device *udev, u32 *cmd_status) { int ret; @@ -1540,7 +1540,7 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, } static int xhci_evaluate_context_result(struct xhci_hcd *xhci, - struct usb_device *udev, int *cmd_status) + struct usb_device *udev, u32 *cmd_status) { int ret; struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id]; -- cgit v1.2.3 From af8b9e636065ba1701c4215a8dc4f7a1d69d934b Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 23 Mar 2011 16:26:26 -0700 Subject: xhci 1.0: Only interrupt on short packet for IN EPs. It doesn't make sense to set the interrupt on short packet (TRB_ISP) flag for TRBs queued to endpoints that only receive packets from the host controller (i.e. OUT endpoints). Packets can only be short when they are sent from a USB device. Plus, the xHCI 1.0 specification forbids setting the flag for anything but IN endpoints. While we're at it, remove some of my snide remarks about the inefficiency of event data TRBs. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 45 +++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 73f8db0ecc4..766f6a615b2 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2741,6 +2741,11 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td->last_trb = ep_ring->enqueue; field |= TRB_IOC; } + + /* Only set interrupt on short packet for IN endpoints */ + if (usb_urb_dir_in(urb)) + field |= TRB_ISP; + xhci_dbg(xhci, " sg entry: dma = %#x, len = %#x (%d), " "64KB boundary at %#x, end dma = %#x\n", (unsigned int) addr, trb_buff_len, trb_buff_len, @@ -2766,12 +2771,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, lower_32_bits(addr), upper_32_bits(addr), length_field, - /* We always want to know if the TRB was short, - * or we won't get an event when it completes. - * (Unless we use event data TRBs, which are a - * waste of space and HC resources.) - */ - field | TRB_ISP | TRB_TYPE(TRB_NORMAL)); + field | TRB_TYPE(TRB_NORMAL)); --num_trbs; running_total += trb_buff_len; @@ -2905,6 +2905,11 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td->last_trb = ep_ring->enqueue; field |= TRB_IOC; } + + /* Only set interrupt on short packet for IN endpoints */ + if (usb_urb_dir_in(urb)) + field |= TRB_ISP; + remainder = xhci_td_remainder(urb->transfer_buffer_length - running_total); length_field = TRB_LEN(trb_buff_len) | @@ -2918,12 +2923,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, lower_32_bits(addr), upper_32_bits(addr), length_field, - /* We always want to know if the TRB was short, - * or we won't get an event when it completes. - * (Unless we use event data TRBs, which are a - * waste of space and HC resources.) - */ - field | TRB_ISP | TRB_TYPE(TRB_NORMAL)); + field | TRB_TYPE(TRB_NORMAL)); --num_trbs; running_total += trb_buff_len; @@ -3009,7 +3009,12 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field); /* If there's data, queue data TRBs */ - field = 0; + /* Only set interrupt on short packet for IN endpoints */ + if (usb_urb_dir_in(urb)) + field = TRB_ISP | TRB_TYPE(TRB_DATA); + else + field = TRB_TYPE(TRB_DATA); + length_field = TRB_LEN(urb->transfer_buffer_length) | xhci_td_remainder(urb->transfer_buffer_length) | TRB_INTR_TARGET(0); @@ -3020,8 +3025,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, lower_32_bits(urb->transfer_dma), upper_32_bits(urb->transfer_dma), length_field, - /* Event on short tx */ - field | TRB_ISP | TRB_TYPE(TRB_DATA) | ep_ring->cycle_state); + field | ep_ring->cycle_state); } /* Save the DMA address of the last TRB in the TD */ @@ -3145,6 +3149,10 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field |= ep_ring->cycle_state; } + /* Only set interrupt on short packet for IN EPs */ + if (usb_urb_dir_in(urb)) + field |= TRB_ISP; + /* Chain all the TRBs together; clear the chain bit in * the last TRB to indicate it's the last TRB in the * chain. @@ -3172,12 +3180,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, lower_32_bits(addr), upper_32_bits(addr), length_field, - /* We always want to know if the TRB was short, - * or we won't get an event when it completes. - * (Unless we use event data TRBs, which are a - * waste of space and HC resources.) - */ - field | TRB_ISP); + field); running_total += trb_buff_len; addr += trb_buff_len; -- cgit v1.2.3 From 4da6e6f247a2601ab9f1e63424e4d944ed4124f3 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 1 Apr 2011 14:01:30 -0700 Subject: xhci 1.0: Update TD size field format. The xHCI 1.0 specification changes the format of the TD size field in Normal and Isochronous TRBs. The field in control TRBs is still set to reserved zero. Instead of representing the number of bytes left to transfer in the TD (including the current TRB's buffer), it now represents the number of packets left to transfer (*not* including this TRB). See section 4.11.2.4 of the xHCI 1.0 specification for details. The math is basically copied straight from there. Create a new function, xhci_v1_0_td_remainder(), that should be called for all xHCI 1.0 host controllers. The field location and maximum value is still the same, so reuse the old function, xhci_td_remainder(), to handle the bit shifting. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 76 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 766f6a615b2..27d690d889a 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2655,6 +2655,35 @@ static u32 xhci_td_remainder(unsigned int remainder) return (remainder >> 10) << 17; } +/* + * For xHCI 1.0 host controllers, TD size is the number of packets remaining in + * the TD (*not* including this TRB). + * + * Total TD packet count = total_packet_count = + * roundup(TD size in bytes / wMaxPacketSize) + * + * Packets transferred up to and including this TRB = packets_transferred = + * rounddown(total bytes transferred including this TRB / wMaxPacketSize) + * + * TD size = total_packet_count - packets_transferred + * + * It must fit in bits 21:17, so it can't be bigger than 31. + */ + +static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len, + unsigned int total_packet_count, struct urb *urb) +{ + int packets_transferred; + + /* All the TRB queueing functions don't count the current TRB in + * running_total. + */ + packets_transferred = (running_total + trb_buff_len) / + le16_to_cpu(urb->ep->desc.wMaxPacketSize); + + return xhci_td_remainder(total_packet_count - packets_transferred); +} + static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index) { @@ -2665,6 +2694,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct scatterlist *sg; int num_sgs; int trb_buff_len, this_sg_len, running_total; + unsigned int total_packet_count; bool first_trb; u64 addr; bool more_trbs_coming; @@ -2678,6 +2708,8 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, num_trbs = count_sg_trbs_needed(xhci, urb); num_sgs = urb->num_sgs; + total_packet_count = roundup(urb->transfer_buffer_length, + le16_to_cpu(urb->ep->desc.wMaxPacketSize)); trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, @@ -2758,11 +2790,20 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, (unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1), (unsigned int) addr + trb_buff_len); } - remainder = xhci_td_remainder(urb->transfer_buffer_length - - running_total) ; + + /* Set the TRB length, TD size, and interrupter fields. */ + if (xhci->hci_version < 0x100) { + remainder = xhci_td_remainder( + urb->transfer_buffer_length - + running_total); + } else { + remainder = xhci_v1_0_td_remainder(running_total, + trb_buff_len, total_packet_count, urb); + } length_field = TRB_LEN(trb_buff_len) | remainder | TRB_INTR_TARGET(0); + if (num_trbs > 1) more_trbs_coming = true; else @@ -2819,6 +2860,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, u32 field, length_field; int running_total, trb_buff_len, ret; + unsigned int total_packet_count; u64 addr; if (urb->num_sgs) @@ -2873,6 +2915,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, start_cycle = ep_ring->cycle_state; running_total = 0; + total_packet_count = roundup(urb->transfer_buffer_length, + le16_to_cpu(urb->ep->desc.wMaxPacketSize)); /* How much data is in the first TRB? */ addr = (u64) urb->transfer_dma; trb_buff_len = TRB_MAX_BUFF_SIZE - @@ -2910,11 +2954,19 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (usb_urb_dir_in(urb)) field |= TRB_ISP; - remainder = xhci_td_remainder(urb->transfer_buffer_length - - running_total); + /* Set the TRB length, TD size, and interrupter fields. */ + if (xhci->hci_version < 0x100) { + remainder = xhci_td_remainder( + urb->transfer_buffer_length - + running_total); + } else { + remainder = xhci_v1_0_td_remainder(running_total, + trb_buff_len, total_packet_count, urb); + } length_field = TRB_LEN(trb_buff_len) | remainder | TRB_INTR_TARGET(0); + if (num_trbs > 1) more_trbs_coming = true; else @@ -3111,12 +3163,15 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* Queue the first TRB, even if it's zero-length */ for (i = 0; i < num_tds; i++) { - first_trb = true; + unsigned int total_packet_count; + first_trb = true; running_total = 0; addr = start_addr + urb->iso_frame_desc[i].offset; td_len = urb->iso_frame_desc[i].length; td_remain_len = td_len; + total_packet_count = roundup(td_len, + le16_to_cpu(urb->ep->desc.wMaxPacketSize)); trbs_per_td = count_isoc_trbs_needed(xhci, urb, i); @@ -3172,10 +3227,19 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (trb_buff_len > td_remain_len) trb_buff_len = td_remain_len; - remainder = xhci_td_remainder(td_len - running_total); + /* Set the TRB length, TD size, & interrupter fields. */ + if (xhci->hci_version < 0x100) { + remainder = xhci_td_remainder( + td_len - running_total); + } else { + remainder = xhci_v1_0_td_remainder( + running_total, trb_buff_len, + total_packet_count, urb); + } length_field = TRB_LEN(trb_buff_len) | remainder | TRB_INTR_TARGET(0); + queue_trb(xhci, ep_ring, false, more_trbs_coming, lower_32_bits(addr), upper_32_bits(addr), -- cgit v1.2.3 From 5cd43e33b9519143f06f507dd7cbee6b7a621885 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 8 Apr 2011 09:37:29 -0700 Subject: xhci 1.0: Set transfer burst count field. The xHCI 1.0 specification adds a new field to the fourth dword in an isochronous TRB: the transfer burst count (TBC). This field is only non-zero for SuperSpeed devices. Each SS endpoint sets the bMaxBurst field in the SuperSpeed endpoint companion descriptor, which indicates how many max-packet-sized "bursts" it can handle in one service interval. The device driver may choose to burst less max packet sized chunks each service interval (which is defined by one TD). The xHCI driver indicates to the host controller how many bursts it needs to schedule through the transfer burst count field. This patch will only effect xHCI hosts that advertise 1.0 support (0x100) in the HCI version field of their capabilities register. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 27 ++++++++++++++++++++++++++- drivers/usb/host/xhci.h | 1 + 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 27d690d889a..cc5963263a6 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3123,6 +3123,27 @@ static int count_isoc_trbs_needed(struct xhci_hcd *xhci, return num_trbs; } +/* + * The transfer burst count field of the isochronous TRB defines the number of + * bursts that are required to move all packets in this TD. Only SuperSpeed + * devices can burst up to bMaxBurst number of packets per service interval. + * This field is zero based, meaning a value of zero in the field means one + * burst. Basically, for everything but SuperSpeed devices, this field will be + * zero. Only xHCI 1.0 host controllers support this field. + */ +static unsigned int xhci_get_burst_count(struct xhci_hcd *xhci, + struct usb_device *udev, + struct urb *urb, unsigned int total_packet_count) +{ + unsigned int max_burst; + + if (xhci->hci_version < 0x100 || udev->speed != USB_SPEED_SUPER) + return 0; + + max_burst = urb->ep->ss_ep_comp.bMaxBurst; + return roundup(total_packet_count, max_burst + 1) - 1; +} + /* This is for isoc transfer */ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index) @@ -3164,14 +3185,18 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* Queue the first TRB, even if it's zero-length */ for (i = 0; i < num_tds; i++) { unsigned int total_packet_count; + unsigned int burst_count; first_trb = true; running_total = 0; addr = start_addr + urb->iso_frame_desc[i].offset; td_len = urb->iso_frame_desc[i].length; td_remain_len = td_len; + /* FIXME: Ignoring zero-length packets, can those happen? */ total_packet_count = roundup(td_len, le16_to_cpu(urb->ep->desc.wMaxPacketSize)); + burst_count = xhci_get_burst_count(xhci, urb->dev, urb, + total_packet_count); trbs_per_td = count_isoc_trbs_needed(xhci, urb, i); @@ -3185,7 +3210,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, for (j = 0; j < trbs_per_td; j++) { u32 remainder = 0; - field = 0; + field = TRB_TBC(burst_count); if (first_trb) { /* Queue the isoc TRB */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 85e77980818..87ec3b07972 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -943,6 +943,7 @@ struct xhci_event_cmd { /* Interrupter Target - which MSI-X vector to target the completion event at */ #define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) +#define TRB_TBC(p) (((p) & 0x3) << 7) /* Cycle bit - indicates TRB ownership by HC or HCD */ #define TRB_CYCLE (1<<0) -- cgit v1.2.3 From b61d378f2da41c748aba6ca19d77e1e1c02bcea5 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 19 Apr 2011 17:43:33 -0700 Subject: xhci 1.0: Set transfer burst last packet count field. The xHCI 1.0 specification defines a new isochronous TRB field, called transfer burst last packet count (TBLPC). This field defines the number of packets in the last "burst" of packets in a TD. Only SuperSpeed endpoints can handle more than one burst, so this is set to the number for packets in a TD for all non-SuperSpeed devices (minus one, since the field is zero based). This patch should have no effect on host controllers that don't advertise the xHCI 1.0 (0x100) version number in their hci_version field. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 41 ++++++++++++++++++++++++++++++++++++++++- drivers/usb/host/xhci.h | 1 + 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index cc5963263a6..396f8d2a2e8 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3144,6 +3144,42 @@ static unsigned int xhci_get_burst_count(struct xhci_hcd *xhci, return roundup(total_packet_count, max_burst + 1) - 1; } +/* + * Returns the number of packets in the last "burst" of packets. This field is + * valid for all speeds of devices. USB 2.0 devices can only do one "burst", so + * the last burst packet count is equal to the total number of packets in the + * TD. SuperSpeed endpoints can have up to 3 bursts. All but the last burst + * must contain (bMaxBurst + 1) number of packets, but the last burst can + * contain 1 to (bMaxBurst + 1) packets. + */ +static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci, + struct usb_device *udev, + struct urb *urb, unsigned int total_packet_count) +{ + unsigned int max_burst; + unsigned int residue; + + if (xhci->hci_version < 0x100) + return 0; + + switch (udev->speed) { + case USB_SPEED_SUPER: + /* bMaxBurst is zero based: 0 means 1 packet per burst */ + max_burst = urb->ep->ss_ep_comp.bMaxBurst; + residue = total_packet_count % (max_burst + 1); + /* If residue is zero, the last burst contains (max_burst + 1) + * number of packets, but the TLBPC field is zero-based. + */ + if (residue == 0) + return max_burst; + return residue - 1; + default: + if (total_packet_count == 0) + return 0; + return total_packet_count - 1; + } +} + /* This is for isoc transfer */ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index) @@ -3186,6 +3222,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, for (i = 0; i < num_tds; i++) { unsigned int total_packet_count; unsigned int burst_count; + unsigned int residue; first_trb = true; running_total = 0; @@ -3197,6 +3234,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, le16_to_cpu(urb->ep->desc.wMaxPacketSize)); burst_count = xhci_get_burst_count(xhci, urb->dev, urb, total_packet_count); + residue = xhci_get_last_burst_packet_count(xhci, + urb->dev, urb, total_packet_count); trbs_per_td = count_isoc_trbs_needed(xhci, urb, i); @@ -3210,7 +3249,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, for (j = 0; j < trbs_per_td; j++) { u32 remainder = 0; - field = TRB_TBC(burst_count); + field = TRB_TBC(burst_count) | TRB_TLBPC(residue); if (first_trb) { /* Queue the isoc TRB */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 87ec3b07972..db661543a80 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -944,6 +944,7 @@ struct xhci_event_cmd { #define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) #define TRB_TBC(p) (((p) & 0x3) << 7) +#define TRB_TLBPC(p) (((p) & 0xf) << 16) /* Cycle bit - indicates TRB ownership by HC or HCD */ #define TRB_CYCLE (1<<0) -- cgit v1.2.3 From 13b7ee2a953f07d994b6bc3439cdd4a718de6f80 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 18 Apr 2011 22:01:55 +0200 Subject: USB: ehci-fsl: add MPC5121E specific suspend and resume Signed-off-by: Anatolij Gustschin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 153 ++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci-fsl.h | 4 ++ include/linux/fsl_devices.h | 15 +++++ 3 files changed, 172 insertions(+) diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 5c761df7fa8..caf3d4ac42b 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -328,6 +328,149 @@ struct ehci_fsl { #ifdef CONFIG_PM +#ifdef CONFIG_PPC_MPC512x +static int ehci_fsl_mpc512x_drv_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct fsl_usb2_platform_data *pdata = dev->platform_data; + u32 tmp; + +#ifdef DEBUG + u32 mode = ehci_readl(ehci, hcd->regs + FSL_SOC_USB_USBMODE); + mode &= USBMODE_CM_MASK; + tmp = ehci_readl(ehci, hcd->regs + 0x140); /* usbcmd */ + + dev_dbg(dev, "suspend=%d already_suspended=%d " + "mode=%d usbcmd %08x\n", pdata->suspended, + pdata->already_suspended, mode, tmp); +#endif + + /* + * If the controller is already suspended, then this must be a + * PM suspend. Remember this fact, so that we will leave the + * controller suspended at PM resume time. + */ + if (pdata->suspended) { + dev_dbg(dev, "already suspended, leaving early\n"); + pdata->already_suspended = 1; + return 0; + } + + dev_dbg(dev, "suspending...\n"); + + hcd->state = HC_STATE_SUSPENDED; + dev->power.power_state = PMSG_SUSPEND; + + /* ignore non-host interrupts */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + /* stop the controller */ + tmp = ehci_readl(ehci, &ehci->regs->command); + tmp &= ~CMD_RUN; + ehci_writel(ehci, tmp, &ehci->regs->command); + + /* save EHCI registers */ + pdata->pm_command = ehci_readl(ehci, &ehci->regs->command); + pdata->pm_command &= ~CMD_RUN; + pdata->pm_status = ehci_readl(ehci, &ehci->regs->status); + pdata->pm_intr_enable = ehci_readl(ehci, &ehci->regs->intr_enable); + pdata->pm_frame_index = ehci_readl(ehci, &ehci->regs->frame_index); + pdata->pm_segment = ehci_readl(ehci, &ehci->regs->segment); + pdata->pm_frame_list = ehci_readl(ehci, &ehci->regs->frame_list); + pdata->pm_async_next = ehci_readl(ehci, &ehci->regs->async_next); + pdata->pm_configured_flag = + ehci_readl(ehci, &ehci->regs->configured_flag); + pdata->pm_portsc = ehci_readl(ehci, &ehci->regs->port_status[0]); + pdata->pm_usbgenctrl = ehci_readl(ehci, + hcd->regs + FSL_SOC_USB_USBGENCTRL); + + /* clear the W1C bits */ + pdata->pm_portsc &= cpu_to_hc32(ehci, ~PORT_RWC_BITS); + + pdata->suspended = 1; + + /* clear PP to cut power to the port */ + tmp = ehci_readl(ehci, &ehci->regs->port_status[0]); + tmp &= ~PORT_POWER; + ehci_writel(ehci, tmp, &ehci->regs->port_status[0]); + + return 0; +} + +static int ehci_fsl_mpc512x_drv_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct fsl_usb2_platform_data *pdata = dev->platform_data; + u32 tmp; + + dev_dbg(dev, "suspend=%d already_suspended=%d\n", + pdata->suspended, pdata->already_suspended); + + /* + * If the controller was already suspended at suspend time, + * then don't resume it now. + */ + if (pdata->already_suspended) { + dev_dbg(dev, "already suspended, leaving early\n"); + pdata->already_suspended = 0; + return 0; + } + + if (!pdata->suspended) { + dev_dbg(dev, "not suspended, leaving early\n"); + return 0; + } + + pdata->suspended = 0; + + dev_dbg(dev, "resuming...\n"); + + /* set host mode */ + tmp = USBMODE_CM_HOST | (pdata->es ? USBMODE_ES : 0); + ehci_writel(ehci, tmp, hcd->regs + FSL_SOC_USB_USBMODE); + + ehci_writel(ehci, pdata->pm_usbgenctrl, + hcd->regs + FSL_SOC_USB_USBGENCTRL); + ehci_writel(ehci, ISIPHYCTRL_PXE | ISIPHYCTRL_PHYE, + hcd->regs + FSL_SOC_USB_ISIPHYCTRL); + + /* restore EHCI registers */ + ehci_writel(ehci, pdata->pm_command, &ehci->regs->command); + ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable); + ehci_writel(ehci, pdata->pm_frame_index, &ehci->regs->frame_index); + ehci_writel(ehci, pdata->pm_segment, &ehci->regs->segment); + ehci_writel(ehci, pdata->pm_frame_list, &ehci->regs->frame_list); + ehci_writel(ehci, pdata->pm_async_next, &ehci->regs->async_next); + ehci_writel(ehci, pdata->pm_configured_flag, + &ehci->regs->configured_flag); + ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]); + + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + hcd->state = HC_STATE_RUNNING; + dev->power.power_state = PMSG_ON; + + tmp = ehci_readl(ehci, &ehci->regs->command); + tmp |= CMD_RUN; + ehci_writel(ehci, tmp, &ehci->regs->command); + + usb_hcd_resume_root_hub(hcd); + + return 0; +} +#else +static inline int ehci_fsl_mpc512x_drv_suspend(struct device *dev) +{ + return 0; +} + +static inline int ehci_fsl_mpc512x_drv_resume(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_PPC_MPC512x */ + static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); @@ -341,6 +484,11 @@ static int ehci_fsl_drv_suspend(struct device *dev) struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd); void __iomem *non_ehci = hcd->regs; + if (of_device_is_compatible(dev->parent->of_node, + "fsl,mpc5121-usb2-dr")) { + return ehci_fsl_mpc512x_drv_suspend(dev); + } + ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), device_may_wakeup(dev)); if (!fsl_deep_sleep()) @@ -357,6 +505,11 @@ static int ehci_fsl_drv_resume(struct device *dev) struct ehci_hcd *ehci = hcd_to_ehci(hcd); void __iomem *non_ehci = hcd->regs; + if (of_device_is_compatible(dev->parent->of_node, + "fsl,mpc5121-usb2-dr")) { + return ehci_fsl_mpc512x_drv_resume(dev); + } + ehci_prepare_ports_for_controller_resume(ehci); if (!fsl_deep_sleep()) return 0; diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h index 3fabed33d94..49180622116 100644 --- a/drivers/usb/host/ehci-fsl.h +++ b/drivers/usb/host/ehci-fsl.h @@ -27,6 +27,10 @@ #define PORT_PTS_SERIAL (3<<30) #define PORT_PTS_PTW (1<<28) #define FSL_SOC_USB_PORTSC2 0x188 +#define FSL_SOC_USB_USBMODE 0x1a8 +#define USBMODE_CM_MASK (3 << 0) /* controller mode mask */ +#define USBMODE_CM_HOST (3 << 0) /* controller mode: host */ +#define USBMODE_ES (1 << 2) /* (Big) Endian Select */ #define FSL_SOC_USB_USBGENCTRL 0x200 #define USBGENCTRL_PPP (1 << 3) diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 4eb56ed75fb..3773c5dab8f 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -79,6 +79,21 @@ struct fsl_usb2_platform_data { unsigned have_sysif_regs:1; unsigned invert_drvvbus:1; unsigned invert_pwr_fault:1; + + unsigned suspended:1; + unsigned already_suspended:1; + + /* register save area for suspend/resume */ + u32 pm_command; + u32 pm_status; + u32 pm_intr_enable; + u32 pm_frame_index; + u32 pm_segment; + u32 pm_frame_list; + u32 pm_async_next; + u32 pm_configured_flag; + u32 pm_portsc; + u32 pm_usbgenctrl; }; /* Flags in fsl_usb2_mph_platform_data */ -- cgit v1.2.3 From b703e47ec0d16ccaf2895e2069b6c83a92c51efc Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 18 Apr 2011 22:01:56 +0200 Subject: USB: fix build of FSL MPH DR OF platform driver When only FSL USB gadget driver is selected in the kernel configuration the MPH DR OF driver for creation of FSL USB platform devices from device tree won't be built. As a result no USB platform devices for MPH DR USB controller will be created at run time and no probing will be done in the fsl_udc_core driver. Add an entry to the Makefile to build the MPH DR OF platform driver if CONFIG_USB_FSL_MPH_DR_OF is defined. Signed-off-by: Anatolij Gustschin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 9bc8aeb3c96..30ddf8dc4f7 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_HWA_HCD) += host/ obj-$(CONFIG_USB_ISP1760_HCD) += host/ obj-$(CONFIG_USB_IMX21_HCD) += host/ +obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ -- cgit v1.2.3 From 09ba0def9aefc16c1c8a6d166f024c9d704f0ab0 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 18 Apr 2011 22:01:57 +0200 Subject: USB: fsl_udc_core: prepare for SoCs with BE registers and descriptors On some SoCs, the USB controller registers and descriptors can be big or little endian, depending on the version of the chip. In order to be able to run the same kernel binary on different versions of an SoC, the BE/LE decision must be made at run time. Provide appropriate register and descriptor accessors which are configurable at run time using the configuration flags from fsl_usb2_platform_data data structure. This is in preparation for adding support for MPC5121E DR USB2 Controller to the FSL UDC driver. Signed-off-by: Anatolij Gustschin Cc: Li Yang Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_udc_core.c | 123 ++++++++++++++++++++++++++++++-------- drivers/usb/gadget/fsl_usb2_udc.h | 2 + 2 files changed, 100 insertions(+), 25 deletions(-) diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 07499c1cdcc..b101b7e9c9b 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -77,12 +77,64 @@ fsl_ep0_desc = { static void fsl_ep_fifo_flush(struct usb_ep *_ep); #ifdef CONFIG_PPC32 -#define fsl_readl(addr) in_le32(addr) -#define fsl_writel(val32, addr) out_le32(addr, val32) -#else +/* + * On some SoCs, the USB controller registers can be big or little endian, + * depending on the version of the chip. In order to be able to run the + * same kernel binary on 2 different versions of an SoC, the BE/LE decision + * must be made at run time. _fsl_readl and fsl_writel are pointers to the + * BE or LE readl() and writel() functions, and fsl_readl() and fsl_writel() + * call through those pointers. Platform code for SoCs that have BE USB + * registers should set pdata->big_endian_mmio flag. + * + * This also applies to controller-to-cpu accessors for the USB descriptors, + * since their endianness is also SoC dependant. Platform code for SoCs that + * have BE USB descriptors should set pdata->big_endian_desc flag. + */ +static u32 _fsl_readl_be(const unsigned __iomem *p) +{ + return in_be32(p); +} + +static u32 _fsl_readl_le(const unsigned __iomem *p) +{ + return in_le32(p); +} + +static void _fsl_writel_be(u32 v, unsigned __iomem *p) +{ + out_be32(p, v); +} + +static void _fsl_writel_le(u32 v, unsigned __iomem *p) +{ + out_le32(p, v); +} + +static u32 (*_fsl_readl)(const unsigned __iomem *p); +static void (*_fsl_writel)(u32 v, unsigned __iomem *p); + +#define fsl_readl(p) (*_fsl_readl)((p)) +#define fsl_writel(v, p) (*_fsl_writel)((v), (p)) + +static inline u32 cpu_to_hc32(const u32 x) +{ + return udc_controller->pdata->big_endian_desc + ? (__force u32)cpu_to_be32(x) + : (__force u32)cpu_to_le32(x); +} + +static inline u32 hc32_to_cpu(const u32 x) +{ + return udc_controller->pdata->big_endian_desc + ? be32_to_cpu((__force __be32)x) + : le32_to_cpu((__force __le32)x); +} +#else /* !CONFIG_PPC32 */ #define fsl_readl(addr) readl(addr) #define fsl_writel(val32, addr) writel(val32, addr) -#endif +#define cpu_to_hc32(x) cpu_to_le32(x) +#define hc32_to_cpu(x) le32_to_cpu(x) +#endif /* CONFIG_PPC32 */ /******************************************************************** * Internal Used Function @@ -409,7 +461,7 @@ static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num, if (zlt) tmp |= EP_QUEUE_HEAD_ZLT_SEL; - p_QH->max_pkt_length = cpu_to_le32(tmp); + p_QH->max_pkt_length = cpu_to_hc32(tmp); p_QH->next_dtd_ptr = 1; p_QH->size_ioc_int_sts = 0; } @@ -616,7 +668,7 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) struct fsl_req *lastreq; lastreq = list_entry(ep->queue.prev, struct fsl_req, queue); lastreq->tail->next_td_ptr = - cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK); + cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK); /* Read prime bit, if 1 goto done */ if (fsl_readl(&dr_regs->endpointprime) & bitmask) goto out; @@ -641,10 +693,10 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) /* Write dQH next pointer and terminate bit to 0 */ temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK; - dQH->next_dtd_ptr = cpu_to_le32(temp); + dQH->next_dtd_ptr = cpu_to_hc32(temp); /* Clear active and halt bit */ - temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE + temp = cpu_to_hc32(~(EP_QUEUE_HEAD_STATUS_ACTIVE | EP_QUEUE_HEAD_STATUS_HALT)); dQH->size_ioc_int_sts &= temp; @@ -682,17 +734,17 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, dtd->td_dma = *dma; /* Clear reserved field */ - swap_temp = cpu_to_le32(dtd->size_ioc_sts); + swap_temp = hc32_to_cpu(dtd->size_ioc_sts); swap_temp &= ~DTD_RESERVED_FIELDS; - dtd->size_ioc_sts = cpu_to_le32(swap_temp); + dtd->size_ioc_sts = cpu_to_hc32(swap_temp); /* Init all of buffer page pointers */ swap_temp = (u32) (req->req.dma + req->req.actual); - dtd->buff_ptr0 = cpu_to_le32(swap_temp); - dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); - dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); - dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000); - dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000); + dtd->buff_ptr0 = cpu_to_hc32(swap_temp); + dtd->buff_ptr1 = cpu_to_hc32(swap_temp + 0x1000); + dtd->buff_ptr2 = cpu_to_hc32(swap_temp + 0x2000); + dtd->buff_ptr3 = cpu_to_hc32(swap_temp + 0x3000); + dtd->buff_ptr4 = cpu_to_hc32(swap_temp + 0x4000); req->req.actual += *length; @@ -716,7 +768,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, if (*is_last && !req->req.no_interrupt) swap_temp |= DTD_IOC; - dtd->size_ioc_sts = cpu_to_le32(swap_temp); + dtd->size_ioc_sts = cpu_to_hc32(swap_temp); mb(); @@ -743,7 +795,7 @@ static int fsl_req_to_dtd(struct fsl_req *req) is_first = 0; req->head = dtd; } else { - last_dtd->next_td_ptr = cpu_to_le32(dma); + last_dtd->next_td_ptr = cpu_to_hc32(dma); last_dtd->next_td_virt = dtd; } last_dtd = dtd; @@ -751,7 +803,7 @@ static int fsl_req_to_dtd(struct fsl_req *req) req->dtd_count++; } while (!is_last); - dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE); + dtd->next_td_ptr = cpu_to_hc32(DTD_NEXT_TERMINATE); req->tail = dtd; @@ -1394,6 +1446,7 @@ static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr) { u32 temp; struct ep_queue_head *qh; + struct fsl_usb2_platform_data *pdata = udc->pdata; qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT]; @@ -1408,7 +1461,16 @@ static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr) fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd); /* Copy the setup packet to local buffer */ - memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); + if (pdata->le_setup_buf) { + u32 *p = (u32 *)buffer_ptr; + u32 *s = (u32 *)qh->setup_buffer; + + /* Convert little endian setup buffer to CPU endian */ + *p++ = le32_to_cpu(*s++); + *p = le32_to_cpu(*s); + } else { + memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); + } } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW)); /* Clear Setup Tripwire */ @@ -1432,19 +1494,19 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, actual = curr_req->req.length; for (j = 0; j < curr_req->dtd_count; j++) { - remaining_length = (le32_to_cpu(curr_td->size_ioc_sts) + remaining_length = (hc32_to_cpu(curr_td->size_ioc_sts) & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS; actual -= remaining_length; - if ((errors = le32_to_cpu(curr_td->size_ioc_sts) & - DTD_ERROR_MASK)) { + errors = hc32_to_cpu(curr_td->size_ioc_sts); + if (errors & DTD_ERROR_MASK) { if (errors & DTD_STATUS_HALTED) { ERR("dTD error %08x QH=%d\n", errors, pipe); /* Clear the errors and Halt condition */ - tmp = le32_to_cpu(curr_qh->size_ioc_int_sts); + tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts); tmp &= ~errors; - curr_qh->size_ioc_int_sts = cpu_to_le32(tmp); + curr_qh->size_ioc_int_sts = cpu_to_hc32(tmp); status = -EPIPE; /* FIXME: continue with next queued TD? */ @@ -1462,7 +1524,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, ERR("Unknown error has occurred (0x%x)!\n", errors); - } else if (le32_to_cpu(curr_td->size_ioc_sts) + } else if (hc32_to_cpu(curr_td->size_ioc_sts) & DTD_STATUS_ACTIVE) { VDBG("Request not complete"); status = REQ_UNCOMPLETE; @@ -2233,6 +2295,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, */ static int __init fsl_udc_probe(struct platform_device *pdev) { + struct fsl_usb2_platform_data *pdata; struct resource *res; int ret = -ENODEV; unsigned int i; @@ -2249,6 +2312,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev) return -ENOMEM; } + pdata = pdev->dev.platform_data; + udc_controller->pdata = pdata; spin_lock_init(&udc_controller->lock); udc_controller->stopped = 1; @@ -2271,6 +2336,14 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err_release_mem_region; } + if (pdata->big_endian_mmio) { + _fsl_readl = _fsl_readl_be; + _fsl_writel = _fsl_writel_be; + } else { + _fsl_readl = _fsl_readl_le; + _fsl_writel = _fsl_writel_le; + } + #ifndef CONFIG_ARCH_MXC usb_sys_regs = (struct usb_sys_interface *) ((u32)dr_regs + USB_DR_SYS_OFFSET); diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index e88cce5c2c0..24efba93cef 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -461,6 +461,7 @@ struct fsl_ep { struct fsl_udc { struct usb_gadget gadget; struct usb_gadget_driver *driver; + struct fsl_usb2_platform_data *pdata; struct completion *done; /* to make sure release() is done */ struct fsl_ep *eps; unsigned int max_ep; @@ -473,6 +474,7 @@ struct fsl_udc { unsigned vbus_active:1; unsigned stopped:1; unsigned remote_wakeup:1; + unsigned big_endian_desc:1; struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */ struct fsl_req *status_req; /* ep0 status request */ -- cgit v1.2.3 From 2ea6698d7b9266da53044dddc5f6743adf097fb5 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 18 Apr 2011 22:01:58 +0200 Subject: USB: fsl_udc_core: support device mode of MPC5121E DR USB Controller Extend the FSL UDC driver to support MPC5121E DR USB Controller operation in device mode. Add MPC5121E specific init/uninit at probe and remove and isolate system interface register accesses when running on MPC5121E SoC, as these registers are not available on this platform. This patch relies on previous patch for supporting big endian registers and descriptors access in the FSL UDC driver. Additionally support endpoint FIFO status operation by providing appropriate callback in endpoint ops structure. Also flush cache for the req buffer used for GetStatus reply. Without this, the correct reply to an endpoint GetStatus is written to 'req', but doesn't make it out to the USB bus since the buffer hasn't been flushed. This would cause the USBCV Halt Endpoint test to fail (according to changelog in Freescale LTIB driver code). Signed-off-by: Anatolij Gustschin Cc: Li Yang Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_udc_core.c | 141 +++++++++++++++++++++++++++++--------- drivers/usb/gadget/fsl_usb2_udc.h | 2 + 2 files changed, 112 insertions(+), 31 deletions(-) diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index b101b7e9c9b..28b3a9f25f3 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -6,7 +6,7 @@ * * Description: * Freescale high-speed USB SOC DR module device controller driver. - * This can be found on MPC8349E/MPC8313E cpus. + * This can be found on MPC8349E/MPC8313E/MPC5121E cpus. * The driver is previously named as mpc_udc. Based on bare board * code from Dave Liu and Shlomi Gridish. * @@ -45,6 +45,7 @@ #include #include #include +#include #include "fsl_usb2_udc.h" @@ -278,9 +279,12 @@ static int dr_controller_setup(struct fsl_udc *udc) /* Set the controller as device mode */ tmp = fsl_readl(&dr_regs->usbmode); + tmp &= ~USB_MODE_CTRL_MODE_MASK; /* clear mode bits */ tmp |= USB_MODE_CTRL_MODE_DEVICE; /* Disable Setup Lockout */ tmp |= USB_MODE_SETUP_LOCK_OFF; + if (udc->pdata->es) + tmp |= USB_MODE_ES; fsl_writel(tmp, &dr_regs->usbmode); /* Clear the setup status */ @@ -296,20 +300,24 @@ static int dr_controller_setup(struct fsl_udc *udc) /* Config control enable i/o output, cpu endian register */ #ifndef CONFIG_ARCH_MXC - ctrl = __raw_readl(&usb_sys_regs->control); - ctrl |= USB_CTRL_IOENB; - __raw_writel(ctrl, &usb_sys_regs->control); + if (udc->pdata->have_sysif_regs) { + ctrl = __raw_readl(&usb_sys_regs->control); + ctrl |= USB_CTRL_IOENB; + __raw_writel(ctrl, &usb_sys_regs->control); + } #endif #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) /* Turn on cache snooping hardware, since some PowerPC platforms * wholly rely on hardware to deal with cache coherent. */ - /* Setup Snooping for all the 4GB space */ - tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */ - __raw_writel(tmp, &usb_sys_regs->snoop1); - tmp |= 0x80000000; /* starts from 0x8000000, size 2G */ - __raw_writel(tmp, &usb_sys_regs->snoop2); + if (udc->pdata->have_sysif_regs) { + /* Setup Snooping for all the 4GB space */ + tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */ + __raw_writel(tmp, &usb_sys_regs->snoop1); + tmp |= 0x80000000; /* starts from 0x8000000, size 2G */ + __raw_writel(tmp, &usb_sys_regs->snoop2); + } #endif return 0; @@ -1014,6 +1022,36 @@ out: return status; } +static int fsl_ep_fifo_status(struct usb_ep *_ep) +{ + struct fsl_ep *ep; + struct fsl_udc *udc; + int size = 0; + u32 bitmask; + struct ep_queue_head *d_qh; + + ep = container_of(_ep, struct fsl_ep, ep); + if (!_ep || (!ep->desc && ep_index(ep) != 0)) + return -ENODEV; + + udc = (struct fsl_udc *)ep->udc; + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + d_qh = &ep->udc->ep_qh[ep_index(ep) * 2 + ep_is_in(ep)]; + + bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) : + (1 << (ep_index(ep))); + + if (fsl_readl(&dr_regs->endptstatus) & bitmask) + size = (d_qh->size_ioc_int_sts & DTD_PACKET_SIZE) + >> DTD_LENGTH_BIT_POS; + + pr_debug("%s %u\n", __func__, size); + return size; +} + static void fsl_ep_fifo_flush(struct usb_ep *_ep) { struct fsl_ep *ep; @@ -1066,6 +1104,7 @@ static struct usb_ep_ops fsl_ep_ops = { .dequeue = fsl_ep_dequeue, .set_halt = fsl_ep_set_halt, + .fifo_status = fsl_ep_fifo_status, .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */ }; @@ -1280,6 +1319,10 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, req = udc->status_req; /* Fill in the reqest structure */ *((u16 *) req->req.buf) = cpu_to_le16(tmp); + + /* flush cache for the req buffer */ + flush_dcache_range((u32)req->req.buf, (u32)req->req.buf + 8); + req->ep = ep; req->req.length = 2; req->req.status = -EINPROGRESS; @@ -1332,6 +1375,7 @@ static void setup_received_irq(struct fsl_udc *udc, /* Status phase from udc */ { int rc = -EOPNOTSUPP; + u16 ptc = 0; if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) { @@ -1353,17 +1397,19 @@ static void setup_received_irq(struct fsl_udc *udc, | USB_TYPE_STANDARD)) { /* Note: The driver has not include OTG support yet. * This will be set when OTG support is added */ - if (!gadget_is_otg(&udc->gadget)) - break; - else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) - udc->gadget.b_hnp_enable = 1; - else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) - udc->gadget.a_hnp_support = 1; - else if (setup->bRequest == - USB_DEVICE_A_ALT_HNP_SUPPORT) - udc->gadget.a_alt_hnp_support = 1; - else - break; + if (wValue == USB_DEVICE_TEST_MODE) + ptc = wIndex >> 8; + else if (gadget_is_otg(&udc->gadget)) { + if (setup->bRequest == + USB_DEVICE_B_HNP_ENABLE) + udc->gadget.b_hnp_enable = 1; + else if (setup->bRequest == + USB_DEVICE_A_HNP_SUPPORT) + udc->gadget.a_hnp_support = 1; + else if (setup->bRequest == + USB_DEVICE_A_ALT_HNP_SUPPORT) + udc->gadget.a_alt_hnp_support = 1; + } rc = 0; } else break; @@ -1372,6 +1418,15 @@ static void setup_received_irq(struct fsl_udc *udc, if (ep0_prime_status(udc, EP_DIR_IN)) ep0stall(udc); } + if (ptc) { + u32 tmp; + + mdelay(10); + tmp = fsl_readl(&dr_regs->portsc1) | (ptc << 16); + fsl_writel(tmp, &dr_regs->portsc1); + printk(KERN_INFO "udc: switch to test mode %d.\n", ptc); + } + return; } @@ -2106,16 +2161,18 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, next += t; #ifndef CONFIG_ARCH_MXC - tmp_reg = usb_sys_regs->snoop1; - t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg); - size -= t; - next += t; + if (udc->pdata->have_sysif_regs) { + tmp_reg = usb_sys_regs->snoop1; + t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg); + size -= t; + next += t; - tmp_reg = usb_sys_regs->control; - t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n", - tmp_reg); - size -= t; - next += t; + tmp_reg = usb_sys_regs->control; + t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n", + tmp_reg); + size -= t; + next += t; + } #endif /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */ @@ -2336,6 +2393,17 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err_release_mem_region; } + pdata->regs = (void *)dr_regs; + + /* + * do platform specific init: check the clock, grab/config pins, etc. + */ + if (pdata->init && pdata->init(pdev)) { + ret = -ENODEV; + goto err_iounmap_noclk; + } + + /* Set accessors only after pdata->init() ! */ if (pdata->big_endian_mmio) { _fsl_readl = _fsl_readl_be; _fsl_writel = _fsl_writel_be; @@ -2345,8 +2413,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev) } #ifndef CONFIG_ARCH_MXC - usb_sys_regs = (struct usb_sys_interface *) - ((u32)dr_regs + USB_DR_SYS_OFFSET); + if (pdata->have_sysif_regs) + usb_sys_regs = (struct usb_sys_interface *) + ((u32)dr_regs + USB_DR_SYS_OFFSET); #endif /* Initialize USB clocks */ @@ -2446,6 +2515,8 @@ err_unregister: err_free_irq: free_irq(udc_controller->irq, udc_controller); err_iounmap: + if (pdata->exit) + pdata->exit(pdev); fsl_udc_clk_release(); err_iounmap_noclk: iounmap(dr_regs); @@ -2463,6 +2534,7 @@ err_kfree: static int __exit fsl_udc_remove(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; DECLARE_COMPLETION(done); @@ -2489,6 +2561,13 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) /* free udc --wait for the release() finished */ wait_for_completion(&done); + /* + * do platform specific un-initialization: + * release iomux pins, etc. + */ + if (pdata->exit) + pdata->exit(pdev); + return 0; } diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index 24efba93cef..5647cc21b84 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -275,7 +275,9 @@ struct usb_sys_interface { #define USB_MODE_CTRL_MODE_IDLE 0x00000000 #define USB_MODE_CTRL_MODE_DEVICE 0x00000002 #define USB_MODE_CTRL_MODE_HOST 0x00000003 +#define USB_MODE_CTRL_MODE_MASK 0x00000003 #define USB_MODE_CTRL_MODE_RSV 0x00000001 +#define USB_MODE_ES 0x00000004 /* Endian Select */ #define USB_MODE_SETUP_LOCK_OFF 0x00000008 #define USB_MODE_STREAM_DISABLE 0x00000010 /* Endpoint Flush Register */ -- cgit v1.2.3 From 0807c500a1a6d7fa20cbd7bbe7fea14a66112463 Mon Sep 17 00:00:00 2001 From: Li Yang Date: Mon, 18 Apr 2011 22:01:59 +0200 Subject: USB: add Freescale USB OTG Transceiver driver Slightly reworked and cleaned up driver from Freescale LTIB for MPC5121E. The driver has been ported to the current kernel, proc interface "/proc/driver/fsl_usb2_otg" has been replaced by sysfs interface. Signed-off-by: Li Yang Signed-off-by: Anatolij Gustschin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/Kconfig | 8 + drivers/usb/otg/Makefile | 2 + drivers/usb/otg/fsl_otg.c | 1169 +++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/otg/fsl_otg.h | 406 ++++++++++++++++ drivers/usb/otg/otg_fsm.c | 349 ++++++++++++++ drivers/usb/otg/otg_fsm.h | 154 ++++++ 6 files changed, 2088 insertions(+) create mode 100644 drivers/usb/otg/fsl_otg.c create mode 100644 drivers/usb/otg/fsl_otg.h create mode 100644 drivers/usb/otg/otg_fsm.c create mode 100644 drivers/usb/otg/otg_fsm.h diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index daf3e5f1a0e..c66481ad98d 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -122,4 +122,12 @@ config AB8500_USB This transceiver supports high and full speed devices plus, in host mode, low speed. +config FSL_USB2_OTG + bool "Freescale USB OTG Transceiver Driver" + depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 + select USB_OTG + select USB_OTG_UTILS + help + Enable this to support Freescale USB OTG transceiver. + endif # USB || OTG diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index e22d917de01..566655c5333 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -19,3 +19,5 @@ obj-$(CONFIG_USB_ULPI) += ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o obj-$(CONFIG_AB8500_USB) += ab8500-usb.o +fsl_usb2_otg-objs := fsl_otg.o otg_fsm.o +obj-$(CONFIG_FSL_USB2_OTG) += fsl_usb2_otg.o diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c new file mode 100644 index 00000000000..0f420b25e9a --- /dev/null +++ b/drivers/usb/otg/fsl_otg.c @@ -0,0 +1,1169 @@ +/* + * Copyright (C) 2007,2008 Freescale semiconductor, Inc. + * + * Author: Li Yang + * Jerry Huang + * + * Initialization based on code from Shlomi Gridish. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fsl_otg.h" + +#define DRIVER_VERSION "Rev. 1.55" +#define DRIVER_AUTHOR "Jerry Huang/Li Yang" +#define DRIVER_DESC "Freescale USB OTG Transceiver Driver" +#define DRIVER_INFO DRIVER_DESC " " DRIVER_VERSION + +static const char driver_name[] = "fsl-usb2-otg"; + +const pm_message_t otg_suspend_state = { + .event = 1, +}; + +#define HA_DATA_PULSE + +static struct usb_dr_mmap *usb_dr_regs; +static struct fsl_otg *fsl_otg_dev; +static int srp_wait_done; + +/* FSM timers */ +struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, + *b_ase0_brst_tmr, *b_se0_srp_tmr; + +/* Driver specific timers */ +struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr, + *b_srp_wait_tmr, *a_wait_enum_tmr; + +static struct list_head active_timers; + +static struct fsl_otg_config fsl_otg_initdata = { + .otg_port = 1, +}; + +#ifdef CONFIG_PPC32 +static u32 _fsl_readl_be(const unsigned __iomem *p) +{ + return in_be32(p); +} + +static u32 _fsl_readl_le(const unsigned __iomem *p) +{ + return in_le32(p); +} + +static void _fsl_writel_be(u32 v, unsigned __iomem *p) +{ + out_be32(p, v); +} + +static void _fsl_writel_le(u32 v, unsigned __iomem *p) +{ + out_le32(p, v); +} + +static u32 (*_fsl_readl)(const unsigned __iomem *p); +static void (*_fsl_writel)(u32 v, unsigned __iomem *p); + +#define fsl_readl(p) (*_fsl_readl)((p)) +#define fsl_writel(v, p) (*_fsl_writel)((v), (p)) + +#else +#define fsl_readl(addr) readl(addr) +#define fsl_writel(val, addr) writel(val, addr) +#endif /* CONFIG_PPC32 */ + +/* Routines to access transceiver ULPI registers */ +u8 view_ulpi(u8 addr) +{ + u32 temp; + + temp = 0x40000000 | (addr << 16); + fsl_writel(temp, &usb_dr_regs->ulpiview); + udelay(1000); + while (temp & 0x40) + temp = fsl_readl(&usb_dr_regs->ulpiview); + return (le32_to_cpu(temp) & 0x0000ff00) >> 8; +} + +int write_ulpi(u8 addr, u8 data) +{ + u32 temp; + + temp = 0x60000000 | (addr << 16) | data; + fsl_writel(temp, &usb_dr_regs->ulpiview); + return 0; +} + +/* -------------------------------------------------------------*/ +/* Operations that will be called from OTG Finite State Machine */ + +/* Charge vbus for vbus pulsing in SRP */ +void fsl_otg_chrg_vbus(int on) +{ + u32 tmp; + + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + + if (on) + /* stop discharging, start charging */ + tmp = (tmp & ~OTGSC_CTRL_VBUS_DISCHARGE) | + OTGSC_CTRL_VBUS_CHARGE; + else + /* stop charging */ + tmp &= ~OTGSC_CTRL_VBUS_CHARGE; + + fsl_writel(tmp, &usb_dr_regs->otgsc); +} + +/* Discharge vbus through a resistor to ground */ +void fsl_otg_dischrg_vbus(int on) +{ + u32 tmp; + + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + + if (on) + /* stop charging, start discharging */ + tmp = (tmp & ~OTGSC_CTRL_VBUS_CHARGE) | + OTGSC_CTRL_VBUS_DISCHARGE; + else + /* stop discharging */ + tmp &= ~OTGSC_CTRL_VBUS_DISCHARGE; + + fsl_writel(tmp, &usb_dr_regs->otgsc); +} + +/* A-device driver vbus, controlled through PP bit in PORTSC */ +void fsl_otg_drv_vbus(int on) +{ + u32 tmp; + + if (on) { + tmp = fsl_readl(&usb_dr_regs->portsc) & ~PORTSC_W1C_BITS; + fsl_writel(tmp | PORTSC_PORT_POWER, &usb_dr_regs->portsc); + } else { + tmp = fsl_readl(&usb_dr_regs->portsc) & + ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER; + fsl_writel(tmp, &usb_dr_regs->portsc); + } +} + +/* + * Pull-up D+, signalling connect by periperal. Also used in + * data-line pulsing in SRP + */ +void fsl_otg_loc_conn(int on) +{ + u32 tmp; + + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + + if (on) + tmp |= OTGSC_CTRL_DATA_PULSING; + else + tmp &= ~OTGSC_CTRL_DATA_PULSING; + + fsl_writel(tmp, &usb_dr_regs->otgsc); +} + +/* + * Generate SOF by host. This is controlled through suspend/resume the + * port. In host mode, controller will automatically send SOF. + * Suspend will block the data on the port. + */ +void fsl_otg_loc_sof(int on) +{ + u32 tmp; + + tmp = fsl_readl(&fsl_otg_dev->dr_mem_map->portsc) & ~PORTSC_W1C_BITS; + if (on) + tmp |= PORTSC_PORT_FORCE_RESUME; + else + tmp |= PORTSC_PORT_SUSPEND; + + fsl_writel(tmp, &fsl_otg_dev->dr_mem_map->portsc); + +} + +/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ +void fsl_otg_start_pulse(void) +{ + u32 tmp; + + srp_wait_done = 0; +#ifdef HA_DATA_PULSE + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + tmp |= OTGSC_HA_DATA_PULSE; + fsl_writel(tmp, &usb_dr_regs->otgsc); +#else + fsl_otg_loc_conn(1); +#endif + + fsl_otg_add_timer(b_data_pulse_tmr); +} + +void b_data_pulse_end(unsigned long foo) +{ +#ifdef HA_DATA_PULSE +#else + fsl_otg_loc_conn(0); +#endif + + /* Do VBUS pulse after data pulse */ + fsl_otg_pulse_vbus(); +} + +void fsl_otg_pulse_vbus(void) +{ + srp_wait_done = 0; + fsl_otg_chrg_vbus(1); + /* start the timer to end vbus charge */ + fsl_otg_add_timer(b_vbus_pulse_tmr); +} + +void b_vbus_pulse_end(unsigned long foo) +{ + fsl_otg_chrg_vbus(0); + + /* + * As USB3300 using the same a_sess_vld and b_sess_vld voltage + * we need to discharge the bus for a while to distinguish + * residual voltage of vbus pulsing and A device pull up + */ + fsl_otg_dischrg_vbus(1); + fsl_otg_add_timer(b_srp_wait_tmr); +} + +void b_srp_end(unsigned long foo) +{ + fsl_otg_dischrg_vbus(0); + srp_wait_done = 1; + + if ((fsl_otg_dev->otg.state == OTG_STATE_B_SRP_INIT) && + fsl_otg_dev->fsm.b_sess_vld) + fsl_otg_dev->fsm.b_srp_done = 1; +} + +/* + * Workaround for a_host suspending too fast. When a_bus_req=0, + * a_host will start by SRP. It needs to set b_hnp_enable before + * actually suspending to start HNP + */ +void a_wait_enum(unsigned long foo) +{ + VDBG("a_wait_enum timeout\n"); + if (!fsl_otg_dev->otg.host->b_hnp_enable) + fsl_otg_add_timer(a_wait_enum_tmr); + else + otg_statemachine(&fsl_otg_dev->fsm); +} + +/* The timeout callback function to set time out bit */ +void set_tmout(unsigned long indicator) +{ + *(int *)indicator = 1; +} + +/* Initialize timers */ +int fsl_otg_init_timers(struct otg_fsm *fsm) +{ + /* FSM used timers */ + a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, + (unsigned long)&fsm->a_wait_vrise_tmout); + if (!a_wait_vrise_tmr) + return -ENOMEM; + + a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, + (unsigned long)&fsm->a_wait_bcon_tmout); + if (!a_wait_bcon_tmr) + return -ENOMEM; + + a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, + (unsigned long)&fsm->a_aidl_bdis_tmout); + if (!a_aidl_bdis_tmr) + return -ENOMEM; + + b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, + (unsigned long)&fsm->b_ase0_brst_tmout); + if (!b_ase0_brst_tmr) + return -ENOMEM; + + b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, + (unsigned long)&fsm->b_se0_srp); + if (!b_se0_srp_tmr) + return -ENOMEM; + + b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL, + (unsigned long)&fsm->b_srp_done); + if (!b_srp_fail_tmr) + return -ENOMEM; + + a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10, + (unsigned long)&fsm); + if (!a_wait_enum_tmr) + return -ENOMEM; + + /* device driver used timers */ + b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0); + if (!b_srp_wait_tmr) + return -ENOMEM; + + b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end, + TB_DATA_PLS, 0); + if (!b_data_pulse_tmr) + return -ENOMEM; + + b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end, + TB_VBUS_PLS, 0); + if (!b_vbus_pulse_tmr) + return -ENOMEM; + + return 0; +} + +/* Uninitialize timers */ +void fsl_otg_uninit_timers(void) +{ + /* FSM used timers */ + if (a_wait_vrise_tmr != NULL) + kfree(a_wait_vrise_tmr); + if (a_wait_bcon_tmr != NULL) + kfree(a_wait_bcon_tmr); + if (a_aidl_bdis_tmr != NULL) + kfree(a_aidl_bdis_tmr); + if (b_ase0_brst_tmr != NULL) + kfree(b_ase0_brst_tmr); + if (b_se0_srp_tmr != NULL) + kfree(b_se0_srp_tmr); + if (b_srp_fail_tmr != NULL) + kfree(b_srp_fail_tmr); + if (a_wait_enum_tmr != NULL) + kfree(a_wait_enum_tmr); + + /* device driver used timers */ + if (b_srp_wait_tmr != NULL) + kfree(b_srp_wait_tmr); + if (b_data_pulse_tmr != NULL) + kfree(b_data_pulse_tmr); + if (b_vbus_pulse_tmr != NULL) + kfree(b_vbus_pulse_tmr); +} + +/* Add timer to timer list */ +void fsl_otg_add_timer(void *gtimer) +{ + struct fsl_otg_timer *timer = gtimer; + struct fsl_otg_timer *tmp_timer; + + /* + * 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; + list_add_tail(&timer->list, &active_timers); +} + +/* Remove timer from the timer list; clear timeout status */ +void fsl_otg_del_timer(void *gtimer) +{ + struct fsl_otg_timer *timer = gtimer; + struct fsl_otg_timer *tmp_timer, *del_tmp; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) + if (tmp_timer == timer) + list_del(&timer->list); +} + +/* + * Reduce timer count by 1, and find timeout conditions. + * Called by fsl_otg 1ms timer interrupt + */ +int fsl_otg_tick_timer(void) +{ + struct fsl_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; + } + } + + return expired; +} + +/* Reset controller, not reset the bus */ +void otg_reset_controller(void) +{ + u32 command; + + command = fsl_readl(&usb_dr_regs->usbcmd); + command |= (1 << 1); + fsl_writel(command, &usb_dr_regs->usbcmd); + while (fsl_readl(&usb_dr_regs->usbcmd) & (1 << 1)) + ; +} + +/* Call suspend/resume routines in host driver */ +int fsl_otg_start_host(struct otg_fsm *fsm, int on) +{ + struct otg_transceiver *xceiv = fsm->transceiver; + struct device *dev; + struct fsl_otg *otg_dev = container_of(xceiv, struct fsl_otg, otg); + u32 retval = 0; + + if (!xceiv->host) + return -ENODEV; + dev = xceiv->host->controller; + + /* + * Update a_vbus_vld state as a_vbus_vld int is disabled + * in device mode + */ + fsm->a_vbus_vld = + !!(fsl_readl(&usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID); + if (on) { + /* start fsl usb host controller */ + if (otg_dev->host_working) + goto end; + else { + otg_reset_controller(); + VDBG("host on......\n"); + if (dev->driver->pm && dev->driver->pm->resume) { + retval = dev->driver->pm->resume(dev); + if (fsm->id) { + /* default-b */ + fsl_otg_drv_vbus(1); + /* + * Workaround: b_host can't driver + * vbus, but PP in PORTSC needs to + * be 1 for host to work. + * So we set drv_vbus bit in + * transceiver to 0 thru ULPI. + */ + write_ulpi(0x0c, 0x20); + } + } + + otg_dev->host_working = 1; + } + } else { + /* stop fsl usb host controller */ + if (!otg_dev->host_working) + goto end; + else { + VDBG("host off......\n"); + if (dev && dev->driver) { + if (dev->driver->pm && dev->driver->pm->suspend) + retval = dev->driver->pm->suspend(dev); + if (fsm->id) + /* default-b */ + fsl_otg_drv_vbus(0); + } + otg_dev->host_working = 0; + } + } +end: + return retval; +} + +/* + * Call suspend and resume function in udc driver + * to stop and start udc driver. + */ +int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) +{ + struct otg_transceiver *xceiv = fsm->transceiver; + struct device *dev; + + if (!xceiv->gadget || !xceiv->gadget->dev.parent) + return -ENODEV; + + VDBG("gadget %s\n", on ? "on" : "off"); + dev = xceiv->gadget->dev.parent; + + if (on) { + if (dev->driver->resume) + dev->driver->resume(dev); + } else { + if (dev->driver->suspend) + dev->driver->suspend(dev, otg_suspend_state); + } + + return 0; +} + +/* + * Called by initialization code of host driver. Register host controller + * to the OTG. Suspend host for OTG role detection. + */ +static int fsl_otg_set_host(struct otg_transceiver *otg_p, struct usb_bus *host) +{ + struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); + + if (!otg_p || otg_dev != fsl_otg_dev) + return -ENODEV; + + otg_p->host = host; + + otg_dev->fsm.a_bus_drop = 0; + otg_dev->fsm.a_bus_req = 1; + + if (host) { + VDBG("host off......\n"); + + otg_p->host->otg_port = fsl_otg_initdata.otg_port; + otg_p->host->is_b_host = otg_dev->fsm.id; + /* + * must leave time for khubd to finish its thing + * before yanking the host driver out from under it, + * so suspend the host after a short delay. + */ + otg_dev->host_working = 1; + schedule_delayed_work(&otg_dev->otg_event, 100); + return 0; + } else { + /* host driver going away */ + if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) & + OTGSC_STS_USB_ID)) { + /* Mini-A cable connected */ + struct otg_fsm *fsm = &otg_dev->fsm; + + otg_p->state = OTG_STATE_UNDEFINED; + fsm->protocol = PROTO_UNDEF; + } + } + + otg_dev->host_working = 0; + + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* Called by initialization code of udc. Register udc to OTG. */ +static int fsl_otg_set_peripheral(struct otg_transceiver *otg_p, + struct usb_gadget *gadget) +{ + struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); + + VDBG("otg_dev 0x%x\n", (int)otg_dev); + VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev); + + if (!otg_p || otg_dev != fsl_otg_dev) + return -ENODEV; + + if (!gadget) { + if (!otg_dev->otg.default_a) + otg_p->gadget->ops->vbus_draw(otg_p->gadget, 0); + usb_gadget_vbus_disconnect(otg_dev->otg.gadget); + otg_dev->otg.gadget = 0; + otg_dev->fsm.b_bus_req = 0; + otg_statemachine(&otg_dev->fsm); + return 0; + } + + otg_p->gadget = gadget; + otg_p->gadget->is_a_peripheral = !otg_dev->fsm.id; + + otg_dev->fsm.b_bus_req = 1; + + /* start the gadget right away if the ID pin says Mini-B */ + DBG("ID pin=%d\n", otg_dev->fsm.id); + if (otg_dev->fsm.id == 1) { + fsl_otg_start_host(&otg_dev->fsm, 0); + otg_drv_vbus(&otg_dev->fsm, 0); + fsl_otg_start_gadget(&otg_dev->fsm, 1); + } + + return 0; +} + +/* Set OTG port power, only for B-device */ +static int fsl_otg_set_power(struct otg_transceiver *otg_p, unsigned mA) +{ + if (!fsl_otg_dev) + return -ENODEV; + if (otg_p->state == OTG_STATE_B_PERIPHERAL) + pr_info("FSL OTG: Draw %d mA\n", mA); + + return 0; +} + +/* + * Delayed pin detect interrupt processing. + * + * When the Mini-A cable is disconnected from the board, + * the pin-detect interrupt happens before the disconnnect + * interrupts for the connected device(s). In order to + * process the disconnect interrupt(s) prior to switching + * roles, the pin-detect interrupts are delayed, and handled + * by this routine. + */ +static void fsl_otg_event(struct work_struct *work) +{ + struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work); + struct otg_fsm *fsm = &og->fsm; + + if (fsm->id) { /* switch to gadget */ + fsl_otg_start_host(fsm, 0); + otg_drv_vbus(fsm, 0); + fsl_otg_start_gadget(fsm, 1); + } +} + +/* B-device start SRP */ +static int fsl_otg_start_srp(struct otg_transceiver *otg_p) +{ + struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); + + if (!otg_p || otg_dev != fsl_otg_dev + || otg_p->state != OTG_STATE_B_IDLE) + return -ENODEV; + + otg_dev->fsm.b_bus_req = 1; + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* A_host suspend will call this function to start hnp */ +static int fsl_otg_start_hnp(struct otg_transceiver *otg_p) +{ + struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); + + if (!otg_p || otg_dev != fsl_otg_dev) + return -ENODEV; + + DBG("start_hnp...n"); + + /* clear a_bus_req to enter a_suspend state */ + otg_dev->fsm.a_bus_req = 0; + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* + * Interrupt handler. OTG/host/peripheral share the same int line. + * OTG driver clears OTGSC interrupts and leaves USB interrupts + * intact. It needs to have knowledge of some USB interrupts + * such as port change. + */ +irqreturn_t fsl_otg_isr(int irq, void *dev_id) +{ + struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; + struct otg_transceiver *otg = &((struct fsl_otg *)dev_id)->otg; + u32 otg_int_src, otg_sc; + + otg_sc = fsl_readl(&usb_dr_regs->otgsc); + otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8); + + /* Only clear otg interrupts */ + fsl_writel(otg_sc, &usb_dr_regs->otgsc); + + /*FIXME: ID change not generate when init to 0 */ + fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; + otg->default_a = (fsm->id == 0); + + /* process OTG interrupts */ + if (otg_int_src) { + if (otg_int_src & OTGSC_INTSTS_USB_ID) { + fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; + otg->default_a = (fsm->id == 0); + /* clear conn information */ + if (fsm->id) + fsm->b_conn = 0; + else + fsm->a_conn = 0; + + if (otg->host) + otg->host->is_b_host = fsm->id; + if (otg->gadget) + otg->gadget->is_a_peripheral = !fsm->id; + VDBG("ID int (ID is %d)\n", fsm->id); + + if (fsm->id) { /* switch to gadget */ + schedule_delayed_work( + &((struct fsl_otg *)dev_id)->otg_event, + 100); + } else { /* switch to host */ + cancel_delayed_work(& + ((struct fsl_otg *)dev_id)-> + otg_event); + fsl_otg_start_gadget(fsm, 0); + otg_drv_vbus(fsm, 1); + fsl_otg_start_host(fsm, 1); + } + return IRQ_HANDLED; + } + } + return IRQ_NONE; +} + +static struct otg_fsm_ops fsl_otg_ops = { + .chrg_vbus = fsl_otg_chrg_vbus, + .drv_vbus = fsl_otg_drv_vbus, + .loc_conn = fsl_otg_loc_conn, + .loc_sof = fsl_otg_loc_sof, + .start_pulse = fsl_otg_start_pulse, + + .add_timer = fsl_otg_add_timer, + .del_timer = fsl_otg_del_timer, + + .start_host = fsl_otg_start_host, + .start_gadget = fsl_otg_start_gadget, +}; + +/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */ +static int fsl_otg_conf(struct platform_device *pdev) +{ + struct fsl_otg *fsl_otg_tc; + int status; + + if (fsl_otg_dev) + return 0; + + /* allocate space to fsl otg device */ + fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL); + if (!fsl_otg_tc) + return -ENOMEM; + + INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event); + + INIT_LIST_HEAD(&active_timers); + status = fsl_otg_init_timers(&fsl_otg_tc->fsm); + if (status) { + pr_info("Couldn't init OTG timers\n"); + goto err; + } + spin_lock_init(&fsl_otg_tc->fsm.lock); + + /* Set OTG state machine operations */ + fsl_otg_tc->fsm.ops = &fsl_otg_ops; + + /* initialize the otg structure */ + fsl_otg_tc->otg.label = DRIVER_DESC; + fsl_otg_tc->otg.set_host = fsl_otg_set_host; + fsl_otg_tc->otg.set_peripheral = fsl_otg_set_peripheral; + fsl_otg_tc->otg.set_power = fsl_otg_set_power; + fsl_otg_tc->otg.start_hnp = fsl_otg_start_hnp; + fsl_otg_tc->otg.start_srp = fsl_otg_start_srp; + + fsl_otg_dev = fsl_otg_tc; + + /* Store the otg transceiver */ + status = otg_set_transceiver(&fsl_otg_tc->otg); + if (status) { + pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); + goto err; + } + + return 0; +err: + fsl_otg_uninit_timers(); + kfree(fsl_otg_tc); + return status; +} + +/* OTG Initialization */ +int usb_otg_start(struct platform_device *pdev) +{ + struct fsl_otg *p_otg; + struct otg_transceiver *otg_trans = otg_get_transceiver(); + struct otg_fsm *fsm; + int status; + struct resource *res; + u32 temp; + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + + p_otg = container_of(otg_trans, struct fsl_otg, otg); + fsm = &p_otg->fsm; + + /* Initialize the state machine structure with default values */ + SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED); + fsm->transceiver = &p_otg->otg; + + /* We don't require predefined MEM/IRQ resource index */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + /* We don't request_mem_region here to enable resource sharing + * with host/device */ + + usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap)); + p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs; + pdata->regs = (void *)usb_dr_regs; + + if (pdata->init && pdata->init(pdev) != 0) + return -EINVAL; + + if (pdata->big_endian_mmio) { + _fsl_readl = _fsl_readl_be; + _fsl_writel = _fsl_writel_be; + } else { + _fsl_readl = _fsl_readl_le; + _fsl_writel = _fsl_writel_le; + } + + /* request irq */ + p_otg->irq = platform_get_irq(pdev, 0); + status = request_irq(p_otg->irq, fsl_otg_isr, + IRQF_SHARED, driver_name, p_otg); + if (status) { + dev_dbg(p_otg->otg.dev, "can't get IRQ %d, error %d\n", + p_otg->irq, status); + iounmap(p_otg->dr_mem_map); + kfree(p_otg); + return status; + } + + /* stop the controller */ + temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); + temp &= ~USB_CMD_RUN_STOP; + fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); + + /* reset the controller */ + temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); + temp |= USB_CMD_CTRL_RESET; + fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); + + /* wait reset completed */ + while (fsl_readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET) + ; + + /* configure the VBUSHS as IDLE(both host and device) */ + temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0); + fsl_writel(temp, &p_otg->dr_mem_map->usbmode); + + /* configure PHY interface */ + temp = fsl_readl(&p_otg->dr_mem_map->portsc); + temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW); + switch (pdata->phy_mode) { + case FSL_USB2_PHY_ULPI: + temp |= PORTSC_PTS_ULPI; + break; + case FSL_USB2_PHY_UTMI_WIDE: + temp |= PORTSC_PTW_16BIT; + /* fall through */ + case FSL_USB2_PHY_UTMI: + temp |= PORTSC_PTS_UTMI; + /* fall through */ + default: + break; + } + fsl_writel(temp, &p_otg->dr_mem_map->portsc); + + if (pdata->have_sysif_regs) { + /* configure control enable IO output, big endian register */ + temp = __raw_readl(&p_otg->dr_mem_map->control); + temp |= USB_CTRL_IOENB; + __raw_writel(temp, &p_otg->dr_mem_map->control); + } + + /* disable all interrupt and clear all OTGSC status */ + temp = fsl_readl(&p_otg->dr_mem_map->otgsc); + temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK; + temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE; + fsl_writel(temp, &p_otg->dr_mem_map->otgsc); + + /* + * The identification (id) input is FALSE when a Mini-A plug is inserted + * in the devices Mini-AB receptacle. Otherwise, this input is TRUE. + * Also: record initial state of ID pin + */ + if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) { + p_otg->otg.state = OTG_STATE_UNDEFINED; + p_otg->fsm.id = 1; + } else { + p_otg->otg.state = OTG_STATE_A_IDLE; + p_otg->fsm.id = 0; + } + + DBG("initial ID pin=%d\n", p_otg->fsm.id); + + /* enable OTG ID pin interrupt */ + temp = fsl_readl(&p_otg->dr_mem_map->otgsc); + temp |= OTGSC_INTR_USB_ID_EN; + temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN); + fsl_writel(temp, &p_otg->dr_mem_map->otgsc); + + return 0; +} + +/* + * state file in sysfs + */ +static int show_fsl_usb2_otg_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct otg_fsm *fsm = &fsl_otg_dev->fsm; + char *next = buf; + unsigned size = PAGE_SIZE; + unsigned long flags; + int t; + + spin_lock_irqsave(&fsm->lock, flags); + + /* basic driver infomation */ + t = scnprintf(next, size, + DRIVER_DESC "\n" "fsl_usb2_otg version: %s\n\n", + DRIVER_VERSION); + size -= t; + next += t; + + /* Registers */ + t = scnprintf(next, size, + "OTGSC: 0x%08x\n" + "PORTSC: 0x%08x\n" + "USBMODE: 0x%08x\n" + "USBCMD: 0x%08x\n" + "USBSTS: 0x%08x\n" + "USBINTR: 0x%08x\n", + fsl_readl(&usb_dr_regs->otgsc), + fsl_readl(&usb_dr_regs->portsc), + fsl_readl(&usb_dr_regs->usbmode), + fsl_readl(&usb_dr_regs->usbcmd), + fsl_readl(&usb_dr_regs->usbsts), + fsl_readl(&usb_dr_regs->usbintr)); + size -= t; + next += t; + + /* State */ + t = scnprintf(next, size, + "OTG state: %s\n\n", + otg_state_string(fsl_otg_dev->otg.state)); + size -= t; + next += t; + + /* State Machine Variables */ + t = scnprintf(next, size, + "a_bus_req: %d\n" + "b_bus_req: %d\n" + "a_bus_resume: %d\n" + "a_bus_suspend: %d\n" + "a_conn: %d\n" + "a_sess_vld: %d\n" + "a_srp_det: %d\n" + "a_vbus_vld: %d\n" + "b_bus_resume: %d\n" + "b_bus_suspend: %d\n" + "b_conn: %d\n" + "b_se0_srp: %d\n" + "b_sess_end: %d\n" + "b_sess_vld: %d\n" + "id: %d\n", + fsm->a_bus_req, + fsm->b_bus_req, + fsm->a_bus_resume, + fsm->a_bus_suspend, + fsm->a_conn, + fsm->a_sess_vld, + fsm->a_srp_det, + fsm->a_vbus_vld, + fsm->b_bus_resume, + fsm->b_bus_suspend, + fsm->b_conn, + fsm->b_se0_srp, + fsm->b_sess_end, + fsm->b_sess_vld, + fsm->id); + size -= t; + next += t; + + spin_unlock_irqrestore(&fsm->lock, flags); + + return PAGE_SIZE - size; +} + +static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL); + + +/* Char driver interface to control some OTG input */ + +/* + * Handle some ioctl command, such as get otg + * status and set host suspend + */ +static long fsl_otg_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + u32 retval = 0; + + switch (cmd) { + case GET_OTG_STATUS: + retval = fsl_otg_dev->host_working; + break; + + case SET_A_SUSPEND_REQ: + fsl_otg_dev->fsm.a_suspend_req = arg; + break; + + case SET_A_BUS_DROP: + fsl_otg_dev->fsm.a_bus_drop = arg; + break; + + case SET_A_BUS_REQ: + fsl_otg_dev->fsm.a_bus_req = arg; + break; + + case SET_B_BUS_REQ: + fsl_otg_dev->fsm.b_bus_req = arg; + break; + + default: + break; + } + + otg_statemachine(&fsl_otg_dev->fsm); + + return retval; +} + +static int fsl_otg_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int fsl_otg_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations otg_fops = { + .owner = THIS_MODULE, + .llseek = NULL, + .read = NULL, + .write = NULL, + .unlocked_ioctl = fsl_otg_ioctl, + .open = fsl_otg_open, + .release = fsl_otg_release, +}; + +static int __devinit fsl_otg_probe(struct platform_device *pdev) +{ + int ret; + + if (!pdev->dev.platform_data) + return -ENODEV; + + /* configure the OTG */ + ret = fsl_otg_conf(pdev); + if (ret) { + dev_err(&pdev->dev, "Couldn't configure OTG module\n"); + return ret; + } + + /* start OTG */ + ret = usb_otg_start(pdev); + if (ret) { + dev_err(&pdev->dev, "Can't init FSL OTG device\n"); + return ret; + } + + ret = register_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME, &otg_fops); + if (ret) { + dev_err(&pdev->dev, "unable to register FSL OTG device\n"); + return ret; + } + + ret = device_create_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); + if (ret) + dev_warn(&pdev->dev, "Can't register sysfs attribute\n"); + + return ret; +} + +static int __devexit fsl_otg_remove(struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + + otg_set_transceiver(NULL); + free_irq(fsl_otg_dev->irq, fsl_otg_dev); + + iounmap((void *)usb_dr_regs); + + fsl_otg_uninit_timers(); + kfree(fsl_otg_dev); + + device_remove_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); + + unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME); + + if (pdata->exit) + pdata->exit(pdev); + + return 0; +} + +struct platform_driver fsl_otg_driver = { + .probe = fsl_otg_probe, + .remove = __devexit_p(fsl_otg_remove), + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + }, +}; + +static int __init fsl_usb_otg_init(void) +{ + pr_info(DRIVER_INFO "\n"); + return platform_driver_register(&fsl_otg_driver); +} +module_init(fsl_usb_otg_init); + +static void __exit fsl_usb_otg_exit(void) +{ + platform_driver_unregister(&fsl_otg_driver); +} +module_exit(fsl_usb_otg_exit); + +MODULE_DESCRIPTION(DRIVER_INFO); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/otg/fsl_otg.h b/drivers/usb/otg/fsl_otg.h new file mode 100644 index 00000000000..3f8ef731aac --- /dev/null +++ b/drivers/usb/otg/fsl_otg.h @@ -0,0 +1,406 @@ +/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "otg_fsm.h" +#include +#include + +/* USB Command Register Bit Masks */ +#define USB_CMD_RUN_STOP (0x1<<0) +#define USB_CMD_CTRL_RESET (0x1<<1) +#define USB_CMD_PERIODIC_SCHEDULE_EN (0x1<<4) +#define USB_CMD_ASYNC_SCHEDULE_EN (0x1<<5) +#define USB_CMD_INT_AA_DOORBELL (0x1<<6) +#define USB_CMD_ASP (0x3<<8) +#define USB_CMD_ASYNC_SCH_PARK_EN (0x1<<11) +#define USB_CMD_SUTW (0x1<<13) +#define USB_CMD_ATDTW (0x1<<14) +#define USB_CMD_ITC (0xFF<<16) + +/* bit 15,3,2 are frame list size */ +#define USB_CMD_FRAME_SIZE_1024 (0x0<<15 | 0x0<<2) +#define USB_CMD_FRAME_SIZE_512 (0x0<<15 | 0x1<<2) +#define USB_CMD_FRAME_SIZE_256 (0x0<<15 | 0x2<<2) +#define USB_CMD_FRAME_SIZE_128 (0x0<<15 | 0x3<<2) +#define USB_CMD_FRAME_SIZE_64 (0x1<<15 | 0x0<<2) +#define USB_CMD_FRAME_SIZE_32 (0x1<<15 | 0x1<<2) +#define USB_CMD_FRAME_SIZE_16 (0x1<<15 | 0x2<<2) +#define USB_CMD_FRAME_SIZE_8 (0x1<<15 | 0x3<<2) + +/* bit 9-8 are async schedule park mode count */ +#define USB_CMD_ASP_00 (0x0<<8) +#define USB_CMD_ASP_01 (0x1<<8) +#define USB_CMD_ASP_10 (0x2<<8) +#define USB_CMD_ASP_11 (0x3<<8) +#define USB_CMD_ASP_BIT_POS (8) + +/* bit 23-16 are interrupt threshold control */ +#define USB_CMD_ITC_NO_THRESHOLD (0x00<<16) +#define USB_CMD_ITC_1_MICRO_FRM (0x01<<16) +#define USB_CMD_ITC_2_MICRO_FRM (0x02<<16) +#define USB_CMD_ITC_4_MICRO_FRM (0x04<<16) +#define USB_CMD_ITC_8_MICRO_FRM (0x08<<16) +#define USB_CMD_ITC_16_MICRO_FRM (0x10<<16) +#define USB_CMD_ITC_32_MICRO_FRM (0x20<<16) +#define USB_CMD_ITC_64_MICRO_FRM (0x40<<16) +#define USB_CMD_ITC_BIT_POS (16) + +/* USB Status Register Bit Masks */ +#define USB_STS_INT (0x1<<0) +#define USB_STS_ERR (0x1<<1) +#define USB_STS_PORT_CHANGE (0x1<<2) +#define USB_STS_FRM_LST_ROLL (0x1<<3) +#define USB_STS_SYS_ERR (0x1<<4) +#define USB_STS_IAA (0x1<<5) +#define USB_STS_RESET_RECEIVED (0x1<<6) +#define USB_STS_SOF (0x1<<7) +#define USB_STS_DCSUSPEND (0x1<<8) +#define USB_STS_HC_HALTED (0x1<<12) +#define USB_STS_RCL (0x1<<13) +#define USB_STS_PERIODIC_SCHEDULE (0x1<<14) +#define USB_STS_ASYNC_SCHEDULE (0x1<<15) + +/* USB Interrupt Enable Register Bit Masks */ +#define USB_INTR_INT_EN (0x1<<0) +#define USB_INTR_ERR_INT_EN (0x1<<1) +#define USB_INTR_PC_DETECT_EN (0x1<<2) +#define USB_INTR_FRM_LST_ROLL_EN (0x1<<3) +#define USB_INTR_SYS_ERR_EN (0x1<<4) +#define USB_INTR_ASYN_ADV_EN (0x1<<5) +#define USB_INTR_RESET_EN (0x1<<6) +#define USB_INTR_SOF_EN (0x1<<7) +#define USB_INTR_DEVICE_SUSPEND (0x1<<8) + +/* Device Address bit masks */ +#define USB_DEVICE_ADDRESS_MASK (0x7F<<25) +#define USB_DEVICE_ADDRESS_BIT_POS (25) +/* PORTSC Register Bit Masks,Only one PORT in OTG mode*/ +#define PORTSC_CURRENT_CONNECT_STATUS (0x1<<0) +#define PORTSC_CONNECT_STATUS_CHANGE (0x1<<1) +#define PORTSC_PORT_ENABLE (0x1<<2) +#define PORTSC_PORT_EN_DIS_CHANGE (0x1<<3) +#define PORTSC_OVER_CURRENT_ACT (0x1<<4) +#define PORTSC_OVER_CUURENT_CHG (0x1<<5) +#define PORTSC_PORT_FORCE_RESUME (0x1<<6) +#define PORTSC_PORT_SUSPEND (0x1<<7) +#define PORTSC_PORT_RESET (0x1<<8) +#define PORTSC_LINE_STATUS_BITS (0x3<<10) +#define PORTSC_PORT_POWER (0x1<<12) +#define PORTSC_PORT_INDICTOR_CTRL (0x3<<14) +#define PORTSC_PORT_TEST_CTRL (0xF<<16) +#define PORTSC_WAKE_ON_CONNECT_EN (0x1<<20) +#define PORTSC_WAKE_ON_CONNECT_DIS (0x1<<21) +#define PORTSC_WAKE_ON_OVER_CURRENT (0x1<<22) +#define PORTSC_PHY_LOW_POWER_SPD (0x1<<23) +#define PORTSC_PORT_FORCE_FULL_SPEED (0x1<<24) +#define PORTSC_PORT_SPEED_MASK (0x3<<26) +#define PORTSC_TRANSCEIVER_WIDTH (0x1<<28) +#define PORTSC_PHY_TYPE_SEL (0x3<<30) +/* bit 11-10 are line status */ +#define PORTSC_LINE_STATUS_SE0 (0x0<<10) +#define PORTSC_LINE_STATUS_JSTATE (0x1<<10) +#define PORTSC_LINE_STATUS_KSTATE (0x2<<10) +#define PORTSC_LINE_STATUS_UNDEF (0x3<<10) +#define PORTSC_LINE_STATUS_BIT_POS (10) + +/* bit 15-14 are port indicator control */ +#define PORTSC_PIC_OFF (0x0<<14) +#define PORTSC_PIC_AMBER (0x1<<14) +#define PORTSC_PIC_GREEN (0x2<<14) +#define PORTSC_PIC_UNDEF (0x3<<14) +#define PORTSC_PIC_BIT_POS (14) + +/* bit 19-16 are port test control */ +#define PORTSC_PTC_DISABLE (0x0<<16) +#define PORTSC_PTC_JSTATE (0x1<<16) +#define PORTSC_PTC_KSTATE (0x2<<16) +#define PORTSC_PTC_SEQNAK (0x3<<16) +#define PORTSC_PTC_PACKET (0x4<<16) +#define PORTSC_PTC_FORCE_EN (0x5<<16) +#define PORTSC_PTC_BIT_POS (16) + +/* bit 27-26 are port speed */ +#define PORTSC_PORT_SPEED_FULL (0x0<<26) +#define PORTSC_PORT_SPEED_LOW (0x1<<26) +#define PORTSC_PORT_SPEED_HIGH (0x2<<26) +#define PORTSC_PORT_SPEED_UNDEF (0x3<<26) +#define PORTSC_SPEED_BIT_POS (26) + +/* bit 28 is parallel transceiver width for UTMI interface */ +#define PORTSC_PTW (0x1<<28) +#define PORTSC_PTW_8BIT (0x0<<28) +#define PORTSC_PTW_16BIT (0x1<<28) + +/* bit 31-30 are port transceiver select */ +#define PORTSC_PTS_UTMI (0x0<<30) +#define PORTSC_PTS_ULPI (0x2<<30) +#define PORTSC_PTS_FSLS_SERIAL (0x3<<30) +#define PORTSC_PTS_BIT_POS (30) + +#define PORTSC_W1C_BITS \ + (PORTSC_CONNECT_STATUS_CHANGE | \ + PORTSC_PORT_EN_DIS_CHANGE | \ + PORTSC_OVER_CUURENT_CHG) + +/* OTG Status Control Register Bit Masks */ +#define OTGSC_CTRL_VBUS_DISCHARGE (0x1<<0) +#define OTGSC_CTRL_VBUS_CHARGE (0x1<<1) +#define OTGSC_CTRL_OTG_TERMINATION (0x1<<3) +#define OTGSC_CTRL_DATA_PULSING (0x1<<4) +#define OTGSC_CTRL_ID_PULL_EN (0x1<<5) +#define OTGSC_HA_DATA_PULSE (0x1<<6) +#define OTGSC_HA_BA (0x1<<7) +#define OTGSC_STS_USB_ID (0x1<<8) +#define OTGSC_STS_A_VBUS_VALID (0x1<<9) +#define OTGSC_STS_A_SESSION_VALID (0x1<<10) +#define OTGSC_STS_B_SESSION_VALID (0x1<<11) +#define OTGSC_STS_B_SESSION_END (0x1<<12) +#define OTGSC_STS_1MS_TOGGLE (0x1<<13) +#define OTGSC_STS_DATA_PULSING (0x1<<14) +#define OTGSC_INTSTS_USB_ID (0x1<<16) +#define OTGSC_INTSTS_A_VBUS_VALID (0x1<<17) +#define OTGSC_INTSTS_A_SESSION_VALID (0x1<<18) +#define OTGSC_INTSTS_B_SESSION_VALID (0x1<<19) +#define OTGSC_INTSTS_B_SESSION_END (0x1<<20) +#define OTGSC_INTSTS_1MS (0x1<<21) +#define OTGSC_INTSTS_DATA_PULSING (0x1<<22) +#define OTGSC_INTR_USB_ID_EN (0x1<<24) +#define OTGSC_INTR_A_VBUS_VALID_EN (0x1<<25) +#define OTGSC_INTR_A_SESSION_VALID_EN (0x1<<26) +#define OTGSC_INTR_B_SESSION_VALID_EN (0x1<<27) +#define OTGSC_INTR_B_SESSION_END_EN (0x1<<28) +#define OTGSC_INTR_1MS_TIMER_EN (0x1<<29) +#define OTGSC_INTR_DATA_PULSING_EN (0x1<<30) +#define OTGSC_INTSTS_MASK (0x00ff0000) + +/* USB MODE Register Bit Masks */ +#define USB_MODE_CTRL_MODE_IDLE (0x0<<0) +#define USB_MODE_CTRL_MODE_DEVICE (0x2<<0) +#define USB_MODE_CTRL_MODE_HOST (0x3<<0) +#define USB_MODE_CTRL_MODE_RSV (0x1<<0) +#define USB_MODE_SETUP_LOCK_OFF (0x1<<3) +#define USB_MODE_STREAM_DISABLE (0x1<<4) +#define USB_MODE_ES (0x1<<2) /* Endian Select */ + +/* control Register Bit Masks */ +#define USB_CTRL_IOENB (0x1<<2) +#define USB_CTRL_ULPI_INT0EN (0x1<<0) + +/* BCSR5 */ +#define BCSR5_INT_USB (0x02) + +/* USB module clk cfg */ +#define SCCR_OFFS (0xA08) +#define SCCR_USB_CLK_DISABLE (0x00000000) /* USB clk disable */ +#define SCCR_USB_MPHCM_11 (0x00c00000) +#define SCCR_USB_MPHCM_01 (0x00400000) +#define SCCR_USB_MPHCM_10 (0x00800000) +#define SCCR_USB_DRCM_11 (0x00300000) +#define SCCR_USB_DRCM_01 (0x00100000) +#define SCCR_USB_DRCM_10 (0x00200000) + +#define SICRL_OFFS (0x114) +#define SICRL_USB0 (0x40000000) +#define SICRL_USB1 (0x20000000) + +#define SICRH_OFFS (0x118) +#define SICRH_USB_UTMI (0x00020000) + +/* OTG interrupt enable bit masks */ +#define OTGSC_INTERRUPT_ENABLE_BITS_MASK \ + (OTGSC_INTR_USB_ID_EN | \ + OTGSC_INTR_1MS_TIMER_EN | \ + OTGSC_INTR_A_VBUS_VALID_EN | \ + OTGSC_INTR_A_SESSION_VALID_EN | \ + OTGSC_INTR_B_SESSION_VALID_EN | \ + OTGSC_INTR_B_SESSION_END_EN | \ + OTGSC_INTR_DATA_PULSING_EN) + +/* OTG interrupt status bit masks */ +#define OTGSC_INTERRUPT_STATUS_BITS_MASK \ + (OTGSC_INTSTS_USB_ID | \ + OTGSC_INTR_1MS_TIMER_EN | \ + OTGSC_INTSTS_A_VBUS_VALID | \ + OTGSC_INTSTS_A_SESSION_VALID | \ + OTGSC_INTSTS_B_SESSION_VALID | \ + OTGSC_INTSTS_B_SESSION_END | \ + OTGSC_INTSTS_DATA_PULSING) + +/* + * A-DEVICE timing constants + */ + +/* Wait for VBUS Rise */ +#define TA_WAIT_VRISE (100) /* a_wait_vrise 100 ms, section: 6.6.5.1 */ + +/* Wait for B-Connect */ +#define TA_WAIT_BCON (10000) /* a_wait_bcon > 1 sec, section: 6.6.5.2 + * This is only used to get out of + * OTG_STATE_A_WAIT_BCON state if there was + * no connection for these many milliseconds + */ + +/* A-Idle to B-Disconnect */ +/* It is necessary for this timer to be more than 750 ms because of a bug in OPT + * test 5.4 in which B OPT disconnects after 750 ms instead of 75ms as stated + * in the test description + */ +#define TA_AIDL_BDIS (5000) /* a_suspend minimum 200 ms, section: 6.6.5.3 */ + +/* B-Idle to A-Disconnect */ +#define TA_BIDL_ADIS (12) /* 3 to 200 ms */ + +/* B-device timing constants */ + + +/* Data-Line Pulse Time*/ +#define TB_DATA_PLS (10) /* b_srp_init,continue 5~10ms, section:5.3.3 */ +#define TB_DATA_PLS_MIN (5) /* minimum 5 ms */ +#define TB_DATA_PLS_MAX (10) /* maximum 10 ms */ + +/* SRP Initiate Time */ +#define TB_SRP_INIT (100) /* b_srp_init,maximum 100 ms, section:5.3.8 */ + +/* SRP Fail Time */ +#define TB_SRP_FAIL (7000) /* b_srp_init,Fail time 5~30s, section:6.8.2.2*/ + +/* SRP result wait time */ +#define TB_SRP_WAIT (60) + +/* VBus time */ +#define TB_VBUS_PLS (30) /* time to keep vbus pulsing asserted */ + +/* Discharge time */ +/* This time should be less than 10ms. It varies from system to system. */ +#define TB_VBUS_DSCHRG (8) + +/* A-SE0 to B-Reset */ +#define TB_ASE0_BRST (20) /* b_wait_acon, mini 3.125 ms,section:6.8.2.4 */ + +/* A bus suspend timer before we can switch to b_wait_aconn */ +#define TB_A_SUSPEND (7) +#define TB_BUS_RESUME (12) + +/* SE0 Time Before SRP */ +#define TB_SE0_SRP (2) /* b_idle,minimum 2 ms, section:5.3.2 */ + +#define SET_OTG_STATE(otg_ptr, newstate) ((otg_ptr)->state = newstate) + +struct usb_dr_mmap { + /* Capability register */ + u8 res1[256]; + u16 caplength; /* Capability Register Length */ + u16 hciversion; /* Host Controller Interface Version */ + u32 hcsparams; /* Host Controller Structual Parameters */ + u32 hccparams; /* Host Controller Capability Parameters */ + u8 res2[20]; + u32 dciversion; /* Device Controller Interface Version */ + u32 dccparams; /* Device Controller Capability Parameters */ + u8 res3[24]; + /* Operation register */ + u32 usbcmd; /* USB Command Register */ + u32 usbsts; /* USB Status Register */ + u32 usbintr; /* USB Interrupt Enable Register */ + u32 frindex; /* Frame Index Register */ + u8 res4[4]; + u32 deviceaddr; /* Device Address */ + u32 endpointlistaddr; /* Endpoint List Address Register */ + u8 res5[4]; + u32 burstsize; /* Master Interface Data Burst Size Register */ + u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ + u8 res6[8]; + u32 ulpiview; /* ULPI register access */ + u8 res7[12]; + u32 configflag; /* Configure Flag Register */ + u32 portsc; /* Port 1 Status and Control Register */ + u8 res8[28]; + u32 otgsc; /* On-The-Go Status and Control */ + u32 usbmode; /* USB Mode Register */ + u32 endptsetupstat; /* Endpoint Setup Status Register */ + u32 endpointprime; /* Endpoint Initialization Register */ + u32 endptflush; /* Endpoint Flush Register */ + u32 endptstatus; /* Endpoint Status Register */ + u32 endptcomplete; /* Endpoint Complete Register */ + u32 endptctrl[6]; /* Endpoint Control Registers */ + u8 res9[552]; + u32 snoop1; + u32 snoop2; + u32 age_cnt_thresh; /* Age Count Threshold Register */ + u32 pri_ctrl; /* Priority Control Register */ + u32 si_ctrl; /* System Interface Control Register */ + u8 res10[236]; + u32 control; /* General Purpose Control Register */ +}; + +struct fsl_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; +}; + +inline struct fsl_otg_timer *otg_timer_initializer +(void (*function)(unsigned long), unsigned long expires, unsigned long data) +{ + struct fsl_otg_timer *timer; + + timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL); + if (!timer) + return NULL; + timer->function = function; + timer->expires = expires; + timer->data = data; + return timer; +} + +struct fsl_otg { + struct otg_transceiver otg; + struct otg_fsm fsm; + struct usb_dr_mmap *dr_mem_map; + struct delayed_work otg_event; + + /* used for usb host */ + struct work_struct work_wq; + u8 host_working; + + int irq; +}; + +struct fsl_otg_config { + u8 otg_port; +}; + +/* For SRP and HNP handle */ +#define FSL_OTG_MAJOR 240 +#define FSL_OTG_NAME "fsl-usb2-otg" +/* Command to OTG driver ioctl */ +#define OTG_IOCTL_MAGIC FSL_OTG_MAJOR +/* if otg work as host, it should return 1, otherwise return 0 */ +#define GET_OTG_STATUS _IOR(OTG_IOCTL_MAGIC, 1, int) +#define SET_A_SUSPEND_REQ _IOW(OTG_IOCTL_MAGIC, 2, int) +#define SET_A_BUS_DROP _IOW(OTG_IOCTL_MAGIC, 3, int) +#define SET_A_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 4, int) +#define SET_B_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 5, int) +#define GET_A_SUSPEND_REQ _IOR(OTG_IOCTL_MAGIC, 6, int) +#define GET_A_BUS_DROP _IOR(OTG_IOCTL_MAGIC, 7, int) +#define GET_A_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 8, int) +#define GET_B_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 9, int) + +void fsl_otg_add_timer(void *timer); +void fsl_otg_del_timer(void *timer); +void fsl_otg_pulse_vbus(void); diff --git a/drivers/usb/otg/otg_fsm.c b/drivers/usb/otg/otg_fsm.c new file mode 100644 index 00000000000..b0cc422f2ff --- /dev/null +++ b/drivers/usb/otg/otg_fsm.c @@ -0,0 +1,349 @@ +/* + * OTG Finite State Machine from OTG spec + * + * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * Author: Li Yang + * Jerry Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "otg_fsm.h" + +/* Change USB protocol when there is a protocol change */ +static int otg_set_protocol(struct otg_fsm *fsm, int protocol) +{ + int ret = 0; + + if (fsm->protocol != protocol) { + VDBG("Changing role fsm->protocol= %d; new protocol= %d\n", + fsm->protocol, protocol); + /* stop old protocol */ + if (fsm->protocol == PROTO_HOST) + ret = fsm->ops->start_host(fsm, 0); + else if (fsm->protocol == PROTO_GADGET) + ret = fsm->ops->start_gadget(fsm, 0); + if (ret) + return ret; + + /* start new protocol */ + if (protocol == PROTO_HOST) + ret = fsm->ops->start_host(fsm, 1); + else if (protocol == PROTO_GADGET) + ret = fsm->ops->start_gadget(fsm, 1); + if (ret) + return ret; + + fsm->protocol = protocol; + return 0; + } + + return 0; +} + +static int state_changed; + +/* Called when leaving a state. Do state clean up jobs here */ +void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) +{ + switch (old_state) { + case OTG_STATE_B_IDLE: + otg_del_timer(fsm, b_se0_srp_tmr); + fsm->b_se0_srp = 0; + break; + case OTG_STATE_B_SRP_INIT: + fsm->b_srp_done = 0; + break; + case OTG_STATE_B_PERIPHERAL: + break; + case OTG_STATE_B_WAIT_ACON: + otg_del_timer(fsm, b_ase0_brst_tmr); + fsm->b_ase0_brst_tmout = 0; + break; + case OTG_STATE_B_HOST: + break; + case OTG_STATE_A_IDLE: + break; + case OTG_STATE_A_WAIT_VRISE: + otg_del_timer(fsm, a_wait_vrise_tmr); + fsm->a_wait_vrise_tmout = 0; + break; + case OTG_STATE_A_WAIT_BCON: + otg_del_timer(fsm, a_wait_bcon_tmr); + fsm->a_wait_bcon_tmout = 0; + break; + case OTG_STATE_A_HOST: + otg_del_timer(fsm, a_wait_enum_tmr); + break; + case OTG_STATE_A_SUSPEND: + otg_del_timer(fsm, a_aidl_bdis_tmr); + fsm->a_aidl_bdis_tmout = 0; + fsm->a_suspend_req = 0; + break; + case OTG_STATE_A_PERIPHERAL: + break; + case OTG_STATE_A_WAIT_VFALL: + otg_del_timer(fsm, a_wait_vrise_tmr); + break; + case OTG_STATE_A_VBUS_ERR: + break; + default: + break; + } +} + +/* Called when entering a state */ +int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) +{ + state_changed = 1; + if (fsm->transceiver->state == new_state) + return 0; + VDBG("Set state: %s\n", otg_state_string(new_state)); + otg_leave_state(fsm, fsm->transceiver->state); + switch (new_state) { + case OTG_STATE_B_IDLE: + otg_drv_vbus(fsm, 0); + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + otg_add_timer(fsm, b_se0_srp_tmr); + break; + case OTG_STATE_B_SRP_INIT: + otg_start_pulse(fsm); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + otg_add_timer(fsm, b_srp_fail_tmr); + break; + case OTG_STATE_B_PERIPHERAL: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 1); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_GADGET); + break; + case OTG_STATE_B_WAIT_ACON: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, b_ase0_brst_tmr); + fsm->a_bus_suspend = 0; + break; + case OTG_STATE_B_HOST: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 1); + otg_set_protocol(fsm, PROTO_HOST); + usb_bus_start_enum(fsm->transceiver->host, + fsm->transceiver->host->otg_port); + break; + case OTG_STATE_A_IDLE: + otg_drv_vbus(fsm, 0); + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + break; + case OTG_STATE_A_WAIT_VRISE: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_wait_vrise_tmr); + break; + case OTG_STATE_A_WAIT_BCON: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_wait_bcon_tmr); + break; + case OTG_STATE_A_HOST: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 1); + otg_set_protocol(fsm, PROTO_HOST); + /* + * When HNP is triggered while a_bus_req = 0, a_host will + * suspend too fast to complete a_set_b_hnp_en + */ + if (!fsm->a_bus_req || fsm->a_suspend_req) + otg_add_timer(fsm, a_wait_enum_tmr); + break; + case OTG_STATE_A_SUSPEND: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_aidl_bdis_tmr); + + break; + case OTG_STATE_A_PERIPHERAL: + otg_loc_conn(fsm, 1); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_GADGET); + otg_drv_vbus(fsm, 1); + break; + case OTG_STATE_A_WAIT_VFALL: + otg_drv_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + break; + case OTG_STATE_A_VBUS_ERR: + otg_drv_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + break; + default: + break; + } + + fsm->transceiver->state = new_state; + return 0; +} + +/* State change judgement */ +int otg_statemachine(struct otg_fsm *fsm) +{ + enum usb_otg_state state; + unsigned long flags; + + spin_lock_irqsave(&fsm->lock, flags); + + state = fsm->transceiver->state; + state_changed = 0; + /* State machine state change judgement */ + + switch (state) { + case OTG_STATE_UNDEFINED: + VDBG("fsm->id = %d\n", fsm->id); + if (fsm->id) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else + otg_set_state(fsm, OTG_STATE_A_IDLE); + break; + case OTG_STATE_B_IDLE: + if (!fsm->id) + otg_set_state(fsm, OTG_STATE_A_IDLE); + else if (fsm->b_sess_vld && fsm->transceiver->gadget) + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp) + otg_set_state(fsm, OTG_STATE_B_SRP_INIT); + break; + case OTG_STATE_B_SRP_INIT: + if (!fsm->id || fsm->b_srp_done) + otg_set_state(fsm, OTG_STATE_B_IDLE); + break; + case OTG_STATE_B_PERIPHERAL: + if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (fsm->b_bus_req && fsm->transceiver-> + gadget->b_hnp_enable && fsm->a_bus_suspend) + otg_set_state(fsm, OTG_STATE_B_WAIT_ACON); + break; + case OTG_STATE_B_WAIT_ACON: + if (fsm->a_conn) + otg_set_state(fsm, OTG_STATE_B_HOST); + else if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (fsm->a_bus_resume || fsm->b_ase0_brst_tmout) { + fsm->b_ase0_brst_tmout = 0; + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + } + break; + case OTG_STATE_B_HOST: + if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (!fsm->b_bus_req || !fsm->a_conn) + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + break; + case OTG_STATE_A_IDLE: + if (fsm->id) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det)) + otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE); + break; + case OTG_STATE_A_WAIT_VRISE: + if (fsm->id || fsm->a_bus_drop || fsm->a_vbus_vld || + fsm->a_wait_vrise_tmout) { + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + } + break; + case OTG_STATE_A_WAIT_BCON: + if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + else if (fsm->b_conn) + otg_set_state(fsm, OTG_STATE_A_HOST); + else if (fsm->id | fsm->a_bus_drop | fsm->a_wait_bcon_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + case OTG_STATE_A_HOST: + if ((!fsm->a_bus_req || fsm->a_suspend_req) && + fsm->transceiver->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_SUSPEND); + else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_SUSPEND: + if (!fsm->b_conn && fsm->transceiver->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_PERIPHERAL); + else if (!fsm->b_conn && !fsm->transceiver->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (fsm->a_bus_req || fsm->b_bus_resume) + otg_set_state(fsm, OTG_STATE_A_HOST); + else if (fsm->id || fsm->a_bus_drop || fsm->a_aidl_bdis_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_PERIPHERAL: + if (fsm->id || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if (fsm->b_bus_suspend) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_WAIT_VFALL: + if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld && + !fsm->b_conn)) + otg_set_state(fsm, OTG_STATE_A_IDLE); + break; + case OTG_STATE_A_VBUS_ERR: + if (fsm->id || fsm->a_bus_drop || fsm->a_clr_err) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + default: + break; + } + spin_unlock_irqrestore(&fsm->lock, flags); + + VDBG("quit statemachine, changed = %d\n", state_changed); + return state_changed; +} diff --git a/drivers/usb/otg/otg_fsm.h b/drivers/usb/otg/otg_fsm.h new file mode 100644 index 00000000000..0cecf1d593a --- /dev/null +++ b/drivers/usb/otg/otg_fsm.h @@ -0,0 +1,154 @@ +/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#undef DEBUG +#undef VERBOSE + +#ifdef DEBUG +#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt , \ + __func__, ## args) +#else +#define DBG(fmt, args...) do {} while (0) +#endif + +#ifdef VERBOSE +#define VDBG DBG +#else +#define VDBG(stuff...) do {} while (0) +#endif + +#ifdef VERBOSE +#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__) +#else +#define MPC_LOC do {} while (0) +#endif + +#define PROTO_UNDEF (0) +#define PROTO_HOST (1) +#define PROTO_GADGET (2) + +/* OTG state machine according to the OTG spec */ +struct otg_fsm { + /* 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_sess_end; + int b_sess_vld; + int id; + + /* Internal variables */ + int a_set_b_hnp_en; + int b_srp_done; + int b_hnp_enable; + + /* Timeout indicator for timers */ + int a_wait_vrise_tmout; + int a_wait_bcon_tmout; + int a_aidl_bdis_tmout; + int b_ase0_brst_tmout; + + /* Informative variables */ + int a_bus_drop; + int a_bus_req; + int a_clr_err; + int a_suspend_req; + int b_bus_req; + + /* Output */ + int drv_vbus; + int loc_conn; + int loc_sof; + + struct otg_fsm_ops *ops; + struct otg_transceiver *transceiver; + + /* Current usb protocol used: 0:undefine; 1:host; 2:client */ + int protocol; + spinlock_t lock; +}; + +struct otg_fsm_ops { + void (*chrg_vbus)(int on); + void (*drv_vbus)(int on); + void (*loc_conn)(int on); + void (*loc_sof)(int on); + void (*start_pulse)(void); + void (*add_timer)(void *timer); + void (*del_timer)(void *timer); + int (*start_host)(struct otg_fsm *fsm, int on); + int (*start_gadget)(struct otg_fsm *fsm, int on); +}; + + +static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on) +{ + fsm->ops->chrg_vbus(on); +} + +static inline void otg_drv_vbus(struct otg_fsm *fsm, int on) +{ + if (fsm->drv_vbus != on) { + fsm->drv_vbus = on; + fsm->ops->drv_vbus(on); + } +} + +static inline void otg_loc_conn(struct otg_fsm *fsm, int on) +{ + if (fsm->loc_conn != on) { + fsm->loc_conn = on; + fsm->ops->loc_conn(on); + } +} + +static inline void otg_loc_sof(struct otg_fsm *fsm, int on) +{ + if (fsm->loc_sof != on) { + fsm->loc_sof = on; + fsm->ops->loc_sof(on); + } +} + +static inline void otg_start_pulse(struct otg_fsm *fsm) +{ + fsm->ops->start_pulse(); +} + +static inline void otg_add_timer(struct otg_fsm *fsm, void *timer) +{ + fsm->ops->add_timer(timer); +} + +static inline void otg_del_timer(struct otg_fsm *fsm, void *timer) +{ + fsm->ops->del_timer(timer); +} + +int otg_statemachine(struct otg_fsm *fsm); + +/* Defined by device specific driver, for different timer implementation */ +extern struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, + *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr, + *a_wait_enum_tmr; -- cgit v1.2.3 From 83722bc9430424de1614ff31696f73a40b3d81a9 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 18 Apr 2011 22:02:00 +0200 Subject: USB: extend ehci-fsl and fsl_udc_core driver for OTG operation Signed-off-by: Anatolij Gustschin Cc: Li Yang Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_udc_core.c | 147 ++++++++++++++++++++++++++++++++++---- drivers/usb/gadget/fsl_usb2_udc.h | 2 + drivers/usb/host/ehci-fsl.c | 66 +++++++++++++++++ drivers/usb/host/ehci-hub.c | 8 +++ drivers/usb/host/ehci.h | 4 ++ include/linux/fsl_devices.h | 1 + 6 files changed, 213 insertions(+), 15 deletions(-) diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 28b3a9f25f3..999eafe8965 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -353,6 +353,19 @@ static void dr_controller_stop(struct fsl_udc *udc) { unsigned int tmp; + pr_debug("%s\n", __func__); + + /* if we're in OTG mode, and the Host is currently using the port, + * stop now and don't rip the controller out from under the + * ehci driver + */ + if (udc->gadget.is_otg) { + if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) { + pr_debug("udc: Leaving early\n"); + return; + } + } + /* disable all INTR */ fsl_writel(0, &dr_regs->usbintr); @@ -1668,6 +1681,9 @@ static void port_change_irq(struct fsl_udc *udc) { u32 speed; + if (udc->bus_reset) + udc->bus_reset = 0; + /* Bus resetting is finished */ if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) { /* Get the speed */ @@ -1775,6 +1791,8 @@ static void reset_irq(struct fsl_udc *udc) if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) { VDBG("Bus reset"); + /* Bus is reseting */ + udc->bus_reset = 1; /* Reset all the queues, include XD, dTD, EP queue * head and TR Queue */ reset_queues(udc); @@ -1852,6 +1870,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* Reset Received */ if (irq_src & USB_STS_RESET) { + VDBG("reset int"); reset_irq(udc); status = IRQ_HANDLED; } @@ -1909,11 +1928,30 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, goto out; } - /* Enable DR IRQ reg and Set usbcmd reg Run bit */ - dr_controller_run(udc_controller); - udc_controller->usb_state = USB_STATE_ATTACHED; - udc_controller->ep0_state = WAIT_FOR_SETUP; - udc_controller->ep0_dir = 0; + if (udc_controller->transceiver) { + /* Suspend the controller until OTG enable it */ + udc_controller->stopped = 1; + printk(KERN_INFO "Suspend udc for OTG auto detect\n"); + + /* connect to bus through transceiver */ + if (udc_controller->transceiver) { + retval = otg_set_peripheral(udc_controller->transceiver, + &udc_controller->gadget); + if (retval < 0) { + ERR("can't bind to transceiver\n"); + driver->unbind(&udc_controller->gadget); + udc_controller->gadget.dev.driver = 0; + udc_controller->driver = 0; + return retval; + } + } + } else { + /* Enable DR IRQ reg and set USBCMD reg Run bit */ + dr_controller_run(udc_controller); + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + udc_controller->ep0_dir = 0; + } printk(KERN_INFO "%s: bind to driver %s\n", udc_controller->gadget.name, driver->driver.name); @@ -2374,17 +2412,30 @@ static int __init fsl_udc_probe(struct platform_device *pdev) spin_lock_init(&udc_controller->lock); udc_controller->stopped = 1; +#ifdef CONFIG_USB_OTG + if (pdata->operating_mode == FSL_USB2_DR_OTG) { + udc_controller->transceiver = otg_get_transceiver(); + if (!udc_controller->transceiver) { + ERR("Can't find OTG driver!\n"); + ret = -ENODEV; + goto err_kfree; + } + } +#endif + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENXIO; goto err_kfree; } - if (!request_mem_region(res->start, res->end - res->start + 1, - driver_name)) { - ERR("request mem region for %s failed\n", pdev->name); - ret = -EBUSY; - goto err_kfree; + if (pdata->operating_mode == FSL_USB2_DR_DEVICE) { + if (!request_mem_region(res->start, res->end - res->start + 1, + driver_name)) { + ERR("request mem region for %s failed\n", pdev->name); + ret = -EBUSY; + goto err_kfree; + } } dr_regs = ioremap(res->start, resource_size(res)); @@ -2455,9 +2506,11 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err_free_irq; } - /* initialize usb hw reg except for regs for EP, - * leave usbintr reg untouched */ - dr_controller_setup(udc_controller); + if (!udc_controller->transceiver) { + /* initialize usb hw reg except for regs for EP, + * leave usbintr reg untouched */ + dr_controller_setup(udc_controller); + } fsl_udc_clk_finalize(pdev); @@ -2477,6 +2530,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev) if (ret < 0) goto err_free_irq; + if (udc_controller->transceiver) + udc_controller->gadget.is_otg = 1; + /* setup QH and epctrl for ep0 */ ep0_setup(udc_controller); @@ -2521,7 +2577,8 @@ err_iounmap: err_iounmap_noclk: iounmap(dr_regs); err_release_mem_region: - release_mem_region(res->start, res->end - res->start + 1); + if (pdata->operating_mode == FSL_USB2_DR_DEVICE) + release_mem_region(res->start, res->end - res->start + 1); err_kfree: kfree(udc_controller); udc_controller = NULL; @@ -2555,7 +2612,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) dma_pool_destroy(udc_controller->td_pool); free_irq(udc_controller->irq, udc_controller); iounmap(dr_regs); - release_mem_region(res->start, res->end - res->start + 1); + if (pdata->operating_mode == FSL_USB2_DR_DEVICE) + release_mem_region(res->start, res->end - res->start + 1); device_unregister(&udc_controller->gadget.dev); /* free udc --wait for the release() finished */ @@ -2598,6 +2656,62 @@ static int fsl_udc_resume(struct platform_device *pdev) return 0; } +static int fsl_udc_otg_suspend(struct device *dev, pm_message_t state) +{ + struct fsl_udc *udc = udc_controller; + u32 mode, usbcmd; + + mode = fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_MASK; + + pr_debug("%s(): mode 0x%x stopped %d\n", __func__, mode, udc->stopped); + + /* + * If the controller is already stopped, then this must be a + * PM suspend. Remember this fact, so that we will leave the + * controller stopped at PM resume time. + */ + if (udc->stopped) { + pr_debug("gadget already stopped, leaving early\n"); + udc->already_stopped = 1; + return 0; + } + + if (mode != USB_MODE_CTRL_MODE_DEVICE) { + pr_debug("gadget not in device mode, leaving early\n"); + return 0; + } + + /* stop the controller */ + usbcmd = fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP; + fsl_writel(usbcmd, &dr_regs->usbcmd); + + udc->stopped = 1; + + pr_info("USB Gadget suspended\n"); + + return 0; +} + +static int fsl_udc_otg_resume(struct device *dev) +{ + pr_debug("%s(): stopped %d already_stopped %d\n", __func__, + udc_controller->stopped, udc_controller->already_stopped); + + /* + * If the controller was stopped at suspend time, then + * don't resume it now. + */ + if (udc_controller->already_stopped) { + udc_controller->already_stopped = 0; + pr_debug("gadget was already stopped, leaving early\n"); + return 0; + } + + pr_info("USB Gadget resume\n"); + + return fsl_udc_resume(NULL); +} + /*------------------------------------------------------------------------- Register entry point for the peripheral controller driver --------------------------------------------------------------------------*/ @@ -2610,6 +2724,9 @@ static struct platform_driver udc_driver = { .driver = { .name = (char *)driver_name, .owner = THIS_MODULE, + /* udc suspend/resume called from OTG driver */ + .suspend = fsl_udc_otg_suspend, + .resume = fsl_udc_otg_resume, }, }; diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index 5647cc21b84..1d51be83fda 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -476,6 +476,7 @@ struct fsl_udc { unsigned vbus_active:1; unsigned stopped:1; unsigned remote_wakeup:1; + unsigned already_stopped:1; unsigned big_endian_desc:1; struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */ @@ -487,6 +488,7 @@ struct fsl_udc { dma_addr_t ep_qh_dma; /* dma address of QH */ u32 max_pipes; /* Device max pipes */ + u32 bus_reset; /* Device is bus resetting */ u32 resume_state; /* USB state to resume */ u32 usb_state; /* USB current state */ u32 ep0_state; /* Endpoint zero state */ diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index caf3d4ac42b..623732a312d 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -117,6 +117,9 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, pdata->regs = hcd->regs; + if (pdata->power_budget) + hcd->power_budget = pdata->power_budget; + /* * do platform specific init: check the clock, grab/config pins, etc. */ @@ -134,6 +137,30 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); if (retval != 0) goto err4; + +#ifdef CONFIG_USB_OTG + if (pdata->operating_mode == FSL_USB2_DR_OTG) { + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + ehci->transceiver = otg_get_transceiver(); + dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", + hcd, ehci, ehci->transceiver); + + if (ehci->transceiver) { + retval = otg_set_host(ehci->transceiver, + &ehci_to_hcd(ehci)->self); + if (retval) { + if (ehci->transceiver) + put_device(ehci->transceiver->dev); + goto err4; + } + } else { + dev_err(&pdev->dev, "can't find transceiver\n"); + retval = -ENODEV; + goto err4; + } + } +#endif return retval; err4: @@ -164,6 +191,12 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + if (ehci->transceiver) { + otg_set_host(ehci->transceiver, NULL); + put_device(ehci->transceiver->dev); + } usb_remove_hcd(hcd); @@ -544,6 +577,38 @@ static struct dev_pm_ops ehci_fsl_pm_ops = { #define EHCI_FSL_PM_OPS NULL #endif /* CONFIG_PM */ +#ifdef CONFIG_USB_OTG +static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + u32 status; + + if (!port) + return -EINVAL; + + port--; + + /* start port reset before HNP protocol time out */ + status = readl(&ehci->regs->port_status[port]); + if (!(status & PORT_CONNECT)) + return -ENODEV; + + /* khubd will finish the reset later */ + if (ehci_is_TDI(ehci)) { + writel(PORT_RESET | + (status & ~(PORT_CSC | PORT_PEC | PORT_OCC)), + &ehci->regs->port_status[port]); + } else { + writel(PORT_RESET, &ehci->regs->port_status[port]); + } + + return 0; +} +#else +#define ehci_start_port_reset NULL +#endif /* CONFIG_USB_OTG */ + + static const struct hc_driver ehci_fsl_hc_driver = { .description = hcd_name, .product_desc = "Freescale On-Chip EHCI Host Controller", @@ -583,6 +648,7 @@ static const struct hc_driver ehci_fsl_hc_driver = { .hub_control = ehci_hub_control, .bus_suspend = ehci_bus_suspend, .bus_resume = ehci_bus_resume, + .start_port_reset = ehci_start_port_reset, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 1a21799195a..ea6184bf48d 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -27,6 +27,7 @@ */ /*-------------------------------------------------------------------------*/ +#include #define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) @@ -801,6 +802,13 @@ static int ehci_hub_control ( goto error; if (ehci->no_selective_suspend) break; +#ifdef CONFIG_USB_OTG + if ((hcd->self.otg_port == (wIndex + 1)) + && hcd->self.b_hnp_enable) { + otg_start_hnp(ehci->transceiver); + break; + } +#endif if (!(temp & PORT_SUSPEND)) break; if ((temp & PORT_PE) == 0) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 168f1a88c4d..e9ba8e25248 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -161,6 +161,10 @@ struct ehci_hcd { /* one per controller */ #ifdef DEBUG struct dentry *debug_dir; #endif + /* + * OTG controllers and transceivers need software interaction + */ + struct otg_transceiver *transceiver; }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 3773c5dab8f..fffdf00f87b 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -72,6 +72,7 @@ struct fsl_usb2_platform_data { void (*exit)(struct platform_device *); void __iomem *regs; /* ioremap'd register base */ struct clk *clk; + unsigned power_budget; /* hcd->power_budget */ unsigned big_endian_mmio:1; unsigned big_endian_desc:1; unsigned es:1; /* need USBMODE:ES */ -- cgit v1.2.3 From d00f500400a6e309f9bc43d385572f395eba2871 Mon Sep 17 00:00:00 2001 From: Anton Tikhomirov Date: Thu, 21 Apr 2011 17:06:38 +0900 Subject: USB: s3c-hsotg: Fix core reset This patch fixes code responsible for core reset. Signed-off-by: Anton Tikhomirov Reviewed-by: Kyoungil Kim Cc: Ben Dooks Signed-off-by: Kukjin Kim Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 0912679de99..7cd597ca162 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2491,9 +2491,9 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg) timeout = 1000; do { grstctl = readl(hsotg->regs + S3C_GRSTCTL); - } while (!(grstctl & S3C_GRSTCTL_CSftRst) && timeout-- > 0); + } while ((grstctl & S3C_GRSTCTL_CSftRst) && timeout-- > 0); - if (!(grstctl & S3C_GRSTCTL_CSftRst)) { + if (grstctl & S3C_GRSTCTL_CSftRst) { dev_err(hsotg->dev, "Failed to get CSftRst asserted\n"); return -EINVAL; } @@ -2510,9 +2510,6 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg) return -ETIMEDOUT; } - if (grstctl & S3C_GRSTCTL_CSftRst) - continue; - if (!(grstctl & S3C_GRSTCTL_AHBIdle)) continue; -- cgit v1.2.3 From a3395f0dd016f18273eac8e689e1e81e075e025e Mon Sep 17 00:00:00 2001 From: Anton Tikhomirov Date: Thu, 21 Apr 2011 17:06:39 +0900 Subject: USB: s3c-hsotg: Fix interrupt cleaning code This commit does the following: 1) clears all pending interrupts before unmasking; 2) clears interrupts as soon as possible to avoid missing next coming that may occur during handling; 3) removes ineffective interrupt cleaning code. Signed-off-by: Anton Tikhomirov Reviewed-by: Kyoungil Kim Cc: Ben Dooks Signed-off-by: Kukjin Kim Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 58 +++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 7cd597ca162..d7a279bb51d 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -1775,10 +1775,12 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, u32 epctl_reg = dir_in ? S3C_DIEPCTL(idx) : S3C_DOEPCTL(idx); u32 epsiz_reg = dir_in ? S3C_DIEPTSIZ(idx) : S3C_DOEPTSIZ(idx); u32 ints; - u32 clear = 0; ints = readl(hsotg->regs + epint_reg); + /* Clear endpoint interrupts */ + writel(ints, hsotg->regs + epint_reg); + dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n", __func__, idx, dir_in ? "in" : "out", ints); @@ -1801,19 +1803,13 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, s3c_hsotg_handle_outdone(hsotg, idx, false); } - - clear |= S3C_DxEPINT_XferCompl; } - if (ints & S3C_DxEPINT_EPDisbld) { + if (ints & S3C_DxEPINT_EPDisbld) dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__); - clear |= S3C_DxEPINT_EPDisbld; - } - if (ints & S3C_DxEPINT_AHBErr) { + if (ints & S3C_DxEPINT_AHBErr) dev_dbg(hsotg->dev, "%s: AHBErr\n", __func__); - clear |= S3C_DxEPINT_AHBErr; - } if (ints & S3C_DxEPINT_Setup) { /* Setup or Timeout */ dev_dbg(hsotg->dev, "%s: Setup/Timeout\n", __func__); @@ -1829,14 +1825,10 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, else s3c_hsotg_handle_outdone(hsotg, 0, true); } - - clear |= S3C_DxEPINT_Setup; } - if (ints & S3C_DxEPINT_Back2BackSetup) { + if (ints & S3C_DxEPINT_Back2BackSetup) dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__); - clear |= S3C_DxEPINT_Back2BackSetup; - } if (dir_in) { /* not sure if this is important, but we'll clear it anyway @@ -1844,14 +1836,12 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, if (ints & S3C_DIEPMSK_INTknTXFEmpMsk) { dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n", __func__, idx); - clear |= S3C_DIEPMSK_INTknTXFEmpMsk; } /* this probably means something bad is happening */ if (ints & S3C_DIEPMSK_INTknEPMisMsk) { dev_warn(hsotg->dev, "%s: ep%d: INTknEP\n", __func__, idx); - clear |= S3C_DIEPMSK_INTknEPMisMsk; } /* FIFO has space or is empty (see GAHBCFG) */ @@ -1860,11 +1850,8 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n", __func__, idx); s3c_hsotg_trytx(hsotg, hs_ep); - clear |= S3C_DIEPMSK_TxFIFOEmpty; } } - - writel(clear, hsotg->regs + epint_reg); } /** @@ -2056,7 +2043,6 @@ irq_retry: dev_info(hsotg->dev, "OTGInt: %08x\n", otgint); writel(otgint, hsotg->regs + S3C_GOTGINT); - writel(S3C_GINTSTS_OTGInt, hsotg->regs + S3C_GINTSTS); } if (gintsts & S3C_GINTSTS_DisconnInt) { @@ -2072,8 +2058,9 @@ irq_retry: } if (gintsts & S3C_GINTSTS_EnumDone) { - s3c_hsotg_irq_enumdone(hsotg); writel(S3C_GINTSTS_EnumDone, hsotg->regs + S3C_GINTSTS); + + s3c_hsotg_irq_enumdone(hsotg); } if (gintsts & S3C_GINTSTS_ConIDStsChng) { @@ -2101,10 +2088,6 @@ irq_retry: if (daint_in & 1) s3c_hsotg_epint(hsotg, ep, 1); } - - writel(daint, hsotg->regs + S3C_DAINT); - writel(gintsts & (S3C_GINTSTS_OEPInt | S3C_GINTSTS_IEPInt), - hsotg->regs + S3C_GINTSTS); } if (gintsts & S3C_GINTSTS_USBRst) { @@ -2112,6 +2095,8 @@ irq_retry: dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n", readl(hsotg->regs + S3C_GNPTXSTS)); + writel(S3C_GINTSTS_USBRst, hsotg->regs + S3C_GINTSTS); + kill_all_requests(hsotg, &hsotg->eps[0], -ECONNRESET, true); /* it seems after a reset we can end up with a situation @@ -2123,8 +2108,6 @@ irq_retry: s3c_hsotg_init_fifo(hsotg); s3c_hsotg_enqueue_setup(hsotg); - - writel(S3C_GINTSTS_USBRst, hsotg->regs + S3C_GINTSTS); } /* check both FIFOs */ @@ -2138,8 +2121,6 @@ irq_retry: s3c_hsotg_disable_gsint(hsotg, S3C_GINTSTS_NPTxFEmp); s3c_hsotg_irq_fifoempty(hsotg, false); - - writel(S3C_GINTSTS_NPTxFEmp, hsotg->regs + S3C_GINTSTS); } if (gintsts & S3C_GINTSTS_PTxFEmp) { @@ -2149,8 +2130,6 @@ irq_retry: s3c_hsotg_disable_gsint(hsotg, S3C_GINTSTS_PTxFEmp); s3c_hsotg_irq_fifoempty(hsotg, true); - - writel(S3C_GINTSTS_PTxFEmp, hsotg->regs + S3C_GINTSTS); } if (gintsts & S3C_GINTSTS_RxFLvl) { @@ -2159,7 +2138,6 @@ irq_retry: * set. */ s3c_hsotg_handle_rx(hsotg); - writel(S3C_GINTSTS_RxFLvl, hsotg->regs + S3C_GINTSTS); } if (gintsts & S3C_GINTSTS_ModeMis) { @@ -2193,19 +2171,17 @@ irq_retry: if (gintsts & S3C_GINTSTS_GOUTNakEff) { dev_info(hsotg->dev, "GOUTNakEff triggered\n"); - s3c_hsotg_dump(hsotg); - writel(S3C_DCTL_CGOUTNak, hsotg->regs + S3C_DCTL); - writel(S3C_GINTSTS_GOUTNakEff, hsotg->regs + S3C_GINTSTS); + + s3c_hsotg_dump(hsotg); } if (gintsts & S3C_GINTSTS_GINNakEff) { dev_info(hsotg->dev, "GINNakEff triggered\n"); - s3c_hsotg_dump(hsotg); - writel(S3C_DCTL_CGNPInNAK, hsotg->regs + S3C_DCTL); - writel(S3C_GINTSTS_GINNakEff, hsotg->regs + S3C_GINTSTS); + + s3c_hsotg_dump(hsotg); } /* if we've had fifo events, we should try and go around the @@ -2585,6 +2561,12 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, writel(1 << 18 | S3C_DCFG_DevSpd_HS, hsotg->regs + S3C_DCFG); + /* Clear any pending OTG interrupts */ + writel(0xffffffff, hsotg->regs + S3C_GOTGINT); + + /* Clear any pending interrupts */ + writel(0xffffffff, hsotg->regs + S3C_GINTSTS); + writel(S3C_GINTSTS_DisconnInt | S3C_GINTSTS_SessReqInt | S3C_GINTSTS_ConIDStsChng | S3C_GINTSTS_USBRst | S3C_GINTSTS_EnumDone | S3C_GINTSTS_OTGInt | -- cgit v1.2.3 From 26ab3d0ce95c270b1ca491d6970aa14852d8e5a3 Mon Sep 17 00:00:00 2001 From: Anton Tikhomirov Date: Thu, 21 Apr 2011 17:06:40 +0900 Subject: USB: s3c-hsotg: Fix control request processing UDC driver does not need to generate reply to host if request is delivered to gadget. This is gadget's responsibility. This commit fixes that. Signed-off-by: Anton Tikhomirov Reviewed-by: Kyoungil Kim Cc: Ben Dooks Signed-off-by: Kukjin Kim Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index d7a279bb51d..cbbc33677fd 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -1055,8 +1055,10 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value); static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, struct usb_ctrlrequest *ctrl) { + struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); struct s3c_hsotg_ep *ep; + int ret; dev_dbg(hsotg->dev, "%s: %s_FEATURE\n", __func__, set ? "SET" : "CLEAR"); @@ -1072,6 +1074,13 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, switch (le16_to_cpu(ctrl->wValue)) { case USB_ENDPOINT_HALT: s3c_hsotg_ep_sethalt(&ep->ep, set); + + ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); + if (ret) { + dev_err(hsotg->dev, + "%s: failed to send reply\n", __func__); + return ret; + } break; default: @@ -1148,14 +1157,6 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg, dev_dbg(hsotg->dev, "driver->setup() ret %d\n", ret); } - if (ret > 0) { - if (!ep0->dir_in) { - /* need to generate zlp in reply or take data */ - /* todo - deal with any data we might be sent? */ - ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); - } - } - /* the request is either unhandlable, or is not formatted correctly * so respond with a STALL for the status stage to indicate failure. */ -- cgit v1.2.3 From 9c39ddc60ee9eb70569d1658e512cfa232b9dd16 Mon Sep 17 00:00:00 2001 From: Anton Tikhomirov Date: Thu, 21 Apr 2011 17:06:41 +0900 Subject: USB: s3c-hsotg: Fix stall condition processing The following should be done for requests after endpoint stall condition is cleared: 1) 'in progress' request (if any) should be completed since Tx FIFO was flushed; 2) next request from queue (if any) should be started. This commit does that. Additionally set/clear stall condition code is fixed. Signed-off-by: Anton Tikhomirov Reviewed-by: Kyoungil Kim Cc: Ben Dooks Signed-off-by: Kukjin Kim Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 142 ++++++++++++++++++++++++++++++++--------- 1 file changed, 111 insertions(+), 31 deletions(-) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index cbbc33677fd..6be424e2cc6 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -679,6 +679,14 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, __func__, readl(hsotg->regs + epctrl_reg), index, hs_ep->dir_in ? "in" : "out"); + /* If endpoint is stalled, we will restart request later */ + ctrl = readl(hsotg->regs + epctrl_reg); + + if (ctrl & S3C_DxEPCTL_Stall) { + dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index); + return; + } + length = ureq->length - ureq->actual; if (0) @@ -731,18 +739,6 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, /* write size / packets */ writel(epsize, hsotg->regs + epsize_reg); - ctrl = readl(hsotg->regs + epctrl_reg); - - if (ctrl & S3C_DxEPCTL_Stall) { - dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index); - - /* not sure what we can do here, if it is EP0 then we should - * get this cleared once the endpoint has transmitted the - * STALL packet, otherwise it needs to be cleared by the - * host. - */ - } - if (using_dma(hsotg)) { unsigned int dma_reg; @@ -1047,6 +1043,20 @@ static int s3c_hsotg_process_req_status(struct s3c_hsotg *hsotg, static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value); +/** + * get_ep_head - return the first request on the endpoint + * @hs_ep: The controller endpoint to get + * + * Get the first request on the endpoint. + */ +static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep) +{ + if (list_empty(&hs_ep->queue)) + return NULL; + + return list_first_entry(&hs_ep->queue, struct s3c_hsotg_req, queue); +} + /** * s3c_hsotg_process_req_featire - process request {SET,CLEAR}_FEATURE * @hsotg: The device state @@ -1056,6 +1066,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, struct usb_ctrlrequest *ctrl) { struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; + struct s3c_hsotg_req *hs_req; + bool restart; bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); struct s3c_hsotg_ep *ep; int ret; @@ -1081,6 +1093,29 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, "%s: failed to send reply\n", __func__); return ret; } + + if (!set) { + /* + * If we have request in progress, + * then complete it + */ + if (ep->req) { + hs_req = ep->req; + ep->req = NULL; + list_del_init(&hs_req->queue); + hs_req->req.complete(&ep->ep, + &hs_req->req); + } + + /* If we have pending request, then start it */ + restart = !list_empty(&ep->queue); + if (restart) { + hs_req = get_ep_head(ep); + s3c_hsotg_start_req(hsotg, ep, + hs_req, false); + } + } + break; default: @@ -1247,20 +1282,6 @@ static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg) } } -/** - * get_ep_head - return the first request on the endpoint - * @hs_ep: The controller endpoint to get - * - * Get the first request on the endpoint. -*/ -static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep) -{ - if (list_empty(&hs_ep->queue)) - return NULL; - - return list_first_entry(&hs_ep->queue, struct s3c_hsotg_req, queue); -} - /** * s3c_hsotg_complete_request - complete a request given to us * @hsotg: The device state. @@ -1684,6 +1705,37 @@ bad_mps: dev_err(hsotg->dev, "ep%d: bad mps of %d\n", ep, mps); } +/** + * s3c_hsotg_txfifo_flush - flush Tx FIFO + * @hsotg: The driver state + * @idx: The index for the endpoint (0..15) + */ +static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx) +{ + int timeout; + int val; + + writel(S3C_GRSTCTL_TxFNum(idx) | S3C_GRSTCTL_TxFFlsh, + hsotg->regs + S3C_GRSTCTL); + + /* wait until the fifo is flushed */ + timeout = 100; + + while (1) { + val = readl(hsotg->regs + S3C_GRSTCTL); + + if ((val & (S3C_GRSTCTL_TxFFlsh)) == 0) + break; + + if (--timeout == 0) { + dev_err(hsotg->dev, + "%s: timeout flushing fifo (GRSTCTL=%08x)\n", + __func__, val); + } + + udelay(1); + } +} /** * s3c_hsotg_trytx - check to see if anything needs transmitting @@ -1806,9 +1858,24 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, } } - if (ints & S3C_DxEPINT_EPDisbld) + if (ints & S3C_DxEPINT_EPDisbld) { dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__); + if (dir_in) { + int epctl = readl(hsotg->regs + epctl_reg); + + s3c_hsotg_txfifo_flush(hsotg, idx); + + if ((epctl & S3C_DxEPCTL_Stall) && + (epctl & S3C_DxEPCTL_EPType_Bulk)) { + int dctl = readl(hsotg->regs + S3C_DCTL); + + dctl |= S3C_DCTL_CGNPInNAK; + writel(dctl, hsotg->regs + S3C_DCTL); + } + } + } + if (ints & S3C_DxEPINT_AHBErr) dev_dbg(hsotg->dev, "%s: AHBErr\n", __func__); @@ -2406,6 +2473,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) unsigned long irqflags; u32 epreg; u32 epctl; + u32 xfertype; dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value); @@ -2416,10 +2484,17 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) epreg = S3C_DIEPCTL(index); epctl = readl(hs->regs + epreg); - if (value) - epctl |= S3C_DxEPCTL_Stall; - else + if (value) { + epctl |= S3C_DxEPCTL_Stall + S3C_DxEPCTL_SNAK; + if (epctl & S3C_DxEPCTL_EPEna) + epctl |= S3C_DxEPCTL_EPDis; + } else { epctl &= ~S3C_DxEPCTL_Stall; + xfertype = epctl & S3C_DxEPCTL_EPType_MASK; + if (xfertype == S3C_DxEPCTL_EPType_Bulk || + xfertype == S3C_DxEPCTL_EPType_Intterupt) + epctl |= S3C_DxEPCTL_SetD0PID; + } writel(epctl, hs->regs + epreg); @@ -2428,8 +2503,13 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) if (value) epctl |= S3C_DxEPCTL_Stall; - else + else { epctl &= ~S3C_DxEPCTL_Stall; + xfertype = epctl & S3C_DxEPCTL_EPType_MASK; + if (xfertype == S3C_DxEPCTL_EPType_Bulk || + xfertype == S3C_DxEPCTL_EPType_Intterupt) + epctl |= S3C_DxEPCTL_SetD0PID; + } writel(epctl, hs->regs + epreg); -- cgit v1.2.3 From f8acb08d8a1b2edab352430bd32c0e694bc65bc5 Mon Sep 17 00:00:00 2001 From: Anton Tikhomirov Date: Thu, 21 Apr 2011 17:06:42 +0900 Subject: USB: s3c-hsotg: Fix hang up after reset When File Storage gadget receives SET CONFIGURATION request it tries to cancel all pending transfers. If some request is in progress, gadget waits for its completion. This commit allows gadget to dequeue invalid requests in progress left after reset. Signed-off-by: Anton Tikhomirov Reviewed-by: Kyoungil Kim Cc: Ben Dooks Signed-off-by: Kukjin Kim Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 6be424e2cc6..ea38d306780 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2447,11 +2447,6 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req); - if (hs_req == hs_ep->req) { - dev_dbg(hs->dev, "%s: already in progress\n", __func__); - return -EINPROGRESS; - } - spin_lock_irqsave(&hs_ep->lock, flags); if (!on_list(hs_ep, hs_req)) { -- cgit v1.2.3 From dfbc6fa3e1f19d31aeb6afdffeede7271e29bfbf Mon Sep 17 00:00:00 2001 From: Anton Tikhomirov Date: Thu, 21 Apr 2011 17:06:43 +0900 Subject: USB: s3c-hsotg: Add copyright string Signed-off-by: Anton Tikhomirov Cc: Ben Dooks Signed-off-by: Kukjin Kim Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index ea38d306780..baf96ce16b6 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -1,4 +1,7 @@ /* linux/drivers/usb/gadget/s3c-hsotg.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com * * Copyright 2008 Openmoko, Inc. * Copyright 2008 Simtec Electronics -- cgit v1.2.3 From 847ed3e8f18b9cc401677e6e14eb7c89c7b8dfb6 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Tue, 26 Apr 2011 21:47:12 +0200 Subject: usb/isp1760: Remove false error printout This removes the "qh is 0" printout. qh == NULL if the urb has been unlinked, so this condition is normal. Signed-off-by: Arvid Brodin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1760-hcd.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 795345ad45e..ff3b3165d19 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -1021,10 +1021,10 @@ static void do_atl_int(struct usb_hcd *hcd) qtd = priv->atl_ints[slot].qtd; qh = priv->atl_ints[slot].qh; - if (!qh) { - dev_err(hcd->self.controller, "qh is 0\n"); + /* urb unlinked? */ + if (!qh) continue; - } + ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); rl = (ptd.dw2 >> 25) & 0x0f; @@ -1213,10 +1213,9 @@ static void do_intl_int(struct usb_hcd *hcd) qtd = priv->int_ints[slot].qtd; qh = priv->int_ints[slot].qh; - if (!qh) { - dev_err(hcd->self.controller, "(INT) qh is 0\n"); + /* urb unlinked? */ + if (!qh) continue; - } ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd); check_int_err_status(hcd, ptd.dw4); -- cgit v1.2.3 From 34537731d7f64d20116fbef4a665ec6a37195573 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Tue, 26 Apr 2011 21:47:37 +0200 Subject: usb/isp1760: Clean up urb enqueueing This collects urb enqueue code that was spread out all over the place into a couple of more readable functions. Signed-off-by: Arvid Brodin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1760-hcd.c | 327 ++++++++++++++++------------------------- 1 file changed, 129 insertions(+), 198 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index ff3b3165d19..6b2bf4684f4 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -272,7 +272,7 @@ static void init_memory(struct isp1760_hcd *priv) payload_addr += priv->memory_pool[curr + i].size; } - BUG_ON(payload_addr - priv->memory_pool[0].start > PAYLOAD_AREA_SIZE); + WARN_ON(payload_addr - priv->memory_pool[0].start > PAYLOAD_AREA_SIZE); } static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) @@ -280,7 +280,7 @@ static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) struct isp1760_hcd *priv = hcd_to_priv(hcd); int i; - BUG_ON(qtd->payload_addr); + WARN_ON(qtd->payload_addr); if (!qtd->length) return; @@ -318,7 +318,7 @@ static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) for (i = 0; i < BLOCKS; i++) { if (priv->memory_pool[i].start == qtd->payload_addr) { - BUG_ON(priv->memory_pool[i].free); + WARN_ON(priv->memory_pool[i].free); priv->memory_pool[i].free = 1; qtd->payload_addr = 0; return; @@ -379,7 +379,7 @@ static int ehci_reset(struct usb_hcd *hcd) static void qh_destroy(struct isp1760_qh *qh) { - BUG_ON(!list_empty(&qh->qtd_list)); + WARN_ON(!list_empty(&qh->qtd_list)); kmem_cache_free(qh_cachep, qh); } @@ -738,23 +738,6 @@ static void transform_into_int(struct isp1760_qh *qh, transform_add_int(qh, qtd, ptd); } -static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len, - u32 token) -{ - int count; - - qtd->data_buffer = databuffer; - qtd->packet_type = GET_QTD_TOKEN_TYPE(token); - - if (len > MAX_PAYLOAD_SIZE) - count = MAX_PAYLOAD_SIZE; - else - count = len; - - qtd->length = count; - return count; -} - static int check_error(struct usb_hcd *hcd, struct ptd *ptd) { int error = 0; @@ -948,9 +931,25 @@ __acquires(priv->lock) spin_lock(&priv->lock); } -static void isp1760_qtd_free(struct isp1760_qtd *qtd) +static struct isp1760_qtd *qtd_alloc(gfp_t flags, struct urb *urb, + u8 packet_type) +{ + struct isp1760_qtd *qtd; + + qtd = kmem_cache_zalloc(qtd_cachep, flags); + if (!qtd) + return NULL; + + INIT_LIST_HEAD(&qtd->qtd_list); + qtd->urb = urb; + qtd->packet_type = packet_type; + + return qtd; +} + +static void qtd_free(struct isp1760_qtd *qtd) { - BUG_ON(qtd->payload_addr); + WARN_ON(qtd->payload_addr); kmem_cache_free(qtd_cachep, qtd); } @@ -965,7 +964,7 @@ static struct isp1760_qtd *clean_this_qtd(struct isp1760_qtd *qtd, tmp_qtd = list_entry(qtd->qtd_list.next, struct isp1760_qtd, qtd_list); list_del(&qtd->qtd_list); - isp1760_qtd_free(qtd); + qtd_free(qtd); return tmp_qtd; } @@ -1294,210 +1293,95 @@ static void do_intl_int(struct usb_hcd *hcd) } } -static struct isp1760_qh *qh_make(struct usb_hcd *hcd, struct urb *urb, - gfp_t flags) -{ - struct isp1760_qh *qh; - int is_input, type; - - qh = isp1760_qh_alloc(flags); - if (!qh) - return qh; - - /* - * init endpoint/device data for this QH - */ - is_input = usb_pipein(urb->pipe); - type = usb_pipetype(urb->pipe); - - if (!usb_pipecontrol(urb->pipe)) - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, - 1); - return qh; -} - -/* - * For control/bulk/interrupt, return QH with these TDs appended. - * Allocates and initializes the QH if necessary. - * Returns null if it can't allocate a QH it needs to. - * If the QH has TDs (urbs) already, that's great. - */ -static struct isp1760_qh *qh_append_tds(struct usb_hcd *hcd, - struct urb *urb, struct list_head *qtd_list, int epnum, - void **ptr) +static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len) { - struct isp1760_qh *qh; - - qh = (struct isp1760_qh *)*ptr; - if (!qh) { - /* can't sleep here, we have priv->lock... */ - qh = qh_make(hcd, urb, GFP_ATOMIC); - if (!qh) - return qh; - *ptr = qh; - } + qtd->data_buffer = databuffer; - list_splice(qtd_list, qh->qtd_list.prev); + if (len > MAX_PAYLOAD_SIZE) + len = MAX_PAYLOAD_SIZE; + qtd->length = len; - return qh; + return qtd->length; } -static void qtd_list_free(struct urb *urb, struct list_head *qtd_list) +static void qtd_list_free(struct list_head *qtd_list) { - struct list_head *entry, *temp; + struct isp1760_qtd *qtd, *qtd_next; - list_for_each_safe(entry, temp, qtd_list) { - struct isp1760_qtd *qtd; - - qtd = list_entry(entry, struct isp1760_qtd, qtd_list); + list_for_each_entry_safe(qtd, qtd_next, qtd_list, qtd_list) { list_del(&qtd->qtd_list); - isp1760_qtd_free(qtd); + qtd_free(qtd); } } -static int isp1760_prepare_enqueue(struct usb_hcd *hcd, struct urb *urb, - struct list_head *qtd_list, gfp_t mem_flags, packet_enqueue *p) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct isp1760_qtd *qtd; - int epnum; - unsigned long flags; - struct isp1760_qh *qh = NULL; - int rc; - int qh_busy; - - qtd = list_entry(qtd_list->next, struct isp1760_qtd, qtd_list); - epnum = urb->ep->desc.bEndpointAddress; - - spin_lock_irqsave(&priv->lock, flags); - if (!HCD_HW_ACCESSIBLE(hcd)) { - rc = -ESHUTDOWN; - goto done; - } - rc = usb_hcd_link_urb_to_ep(hcd, urb); - if (rc) - goto done; - - qh = urb->ep->hcpriv; - if (qh) - qh_busy = !list_empty(&qh->qtd_list); - else - qh_busy = 0; - - qh = qh_append_tds(hcd, urb, qtd_list, epnum, &urb->ep->hcpriv); - if (!qh) { - usb_hcd_unlink_urb_from_ep(hcd, urb); - rc = -ENOMEM; - goto done; - } - - if (!qh_busy) - p(hcd, qh, qtd); - -done: - spin_unlock_irqrestore(&priv->lock, flags); - if (!qh) - qtd_list_free(urb, qtd_list); - return rc; -} - -static struct isp1760_qtd *isp1760_qtd_alloc(gfp_t flags) -{ - struct isp1760_qtd *qtd; - - qtd = kmem_cache_zalloc(qtd_cachep, flags); - if (qtd) - INIT_LIST_HEAD(&qtd->qtd_list); - - return qtd; -} - /* - * create a list of filled qtds for this URB; won't link into qh. + * Packetize urb->transfer_buffer into list of packets of size wMaxPacketSize. + * Also calculate the PID type (SETUP/IN/OUT) for each packet. */ #define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) -static struct list_head *qh_urb_transaction(struct usb_hcd *hcd, +static void packetize_urb(struct usb_hcd *hcd, struct urb *urb, struct list_head *head, gfp_t flags) { struct isp1760_qtd *qtd; void *buf; - int len, maxpacket; - int is_input; - u32 token; + int len, maxpacketsize; + u8 packet_type; /* * URBs map to sequences of QTDs: one logical transaction */ - qtd = isp1760_qtd_alloc(flags); - if (!qtd) - return NULL; - list_add_tail(&qtd->qtd_list, head); - qtd->urb = urb; - urb->status = -EINPROGRESS; + if (!urb->transfer_buffer && urb->transfer_buffer_length) { + /* XXX This looks like usb storage / SCSI bug */ + dev_err(hcd->self.controller, + "buf is null, dma is %08lx len is %d\n", + (long unsigned)urb->transfer_dma, + urb->transfer_buffer_length); + WARN_ON(1); + } - token = 0; - /* for split transactions, SplitXState initialized to zero */ + if (usb_pipein(urb->pipe)) + packet_type = IN_PID; + else + packet_type = OUT_PID; - len = urb->transfer_buffer_length; - is_input = usb_pipein(urb->pipe); if (usb_pipecontrol(urb->pipe)) { - /* SETUP pid */ - qtd_fill(qtd, urb->setup_packet, - sizeof(struct usb_ctrlrequest), - token | SETUP_PID); - - /* ... and always at least one more pid */ - qtd = isp1760_qtd_alloc(flags); + qtd = qtd_alloc(flags, urb, SETUP_PID); if (!qtd) goto cleanup; - qtd->urb = urb; + qtd_fill(qtd, urb->setup_packet, sizeof(struct usb_ctrlrequest)); list_add_tail(&qtd->qtd_list, head); /* for zero length DATA stages, STATUS is always IN */ - if (len == 0) - token |= IN_PID; + if (urb->transfer_buffer_length == 0) + packet_type = IN_PID; } - /* - * data transfer stage: buffer setup - */ - buf = urb->transfer_buffer; - - if (is_input) - token |= IN_PID; - else - token |= OUT_PID; - - maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input)); + maxpacketsize = max_packet(usb_maxpacket(urb->dev, urb->pipe, + usb_pipeout(urb->pipe))); /* * buffer gets wrapped in one or more qtds; * last one may be "short" (including zero len) * and may serve as a control status ack */ + buf = urb->transfer_buffer; + len = urb->transfer_buffer_length; + for (;;) { int this_qtd_len; - if (!buf && len) { - /* XXX This looks like usb storage / SCSI bug */ - dev_err(hcd->self.controller, "buf is null, dma is %08lx len is %d\n", - (long unsigned)urb->transfer_dma, len); - WARN_ON(1); - } + qtd = qtd_alloc(flags, urb, packet_type); + if (!qtd) + goto cleanup; + this_qtd_len = qtd_fill(qtd, buf, len); + list_add_tail(&qtd->qtd_list, head); - this_qtd_len = qtd_fill(qtd, buf, len, token); len -= this_qtd_len; buf += this_qtd_len; if (len <= 0) break; - - qtd = isp1760_qtd_alloc(flags); - if (!qtd) - goto cleanup; - qtd->urb = urb; - list_add_tail(&qtd->qtd_list, head); } /* @@ -1509,31 +1393,78 @@ static struct list_head *qh_urb_transaction(struct usb_hcd *hcd, if (usb_pipecontrol(urb->pipe)) { one_more = 1; - /* "in" <--> "out" */ - token ^= IN_PID; + if (packet_type == IN_PID) + packet_type = OUT_PID; + else + packet_type = IN_PID; } else if (usb_pipebulk(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) - && !(urb->transfer_buffer_length % maxpacket)) { + && !(urb->transfer_buffer_length % + maxpacketsize)) { one_more = 1; } if (one_more) { - qtd = isp1760_qtd_alloc(flags); + qtd = qtd_alloc(flags, urb, packet_type); if (!qtd) goto cleanup; - qtd->urb = urb; - list_add_tail(&qtd->qtd_list, head); /* never any data in such packets */ - qtd_fill(qtd, NULL, 0, token); + qtd_fill(qtd, NULL, 0); + list_add_tail(&qtd->qtd_list, head); } } - qtd->status = 0; - return head; + return; cleanup: - qtd_list_free(urb, head); - return NULL; + qtd_list_free(head); +} + +static int enqueue_qtdlist(struct usb_hcd *hcd, struct urb *urb, + struct list_head *qtd_list, gfp_t mem_flags, packet_enqueue *p) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + struct isp1760_qtd *qtd; + struct isp1760_qh *qh = NULL; + unsigned long flags; + int qh_empty; + int rc; + + spin_lock_irqsave(&priv->lock, flags); + if (!HCD_HW_ACCESSIBLE(hcd)) { + rc = -ESHUTDOWN; + goto done; + } + rc = usb_hcd_link_urb_to_ep(hcd, urb); + if (rc) + goto done; + + qh = urb->ep->hcpriv; + if (!qh) { + qh = isp1760_qh_alloc(GFP_ATOMIC); + if (!qh) { + usb_hcd_unlink_urb_from_ep(hcd, urb); + rc = -ENOMEM; + goto done; + } + if (!usb_pipecontrol(urb->pipe)) + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + !usb_pipein(urb->pipe), 1); + urb->ep->hcpriv = qh; + } + + qh_empty = list_empty(&qh->qtd_list); + list_splice_tail(qtd_list, &qh->qtd_list); + if (qh_empty) { + qtd = list_entry(qtd_list->next, struct isp1760_qtd, qtd_list); + p(hcd, qh, qtd); + } + +done: + spin_unlock_irqrestore(&priv->lock, flags); + if (!qh) + qtd_list_free(qtd_list); + return rc; } static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, @@ -1547,14 +1478,10 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: case PIPE_BULK: - if (!qh_urb_transaction(hcd, urb, &qtd_list, mem_flags)) - return -ENOMEM; - pe = enqueue_an_ATL_packet; + pe = enqueue_an_ATL_packet; break; case PIPE_INTERRUPT: - if (!qh_urb_transaction(hcd, urb, &qtd_list, mem_flags)) - return -ENOMEM; pe = enqueue_an_INT_packet; break; @@ -1564,7 +1491,11 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, return -EPIPE; } - return isp1760_prepare_enqueue(hcd, urb, &qtd_list, mem_flags, pe); + packetize_urb(hcd, urb, &qtd_list, mem_flags); + if (list_empty(&qtd_list)) + return -ENOMEM; + + return enqueue_qtdlist(hcd, urb, &qtd_list, mem_flags, pe); } static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) @@ -1605,7 +1536,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) for (i = 0; i < 32; i++) { if (!ints[i].qh) continue; - BUG_ON(!ints[i].qtd); + WARN_ON(!ints[i].qtd); if (ints[i].qtd->urb == urb) { u32 skip_map; -- cgit v1.2.3 From eb1a796868effbf33ec2cfa3d15567d7e31f2ee2 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Tue, 26 Apr 2011 21:48:02 +0200 Subject: usb/isp1760: Remove unneeded OR map and HcBufferStatus code Since we always set the OR flag for each transfer, we can just as well set all these bits to 1 at init and be done with it. Also, HcBufferStatus can be set at init as per the ISP1761 datasheet page 47 with no loss of performance. Signed-off-by: Arvid Brodin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1760-hcd.c | 63 ++++++------------------------------------ drivers/usb/host/isp1760-hcd.h | 12 ++++---- 2 files changed, 13 insertions(+), 62 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 6b2bf4684f4..a5f03cdf9ba 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -336,10 +336,6 @@ static void isp1760_init_regs(struct usb_hcd *hcd) reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); - - reg_write32(hcd->regs, HC_ATL_PTD_DONEMAP_REG, ~NO_TRANSFER_ACTIVE); - reg_write32(hcd->regs, HC_INT_PTD_DONEMAP_REG, ~NO_TRANSFER_ACTIVE); - reg_write32(hcd->regs, HC_ISO_PTD_DONEMAP_REG, ~NO_TRANSFER_ACTIVE); } static int handshake(struct usb_hcd *hcd, u32 reg, @@ -516,14 +512,17 @@ static void isp1760_init_maps(struct usb_hcd *hcd) reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000); reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); + + reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, + ATL_BUF_FILL | INT_BUF_FILL); } static void isp1760_enable_interrupts(struct usb_hcd *hcd) { reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0); + reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff); reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0); + reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff); reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0); reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff); /* step 23 passed */ @@ -835,9 +834,8 @@ static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, struct isp1760_qtd *qtd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 skip_map, or_map; + u32 skip_map; u32 slot; - u32 buffstatus; /* * When this function is called from the interrupt handler to enqueue @@ -854,10 +852,6 @@ static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, enqueue_one_atl_qtd(hcd, qh, slot, qtd); - or_map = reg_read32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG); - or_map |= (1 << slot); - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, or_map); - skip_map &= ~(1 << slot); reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); @@ -865,18 +859,13 @@ static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, if (priv->atl_queued == 2) reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_SOT_MASK); - - buffstatus = reg_read32(hcd->regs, HC_BUFFER_STATUS_REG); - buffstatus |= ATL_BUFFER; - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, buffstatus); } static void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, struct isp1760_qtd *qtd) { - u32 skip_map, or_map; + u32 skip_map; u32 slot; - u32 buffstatus; /* * When this function is called from the interrupt handler to enqueue @@ -893,16 +882,8 @@ static void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, enqueue_one_int_qtd(hcd, qh, slot, qtd); - or_map = reg_read32(hcd->regs, HC_INT_IRQ_MASK_OR_REG); - or_map |= (1 << slot); - reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, or_map); - skip_map &= ~(1 << slot); reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); - - buffstatus = reg_read32(hcd->regs, HC_BUFFER_STATUS_REG); - buffstatus |= INT_BUFFER; - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, buffstatus); } static void isp1760_urb_done(struct usb_hcd *hcd, struct urb *urb) @@ -994,7 +975,6 @@ static void do_atl_int(struct usb_hcd *hcd) struct urb *urb; u32 slot; u32 length; - u32 or_map; u32 status = -EINVAL; int error; struct isp1760_qtd *qtd; @@ -1005,10 +985,6 @@ static void do_atl_int(struct usb_hcd *hcd) done_map = reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); - or_map = reg_read32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG); - or_map &= ~done_map; - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, or_map); - while (done_map) { status = 0; priv->atl_queued--; @@ -1048,8 +1024,6 @@ static void do_atl_int(struct usb_hcd *hcd) } if (!nakcount && (ptd.dw3 & DW3_QTD_ACTIVE)) { - u32 buffstatus; - /* * NAKs are handled in HW by the chip. Usually if the * device is not able to send data fast enough. @@ -1068,9 +1042,6 @@ static void do_atl_int(struct usb_hcd *hcd) * unskipped once it gets written to the HW. */ skip_map &= ~(1 << slot); - or_map = reg_read32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG); - or_map |= 1 << slot; - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, or_map); ptd.dw0 |= PTD_VALID; ptd_write(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); @@ -1079,12 +1050,6 @@ static void do_atl_int(struct usb_hcd *hcd) if (priv->atl_queued == 2) reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_SOT_MASK); - - buffstatus = reg_read32(hcd->regs, - HC_BUFFER_STATUS_REG); - buffstatus |= ATL_BUFFER; - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, - buffstatus); continue; } @@ -1191,7 +1156,6 @@ static void do_intl_int(struct usb_hcd *hcd) struct ptd ptd; struct urb *urb; u32 length; - u32 or_map; int error; u32 slot; struct isp1760_qtd *qtd; @@ -1200,10 +1164,6 @@ static void do_intl_int(struct usb_hcd *hcd) done_map = reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); - or_map = reg_read32(hcd->regs, HC_INT_IRQ_MASK_OR_REG); - or_map &= ~done_map; - reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, or_map); - while (done_map) { slot = __ffs(done_map); done_map &= ~(1 << slot); @@ -1503,7 +1463,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) struct isp1760_hcd *priv = hcd_to_priv(hcd); struct inter_packet_info *ints; u32 i; - u32 reg_base, or_reg, skip_reg; + u32 reg_base, skip_reg; unsigned long flags; struct ptd ptd; packet_enqueue *pe; @@ -1516,7 +1476,6 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) case PIPE_INTERRUPT: ints = priv->int_ints; reg_base = INT_PTD_OFFSET; - or_reg = HC_INT_IRQ_MASK_OR_REG; skip_reg = HC_INT_PTD_SKIPMAP_REG; pe = enqueue_an_INT_packet; break; @@ -1524,7 +1483,6 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) default: ints = priv->atl_ints; reg_base = ATL_PTD_OFFSET; - or_reg = HC_ATL_IRQ_MASK_OR_REG; skip_reg = HC_ATL_PTD_SKIPMAP_REG; pe = enqueue_an_ATL_packet; break; @@ -1540,7 +1498,6 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) if (ints[i].qtd->urb == urb) { u32 skip_map; - u32 or_map; struct isp1760_qtd *qtd; struct isp1760_qh *qh; @@ -1548,10 +1505,6 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) skip_map |= 1 << i; reg_write32(hcd->regs, skip_reg, skip_map); - or_map = reg_read32(hcd->regs, or_reg); - or_map &= ~(1 << i); - reg_write32(hcd->regs, or_reg, or_map); - ptd_write(hcd->regs, reg_base, i, &ptd); qtd = ints[i].qtd; diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 87050769060..056e046b0d4 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -49,10 +49,9 @@ void deinit_kmem_cache(void); #define SW_RESET_RESET_ALL (1 << 0) #define HC_BUFFER_STATUS_REG 0x334 -#define ATL_BUFFER 0x1 -#define INT_BUFFER 0x2 -#define ISO_BUFFER 0x4 -#define BUFFER_MAP 0x7 +#define ISO_BUF_FILL (1 << 2) +#define INT_BUF_FILL (1 << 1) +#define ATL_BUF_FILL (1 << 0) #define HC_MEMORY_REG 0x33c #define ISP_BANK(x) ((x) << 16) @@ -68,14 +67,13 @@ void deinit_kmem_cache(void); #define HC_INTERRUPT_REG 0x310 #define HC_INTERRUPT_ENABLE 0x314 -#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT | HC_EOT_INT) -#define INTERRUPT_ENABLE_SOT_MASK (HC_INTL_INT | HC_SOT_INT | HC_EOT_INT) - #define HC_ISO_INT (1 << 9) #define HC_ATL_INT (1 << 8) #define HC_INTL_INT (1 << 7) #define HC_EOT_INT (1 << 3) #define HC_SOT_INT (1 << 1) +#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT) +#define INTERRUPT_ENABLE_SOT_MASK (HC_SOT_INT) #define HC_ISO_IRQ_MASK_OR_REG 0x318 #define HC_INT_IRQ_MASK_OR_REG 0x31C -- cgit v1.2.3 From 22bea9cef810ec54abdb057de46cea04c972dc64 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Tue, 26 Apr 2011 21:46:47 +0200 Subject: usb/isp1760: Report correct urb status after unlink This fixes a bug in my previous (2.6.38) patch series which caused urb->status value to be wrong after unlink (broke usbtest 11, 12). Signed-off-by: Arvid Brodin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1760-hcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index a5f03cdf9ba..b38cfe98f22 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -1516,6 +1516,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ints[i].qh = NULL; ints[i].qtd = NULL; + urb->status = status; isp1760_urb_done(hcd, urb); if (qtd) pe(hcd, qh, qtd); -- cgit v1.2.3 From 71a9f9d268a5c2b0a80ae606cf8e502f3410a5df Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Tue, 26 Apr 2011 21:48:30 +0200 Subject: usb/isp1760: Improve urb queueing, get rid of BUG():s in normal code paths This patch replaces the code that handles qtds. Intead of directly allocating chip mem and chip slot, enqueue the transfer in a list of queue heads. Use a centralized function enqueue_qtds() to prioritize and enqueue transfers. This removes all of the interrupt context BUG() calls when out of chip mem or transfer slots. It also makes it possible to efficiently use the dual-port mem on the chip for double-buffered transfers, which improve transfer times to/from/between usb sticks by about 40 % on my HW. With this patch it should also be possible to handle qtd scheduling outside of the interrupt handler, for significantly improved kernel latency. I have not implemented this since there are some locking issues which I haven't had time to look at. Signed-off-by: Arvid Brodin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1760-hcd.c | 1312 ++++++++++++++++++++-------------------- drivers/usb/host/isp1760-hcd.h | 66 +- 2 files changed, 679 insertions(+), 699 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index b38cfe98f22..dd98a966b58 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -8,6 +8,8 @@ * * (c) 2007 Sebastian Siewior * + * (c) 2011 Arvid Brodin + * */ #include #include @@ -26,14 +28,16 @@ static struct kmem_cache *qtd_cachep; static struct kmem_cache *qh_cachep; +static struct kmem_cache *urb_listitem_cachep; struct isp1760_hcd { u32 hcs_params; spinlock_t lock; - struct inter_packet_info atl_ints[32]; - struct inter_packet_info int_ints[32]; + struct slotinfo atl_slots[32]; + struct slotinfo int_slots[32]; struct memory_chunk memory_pool[BLOCKS]; - u32 atl_queued; + struct list_head controlqhs, bulkqhs, interruptqhs; + int active_ptds; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 @@ -85,18 +89,34 @@ struct isp1760_qtd { struct list_head qtd_list; struct urb *urb; size_t length; - - /* isp special*/ + size_t actual_length; + + /* QTD_ENQUEUED: waiting for transfer (inactive) */ + /* QTD_PAYLOAD_ALLOC: chip mem has been allocated for payload */ + /* QTD_XFER_STARTED: valid ptd has been written to isp176x - only + interrupt handler may touch this qtd! */ + /* QTD_XFER_COMPLETE: payload has been transferred successfully */ + /* QTD_RETIRE: transfer error/abort qtd */ +#define QTD_ENQUEUED 0 +#define QTD_PAYLOAD_ALLOC 1 +#define QTD_XFER_STARTED 2 +#define QTD_XFER_COMPLETE 3 +#define QTD_RETIRE 4 u32 status; -#define URB_ENQUEUED (1 << 1) }; +/* Queue head, one for each active endpoint */ struct isp1760_qh { - /* first part defined by EHCI spec */ + struct list_head qh_list; struct list_head qtd_list; - u32 toggle; u32 ping; + int slot; +}; + +struct urb_listitem { + struct list_head urb_list; + struct urb *urb; }; /* @@ -293,19 +313,6 @@ static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) return; } } - - dev_err(hcd->self.controller, - "%s: Cannot allocate %zu bytes of memory\n" - "Current memory map:\n", - __func__, qtd->length); - for (i = 0; i < BLOCKS; i++) { - dev_err(hcd->self.controller, "Pool %2d size %4d status: %d\n", - i, priv->memory_pool[i].size, - priv->memory_pool[i].free); - } - /* XXX maybe -ENOMEM could be possible */ - BUG(); - return; } static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) @@ -327,15 +334,8 @@ static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) dev_err(hcd->self.controller, "%s: Invalid pointer: %08x\n", __func__, qtd->payload_addr); - BUG(); -} - -static void isp1760_init_regs(struct usb_hcd *hcd) -{ - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0); - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); - reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); + WARN_ON(1); + qtd->payload_addr = 0; } static int handshake(struct usb_hcd *hcd, u32 reg, @@ -373,31 +373,27 @@ static int ehci_reset(struct usb_hcd *hcd) return retval; } -static void qh_destroy(struct isp1760_qh *qh) -{ - WARN_ON(!list_empty(&qh->qtd_list)); - kmem_cache_free(qh_cachep, qh); -} - -static struct isp1760_qh *isp1760_qh_alloc(gfp_t flags) +static struct isp1760_qh *qh_alloc(gfp_t flags) { struct isp1760_qh *qh; qh = kmem_cache_zalloc(qh_cachep, flags); if (!qh) - return qh; + return NULL; + INIT_LIST_HEAD(&qh->qh_list); INIT_LIST_HEAD(&qh->qtd_list); + qh->slot = -1; + return qh; } -/* magic numbers that can affect system performance */ -#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ -#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ -#define EHCI_TUNE_RL_TT 0 -#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ -#define EHCI_TUNE_MULT_TT 1 -#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ +static void qh_free(struct isp1760_qh *qh) +{ + WARN_ON(!list_empty(&qh->qtd_list)); + WARN_ON(qh->slot > -1); + kmem_cache_free(qh_cachep, qh); +} /* one-time init, only for memory state */ static int priv_init(struct usb_hcd *hcd) @@ -407,6 +403,10 @@ static int priv_init(struct usb_hcd *hcd) spin_lock_init(&priv->lock); + INIT_LIST_HEAD(&priv->interruptqhs); + INIT_LIST_HEAD(&priv->controlqhs); + INIT_LIST_HEAD(&priv->bulkqhs); + /* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows. @@ -464,7 +464,10 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) } /* pre reset */ - isp1760_init_regs(hcd); + reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); + reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); /* reset */ reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_ALL); @@ -484,12 +487,15 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ? "analog" : "digital"); + /* This is weird: at the first plug-in of a device there seems to be + one packet queued that never gets returned? */ + priv->active_ptds = -1; + /* ATL reset */ reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET); mdelay(10); reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode); - reg_write32(hcd->regs, HC_INTERRUPT_REG, INTERRUPT_ENABLE_MASK); reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_MASK); /* @@ -513,6 +519,10 @@ static void isp1760_init_maps(struct usb_hcd *hcd) reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0); + reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0); + reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, ATL_BUF_FILL | INT_BUF_FILL); } @@ -547,8 +557,7 @@ static int isp1760_run(struct usb_hcd *hcd) command |= CMD_RUN; reg_write32(hcd->regs, HC_USBCMD, command); - retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, - 250 * 1000); + retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000); if (retval) return retval; @@ -597,12 +606,19 @@ static int last_qtd_of_urb(struct isp1760_qtd *qtd, struct isp1760_qh *qh) return (qtd->urb != urb); } -static void transform_into_atl(struct isp1760_qh *qh, +/* magic numbers that can affect system performance */ +#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define EHCI_TUNE_RL_TT 0 +#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define EHCI_TUNE_MULT_TT 1 +#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ + +static void create_ptd_atl(struct isp1760_qh *qh, struct isp1760_qtd *qtd, struct ptd *ptd) { u32 maxpacket; u32 multi; - u32 pid_code; u32 rl = RL_COUNTER; u32 nak = NAK_COUNTER; @@ -615,67 +631,62 @@ static void transform_into_atl(struct isp1760_qh *qh, maxpacket &= 0x7ff; /* DW0 */ - ptd->dw0 = PTD_VALID; - ptd->dw0 |= PTD_LENGTH(qtd->length); - ptd->dw0 |= PTD_MAXPACKET(maxpacket); - ptd->dw0 |= PTD_ENDPOINT(usb_pipeendpoint(qtd->urb->pipe)); + ptd->dw0 = DW0_VALID_BIT; + ptd->dw0 |= TO_DW0_LENGTH(qtd->length); + ptd->dw0 |= TO_DW0_MAXPACKET(maxpacket); + ptd->dw0 |= TO_DW0_ENDPOINT(usb_pipeendpoint(qtd->urb->pipe)); /* DW1 */ ptd->dw1 = usb_pipeendpoint(qtd->urb->pipe) >> 1; - ptd->dw1 |= PTD_DEVICE_ADDR(usb_pipedevice(qtd->urb->pipe)); - - pid_code = qtd->packet_type; - ptd->dw1 |= PTD_PID_TOKEN(pid_code); + ptd->dw1 |= TO_DW1_DEVICE_ADDR(usb_pipedevice(qtd->urb->pipe)); + ptd->dw1 |= TO_DW1_PID_TOKEN(qtd->packet_type); if (usb_pipebulk(qtd->urb->pipe)) - ptd->dw1 |= PTD_TRANS_BULK; + ptd->dw1 |= DW1_TRANS_BULK; else if (usb_pipeint(qtd->urb->pipe)) - ptd->dw1 |= PTD_TRANS_INT; + ptd->dw1 |= DW1_TRANS_INT; if (qtd->urb->dev->speed != USB_SPEED_HIGH) { /* split transaction */ - ptd->dw1 |= PTD_TRANS_SPLIT; + ptd->dw1 |= DW1_TRANS_SPLIT; if (qtd->urb->dev->speed == USB_SPEED_LOW) - ptd->dw1 |= PTD_SE_USB_LOSPEED; + ptd->dw1 |= DW1_SE_USB_LOSPEED; - ptd->dw1 |= PTD_PORT_NUM(qtd->urb->dev->ttport); - ptd->dw1 |= PTD_HUB_NUM(qtd->urb->dev->tt->hub->devnum); + ptd->dw1 |= TO_DW1_PORT_NUM(qtd->urb->dev->ttport); + ptd->dw1 |= TO_DW1_HUB_NUM(qtd->urb->dev->tt->hub->devnum); /* SE bit for Split INT transfers */ if (usb_pipeint(qtd->urb->pipe) && (qtd->urb->dev->speed == USB_SPEED_LOW)) ptd->dw1 |= 2 << 16; - ptd->dw3 = 0; rl = 0; nak = 0; } else { - ptd->dw0 |= PTD_MULTI(multi); + ptd->dw0 |= TO_DW0_MULTI(multi); if (usb_pipecontrol(qtd->urb->pipe) || usb_pipebulk(qtd->urb->pipe)) - ptd->dw3 = qh->ping; - else - ptd->dw3 = 0; + ptd->dw3 |= TO_DW3_PING(qh->ping); } /* DW2 */ ptd->dw2 = 0; - ptd->dw2 |= PTD_DATA_START_ADDR(base_to_chip(qtd->payload_addr)); - ptd->dw2 |= PTD_RL_CNT(rl); - ptd->dw3 |= PTD_NAC_CNT(nak); + ptd->dw2 |= TO_DW2_DATA_START_ADDR(base_to_chip(qtd->payload_addr)); + ptd->dw2 |= TO_DW2_RL(rl); /* DW3 */ - ptd->dw3 |= qh->toggle; + ptd->dw3 |= TO_DW3_NAKCOUNT(nak); + ptd->dw3 |= TO_DW3_DATA_TOGGLE(qh->toggle); if (usb_pipecontrol(qtd->urb->pipe)) { if (qtd->data_buffer == qtd->urb->setup_packet) - ptd->dw3 &= ~PTD_DATA_TOGGLE(1); + ptd->dw3 &= ~TO_DW3_DATA_TOGGLE(1); else if (last_qtd_of_urb(qtd, qh)) - ptd->dw3 |= PTD_DATA_TOGGLE(1); + ptd->dw3 |= TO_DW3_DATA_TOGGLE(1); } - ptd->dw3 |= PTD_ACTIVE; + ptd->dw3 |= DW3_ACTIVE_BIT; /* Cerr */ - ptd->dw3 |= PTD_CERR(ERR_COUNTER); + ptd->dw3 |= TO_DW3_CERR(ERR_COUNTER); } static void transform_add_int(struct isp1760_qh *qh, @@ -730,162 +741,13 @@ static void transform_add_int(struct isp1760_qh *qh, ptd->dw4 = usof; } -static void transform_into_int(struct isp1760_qh *qh, +static void create_ptd_int(struct isp1760_qh *qh, struct isp1760_qtd *qtd, struct ptd *ptd) { - transform_into_atl(qh, qtd, ptd); + create_ptd_atl(qh, qtd, ptd); transform_add_int(qh, qtd, ptd); } -static int check_error(struct usb_hcd *hcd, struct ptd *ptd) -{ - int error = 0; - - if (ptd->dw3 & DW3_HALT_BIT) { - error = -EPIPE; - - if (ptd->dw3 & DW3_ERROR_BIT) - pr_err("error bit is set in DW3\n"); - } - - if (ptd->dw3 & DW3_QTD_ACTIVE) { - dev_err(hcd->self.controller, "Transfer active bit is set DW3\n" - "nak counter: %d, rl: %d\n", - (ptd->dw3 >> 19) & 0xf, (ptd->dw2 >> 25) & 0xf); - } - - return error; -} - -static void check_int_err_status(struct usb_hcd *hcd, u32 dw4) -{ - u32 i; - - dw4 >>= 8; - - for (i = 0; i < 8; i++) { - switch (dw4 & 0x7) { - case INT_UNDERRUN: - dev_err(hcd->self.controller, "Underrun (%d)\n", i); - break; - - case INT_EXACT: - dev_err(hcd->self.controller, - "Transaction error (%d)\n", i); - break; - - case INT_BABBLE: - dev_err(hcd->self.controller, "Babble error (%d)\n", i); - break; - } - dw4 >>= 3; - } -} - -static void enqueue_one_qtd(struct usb_hcd *hcd, struct isp1760_qtd *qtd) -{ - if (qtd->length && (qtd->length <= MAX_PAYLOAD_SIZE)) { - switch (qtd->packet_type) { - case IN_PID: - break; - case OUT_PID: - case SETUP_PID: - mem_writes8(hcd->regs, qtd->payload_addr, - qtd->data_buffer, qtd->length); - } - } -} - -static void enqueue_one_atl_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh, - u32 slot, struct isp1760_qtd *qtd) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct ptd ptd; - - alloc_mem(hcd, qtd); - transform_into_atl(qh, qtd, &ptd); - ptd_write(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); - enqueue_one_qtd(hcd, qtd); - - priv->atl_ints[slot].qh = qh; - priv->atl_ints[slot].qtd = qtd; - qtd->status |= URB_ENQUEUED; - qtd->status |= slot << 16; -} - -static void enqueue_one_int_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh, - u32 slot, struct isp1760_qtd *qtd) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct ptd ptd; - - alloc_mem(hcd, qtd); - transform_into_int(qh, qtd, &ptd); - ptd_write(hcd->regs, INT_PTD_OFFSET, slot, &ptd); - enqueue_one_qtd(hcd, qtd); - - priv->int_ints[slot].qh = qh; - priv->int_ints[slot].qtd = qtd; - qtd->status |= URB_ENQUEUED; - qtd->status |= slot << 16; -} - -static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, - struct isp1760_qtd *qtd) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 skip_map; - u32 slot; - - /* - * When this function is called from the interrupt handler to enqueue - * a follow-up packet, the SKIP register gets written and read back - * almost immediately. With ISP1761, this register requires a delay of - * 195ns between a write and subsequent read (see section 15.1.1.3). - */ - mmiowb(); - ndelay(195); - skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); - - BUG_ON(!skip_map); - slot = __ffs(skip_map); - - enqueue_one_atl_qtd(hcd, qh, slot, qtd); - - skip_map &= ~(1 << slot); - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); - - priv->atl_queued++; - if (priv->atl_queued == 2) - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_SOT_MASK); -} - -static void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, - struct isp1760_qtd *qtd) -{ - u32 skip_map; - u32 slot; - - /* - * When this function is called from the interrupt handler to enqueue - * a follow-up packet, the SKIP register gets written and read back - * almost immediately. With ISP1761, this register requires a delay of - * 195ns between a write and subsequent read (see section 15.1.1.3). - */ - mmiowb(); - ndelay(195); - skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); - - BUG_ON(!skip_map); - slot = __ffs(skip_map); - - enqueue_one_int_qtd(hcd, qh, slot, qtd); - - skip_map &= ~(1 << slot); - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); -} - static void isp1760_urb_done(struct usb_hcd *hcd, struct urb *urb) __releases(priv->lock) __acquires(priv->lock) @@ -924,6 +786,8 @@ static struct isp1760_qtd *qtd_alloc(gfp_t flags, struct urb *urb, INIT_LIST_HEAD(&qtd->qtd_list); qtd->urb = urb; qtd->packet_type = packet_type; + qtd->status = QTD_ENQUEUED; + qtd->actual_length = 0; return qtd; } @@ -934,323 +798,505 @@ static void qtd_free(struct isp1760_qtd *qtd) kmem_cache_free(qtd_cachep, qtd); } -static struct isp1760_qtd *clean_this_qtd(struct isp1760_qtd *qtd, - struct isp1760_qh *qh) +static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, + struct slotinfo *slots, struct isp1760_qtd *qtd, + struct isp1760_qh *qh, struct ptd *ptd) { - struct isp1760_qtd *tmp_qtd; - - if (list_is_last(&qtd->qtd_list, &qh->qtd_list)) - tmp_qtd = NULL; - else - tmp_qtd = list_entry(qtd->qtd_list.next, struct isp1760_qtd, - qtd_list); - list_del(&qtd->qtd_list); - qtd_free(qtd); - return tmp_qtd; + struct isp1760_hcd *priv = hcd_to_priv(hcd); + WARN_ON((slot < 0) || (slot > 31)); + WARN_ON(qtd->length && !qtd->payload_addr); + WARN_ON(slots[slot].qtd); + WARN_ON(slots[slot].qh); + WARN_ON(qtd->status != QTD_PAYLOAD_ALLOC); + + slots[slot].qtd = qtd; + slots[slot].qh = qh; + qh->slot = slot; + qtd->status = QTD_XFER_STARTED; /* Set this before writing ptd, since + interrupt routine may preempt and expects this value. */ + ptd_write(hcd->regs, ptd_offset, slot, ptd); + priv->active_ptds++; } -/* - * Remove this QTD from the QH list and free its memory. If this QTD - * isn't the last one than remove also his successor(s). - * Returns the QTD which is part of an new URB and should be enqueued. - */ -static struct isp1760_qtd *clean_up_qtdlist(struct isp1760_qtd *qtd, - struct isp1760_qh *qh) +static int is_short_bulk(struct isp1760_qtd *qtd) { - struct urb *urb; - - urb = qtd->urb; - do { - qtd = clean_this_qtd(qtd, qh); - } while (qtd && (qtd->urb == urb)); - - return qtd; + return (usb_pipebulk(qtd->urb->pipe) && + (qtd->actual_length < qtd->length)); } -static void do_atl_int(struct usb_hcd *hcd) +static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, + struct list_head *urb_list) { - struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 done_map, skip_map; - struct ptd ptd; - struct urb *urb; - u32 slot; - u32 length; - u32 status = -EINVAL; - int error; - struct isp1760_qtd *qtd; - struct isp1760_qh *qh; - u32 rl; - u32 nakcount; + int last_qtd; + struct isp1760_qtd *qtd, *qtd_next; + struct urb_listitem *urb_listitem; - done_map = reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); - skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + list_for_each_entry_safe(qtd, qtd_next, &qh->qtd_list, qtd_list) { + if (qtd->status < QTD_XFER_COMPLETE) + break; - while (done_map) { - status = 0; - priv->atl_queued--; + if (list_is_last(&qtd->qtd_list, &qh->qtd_list)) + last_qtd = 1; + else + last_qtd = qtd->urb != qtd_next->urb; + + if ((!last_qtd) && (qtd->status == QTD_RETIRE)) + qtd_next->status = QTD_RETIRE; + + if (qtd->status == QTD_XFER_COMPLETE) { + if (qtd->actual_length) { + switch (qtd->packet_type) { + case IN_PID: + mem_reads8(hcd->regs, qtd->payload_addr, + qtd->data_buffer, + qtd->actual_length); + /* Fall through (?) */ + case OUT_PID: + qtd->urb->actual_length += + qtd->actual_length; + /* Fall through ... */ + case SETUP_PID: + break; + } + } - slot = __ffs(done_map); - done_map &= ~(1 << slot); - skip_map |= (1 << slot); + if (is_short_bulk(qtd)) { + if (qtd->urb->transfer_flags & URB_SHORT_NOT_OK) + qtd->urb->status = -EREMOTEIO; + if (!last_qtd) + qtd_next->status = QTD_RETIRE; + } + } - qtd = priv->atl_ints[slot].qtd; - qh = priv->atl_ints[slot].qh; + if (qtd->payload_addr) + free_mem(hcd, qtd); - /* urb unlinked? */ - if (!qh) - continue; + if (last_qtd) { + if ((qtd->status == QTD_RETIRE) && + (qtd->urb->status == -EINPROGRESS)) + qtd->urb->status = -EPIPE; + /* Defer calling of urb_done() since it releases lock */ + urb_listitem = kmem_cache_zalloc(urb_listitem_cachep, + GFP_ATOMIC); + if (unlikely(!urb_listitem)) + break; + urb_listitem->urb = qtd->urb; + list_add_tail(&urb_listitem->urb_list, urb_list); + } - ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); + list_del(&qtd->qtd_list); + qtd_free(qtd); + } +} - rl = (ptd.dw2 >> 25) & 0x0f; - nakcount = (ptd.dw3 >> 19) & 0xf; +#define ENQUEUE_DEPTH 2 +static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + int ptd_offset; + struct slotinfo *slots; + int curr_slot, free_slot; + int n; + struct ptd ptd; + struct isp1760_qtd *qtd; - /* Transfer Error, *but* active and no HALT -> reload */ - if ((ptd.dw3 & DW3_ERROR_BIT) && (ptd.dw3 & DW3_QTD_ACTIVE) && - !(ptd.dw3 & DW3_HALT_BIT)) { - - /* according to ppriv code, we have to - * reload this one if trasfered bytes != requested bytes - * else act like everything went smooth.. - * XXX This just doesn't feel right and hasn't - * triggered so far. - */ + if (unlikely(list_empty(&qh->qtd_list))) { + WARN_ON(1); + return; + } - length = PTD_XFERRED_LENGTH(ptd.dw3); - dev_err(hcd->self.controller, - "Should reload now... transferred %d " - "of %zu\n", length, qtd->length); - BUG(); - } + if (usb_pipeint(list_entry(qh->qtd_list.next, struct isp1760_qtd, + qtd_list)->urb->pipe)) { + ptd_offset = INT_PTD_OFFSET; + slots = priv->int_slots; + } else { + ptd_offset = ATL_PTD_OFFSET; + slots = priv->atl_slots; + } - if (!nakcount && (ptd.dw3 & DW3_QTD_ACTIVE)) { - /* - * NAKs are handled in HW by the chip. Usually if the - * device is not able to send data fast enough. - * This happens mostly on slower hardware. - */ + free_slot = -1; + for (curr_slot = 0; curr_slot < 32; curr_slot++) { + if ((free_slot == -1) && (slots[curr_slot].qtd == NULL)) + free_slot = curr_slot; + if (slots[curr_slot].qh == qh) + break; + } - /* RL counter = ERR counter */ - ptd.dw3 &= ~(0xf << 19); - ptd.dw3 |= rl << 19; - ptd.dw3 &= ~(3 << (55 - 32)); - ptd.dw3 |= ERR_COUNTER << (55 - 32); - - /* - * It is not needed to write skip map back because it - * is unchanged. Just make sure that this entry is - * unskipped once it gets written to the HW. - */ - skip_map &= ~(1 << slot); + n = 0; + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { + if (qtd->status == QTD_ENQUEUED) { + WARN_ON(qtd->payload_addr); + alloc_mem(hcd, qtd); + if ((qtd->length) && (!qtd->payload_addr)) + break; - ptd.dw0 |= PTD_VALID; - ptd_write(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); + if ((qtd->length) && + ((qtd->packet_type == SETUP_PID) || + (qtd->packet_type == OUT_PID))) { + mem_writes8(hcd->regs, qtd->payload_addr, + qtd->data_buffer, qtd->length); + } - priv->atl_queued++; - if (priv->atl_queued == 2) - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_SOT_MASK); - continue; + qtd->status = QTD_PAYLOAD_ALLOC; } - error = check_error(hcd, &ptd); - if (error) { - status = error; - priv->atl_ints[slot].qh->toggle = 0; - priv->atl_ints[slot].qh->ping = 0; - qtd->urb->status = -EPIPE; - -#if 0 - printk(KERN_ERR "Error in %s().\n", __func__); - printk(KERN_ERR "IN dw0: %08x dw1: %08x dw2: %08x " - "dw3: %08x dw4: %08x dw5: %08x dw6: " - "%08x dw7: %08x\n", - ptd.dw0, ptd.dw1, ptd.dw2, ptd.dw3, - ptd.dw4, ptd.dw5, ptd.dw6, ptd.dw7); -#endif - } else { - priv->atl_ints[slot].qh->toggle = ptd.dw3 & (1 << 25); - priv->atl_ints[slot].qh->ping = ptd.dw3 & (1 << 26); + if (qtd->status == QTD_PAYLOAD_ALLOC) { +/* + if ((curr_slot > 31) && (free_slot == -1)) + dev_dbg(hcd->self.controller, "%s: No slot " + "available for transfer\n", __func__); +*/ + /* Start xfer for this endpoint if not already done */ + if ((curr_slot > 31) && (free_slot > -1)) { + if (usb_pipeint(qtd->urb->pipe)) + create_ptd_int(qh, qtd, &ptd); + else + create_ptd_atl(qh, qtd, &ptd); + + start_bus_transfer(hcd, ptd_offset, free_slot, + slots, qtd, qh, &ptd); + curr_slot = free_slot; + } + + n++; + if (n >= ENQUEUE_DEPTH) + break; } + } +} - length = PTD_XFERRED_LENGTH(ptd.dw3); - if (length) { - switch (DW1_GET_PID(ptd.dw1)) { - case IN_PID: - mem_reads8(hcd->regs, qtd->payload_addr, - qtd->data_buffer, length); +void schedule_ptds(struct usb_hcd *hcd) +{ + struct isp1760_hcd *priv; + struct isp1760_qh *qh, *qh_next; + struct list_head *ep_queue; + struct usb_host_endpoint *ep; + LIST_HEAD(urb_list); + struct urb_listitem *urb_listitem, *urb_listitem_next; - case OUT_PID: + if (!hcd) { + WARN_ON(1); + return; + } - qtd->urb->actual_length += length; + priv = hcd_to_priv(hcd); - case SETUP_PID: - break; + /* + * check finished/retired xfers, transfer payloads, call urb_done() + */ + ep_queue = &priv->interruptqhs; + while (ep_queue) { + list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) { + ep = list_entry(qh->qtd_list.next, struct isp1760_qtd, + qtd_list)->urb->ep; + collect_qtds(hcd, qh, &urb_list); + if (list_empty(&qh->qtd_list)) { + list_del(&qh->qh_list); + if (ep->hcpriv == NULL) { + /* Endpoint has been disabled, so we + can free the associated queue head. */ + qh_free(qh); + } } } - priv->atl_ints[slot].qtd = NULL; - priv->atl_ints[slot].qh = NULL; - - free_mem(hcd, qtd); + if (ep_queue == &priv->interruptqhs) + ep_queue = &priv->controlqhs; + else if (ep_queue == &priv->controlqhs) + ep_queue = &priv->bulkqhs; + else + ep_queue = NULL; + } - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); + list_for_each_entry_safe(urb_listitem, urb_listitem_next, &urb_list, + urb_list) { + isp1760_urb_done(hcd, urb_listitem->urb); + kmem_cache_free(urb_listitem_cachep, urb_listitem); + } - if (qtd->urb->status == -EPIPE) { - /* HALT was received */ + /* + * Schedule packets for transfer. + * + * According to USB2.0 specification: + * + * 1st prio: interrupt xfers, up to 80 % of bandwidth + * 2nd prio: control xfers + * 3rd prio: bulk xfers + * + * ... but let's use a simpler scheme here (mostly because ISP1761 doc + * is very unclear on how to prioritize traffic): + * + * 1) Enqueue any queued control transfers, as long as payload chip mem + * and PTD ATL slots are available. + * 2) Enqueue any queued INT transfers, as long as payload chip mem + * and PTD INT slots are available. + * 3) Enqueue any queued bulk transfers, as long as payload chip mem + * and PTD ATL slots are available. + * + * Use double buffering (ENQUEUE_DEPTH==2) as a compromise between + * conservation of chip mem and performance. + * + * I'm sure this scheme could be improved upon! + */ + ep_queue = &priv->controlqhs; + while (ep_queue) { + list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) + enqueue_qtds(hcd, qh); + + if (ep_queue == &priv->controlqhs) + ep_queue = &priv->interruptqhs; + else if (ep_queue == &priv->interruptqhs) + ep_queue = &priv->bulkqhs; + else + ep_queue = NULL; + } +} - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); +#define PTD_STATE_QTD_DONE 1 +#define PTD_STATE_QTD_RELOAD 2 +#define PTD_STATE_URB_RETIRE 3 - } else if (usb_pipebulk(qtd->urb->pipe) && - (length < qtd->length)) { - /* short BULK received */ +static int check_int_transfer(struct usb_hcd *hcd, struct ptd *ptd, + struct urb *urb) +{ + __dw dw4; + int i; - if (qtd->urb->transfer_flags & URB_SHORT_NOT_OK) { - qtd->urb->status = -EREMOTEIO; - dev_dbg(hcd->self.controller, - "short bulk, %d instead %zu " - "with URB_SHORT_NOT_OK flag.\n", - length, qtd->length); - } + dw4 = ptd->dw4; + dw4 >>= 8; - if (qtd->urb->status == -EINPROGRESS) - qtd->urb->status = 0; + /* FIXME: ISP1761 datasheet does not say what to do with these. Do we + need to handle these errors? Is it done in hardware? */ - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); + if (ptd->dw3 & DW3_HALT_BIT) { - } else if (last_qtd_of_urb(qtd, qh)) { - /* that was the last qtd of that URB */ + urb->status = -EPROTO; /* Default unknown error */ - if (qtd->urb->status == -EINPROGRESS) - qtd->urb->status = 0; + for (i = 0; i < 8; i++) { + switch (dw4 & 0x7) { + case INT_UNDERRUN: + dev_dbg(hcd->self.controller, "%s: underrun " + "during uFrame %d\n", + __func__, i); + urb->status = -ECOMM; /* Could not write data */ + break; + case INT_EXACT: + dev_dbg(hcd->self.controller, "%s: transaction " + "error during uFrame %d\n", + __func__, i); + urb->status = -EPROTO; /* timeout, bad CRC, PID + error etc. */ + break; + case INT_BABBLE: + dev_dbg(hcd->self.controller, "%s: babble " + "error during uFrame %d\n", + __func__, i); + urb->status = -EOVERFLOW; + break; + } + dw4 >>= 3; + } - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); + return PTD_STATE_URB_RETIRE; + } - } else { - /* next QTD of this URB */ + return PTD_STATE_QTD_DONE; +} - qtd = clean_this_qtd(qtd, qh); - BUG_ON(!qtd); - } +static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd, + struct urb *urb) +{ + WARN_ON(!ptd); + if (ptd->dw3 & DW3_HALT_BIT) { + if (ptd->dw3 & DW3_BABBLE_BIT) + urb->status = -EOVERFLOW; + else if (FROM_DW3_CERR(ptd->dw3)) + urb->status = -EPIPE; /* Stall */ + else if (ptd->dw3 & DW3_ERROR_BIT) + urb->status = -EPROTO; /* XactErr */ + else + urb->status = -EPROTO; /* Unknown */ +/* + dev_dbg(hcd->self.controller, "%s: ptd error:\n" + " dw0: %08x dw1: %08x dw2: %08x dw3: %08x\n" + " dw4: %08x dw5: %08x dw6: %08x dw7: %08x\n", + __func__, + ptd->dw0, ptd->dw1, ptd->dw2, ptd->dw3, + ptd->dw4, ptd->dw5, ptd->dw6, ptd->dw7); +*/ + return PTD_STATE_URB_RETIRE; + } - if (qtd) - enqueue_an_ATL_packet(hcd, qh, qtd); + if ((ptd->dw3 & DW3_ERROR_BIT) && (ptd->dw3 & DW3_ACTIVE_BIT)) { + /* Transfer Error, *but* active and no HALT -> reload */ + dev_dbg(hcd->self.controller, "PID error; reloading ptd\n"); + return PTD_STATE_QTD_RELOAD; + } - skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + if (!FROM_DW3_NAKCOUNT(ptd->dw3) && (ptd->dw3 & DW3_ACTIVE_BIT)) { + /* + * NAKs are handled in HW by the chip. Usually if the + * device is not able to send data fast enough. + * This happens mostly on slower hardware. + */ + return PTD_STATE_QTD_RELOAD; } - if (priv->atl_queued <= 1) - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_MASK); + + return PTD_STATE_QTD_DONE; } -static void do_intl_int(struct usb_hcd *hcd) +static irqreturn_t isp1760_irq(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 done_map, skip_map; + u32 imask; + irqreturn_t irqret = IRQ_NONE; struct ptd ptd; - struct urb *urb; - u32 length; - int error; - u32 slot; - struct isp1760_qtd *qtd; struct isp1760_qh *qh; + int int_done_map, atl_done_map; + int slot; + int state; + struct slotinfo *slots; + u32 ptd_offset; + struct isp1760_qtd *qtd; + int modified; + static int last_active_ptds; - done_map = reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); - skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); - - while (done_map) { - slot = __ffs(done_map); - done_map &= ~(1 << slot); - skip_map |= (1 << slot); - - qtd = priv->int_ints[slot].qtd; - qh = priv->int_ints[slot].qh; - - /* urb unlinked? */ - if (!qh) - continue; + spin_lock(&priv->lock); - ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd); - check_int_err_status(hcd, ptd.dw4); - - error = check_error(hcd, &ptd); - if (error) { -#if 0 - printk(KERN_ERR "Error in %s().\n", __func__); - printk(KERN_ERR "IN dw0: %08x dw1: %08x dw2: %08x " - "dw3: %08x dw4: %08x dw5: %08x dw6: " - "%08x dw7: %08x\n", - ptd.dw0, ptd.dw1, ptd.dw2, ptd.dw3, - ptd.dw4, ptd.dw5, ptd.dw6, ptd.dw7); -#endif - qtd->urb->status = -EPIPE; - priv->int_ints[slot].qh->toggle = 0; - priv->int_ints[slot].qh->ping = 0; + if (!(hcd->state & HC_STATE_RUNNING)) + goto leave; + imask = reg_read32(hcd->regs, HC_INTERRUPT_REG); + if (unlikely(!imask)) + goto leave; + reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */ + + int_done_map = reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); + atl_done_map = reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); + modified = int_done_map | atl_done_map; + + while (int_done_map || atl_done_map) { + if (int_done_map) { + /* INT ptd */ + slot = __ffs(int_done_map); + int_done_map &= ~(1 << slot); + slots = priv->int_slots; + if (!slots[slot].qh) + continue; + ptd_offset = INT_PTD_OFFSET; + ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd); + state = check_int_transfer(hcd, &ptd, + slots[slot].qtd->urb); } else { - priv->int_ints[slot].qh->toggle = ptd.dw3 & (1 << 25); - priv->int_ints[slot].qh->ping = ptd.dw3 & (1 << 26); + /* ATL ptd */ + slot = __ffs(atl_done_map); + atl_done_map &= ~(1 << slot); + slots = priv->atl_slots; + if (!slots[slot].qh) + continue; + ptd_offset = ATL_PTD_OFFSET; + ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); + state = check_atl_transfer(hcd, &ptd, + slots[slot].qtd->urb); } - if (qtd->urb->dev->speed != USB_SPEED_HIGH) - length = PTD_XFERRED_LENGTH_LO(ptd.dw3); - else - length = PTD_XFERRED_LENGTH(ptd.dw3); - - if (length) { - switch (DW1_GET_PID(ptd.dw1)) { - case IN_PID: - mem_reads8(hcd->regs, qtd->payload_addr, - qtd->data_buffer, length); - case OUT_PID: - - qtd->urb->actual_length += length; + qtd = slots[slot].qtd; + slots[slot].qtd = NULL; + qh = slots[slot].qh; + slots[slot].qh = NULL; + priv->active_ptds--; + qh->slot = -1; + + WARN_ON(qtd->status != QTD_XFER_STARTED); + + switch (state) { + case PTD_STATE_QTD_DONE: + if ((usb_pipeint(qtd->urb->pipe)) && + (qtd->urb->dev->speed != USB_SPEED_HIGH)) + qtd->actual_length = + FROM_DW3_SCS_NRBYTESTRANSFERRED(ptd.dw3); + else + qtd->actual_length = + FROM_DW3_NRBYTESTRANSFERRED(ptd.dw3); - case SETUP_PID: - break; - } - } + qtd->status = QTD_XFER_COMPLETE; + if (list_is_last(&qtd->qtd_list, &qh->qtd_list) || + is_short_bulk(qtd)) + qtd = NULL; + else + qtd = list_entry(qtd->qtd_list.next, + typeof(*qtd), qtd_list); - priv->int_ints[slot].qtd = NULL; - priv->int_ints[slot].qh = NULL; + qh->toggle = FROM_DW3_DATA_TOGGLE(ptd.dw3); + qh->ping = FROM_DW3_PING(ptd.dw3); + break; - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); - free_mem(hcd, qtd); + case PTD_STATE_QTD_RELOAD: /* QTD_RETRY, for atls only */ + qtd->status = QTD_PAYLOAD_ALLOC; + ptd.dw0 |= DW0_VALID_BIT; + /* RL counter = ERR counter */ + ptd.dw3 &= ~TO_DW3_NAKCOUNT(0xf); + ptd.dw3 |= TO_DW3_NAKCOUNT(FROM_DW2_RL(ptd.dw2)); + ptd.dw3 &= ~TO_DW3_CERR(3); + ptd.dw3 |= TO_DW3_CERR(ERR_COUNTER); + qh->toggle = FROM_DW3_DATA_TOGGLE(ptd.dw3); + qh->ping = FROM_DW3_PING(ptd.dw3); + break; - if (qtd->urb->status == -EPIPE) { - /* HALT received */ + case PTD_STATE_URB_RETIRE: + qtd->status = QTD_RETIRE; + qtd = NULL; + qh->toggle = 0; + qh->ping = 0; + break; - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); + default: + WARN_ON(1); + continue; + } - } else if (last_qtd_of_urb(qtd, qh)) { + if (qtd && (qtd->status == QTD_PAYLOAD_ALLOC)) { + if (slots == priv->int_slots) { + if (state == PTD_STATE_QTD_RELOAD) + dev_err(hcd->self.controller, + "%s: PTD_STATE_QTD_RELOAD on " + "interrupt packet\n", __func__); + if (state != PTD_STATE_QTD_RELOAD) + create_ptd_int(qh, qtd, &ptd); + } else { + if (state != PTD_STATE_QTD_RELOAD) + create_ptd_atl(qh, qtd, &ptd); + } - if (qtd->urb->status == -EINPROGRESS) - qtd->urb->status = 0; + start_bus_transfer(hcd, ptd_offset, slot, slots, qtd, + qh, &ptd); + } + } - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); + if (modified) + schedule_ptds(hcd); - } else { - /* next QTD of this URB */ + /* ISP1760 Errata 2 explains that interrupts may be missed (or not + happen?) if two USB devices are running simultaneously. Perhaps + this happens when a PTD is finished during interrupt handling; + enable SOF interrupts if PTDs are still scheduled when exiting this + interrupt handler, just to be safe. */ - qtd = clean_this_qtd(qtd, qh); - BUG_ON(!qtd); - } + if (priv->active_ptds != last_active_ptds) { + if (priv->active_ptds > 0) + reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, + INTERRUPT_ENABLE_SOT_MASK); + else + reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, + INTERRUPT_ENABLE_MASK); + last_active_ptds = priv->active_ptds; + } - if (qtd) - enqueue_an_INT_packet(hcd, qh, qtd); + irqret = IRQ_HANDLED; +leave: + spin_unlock(&priv->lock); - skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); - } + return irqret; } static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len) @@ -1380,197 +1426,136 @@ cleanup: qtd_list_free(head); } -static int enqueue_qtdlist(struct usb_hcd *hcd, struct urb *urb, - struct list_head *qtd_list, gfp_t mem_flags, packet_enqueue *p) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct isp1760_qtd *qtd; - struct isp1760_qh *qh = NULL; - unsigned long flags; - int qh_empty; - int rc; - - spin_lock_irqsave(&priv->lock, flags); - if (!HCD_HW_ACCESSIBLE(hcd)) { - rc = -ESHUTDOWN; - goto done; - } - rc = usb_hcd_link_urb_to_ep(hcd, urb); - if (rc) - goto done; - - qh = urb->ep->hcpriv; - if (!qh) { - qh = isp1760_qh_alloc(GFP_ATOMIC); - if (!qh) { - usb_hcd_unlink_urb_from_ep(hcd, urb); - rc = -ENOMEM; - goto done; - } - if (!usb_pipecontrol(urb->pipe)) - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - !usb_pipein(urb->pipe), 1); - urb->ep->hcpriv = qh; - } - - qh_empty = list_empty(&qh->qtd_list); - list_splice_tail(qtd_list, &qh->qtd_list); - if (qh_empty) { - qtd = list_entry(qtd_list->next, struct isp1760_qtd, qtd_list); - p(hcd, qh, qtd); - } - -done: - spin_unlock_irqrestore(&priv->lock, flags); - if (!qh) - qtd_list_free(qtd_list); - return rc; -} - static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { - struct list_head qtd_list; - packet_enqueue *pe; - - INIT_LIST_HEAD(&qtd_list); + struct isp1760_hcd *priv = hcd_to_priv(hcd); + struct list_head *ep_queue; + struct isp1760_qh *qh, *qhit; + unsigned long spinflags; + LIST_HEAD(new_qtds); + int retval; + int qh_in_queue; switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: + ep_queue = &priv->controlqhs; + break; case PIPE_BULK: - pe = enqueue_an_ATL_packet; + ep_queue = &priv->bulkqhs; break; - case PIPE_INTERRUPT: - pe = enqueue_an_INT_packet; + if (urb->interval < 0) + return -EINVAL; + /* FIXME: Check bandwidth */ + ep_queue = &priv->interruptqhs; break; - case PIPE_ISOCHRONOUS: - dev_err(hcd->self.controller, "PIPE_ISOCHRONOUS ain't supported\n"); + dev_err(hcd->self.controller, "%s: isochronous USB packets " + "not yet supported\n", + __func__); + return -EPIPE; default: + dev_err(hcd->self.controller, "%s: unknown pipe type\n", + __func__); return -EPIPE; } - packetize_urb(hcd, urb, &qtd_list, mem_flags); - if (list_empty(&qtd_list)) - return -ENOMEM; - - return enqueue_qtdlist(hcd, urb, &qtd_list, mem_flags, pe); -} - -static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct inter_packet_info *ints; - u32 i; - u32 reg_base, skip_reg; - unsigned long flags; - struct ptd ptd; - packet_enqueue *pe; + if (usb_pipein(urb->pipe)) + urb->actual_length = 0; - switch (usb_pipetype(urb->pipe)) { - case PIPE_ISOCHRONOUS: - return -EPIPE; - break; + packetize_urb(hcd, urb, &new_qtds, mem_flags); + if (list_empty(&new_qtds)) + return -ENOMEM; + urb->hcpriv = NULL; /* Used to signal unlink to interrupt handler */ - case PIPE_INTERRUPT: - ints = priv->int_ints; - reg_base = INT_PTD_OFFSET; - skip_reg = HC_INT_PTD_SKIPMAP_REG; - pe = enqueue_an_INT_packet; - break; + retval = 0; + spin_lock_irqsave(&priv->lock, spinflags); - default: - ints = priv->atl_ints; - reg_base = ATL_PTD_OFFSET; - skip_reg = HC_ATL_PTD_SKIPMAP_REG; - pe = enqueue_an_ATL_packet; - break; + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + retval = -ESHUTDOWN; + goto out; } + retval = usb_hcd_link_urb_to_ep(hcd, urb); + if (retval) + goto out; - memset(&ptd, 0, sizeof(ptd)); - spin_lock_irqsave(&priv->lock, flags); - - for (i = 0; i < 32; i++) { - if (!ints[i].qh) - continue; - WARN_ON(!ints[i].qtd); - - if (ints[i].qtd->urb == urb) { - u32 skip_map; - struct isp1760_qtd *qtd; - struct isp1760_qh *qh; - - skip_map = reg_read32(hcd->regs, skip_reg); - skip_map |= 1 << i; - reg_write32(hcd->regs, skip_reg, skip_map); - - ptd_write(hcd->regs, reg_base, i, &ptd); - - qtd = ints[i].qtd; - qh = ints[i].qh; - - free_mem(hcd, qtd); - qtd = clean_up_qtdlist(qtd, qh); - - ints[i].qh = NULL; - ints[i].qtd = NULL; - - urb->status = status; - isp1760_urb_done(hcd, urb); - if (qtd) - pe(hcd, qh, qtd); - break; - - } else { - struct isp1760_qtd *qtd; - - list_for_each_entry(qtd, &ints[i].qtd->qtd_list, - qtd_list) { - if (qtd->urb == urb) { - clean_up_qtdlist(qtd, ints[i].qh); - isp1760_urb_done(hcd, urb); - qtd = NULL; - break; - } - } - - /* We found the urb before the last slot */ - if (!qtd) + qh = urb->ep->hcpriv; + if (qh) { + qh_in_queue = 0; + list_for_each_entry(qhit, ep_queue, qh_list) { + if (qhit == qh) { + qh_in_queue = 1; break; + } + } + if (!qh_in_queue) + list_add_tail(&qh->qh_list, ep_queue); + } else { + qh = qh_alloc(GFP_ATOMIC); + if (!qh) { + retval = -ENOMEM; + goto out; } + list_add_tail(&qh->qh_list, ep_queue); + urb->ep->hcpriv = qh; } - spin_unlock_irqrestore(&priv->lock, flags); - return 0; + list_splice_tail(&new_qtds, &qh->qtd_list); + schedule_ptds(hcd); + +out: + spin_unlock_irqrestore(&priv->lock, spinflags); + return retval; } -static irqreturn_t isp1760_irq(struct usb_hcd *hcd) +static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, + int status) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 imask; - irqreturn_t irqret = IRQ_NONE; + struct isp1760_qh *qh; + struct isp1760_qtd *qtd; + struct ptd ptd; + unsigned long spinflags; + int retval = 0; - spin_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, spinflags); - if (!(hcd->state & HC_STATE_RUNNING)) - goto leave; + qh = urb->ep->hcpriv; + if (!qh) { + retval = -EINVAL; + goto out; + } - imask = reg_read32(hcd->regs, HC_INTERRUPT_REG); - if (unlikely(!imask)) - goto leave; + /* We need to forcefully reclaim the slot since some transfers never + return, e.g. interrupt transfers and NAKed bulk transfers. */ + if (qh->slot > -1) { + memset(&ptd, 0, sizeof(ptd)); + if (usb_pipebulk(urb->pipe)) { + priv->atl_slots[qh->slot].qh = NULL; + priv->atl_slots[qh->slot].qtd = NULL; + ptd_write(hcd->regs, ATL_PTD_OFFSET, qh->slot, &ptd); + } else { + priv->int_slots[qh->slot].qh = NULL; + priv->int_slots[qh->slot].qtd = NULL; + ptd_write(hcd->regs, INT_PTD_OFFSET, qh->slot, &ptd); + } + priv->active_ptds--; + qh->slot = -1; + } - reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); - if (imask & (HC_ATL_INT | HC_SOT_INT)) - do_atl_int(hcd); + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { + if (qtd->urb == urb) + qtd->status = QTD_RETIRE; + } - if (imask & HC_INTL_INT) - do_intl_int(hcd); + urb->status = status; + schedule_ptds(hcd); - irqret = IRQ_HANDLED; -leave: - spin_unlock(&priv->lock); - return irqret; +out: + spin_unlock_irqrestore(&priv->lock, spinflags); + + return retval; } static int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf) @@ -1661,7 +1646,7 @@ static int check_reset_complete(struct usb_hcd *hcd, int index, /* if reset finished and it's still not enabled -- handoff */ if (!(port_status & PORT_PE)) { - dev_err(hcd->self.controller, + dev_info(hcd->self.controller, "port %d full speed --> companion\n", index + 1); @@ -1670,7 +1655,7 @@ static int check_reset_complete(struct usb_hcd *hcd, int index, reg_write32(hcd->regs, HC_PORTSC1, port_status); } else - dev_err(hcd->self.controller, "port %d high speed\n", + dev_info(hcd->self.controller, "port %d high speed\n", index + 1); return port_status; @@ -1948,43 +1933,32 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd, struct isp1760_hcd *priv = hcd_to_priv(hcd); struct isp1760_qh *qh; struct isp1760_qtd *qtd; - unsigned long flags; + unsigned long spinflags; + int do_iter; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->lock, spinflags); qh = ep->hcpriv; if (!qh) goto out; - ep->hcpriv = NULL; - do { - /* more than entry might get removed */ - if (list_empty(&qh->qtd_list)) - break; - - qtd = list_first_entry(&qh->qtd_list, struct isp1760_qtd, - qtd_list); - - if (qtd->status & URB_ENQUEUED) { - spin_unlock_irqrestore(&priv->lock, flags); - isp1760_urb_dequeue(hcd, qtd->urb, -ECONNRESET); - spin_lock_irqsave(&priv->lock, flags); - } else { - struct urb *urb; - - urb = qtd->urb; - clean_up_qtdlist(qtd, qh); - urb->status = -ECONNRESET; - isp1760_urb_done(hcd, urb); + do_iter = !list_empty(&qh->qtd_list); + while (do_iter) { + do_iter = 0; + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { + if (qtd->urb->ep == ep) { + spin_unlock_irqrestore(&priv->lock, spinflags); + isp1760_urb_dequeue(hcd, qtd->urb, -ECONNRESET); + spin_lock_irqsave(&priv->lock, spinflags); + do_iter = 1; + break; /* Restart iteration */ + } } - } while (1); + } + ep->hcpriv = NULL; + /* Cannot free qh here since it will be parsed by schedule_ptds() */ - qh_destroy(qh); - /* remove requests and leak them. - * ATL are pretty fast done, INT could take a while... - * The latter shoule be removed - */ out: - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, spinflags); } static int isp1760_get_frame(struct usb_hcd *hcd) @@ -2048,6 +2022,13 @@ static const struct hc_driver isp1760_hc_driver = { int __init init_kmem_once(void) { + urb_listitem_cachep = kmem_cache_create("isp1760 urb_listitem", + sizeof(struct urb_listitem), 0, SLAB_TEMPORARY | + SLAB_MEM_SPREAD, NULL); + + if (!urb_listitem_cachep) + return -ENOMEM; + qtd_cachep = kmem_cache_create("isp1760_qtd", sizeof(struct isp1760_qtd), 0, SLAB_TEMPORARY | SLAB_MEM_SPREAD, NULL); @@ -2070,6 +2051,7 @@ void deinit_kmem_cache(void) { kmem_cache_destroy(qtd_cachep); kmem_cache_destroy(qh_cachep); + kmem_cache_destroy(urb_listitem_cachep); } struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 056e046b0d4..014a7dfadf9 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -104,7 +104,7 @@ struct ptd { #define ATL_PTD_OFFSET 0x0c00 #define PAYLOAD_OFFSET 0x1000 -struct inter_packet_info { +struct slotinfo { struct isp1760_qh *qh; struct isp1760_qtd *qtd; }; @@ -154,54 +154,52 @@ struct memory_chunk { /* ATL */ /* DW0 */ -#define PTD_VALID 1 -#define PTD_LENGTH(x) (((u32) x) << 3) -#define PTD_MAXPACKET(x) (((u32) x) << 18) -#define PTD_MULTI(x) (((u32) x) << 29) -#define PTD_ENDPOINT(x) (((u32) x) << 31) +#define DW0_VALID_BIT 1 +#define FROM_DW0_VALID(x) ((x) & 0x01) +#define TO_DW0_LENGTH(x) (((u32) x) << 3) +#define TO_DW0_MAXPACKET(x) (((u32) x) << 18) +#define TO_DW0_MULTI(x) (((u32) x) << 29) +#define TO_DW0_ENDPOINT(x) (((u32) x) << 31) /* DW1 */ -#define PTD_DEVICE_ADDR(x) (((u32) x) << 3) -#define PTD_PID_TOKEN(x) (((u32) x) << 10) -#define PTD_TRANS_BULK ((u32) 2 << 12) -#define PTD_TRANS_INT ((u32) 3 << 12) -#define PTD_TRANS_SPLIT ((u32) 1 << 14) -#define PTD_SE_USB_LOSPEED ((u32) 2 << 16) -#define PTD_PORT_NUM(x) (((u32) x) << 18) -#define PTD_HUB_NUM(x) (((u32) x) << 25) -#define PTD_PING(x) (((u32) x) << 26) +#define TO_DW1_DEVICE_ADDR(x) (((u32) x) << 3) +#define TO_DW1_PID_TOKEN(x) (((u32) x) << 10) +#define DW1_TRANS_BULK ((u32) 2 << 12) +#define DW1_TRANS_INT ((u32) 3 << 12) +#define DW1_TRANS_SPLIT ((u32) 1 << 14) +#define DW1_SE_USB_LOSPEED ((u32) 2 << 16) +#define TO_DW1_PORT_NUM(x) (((u32) x) << 18) +#define TO_DW1_HUB_NUM(x) (((u32) x) << 25) /* DW2 */ -#define PTD_RL_CNT(x) (((u32) x) << 25) -#define PTD_DATA_START_ADDR(x) (((u32) x) << 8) -#define BASE_ADDR 0x1000 +#define TO_DW2_DATA_START_ADDR(x) (((u32) x) << 8) +#define TO_DW2_RL(x) ((x) << 25) +#define FROM_DW2_RL(x) (((x) >> 25) & 0xf) /* DW3 */ -#define PTD_CERR(x) (((u32) x) << 23) -#define PTD_NAC_CNT(x) (((u32) x) << 19) -#define PTD_ACTIVE ((u32) 1 << 31) -#define PTD_DATA_TOGGLE(x) (((u32) x) << 25) - -#define DW3_HALT_BIT (1 << 30) +#define FROM_DW3_NRBYTESTRANSFERRED(x) ((x) & 0x7fff) +#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x) ((x) & 0x07ff) +#define TO_DW3_NAKCOUNT(x) ((x) << 19) +#define FROM_DW3_NAKCOUNT(x) (((x) >> 19) & 0xf) +#define TO_DW3_CERR(x) ((x) << 23) +#define FROM_DW3_CERR(x) (((x) >> 23) & 0x3) +#define TO_DW3_DATA_TOGGLE(x) ((x) << 25) +#define FROM_DW3_DATA_TOGGLE(x) (((x) >> 25) & 0x1) +#define TO_DW3_PING(x) ((x) << 26) +#define FROM_DW3_PING(x) (((x) >> 26) & 0x1) #define DW3_ERROR_BIT (1 << 28) -#define DW3_QTD_ACTIVE (1 << 31) +#define DW3_BABBLE_BIT (1 << 29) +#define DW3_HALT_BIT (1 << 30) +#define DW3_ACTIVE_BIT (1 << 31) #define INT_UNDERRUN (1 << 2) #define INT_BABBLE (1 << 1) #define INT_EXACT (1 << 0) -#define DW1_GET_PID(x) (((x) >> 10) & 0x3) -#define PTD_XFERRED_LENGTH(x) ((x) & 0x7fff) -#define PTD_XFERRED_LENGTH_LO(x) ((x) & 0x7ff) - #define SETUP_PID (2) #define IN_PID (1) #define OUT_PID (0) -#define GET_QTD_TOKEN_TYPE(x) ((x) & 0x3) - -#define DATA_TOGGLE (1 << 31) -#define GET_DATA_TOGGLE(x) ((x) >> 31) /* Errata 1 */ #define RL_COUNTER (0) #define NAK_COUNTER (0) #define ERR_COUNTER (2) -#endif +#endif /* _ISP1760_HCD_H_ */ -- cgit v1.2.3 From f5ced99725d05f521ef0f597e688c19835e59c55 Mon Sep 17 00:00:00 2001 From: David Daney Date: Wed, 27 Apr 2011 10:54:20 -0700 Subject: USB: octeon2-common: Don't reinitialize the clocks. The UCTL clock initialization will cause the ehci and ohci blocks to become inoperable if the clocks are reinitialized. Check to see if the clocks have already been initialized. Also use a mutex to protect the clock initialization code so that there can be no attempt to use the clocks before they are fully configured. Signed-off-by: David Daney Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/octeon2-common.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/usb/host/octeon2-common.c b/drivers/usb/host/octeon2-common.c index 72d672cfcf3..5a0feed0356 100644 --- a/drivers/usb/host/octeon2-common.c +++ b/drivers/usb/host/octeon2-common.c @@ -3,18 +3,19 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2010 Cavium Networks + * Copyright (C) 2010, 2011 Cavium Networks */ #include +#include #include -#include - #include #include -static atomic_t octeon2_usb_clock_start_cnt = ATOMIC_INIT(0); +static DEFINE_MUTEX(octeon2_usb_clocks_mutex); + +static int octeon2_usb_clock_start_cnt; void octeon2_usb_clocks_start(void) { @@ -26,8 +27,12 @@ void octeon2_usb_clocks_start(void) int i; unsigned long io_clk_64_to_ns; - if (atomic_inc_return(&octeon2_usb_clock_start_cnt) != 1) - return; + + mutex_lock(&octeon2_usb_clocks_mutex); + + octeon2_usb_clock_start_cnt++; + if (octeon2_usb_clock_start_cnt != 1) + goto exit; io_clk_64_to_ns = 64000000000ull / octeon_get_io_clock_rate(); @@ -43,6 +48,13 @@ void octeon2_usb_clocks_start(void) /* Step 3: Configure the reference clock, PHY, and HCLK */ clk_rst_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_CLK_RST_CTL(0)); + + /* + * If the UCTL looks like it has already been started, skip + * the initialization, otherwise bus errors are obtained. + */ + if (clk_rst_ctl.s.hrst) + goto end_clock; /* 3a */ clk_rst_ctl.s.p_por = 1; clk_rst_ctl.s.hrst = 0; @@ -158,6 +170,7 @@ void octeon2_usb_clocks_start(void) clk_rst_ctl.s.hrst = 1; cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); +end_clock: /* Now we can set some other registers. */ for (i = 0; i <= 1; i++) { @@ -168,18 +181,15 @@ void octeon2_usb_clocks_start(void) cvmx_write_csr(CVMX_UCTLX_UPHY_PORTX_CTL_STATUS(i, 0), port_ctl_status.u64); } +exit: + mutex_unlock(&octeon2_usb_clocks_mutex); } EXPORT_SYMBOL(octeon2_usb_clocks_start); void octeon2_usb_clocks_stop(void) { - union cvmx_uctlx_if_ena if_ena; - - if (atomic_dec_return(&octeon2_usb_clock_start_cnt) != 0) - return; - - if_ena.u64 = 0; - if_ena.s.en = 0; - cvmx_write_csr(CVMX_UCTLX_IF_ENA(0), if_ena.u64); + mutex_lock(&octeon2_usb_clocks_mutex); + octeon2_usb_clock_start_cnt--; + mutex_unlock(&octeon2_usb_clocks_mutex); } EXPORT_SYMBOL(octeon2_usb_clocks_stop); -- cgit v1.2.3 From bf5417152154038bbae429e2357731b1dad03328 Mon Sep 17 00:00:00 2001 From: David Daney Date: Wed, 27 Apr 2011 10:54:21 -0700 Subject: usb: octeon2-common.c: Configure ports for proper electrical characteristics. Additional PHY tuning is needed to obtain compliant 'eye' diagram electrical characteristics. Signed-off-by: David Daney Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/octeon2-common.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/octeon2-common.c b/drivers/usb/host/octeon2-common.c index 5a0feed0356..aef6364d631 100644 --- a/drivers/usb/host/octeon2-common.c +++ b/drivers/usb/host/octeon2-common.c @@ -176,8 +176,10 @@ end_clock: for (i = 0; i <= 1; i++) { port_ctl_status.u64 = cvmx_read_csr(CVMX_UCTLX_UPHY_PORTX_CTL_STATUS(i, 0)); - /* Set txvreftune to 15 to obtain complient 'eye' diagram. */ + /* Set txvreftune to 15 to obtain compliant 'eye' diagram. */ port_ctl_status.s.txvreftune = 15; + port_ctl_status.s.txrisetune = 1; + port_ctl_status.s.txpreemphasistune = 1; cvmx_write_csr(CVMX_UCTLX_UPHY_PORTX_CTL_STATUS(i, 0), port_ctl_status.u64); } -- cgit v1.2.3 From 14be249c969817e05c4f1ce042906e1c5be68873 Mon Sep 17 00:00:00 2001 From: David Daney Date: Wed, 27 Apr 2011 10:54:22 -0700 Subject: usb: Configure octeon2 glue logic for proper uSOF cycle period. The reset value of the uSOF cycle period is incorrect. Set it to 60,000 bits. Without this, several commercial USB flash memory devices and hubs fail to work properly. Signed-off-by: David Daney Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/octeon2-common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/host/octeon2-common.c b/drivers/usb/host/octeon2-common.c index aef6364d631..d9df423f3d1 100644 --- a/drivers/usb/host/octeon2-common.c +++ b/drivers/usb/host/octeon2-common.c @@ -183,6 +183,9 @@ end_clock: cvmx_write_csr(CVMX_UCTLX_UPHY_PORTX_CTL_STATUS(i, 0), port_ctl_status.u64); } + + /* Set uSOF cycle period to 60,000 bits. */ + cvmx_write_csr(CVMX_UCTLX_EHCI_FLA(0), 0x20ull); exit: mutex_unlock(&octeon2_usb_clocks_mutex); } -- cgit v1.2.3 From 139540170d9d9b7ead3caaf540f161756b356d56 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 27 Apr 2011 21:07:28 +0530 Subject: USB: ehci: remove structure packing from ehci_def As pointed out by Arnd Bergmann, in include/linux/usb/ehci_def.h, struct ehci_caps is defined with __attribute__((packed)) for no good reason, and this triggers undefined behaviour when using ARM's readl() on pointers to elements of this structure: http://lkml.kernel.org/r/201102021700.20683.arnd@arndb.de The same problem exists with the other two structures in ehci_def.h too, so remove the __attribute__((packed)) from all of them. Cc: Arnd Bergmann Signed-off-by: Rabin Vincent Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/ehci_def.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index e49dfd45baa..78799432008 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -52,7 +52,7 @@ struct ehci_caps { #define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ #define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ u8 portroute[8]; /* nibbles for routing - offset 0xC */ -} __attribute__ ((packed)); +}; /* Section 2.3 Host Controller Operational Registers */ @@ -150,7 +150,7 @@ struct ehci_regs { #define PORT_CSC (1<<1) /* connect status change */ #define PORT_CONNECT (1<<0) /* device connected */ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) -} __attribute__ ((packed)); +}; #define USBMODE 0x68 /* USB Device mode */ #define USBMODE_SDIS (1<<3) /* Stream disable */ @@ -194,7 +194,7 @@ struct ehci_dbg_port { u32 data47; u32 address; #define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) -} __attribute__ ((packed)); +}; #ifdef CONFIG_EARLY_PRINTK_DBGP #include -- cgit v1.2.3 From ce97cac813340eb8ecb1c5410041c9eade58f870 Mon Sep 17 00:00:00 2001 From: Michael Hund Date: Tue, 3 May 2011 10:12:00 -0700 Subject: USB: ldusb: add several new devices Added several new devices to ldusb and excluded them from the HID driver. Signed-off-by: Michael Hund Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-core.c | 20 ++++++++++++++++++- drivers/hid/hid-ids.h | 21 ++++++++++++++++++- drivers/usb/misc/ldusb.c | 52 ++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 81 insertions(+), 12 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 408c4bea4d8..86a64fa6e26 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1760,19 +1760,37 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_KYE, 0x0058) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYVOLTAGE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYCURRENT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTIME) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYPH) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) }, - { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOTOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_ABSESP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_AUTODATABUS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MCT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HYBRID) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HEATCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS) }, { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 00a94b535d2..a8a05835fb4 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -367,19 +367,38 @@ #define USB_VENDOR_ID_LD 0x0f11 #define USB_DEVICE_ID_LD_CASSY 0x1000 +#define USB_DEVICE_ID_LD_CASSY2 0x1001 #define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 +#define USB_DEVICE_ID_LD_POCKETCASSY2 0x1011 #define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 +#define USB_DEVICE_ID_LD_MOBILECASSY2 0x1021 +#define USB_DEVICE_ID_LD_MICROCASSYVOLTAGE 0x1031 +#define USB_DEVICE_ID_LD_MICROCASSYCURRENT 0x1032 +#define USB_DEVICE_ID_LD_MICROCASSYTIME 0x1033 +#define USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE 0x1035 +#define USB_DEVICE_ID_LD_MICROCASSYPH 0x1038 #define USB_DEVICE_ID_LD_JWM 0x1080 #define USB_DEVICE_ID_LD_DMMP 0x1081 #define USB_DEVICE_ID_LD_UMIP 0x1090 -#define USB_DEVICE_ID_LD_XRAY1 0x1100 +#define USB_DEVICE_ID_LD_UMIC 0x10A0 +#define USB_DEVICE_ID_LD_UMIB 0x10B0 +#define USB_DEVICE_ID_LD_XRAY 0x1100 #define USB_DEVICE_ID_LD_XRAY2 0x1101 +#define USB_DEVICE_ID_LD_XRAYCT 0x1110 #define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 +#define USB_DEVICE_ID_LD_MOTOR 0x1210 #define USB_DEVICE_ID_LD_COM3LAB 0x2000 #define USB_DEVICE_ID_LD_TELEPORT 0x2010 #define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 #define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 #define USB_DEVICE_ID_LD_MACHINETEST 0x2040 +#define USB_DEVICE_ID_LD_MOSTANALYSER 0x2050 +#define USB_DEVICE_ID_LD_MOSTANALYSER2 0x2051 +#define USB_DEVICE_ID_LD_ABSESP 0x2060 +#define USB_DEVICE_ID_LD_AUTODATABUS 0x2070 +#define USB_DEVICE_ID_LD_MCT 0x2080 +#define USB_DEVICE_ID_LD_HYBRID 0x2090 +#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0 #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index eefb8275bb7..cb4096201e2 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -20,11 +20,6 @@ * Derived from Lego USB Tower driver * Copyright (C) 2003 David Glance * 2001-2004 Juergen Stuber - * - * V0.1 (mh) Initial version - * V0.11 (mh) Added raw support for HID 1.0 devices (no interrupt out endpoint) - * V0.12 (mh) Added kmalloc check for string buffer - * V0.13 (mh) Added support for LD X-Ray and Machine Test System */ #include @@ -41,20 +36,39 @@ /* Define these values to match your devices */ #define USB_VENDOR_ID_LD 0x0f11 /* USB Vendor ID of LD Didactic GmbH */ -#define USB_DEVICE_ID_LD_CASSY 0x1000 /* USB Product ID of CASSY-S */ +#define USB_DEVICE_ID_LD_CASSY 0x1000 /* USB Product ID of CASSY-S modules with 8 bytes endpoint size */ +#define USB_DEVICE_ID_LD_CASSY2 0x1001 /* USB Product ID of CASSY-S modules with 64 bytes endpoint size */ #define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 /* USB Product ID of Pocket-CASSY */ +#define USB_DEVICE_ID_LD_POCKETCASSY2 0x1011 /* USB Product ID of Pocket-CASSY 2 (reserved) */ #define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 /* USB Product ID of Mobile-CASSY */ +#define USB_DEVICE_ID_LD_MOBILECASSY2 0x1021 /* USB Product ID of Mobile-CASSY 2 (reserved) */ +#define USB_DEVICE_ID_LD_MICROCASSYVOLTAGE 0x1031 /* USB Product ID of Micro-CASSY Voltage */ +#define USB_DEVICE_ID_LD_MICROCASSYCURRENT 0x1032 /* USB Product ID of Micro-CASSY Current */ +#define USB_DEVICE_ID_LD_MICROCASSYTIME 0x1033 /* USB Product ID of Micro-CASSY Time (reserved) */ +#define USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE 0x1035 /* USB Product ID of Micro-CASSY Temperature */ +#define USB_DEVICE_ID_LD_MICROCASSYPH 0x1038 /* USB Product ID of Micro-CASSY pH */ #define USB_DEVICE_ID_LD_JWM 0x1080 /* USB Product ID of Joule and Wattmeter */ #define USB_DEVICE_ID_LD_DMMP 0x1081 /* USB Product ID of Digital Multimeter P (reserved) */ #define USB_DEVICE_ID_LD_UMIP 0x1090 /* USB Product ID of UMI P */ -#define USB_DEVICE_ID_LD_XRAY1 0x1100 /* USB Product ID of X-Ray Apparatus */ -#define USB_DEVICE_ID_LD_XRAY2 0x1101 /* USB Product ID of X-Ray Apparatus */ +#define USB_DEVICE_ID_LD_UMIC 0x10A0 /* USB Product ID of UMI C */ +#define USB_DEVICE_ID_LD_UMIB 0x10B0 /* USB Product ID of UMI B */ +#define USB_DEVICE_ID_LD_XRAY 0x1100 /* USB Product ID of X-Ray Apparatus 55481 */ +#define USB_DEVICE_ID_LD_XRAY2 0x1101 /* USB Product ID of X-Ray Apparatus 554800 */ +#define USB_DEVICE_ID_LD_XRAYCT 0x1110 /* USB Product ID of X-Ray Apparatus CT 554821*/ #define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 /* USB Product ID of VideoCom */ +#define USB_DEVICE_ID_LD_MOTOR 0x1210 /* USB Product ID of Motor (reserved) */ #define USB_DEVICE_ID_LD_COM3LAB 0x2000 /* USB Product ID of COM3LAB */ #define USB_DEVICE_ID_LD_TELEPORT 0x2010 /* USB Product ID of Terminal Adapter */ #define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 /* USB Product ID of Network Analyser */ #define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 /* USB Product ID of Converter Control Unit */ #define USB_DEVICE_ID_LD_MACHINETEST 0x2040 /* USB Product ID of Machine Test System */ +#define USB_DEVICE_ID_LD_MOSTANALYSER 0x2050 /* USB Product ID of MOST Protocol Analyser */ +#define USB_DEVICE_ID_LD_MOSTANALYSER2 0x2051 /* USB Product ID of MOST Protocol Analyser 2 */ +#define USB_DEVICE_ID_LD_ABSESP 0x2060 /* USB Product ID of ABS ESP */ +#define USB_DEVICE_ID_LD_AUTODATABUS 0x2070 /* USB Product ID of Automotive Data Buses */ +#define USB_DEVICE_ID_LD_MCT 0x2080 /* USB Product ID of Microcontroller technique */ +#define USB_DEVICE_ID_LD_HYBRID 0x2090 /* USB Product ID of Automotive Hybrid */ +#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0 /* USB Product ID of Heat control */ #define USB_VENDOR_ID_VERNIER 0x08f7 #define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 @@ -71,19 +85,37 @@ /* table of devices that work with this driver */ static const struct usb_device_id ld_usb_table[] = { { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY2) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY2) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY2) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYVOLTAGE) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYCURRENT) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTIME) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYPH) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) }, - { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIC) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIB) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOTOR) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER2) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_ABSESP) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_AUTODATABUS) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MCT) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HYBRID) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HEATCONTROL) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) }, @@ -91,7 +123,7 @@ static const struct usb_device_id ld_usb_table[] = { { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, ld_usb_table); -MODULE_VERSION("V0.13"); +MODULE_VERSION("V0.14"); MODULE_AUTHOR("Michael Hund "); MODULE_DESCRIPTION("LD USB Driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 2887ba9e3e478522a4a3f1f64506fe1e8b0c740c Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Tue, 3 May 2011 12:46:46 +0400 Subject: USB: gpio-vbus: ask for vbus_draw regulator before registering xceiv Ask for vbus_draw regulator before registering tranceiver to disallow possible race between registration and set_power/etc. Signed-off-by: Dmitry Eremin-Solenikov Cc: Eric Miao Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/gpio_vbus.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index 221c44444ec..52733d9959b 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c @@ -279,6 +279,13 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) } INIT_WORK(&gpio_vbus->work, gpio_vbus_work); + gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); + if (IS_ERR(gpio_vbus->vbus_draw)) { + dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", + PTR_ERR(gpio_vbus->vbus_draw)); + gpio_vbus->vbus_draw = NULL; + } + /* only active when a gadget is registered */ err = otg_set_transceiver(&gpio_vbus->otg); if (err) { @@ -287,13 +294,6 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) goto err_otg; } - gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); - if (IS_ERR(gpio_vbus->vbus_draw)) { - dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", - PTR_ERR(gpio_vbus->vbus_draw)); - gpio_vbus->vbus_draw = NULL; - } - return 0; err_otg: free_irq(irq, &pdev->dev); -- cgit v1.2.3 From ef90748216d80f4afc95657925873a6fc3d3d6e2 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Mon, 2 May 2011 11:56:27 +0530 Subject: USB: gadget: Fix typo (s/EBUSY/-EBUSY) in ci13xxx_udc Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ci13xxx_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index e09178bc145..348d8a0e345 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -1864,7 +1864,7 @@ __acquires(mEp->lock) } } - if (retval == EBUSY) + if (retval == -EBUSY) retval = 0; if (retval < 0) dbg_event(_usb_addr(mEp), "DONE", retval); -- cgit v1.2.3 From 8c2387a71ccbae699cfdc315382afc9a89b01b2d Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Mon, 2 May 2011 11:56:28 +0530 Subject: USB: gadget: Use bitwise AND operator to test flags in ci13xxx_udc Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ci13xxx_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 348d8a0e345..03dff95348a 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -310,7 +310,7 @@ static int hw_device_reset(struct ci13xxx *udc) udc->udc_driver->notify_event(udc, CI13XXX_CONTROLLER_RESET_EVENT); - if (udc->udc_driver->flags && CI13XXX_DISABLE_STREAMING) + if (udc->udc_driver->flags & CI13XXX_DISABLE_STREAMING) hw_cwrite(CAP_USBMODE, USBMODE_SDIS, USBMODE_SDIS); /* USBMODE should be configured step by step */ -- cgit v1.2.3 From 986b11b8c7ac3d8752790654637bd944ea18ee79 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Mon, 2 May 2011 11:56:29 +0530 Subject: USB: gadget: Fix unused variable warning in ci13xxx_udc Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ci13xxx_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 03dff95348a..92498ae5878 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -1843,7 +1843,7 @@ __releases(mEp->lock) __acquires(mEp->lock) { struct ci13xxx_req *mReq, *mReqTemp; - int retval; + int uninitialized_var(retval); trace("%p", mEp); -- cgit v1.2.3 From 4c5212b7688955075b166def5ce08b34beb87a9c Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Mon, 2 May 2011 11:56:30 +0530 Subject: USB: gadget: Fix bug in endpoint feature request processing in ci13xxx_udc The OUT endpoints are stored in 0 - hw_ep_max/2 and IN endpoints are stored from hw_ep_max/2 - hw_ep_max in ci13xxx_ep array. Retrieve the IN endpoint correctly while processing endpoint feature requests. Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ci13xxx_udc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 92498ae5878..1b095cb59c9 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -1894,7 +1894,7 @@ __acquires(udc->lock) for (i = 0; i < hw_ep_max; i++) { struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; - int type, num, err = -EINVAL; + int type, num, dir, err = -EINVAL; struct usb_ctrlrequest req; if (mEp->desc == NULL) @@ -1952,7 +1952,10 @@ __acquires(udc->lock) if (req.wLength != 0) break; num = le16_to_cpu(req.wIndex); + dir = num & USB_ENDPOINT_DIR_MASK; num &= USB_ENDPOINT_NUMBER_MASK; + if (dir) /* TX */ + num += hw_ep_max/2; if (!udc->ci13xxx_ep[num].wedge) { spin_unlock(udc->lock); err = usb_ep_clear_halt( @@ -2001,7 +2004,10 @@ __acquires(udc->lock) if (req.wLength != 0) break; num = le16_to_cpu(req.wIndex); + dir = num & USB_ENDPOINT_DIR_MASK; num &= USB_ENDPOINT_NUMBER_MASK; + if (dir) /* TX */ + num += hw_ep_max/2; spin_unlock(udc->lock); err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep); -- cgit v1.2.3 From 76cd9cfb2e022d19bfc008a6e993e1e407034241 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Mon, 2 May 2011 11:56:31 +0530 Subject: USB: gadget: Use ep0out for control OUT data phase in ci13xxx_udc The current code queue the control OUT data request to ep0in instead of ep0out. Check ep0_dir and use the correct control endpoint. Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ci13xxx_udc.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 1b095cb59c9..09c76a17a10 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -1843,6 +1843,7 @@ __releases(mEp->lock) __acquires(mEp->lock) { struct ci13xxx_req *mReq, *mReqTemp; + struct ci13xxx_ep *mEpTemp = mEp; int uninitialized_var(retval); trace("%p", mEp); @@ -1859,7 +1860,10 @@ __acquires(mEp->lock) dbg_done(_usb_addr(mEp), mReq->ptr->token, retval); if (mReq->req.complete != NULL) { spin_unlock(mEp->lock); - mReq->req.complete(&mEp->ep, &mReq->req); + if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) && + mReq->req.length) + mEpTemp = &_udc->ep0in; + mReq->req.complete(&mEpTemp->ep, &mReq->req); spin_lock(mEp->lock); } } @@ -2248,11 +2252,15 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, spin_lock_irqsave(mEp->lock, flags); - if (mEp->type == USB_ENDPOINT_XFER_CONTROL && - !list_empty(&mEp->qh.queue)) { - _ep_nuke(mEp); - retval = -EOVERFLOW; - warn("endpoint ctrl %X nuked", _usb_addr(mEp)); + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { + if (req->length) + mEp = (_udc->ep0_dir == RX) ? + &_udc->ep0out : &_udc->ep0in; + if (!list_empty(&mEp->qh.queue)) { + _ep_nuke(mEp); + retval = -EOVERFLOW; + warn("endpoint ctrl %X nuked", _usb_addr(mEp)); + } } /* first nuke then test link, e.g. previous status has not sent */ -- cgit v1.2.3 From ac1aa6a21747d02a93b1becb4ab19f1fc9de2beb Mon Sep 17 00:00:00 2001 From: Anji jonnala Date: Mon, 2 May 2011 11:56:32 +0530 Subject: USB: gadget: Initialize ep0 once while registering gadget in ci13xxx_udc Some of the simulators may cache the ep0 maxpacket size to zero if the ep0 dQh is not setup before enabling the pullup. Hence Setup ep0 and initialize the dQh fields once while registering the gadget(before enabling the pullup). HSUSB Chipidea link controller spec says ep0 is enabled always in the HW. Hence disabling and enabling the ep0 as a part of reset interrupt is unneccesary. Remove the disable/enable ep0 logic from reset interrupt handling. Signed-off-by: Anji jonnala Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ci13xxx_udc.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 09c76a17a10..68123ee0139 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -1634,8 +1634,6 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) gadget_for_each_ep(ep, gadget) { usb_ep_disable(ep); } - usb_ep_disable(&udc->ep0out.ep); - usb_ep_disable(&udc->ep0in.ep); if (udc->status != NULL) { usb_ep_free_request(&udc->ep0in.ep, udc->status); @@ -1678,18 +1676,10 @@ __acquires(udc->lock) if (retval) goto done; - retval = usb_ep_enable(&udc->ep0out.ep, &ctrl_endpt_out_desc); - if (retval) - goto done; + udc->status = usb_ep_alloc_request(&udc->ep0in.ep, GFP_ATOMIC); + if (udc->status == NULL) + retval = -ENOMEM; - retval = usb_ep_enable(&udc->ep0in.ep, &ctrl_endpt_in_desc); - if (!retval) { - udc->status = usb_ep_alloc_request(&udc->ep0in.ep, GFP_ATOMIC); - if (udc->status == NULL) { - usb_ep_disable(&udc->ep0out.ep); - retval = -ENOMEM; - } - } spin_lock(udc->lock); done: @@ -2120,7 +2110,12 @@ static int ep_enable(struct usb_ep *ep, (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT; mEp->qh.ptr->td.next |= TD_TERMINATE; /* needed? */ - retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type); + /* + * Enable endpoints in the HW other than ep0 as ep0 + * is always enabled + */ + if (mEp->num) + retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type); spin_unlock_irqrestore(mEp->lock, flags); return retval; @@ -2609,6 +2604,14 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, } if (retval) goto done; + spin_unlock_irqrestore(udc->lock, flags); + retval = usb_ep_enable(&udc->ep0out.ep, &ctrl_endpt_out_desc); + if (retval) + return retval; + retval = usb_ep_enable(&udc->ep0in.ep, &ctrl_endpt_in_desc); + if (retval) + return retval; + spin_lock_irqsave(udc->lock, flags); udc->gadget.ep0 = &udc->ep0in.ep; /* bind gadget */ -- cgit v1.2.3 From 2ce2c3ac8852cfc8f74f4b7b9a2c4cdff007f96a Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Mon, 2 May 2011 11:56:33 +0530 Subject: USB: OTG: msm: Clear in_lpm flag before enabling the IRQ in resume The current code is clearing in_lpm flag after enabling the IRQ. If IRQ comes immediately before in_lpm flag is set, it thinks that hardware is in low power mode and disables the IRQ. Fix this by clearing in_lpm flag before enabling the IRQ. Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/msm_otg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 296598628b8..f58b7dab75a 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -389,14 +389,14 @@ skip_phy_resume: if (bus) set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); + atomic_set(&motg->in_lpm, 0); + if (motg->async_int) { motg->async_int = 0; pm_runtime_put(otg->dev); enable_irq(motg->irq); } - atomic_set(&motg->in_lpm, 0); - dev_info(otg->dev, "USB exited from low power mode\n"); return 0; -- cgit v1.2.3 From c430131a02d677aa708f56342c1565edfdacb3c0 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Tue, 3 May 2011 20:11:57 +0200 Subject: USB: EHCI: Support controllers with big endian capability regs The two first HC capability registers (CAPLENGTH and HCIVERSION) are defined as one 8-bit and one 16-bit register. Most HC implementations have selected to treat these registers as part of a 32-bit register, giving the same layout for both big and small endian systems. This patch adds a new quirk, big_endian_capbase, to support controllers with big endian register interfaces that treat HCIVERSION and CAPLENGTH as individual registers. Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/early/ehci-dbgp.c | 5 ++++- drivers/usb/host/ehci-ath79.c | 8 ++++---- drivers/usb/host/ehci-atmel.c | 2 +- drivers/usb/host/ehci-au1xxx.c | 3 ++- drivers/usb/host/ehci-cns3xxx.c | 2 +- drivers/usb/host/ehci-dbg.c | 2 +- drivers/usb/host/ehci-fsl.c | 2 +- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/ehci-ixp4xx.c | 2 +- drivers/usb/host/ehci-msm.c | 2 +- drivers/usb/host/ehci-mxc.c | 2 +- drivers/usb/host/ehci-octeon.c | 2 +- drivers/usb/host/ehci-omap.c | 2 +- drivers/usb/host/ehci-orion.c | 2 +- drivers/usb/host/ehci-pci.c | 2 +- drivers/usb/host/ehci-pmcmsp.c | 2 +- drivers/usb/host/ehci-ppc-of.c | 2 +- drivers/usb/host/ehci-ps3.c | 2 +- drivers/usb/host/ehci-s5p.c | 3 ++- drivers/usb/host/ehci-sh.c | 2 +- drivers/usb/host/ehci-spear.c | 2 +- drivers/usb/host/ehci-tegra.c | 2 +- drivers/usb/host/ehci-vt8500.c | 3 ++- drivers/usb/host/ehci-w90x900.c | 2 +- drivers/usb/host/ehci-xilinx-of.c | 2 +- drivers/usb/host/ehci.h | 7 +++++++ include/linux/usb/ehci_def.h | 9 +++++++-- 27 files changed, 48 insertions(+), 30 deletions(-) diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c index a6a350f5827..1fc8f124980 100644 --- a/drivers/usb/early/ehci-dbgp.c +++ b/drivers/usb/early/ehci-dbgp.c @@ -102,6 +102,9 @@ static struct kgdb_io kgdbdbgp_io_ops; #define dbgp_kgdb_mode (0) #endif +/* Local version of HC_LENGTH macro as ehci struct is not available here */ +#define EARLY_HC_LENGTH(p) (0x00ff & (p)) /* bits 7 : 0 */ + /* * USB Packet IDs (PIDs) */ @@ -892,7 +895,7 @@ int __init early_dbgp_init(char *s) dbgp_printk("ehci_bar: %p\n", ehci_bar); ehci_caps = ehci_bar; - ehci_regs = ehci_bar + HC_LENGTH(readl(&ehci_caps->hc_capbase)); + ehci_regs = ehci_bar + EARLY_HC_LENGTH(readl(&ehci_caps->hc_capbase)); ehci_debug = ehci_bar + offset; ehci_dev.bus = bus; ehci_dev.slot = slot; diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c index 7ea23b50f5d..98cc8a13169 100644 --- a/drivers/usb/host/ehci-ath79.c +++ b/drivers/usb/host/ehci-ath79.c @@ -44,6 +44,7 @@ static int ehci_ath79_init(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct platform_device *pdev = to_platform_device(hcd->self.controller); const struct platform_device_id *id; + int hclength; int ret; id = platform_get_device_id(pdev); @@ -52,21 +53,20 @@ static int ehci_ath79_init(struct usb_hcd *hcd) return -EINVAL; } + hclength = HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); switch (id->driver_data) { case EHCI_ATH79_IP_V1: ehci->has_synopsys_hc_bug = 1; ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + ehci->regs = hcd->regs + hclength; break; case EHCI_ATH79_IP_V2: hcd->has_tt = 1; ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + ehci->regs = hcd->regs + 0x100 + hclength; break; default: diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index b2ed55cb811..a5a3ef1f009 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -56,7 +56,7 @@ static int ehci_atmel_setup(struct usb_hcd *hcd) /* registers start at offset 0x0 */ ehci->caps = hcd->regs; ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index a869e3c103d..40b002869ac 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -175,7 +175,8 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); + ehci->regs = hcd->regs + + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = readl(&ehci->caps->hcs_params); diff --git a/drivers/usb/host/ehci-cns3xxx.c b/drivers/usb/host/ehci-cns3xxx.c index 708a05b5d25..d41745c6f0c 100644 --- a/drivers/usb/host/ehci-cns3xxx.c +++ b/drivers/usb/host/ehci-cns3xxx.c @@ -34,7 +34,7 @@ static int cns3xxx_ehci_init(struct usb_hcd *hcd) ehci->caps = hcd->regs; ehci->regs = hcd->regs - + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 0; diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 693c29b3052..40a844c1dbb 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -726,7 +726,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) } /* Capability Registers */ - i = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); + i = HC_VERSION(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); temp = scnprintf (next, size, "bus %s, device %s\n" "%s\n" diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 623732a312d..f380bf97e5a 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -324,7 +324,7 @@ static int ehci_fsl_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 83b7d5f02a1..8164ffafd10 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -739,7 +739,7 @@ static int ehci_run (struct usb_hcd *hcd) up_write(&ehci_cf_port_reset_rwsem); ehci->last_periodic_enable = ktime_get_real(); - temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); + temp = HC_VERSION(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci_info (ehci, "USB %x.%x started, EHCI %x.%02x%s\n", ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f), diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c index 89b7c70c6ed..50e600d26e2 100644 --- a/drivers/usb/host/ehci-ixp4xx.c +++ b/drivers/usb/host/ehci-ixp4xx.c @@ -23,7 +23,7 @@ static int ixp4xx_ehci_init(struct usb_hcd *hcd) ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 - + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 9ce1b0bc186..b5a0bf649c9 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -41,7 +41,7 @@ static int ehci_msm_reset(struct usb_hcd *hcd) ehci->caps = USB_CAPLENGTH; ehci->regs = USB_CAPLENGTH + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 25c8c10bb68..0c058be35a3 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -208,7 +208,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); /* set up the PORTSCx register */ ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]); diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index a31a031178a..ff55757ba7d 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -151,7 +151,7 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev) ehci->caps = hcd->regs; ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 7e41a95c5ce..3c482dc99ec 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -188,7 +188,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) /* we know this is the memory we want, no need to ioremap again */ omap_ehci->caps = hcd->regs; omap_ehci->regs = hcd->regs - + HC_LENGTH(readl(&omap_ehci->caps->hc_capbase)); + + HC_LENGTH(ehci, readl(&omap_ehci->caps->hc_capbase)); dbg_hcs_params(omap_ehci, "reset"); dbg_hcc_params(omap_ehci, "reset"); diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 281e094e1c1..395bdb0248d 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -251,7 +251,7 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; ehci->sbrn = 0x20; diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index d5eaea7caf8..660b80a75ca 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -70,7 +70,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ehci->caps = hcd->regs; ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c index a2168642175..cd69099cda1 100644 --- a/drivers/usb/host/ehci-pmcmsp.c +++ b/drivers/usb/host/ehci-pmcmsp.c @@ -83,7 +83,7 @@ static int ehci_msp_setup(struct usb_hcd *hcd) ehci->caps = hcd->regs; ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 1f09f253697..8552db6c29c 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -179,7 +179,7 @@ static int __devinit ehci_hcd_ppc_of_probe(struct platform_device *op) ehci->caps = hcd->regs; ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 1dee33b9139..64626a777d6 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -29,7 +29,7 @@ static int ps3_ehci_hc_reset(struct usb_hcd *hcd) ehci->big_endian_mmio = 1; ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, + ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 0c18f280bf4..321a03301ad 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -126,7 +126,8 @@ static int s5p_ehci_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); + ehci->regs = hcd->regs + + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 595f70f42b5..86a95bb80a6 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -23,7 +23,7 @@ static int ehci_sh_reset(struct usb_hcd *hcd) int ret; ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, + ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 75c00873443..dbf1e4ef3c1 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -38,7 +38,7 @@ static int ehci_spear_setup(struct usb_hcd *hcd) /* registers start at offset 0x0 */ ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, + ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 7359bcbe417..02b2bfd49a1 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -400,7 +400,7 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(readl(&ehci->caps->hc_capbase)); + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c index 20168062035..47d749631bc 100644 --- a/drivers/usb/host/ehci-vt8500.c +++ b/drivers/usb/host/ehci-vt8500.c @@ -121,7 +121,8 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); + ehci->regs = hcd->regs + + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index 6bc35809a5c..52a027aaa37 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -57,7 +57,7 @@ static int __devinit usb_w90x900_probe(const struct hc_driver *driver, ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); /* enable PHY 0,1,the regs only apply to w90p910 * 0xA4,0xA8 were offsets of PHY0 and PHY1 controller of diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index effc58d7af8..a64d6d66d76 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -220,7 +220,7 @@ static int __devinit ehci_hcd_xilinx_of_probe(struct platform_device *op) */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index e9ba8e25248..d0792f59159 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -128,6 +128,7 @@ struct ehci_hcd { /* one per controller */ unsigned has_fsl_port_bug:1; /* FreeScale */ unsigned big_endian_mmio:1; unsigned big_endian_desc:1; + unsigned big_endian_capbase:1; unsigned has_amcc_usb23:1; unsigned need_io_watchdog:1; unsigned broken_periodic:1; @@ -605,12 +606,18 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) * This attempts to support either format at compile time without a * runtime penalty, or both formats with the additional overhead * of checking a flag bit. + * + * ehci_big_endian_capbase is a special quirk for controllers that + * implement the HC capability registers as separate registers and not + * as fields of a 32-bit register. */ #ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO #define ehci_big_endian_mmio(e) ((e)->big_endian_mmio) +#define ehci_big_endian_capbase(e) ((e)->big_endian_capbase) #else #define ehci_big_endian_mmio(e) 0 +#define ehci_big_endian_capbase(e) 0 #endif /* diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 78799432008..7cc95ee3606 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -25,10 +25,15 @@ struct ehci_caps { /* these fields are specified as 8 and 16 bit registers, * but some hosts can't perform 8 or 16 bit PCI accesses. + * some hosts treat caplength and hciversion as parts of a 32-bit + * register, others treat them as two separate registers, this + * affects the memory map for big endian controllers. */ u32 hc_capbase; -#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ -#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ +#define HC_LENGTH(ehci, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \ + (ehci_big_endian_capbase(ehci) ? 24 : 0))) +#define HC_VERSION(ehci, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \ + (ehci_big_endian_capbase(ehci) ? 0 : 16))) u32 hcs_params; /* HCSPARAMS - offset 0x4 */ #define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ #define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ -- cgit v1.2.3 From 9be0392989306361d4a63a06a8ee281efbead548 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Tue, 3 May 2011 20:11:58 +0200 Subject: USB: EHCI: Add bus glue for GRLIB GRUSBHC controller This patch adds support for the GRLIB GRUSBHC EHCI controller from Aeroflex Gaisler. The controller is typically found on LEON/GRLIB SoCs. Tested on GR-LEON4-ITX with with little endian interface and on LEON3 system on GR-PCI-XC5V development board for big endian controller. Signed-off-by: Jan Andersson Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 1 + drivers/usb/host/Kconfig | 4 +- drivers/usb/host/ehci-grlib.c | 242 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci-hcd.c | 5 + drivers/usb/host/ehci.h | 3 + 5 files changed, 253 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/host/ehci-grlib.c diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index a1b3750cadb..48f1781352f 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -68,6 +68,7 @@ config USB_ARCH_HAS_EHCI default y if PLAT_S5P default y if ARCH_MSM default y if MICROBLAZE + default y if SPARC_LEON default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index fe4beca0000..7dd4c44fabb 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -106,13 +106,13 @@ config USB_EHCI_BIG_ENDIAN_MMIO depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || \ ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ PPC_MPC512x || CPU_CAVIUM_OCTEON || \ - PMC_MSP) + PMC_MSP || SPARC_LEON) default y config USB_EHCI_BIG_ENDIAN_DESC bool depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ - PPC_MPC512x || PMC_MSP) + PPC_MPC512x || PMC_MSP || SPARC_LEON) default y config XPS_USB_HCD_XILINX diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c new file mode 100644 index 00000000000..93b230dc51a --- /dev/null +++ b/drivers/usb/host/ehci-grlib.c @@ -0,0 +1,242 @@ +/* + * Driver for Aeroflex Gaisler GRLIB GRUSBHC EHCI host controller + * + * GRUSBHC is typically found on LEON/GRLIB SoCs + * + * (c) Jan Andersson + * + * Based on ehci-ppc-of.c which is: + * (c) Valentine Barshak + * and in turn based on "ehci-ppc-soc.c" by Stefan Roese + * and "ohci-ppc-of.c" by Sylvain Munaut + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include + +#include +#include +#include + +#define GRUSBHC_HCIVERSION 0x0100 /* Known value of cap. reg. HCIVERSION */ + +/* called during probe() after chip reset completes */ +static int ehci_grlib_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + retval = ehci_halt(ehci); + if (retval) + return retval; + + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci->sbrn = 0x20; + ehci_port_power(ehci, 1); + + return ehci_reset(ehci); +} + + +static const struct hc_driver ehci_grlib_hc_driver = { + .description = hcd_name, + .product_desc = "GRLIB GRUSBHC EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_grlib_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + + +static int __devinit ehci_hcd_grlib_probe(struct platform_device *op) +{ + struct device_node *dn = op->dev.of_node; + struct usb_hcd *hcd; + struct ehci_hcd *ehci = NULL; + struct resource res; + u32 hc_capbase; + int irq; + int rv; + + if (usb_disabled()) + return -ENODEV; + + dev_dbg(&op->dev, "initializing GRUSBHC EHCI USB Controller\n"); + + rv = of_address_to_resource(dn, 0, &res); + if (rv) + return rv; + + /* usb_create_hcd requires dma_mask != NULL */ + op->dev.dma_mask = &op->dev.coherent_dma_mask; + hcd = usb_create_hcd(&ehci_grlib_hc_driver, &op->dev, + "GRUSBHC EHCI USB"); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = res.start; + hcd->rsrc_len = res.end - res.start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); + rv = -EBUSY; + goto err_rmr; + } + + irq = irq_of_parse_and_map(dn, 0); + if (irq == NO_IRQ) { + printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); + rv = -EBUSY; + goto err_irq; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + printk(KERN_ERR "%s: ioremap failed\n", __FILE__); + rv = -ENOMEM; + goto err_ioremap; + } + + ehci = hcd_to_ehci(hcd); + + ehci->caps = hcd->regs; + + /* determine endianness of this implementation */ + hc_capbase = ehci_readl(ehci, &ehci->caps->hc_capbase); + if (HC_VERSION(ehci, hc_capbase) != GRUSBHC_HCIVERSION) { + ehci->big_endian_mmio = 1; + ehci->big_endian_desc = 1; + ehci->big_endian_capbase = 1; + } + + ehci->regs = hcd->regs + + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + rv = usb_add_hcd(hcd, irq, 0); + if (rv) + goto err_ehci; + + return 0; + +err_ehci: + iounmap(hcd->regs); +err_ioremap: + irq_dispose_mapping(irq); +err_irq: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_rmr: + usb_put_hcd(hcd); + + return rv; +} + + +static int ehci_hcd_grlib_remove(struct platform_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + + dev_set_drvdata(&op->dev, NULL); + + dev_dbg(&op->dev, "stopping GRLIB GRUSBHC EHCI USB Controller\n"); + + usb_remove_hcd(hcd); + + iounmap(hcd->regs); + irq_dispose_mapping(hcd->irq); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + usb_put_hcd(hcd); + + return 0; +} + + +static void ehci_hcd_grlib_shutdown(struct platform_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + + +static const struct of_device_id ehci_hcd_grlib_of_match[] = { + { + .name = "GAISLER_EHCI", + }, + { + .name = "01_026", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ehci_hcd_grlib_of_match); + + +static struct platform_driver ehci_grlib_driver = { + .probe = ehci_hcd_grlib_probe, + .remove = ehci_hcd_grlib_remove, + .shutdown = ehci_hcd_grlib_shutdown, + .driver = { + .name = "grlib-ehci", + .owner = THIS_MODULE, + .of_match_table = ehci_hcd_grlib_of_match, + }, +}; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8164ffafd10..c5719cd258c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1275,6 +1275,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_ath79_driver #endif +#ifdef CONFIG_SPARC_LEON +#include "ehci-grlib.c" +#define PLATFORM_DRIVER ehci_grlib_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index d0792f59159..829213423de 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -627,6 +627,9 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) #if defined(CONFIG_ARM) && defined(CONFIG_ARCH_IXP4XX) #define readl_be(addr) __raw_readl((__force unsigned *)addr) #define writel_be(val, addr) __raw_writel(val, (__force unsigned *)addr) +#elif defined(CONFIG_SPARC_LEON) +#define readl_be(addr) __raw_readl(addr) +#define writel_be(val, addr) __raw_writel(val, addr) #endif static inline unsigned int ehci_readl(const struct ehci_hcd *ehci, -- cgit v1.2.3 From 3df004532582d0cc721da0df28311bcedd639724 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Thu, 5 May 2011 12:11:21 +0200 Subject: usb: fix building musb drivers Commit 3dacdf11 "usb: factor out state_string() on otg drivers" broke building musb drivers since there is already another otg_state_string() function in musb drivers, but with different prototype. Fix musb drivers to use common otg_state_string(), too. Also provide a nop for otg_state_string() if CONFIG_USB_OTG_UTILS is not defined. Signed-off-by: Anatolij Gustschin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/am35x.c | 11 ++++--- drivers/usb/musb/blackfin.c | 7 +++-- drivers/usb/musb/da8xx.c | 11 ++++--- drivers/usb/musb/davinci.c | 5 ++-- drivers/usb/musb/musb_core.c | 65 ++++++++++++++++------------------------- drivers/usb/musb/musb_debug.h | 2 -- drivers/usb/musb/musb_gadget.c | 9 +++--- drivers/usb/musb/musb_host.c | 2 +- drivers/usb/musb/musb_virthub.c | 5 ++-- drivers/usb/musb/omap2430.c | 7 +++-- drivers/usb/musb/tusb6010.c | 16 +++++----- include/linux/usb/otg.h | 7 ++++- 12 files changed, 74 insertions(+), 73 deletions(-) diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index d5a3da37c90..9e6209f87d3 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -151,7 +151,8 @@ static void otg_timer(unsigned long _musb) * status change events (from the transceiver) otherwise. */ devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(7, "Poll devctl %02x (%s)\n", devctl, otg_state_string(musb)); + DBG(7, "Poll devctl %02x (%s)\n", devctl, + otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -202,7 +203,8 @@ static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { - DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); + DBG(4, "%s active, deleting timer\n", + otg_state_string(musb->xceiv->state)); del_timer(&otg_workaround); last_timer = jiffies; return; @@ -215,7 +217,8 @@ static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout) last_timer = timeout; DBG(4, "%s inactive, starting idle timer for %u ms\n", - otg_state_string(musb), jiffies_to_msecs(timeout - jiffies)); + otg_state_string(musb->xceiv->state), + jiffies_to_msecs(timeout - jiffies)); mod_timer(&otg_workaround, timeout); } @@ -304,7 +307,7 @@ static irqreturn_t am35x_musb_interrupt(int irq, void *hci) /* NOTE: this must complete power-on within 100 ms. */ DBG(2, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 8e2a1ff8a35..e141d89656e 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -279,12 +279,13 @@ static void musb_conn_timer_handler(unsigned long _musb) } break; default: - DBG(1, "%s state not handled\n", otg_state_string(musb)); + DBG(1, "%s state not handled\n", + otg_state_string(musb->xceiv->state)); break; } spin_unlock_irqrestore(&musb->lock, flags); - DBG(4, "state is %s\n", otg_state_string(musb)); + DBG(4, "state is %s\n", otg_state_string(musb->xceiv->state)); } static void bfin_musb_enable(struct musb *musb) @@ -308,7 +309,7 @@ static void bfin_musb_set_vbus(struct musb *musb, int is_on) DBG(1, "VBUS %s, devctl %02x " /* otg %3x conf %08x prcm %08x */ "\n", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL)); } diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 69a0da3c8f0..0d8984993a3 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -199,7 +199,8 @@ static void otg_timer(unsigned long _musb) * status change events (from the transceiver) otherwise. */ devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(7, "Poll devctl %02x (%s)\n", devctl, otg_state_string(musb)); + DBG(7, "Poll devctl %02x (%s)\n", devctl, + otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -273,7 +274,8 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { - DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); + DBG(4, "%s active, deleting timer\n", + otg_state_string(musb->xceiv->state)); del_timer(&otg_workaround); last_timer = jiffies; return; @@ -286,7 +288,8 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout) last_timer = timeout; DBG(4, "%s inactive, starting idle timer for %u ms\n", - otg_state_string(musb), jiffies_to_msecs(timeout - jiffies)); + otg_state_string(musb->xceiv->state), + jiffies_to_msecs(timeout - jiffies)); mod_timer(&otg_workaround, timeout); } @@ -365,7 +368,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) DBG(2, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index e6de097fb7e..3661b81a957 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -220,7 +220,8 @@ static void otg_timer(unsigned long _musb) * status change events (from the transceiver) otherwise. */ devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(7, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb)); + DBG(7, "poll devctl %02x (%s)\n", devctl, + otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -356,7 +357,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci) davinci_musb_source_power(musb, drvvbus, 0); DBG(2, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); retval = IRQ_HANDLED; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index f10ff00ca09..9a280872c2b 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -333,26 +333,6 @@ void musb_load_testpacket(struct musb *musb) /*-------------------------------------------------------------------------*/ -const char *otg_state_string(struct musb *musb) -{ - switch (musb->xceiv->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"; - } -} - #ifdef CONFIG_USB_MUSB_OTG /* @@ -373,12 +353,14 @@ void musb_otg_timer_func(unsigned long data) break; case OTG_STATE_A_SUSPEND: case OTG_STATE_A_WAIT_BCON: - DBG(1, "HNP: %s timeout\n", otg_state_string(musb)); + DBG(1, "HNP: %s timeout\n", + otg_state_string(musb->xceiv->state)); musb_platform_set_vbus(musb, 0); musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; break; default: - DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb)); + DBG(1, "HNP: Unhandled mode %s\n", + otg_state_string(musb->xceiv->state)); } musb->ignore_disconnect = 0; spin_unlock_irqrestore(&musb->lock, flags); @@ -393,12 +375,13 @@ void musb_hnp_stop(struct musb *musb) void __iomem *mbase = musb->mregs; u8 reg; - DBG(1, "HNP: stop from %s\n", otg_state_string(musb)); + DBG(1, "HNP: stop from %s\n", otg_state_string(musb->xceiv->state)); switch (musb->xceiv->state) { case OTG_STATE_A_PERIPHERAL: musb_g_disconnect(musb); - DBG(1, "HNP: back to %s\n", otg_state_string(musb)); + DBG(1, "HNP: back to %s\n", + otg_state_string(musb->xceiv->state)); break; case OTG_STATE_B_HOST: DBG(1, "HNP: Disabling HR\n"); @@ -412,7 +395,7 @@ void musb_hnp_stop(struct musb *musb) break; default: DBG(1, "HNP: Stopping in unknown state %s\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); } /* @@ -451,7 +434,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, */ if (int_usb & MUSB_INTR_RESUME) { handled = IRQ_HANDLED; - DBG(3, "RESUME (%s)\n", otg_state_string(musb)); + DBG(3, "RESUME (%s)\n", otg_state_string(musb->xceiv->state)); if (devctl & MUSB_DEVCTL_HM) { #ifdef CONFIG_USB_MUSB_HDRC_HCD @@ -492,7 +475,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, default: WARNING("bogus %s RESUME (%s)\n", "host", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); } #endif } else { @@ -526,7 +509,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, default: WARNING("bogus %s RESUME (%s)\n", "peripheral", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); } } } @@ -542,7 +525,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, return IRQ_HANDLED; } - DBG(1, "SESSION_REQUEST (%s)\n", otg_state_string(musb)); + DBG(1, "SESSION_REQUEST (%s)\n", + otg_state_string(musb->xceiv->state)); /* IRQ arrives from ID pin sense or (later, if VBUS power * is removed) SRP. responses are time critical: @@ -607,7 +591,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, } DBG(1, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), devctl, ({ char *s; switch (devctl & MUSB_DEVCTL_VBUS) { @@ -633,7 +617,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, #endif if (int_usb & MUSB_INTR_SUSPEND) { DBG(1, "SUSPEND (%s) devctl %02x power %02x\n", - otg_state_string(musb), devctl, power); + otg_state_string(musb->xceiv->state), devctl, power); handled = IRQ_HANDLED; switch (musb->xceiv->state) { @@ -758,13 +742,13 @@ b_host: usb_hcd_resume_root_hub(hcd); DBG(1, "CONNECT (%s) devctl %02x\n", - otg_state_string(musb), devctl); + otg_state_string(musb->xceiv->state), devctl); } #endif /* CONFIG_USB_MUSB_HDRC_HCD */ if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) { DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), MUSB_MODE(musb), devctl); handled = IRQ_HANDLED; @@ -807,7 +791,7 @@ b_host: #endif /* GADGET */ default: WARNING("unhandled DISCONNECT transition (%s)\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); break; } } @@ -832,7 +816,8 @@ b_host: musb_writeb(musb->mregs, MUSB_DEVCTL, 0); } } else if (is_peripheral_capable()) { - DBG(1, "BUS RESET as %s\n", otg_state_string(musb)); + DBG(1, "BUS RESET as %s\n", + otg_state_string(musb->xceiv->state)); switch (musb->xceiv->state) { #ifdef CONFIG_USB_OTG case OTG_STATE_A_SUSPEND: @@ -846,8 +831,8 @@ b_host: case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */ /* never use invalid T(a_wait_bcon) */ DBG(1, "HNP: in %s, %d msec timeout\n", - otg_state_string(musb), - TA_WAIT_BCON(musb)); + otg_state_string(musb->xceiv->state), + TA_WAIT_BCON(musb)); mod_timer(&musb->otg_timer, jiffies + msecs_to_jiffies(TA_WAIT_BCON(musb))); break; @@ -858,7 +843,7 @@ b_host: break; case OTG_STATE_B_WAIT_ACON: DBG(1, "HNP: RESET (%s), to b_peripheral\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb_g_reset(musb); break; @@ -871,7 +856,7 @@ b_host: break; default: DBG(1, "Unhandled BUS RESET as %s\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); } } } @@ -1713,7 +1698,7 @@ musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) int ret = -EINVAL; spin_lock_irqsave(&musb->lock, flags); - ret = sprintf(buf, "%s\n", otg_state_string(musb)); + ret = sprintf(buf, "%s\n", otg_state_string(musb->xceiv->state)); spin_unlock_irqrestore(&musb->lock, flags); return ret; diff --git a/drivers/usb/musb/musb_debug.h b/drivers/usb/musb/musb_debug.h index 94f6973cf8f..f958a49a012 100644 --- a/drivers/usb/musb/musb_debug.h +++ b/drivers/usb/musb/musb_debug.h @@ -54,8 +54,6 @@ static inline int _dbg_level(unsigned l) return musb_debug >= l; } -extern const char *otg_state_string(struct musb *); - #ifdef CONFIG_DEBUG_FS extern int musb_init_debugfs(struct musb *musb); extern void musb_exit_debugfs(struct musb *musb); diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 6dfbf9ffd7a..3d799195c8b 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1556,7 +1556,8 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) status = 0; goto done; default: - DBG(2, "Unhandled wake: %s\n", otg_state_string(musb)); + DBG(2, "Unhandled wake: %s\n", + otg_state_string(musb->xceiv->state)); goto done; } @@ -2039,7 +2040,7 @@ void musb_g_resume(struct musb *musb) break; default: WARNING("unhandled RESUME transition (%s)\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); } } @@ -2069,7 +2070,7 @@ void musb_g_suspend(struct musb *musb) * A_PERIPHERAL may need care too */ WARNING("unhandled SUSPEND transition (%s)\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); } } @@ -2104,7 +2105,7 @@ void musb_g_disconnect(struct musb *musb) default: #ifdef CONFIG_USB_MUSB_OTG DBG(2, "Unhandled disconnect %s, setting a_idle\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); break; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 5eef4a8847d..07b106c7de8 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2304,7 +2304,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd) if (musb->is_active) { WARNING("trying to suspend as %s while active\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); return -EBUSY; } else return 0; diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 489104a5ae1..bb1247d5fd0 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -98,7 +98,7 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend) #endif default: DBG(1, "bogus rh suspend? %s\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); } } else if (power & MUSB_POWER_SUSPENDM) { power &= ~MUSB_POWER_SUSPENDM; @@ -208,7 +208,8 @@ void musb_root_disconnect(struct musb *musb) musb->xceiv->state = OTG_STATE_B_IDLE; break; default: - DBG(1, "host disconnect (%s)\n", otg_state_string(musb)); + DBG(1, "host disconnect (%s)\n", + otg_state_string(musb->xceiv->state)); } } diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 57a27fa954b..d51b15adc0b 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -114,7 +114,8 @@ static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || ((musb->a_wait_bcon == 0) && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { - DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); + DBG(4, "%s active, deleting timer\n", + otg_state_string(musb->xceiv->state)); del_timer(&musb_idle_timer); last_timer = jiffies; return; @@ -131,7 +132,7 @@ static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) last_timer = timeout; DBG(4, "%s inactive, for idle timer for %lu ms\n", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), (unsigned long)jiffies_to_msecs(timeout - jiffies)); mod_timer(&musb_idle_timer, timeout); } @@ -195,7 +196,7 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on) DBG(1, "VBUS %s, devctl %02x " /* otg %3x conf %08x prcm %08x */ "\n", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL)); } diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index c47aac4a1f9..221feaaded7 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -422,7 +422,7 @@ static void musb_do_idle(unsigned long _musb) && (musb->idle_timeout == 0 || time_after(jiffies, musb->idle_timeout))) { DBG(4, "Nothing connected %s, turning off VBUS\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); } /* FALLTHROUGH */ case OTG_STATE_A_IDLE: @@ -481,7 +481,8 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || ((musb->a_wait_bcon == 0) && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { - DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); + DBG(4, "%s active, deleting timer\n", + otg_state_string(musb->xceiv->state)); del_timer(&musb_idle_timer); last_timer = jiffies; return; @@ -498,7 +499,7 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) last_timer = timeout; DBG(4, "%s inactive, for idle timer for %lu ms\n", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), (unsigned long)jiffies_to_msecs(timeout - jiffies)); mod_timer(&musb_idle_timer, timeout); } @@ -573,7 +574,7 @@ static void tusb_musb_set_vbus(struct musb *musb, int is_on) musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); DBG(1, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL), musb_readl(tbase, TUSB_DEV_OTG_STAT), conf, prcm); @@ -702,13 +703,13 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) musb->is_active = 0; } DBG(2, "vbus change, %s, otg %03x\n", - otg_state_string(musb), otg_stat); + otg_state_string(musb->xceiv->state), otg_stat); idle_timeout = jiffies + (1 * HZ); schedule_work(&musb->irq_work); } else /* A-dev state machine */ { DBG(2, "vbus change, %s, otg %03x\n", - otg_state_string(musb), otg_stat); + otg_state_string(musb->xceiv->state), otg_stat); switch (musb->xceiv->state) { case OTG_STATE_A_IDLE: @@ -756,7 +757,8 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) if (int_src & TUSB_INT_SRC_OTG_TIMEOUT) { u8 devctl; - DBG(4, "%s timer, %03x\n", otg_state_string(musb), otg_stat); + DBG(4, "%s timer, %03x\n", + otg_state_string(musb->xceiv->state), otg_stat); switch (musb->xceiv->state) { case OTG_STATE_A_WAIT_VRISE: diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index bc84858b3a4..d87f44f5b04 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -168,6 +168,7 @@ otg_shutdown(struct otg_transceiver *otg) #ifdef CONFIG_USB_OTG_UTILS extern struct otg_transceiver *otg_get_transceiver(void); extern void otg_put_transceiver(struct otg_transceiver *); +extern const char *otg_state_string(enum usb_otg_state state); #else static inline struct otg_transceiver *otg_get_transceiver(void) { @@ -177,6 +178,11 @@ static inline struct otg_transceiver *otg_get_transceiver(void) static inline void otg_put_transceiver(struct otg_transceiver *x) { } + +static inline const char *otg_state_string(enum usb_otg_state state) +{ + return NULL; +} #endif /* Context: can sleep */ @@ -246,6 +252,5 @@ otg_unregister_notifier(struct otg_transceiver *otg, struct notifier_block *nb) /* for OTG controller drivers (and maybe other stuff) */ extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num); -extern const char *otg_state_string(enum usb_otg_state state); #endif /* __LINUX_USB_OTG_H */ -- cgit v1.2.3 From a7535ac05443f234b9c68389fc053976383feca4 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Thu, 5 May 2011 16:35:24 +0200 Subject: USB: ehci-au1xxx: fix suspend callback Remove a stray 'return 0' at the top of the suspend callback, and move au1xxx_stop_ehc() out of the ehci spinlock since it takes some time to complete. Signed-off-by: Manuel Lauss Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-au1xxx.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 40b002869ac..42ae5740990 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -216,10 +216,7 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); unsigned long flags; - int rc; - - return 0; - rc = 0; + int rc = 0; if (time_before(jiffies, ehci->next_statechange)) msleep(10); @@ -234,13 +231,13 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) (void)ehci_readl(ehci, &ehci->regs->intr_enable); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - au1xxx_stop_ehc(); spin_unlock_irqrestore(&ehci->lock, flags); // could save FLADJ in case of Vaux power loss // ... we'd only use it to handle clock skew + au1xxx_stop_ehc(); + return rc; } -- cgit v1.2.3 From 7af85a85878bd1a2695408e5856aba8ef9f71b60 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 4 May 2011 16:45:47 +0900 Subject: USB: ohci-s3c2410: fix checkpatch errors and warnings This patch fixes the checkpatch errors ans warnings listed below: ERROR: do not use assignment in if condition WARNING: line over 80 characters WARNING: braces {} are not necessary for single statement blocks WARNING: space prohibited between function name and open parenthesis '(' Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-s3c2410.c | 46 ++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index 5837d218050..e6e8d98b385 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -56,9 +56,8 @@ static void s3c2410_start_hc(struct platform_device *dev, struct usb_hcd *hcd) info->hcd = hcd; info->report_oc = s3c2410_hcd_oc; - if (info->enable_oc != NULL) { + if (info->enable_oc != NULL) (info->enable_oc)(info, 1); - } } } @@ -72,9 +71,8 @@ static void s3c2410_stop_hc(struct platform_device *dev) info->report_oc = NULL; info->hcd = NULL; - if (info->enable_oc != NULL) { + if (info->enable_oc != NULL) (info->enable_oc)(info, 0); - } } clk_disable(clk); @@ -88,14 +86,14 @@ static void s3c2410_stop_hc(struct platform_device *dev) */ static int -ohci_s3c2410_hub_status_data (struct usb_hcd *hcd, char *buf) +ohci_s3c2410_hub_status_data(struct usb_hcd *hcd, char *buf) { struct s3c2410_hcd_info *info = to_s3c2410_info(hcd); struct s3c2410_hcd_port *port; int orig; int portno; - orig = ohci_hub_status_data (hcd, buf); + orig = ohci_hub_status_data(hcd, buf); if (info == NULL) return orig; @@ -145,7 +143,7 @@ static void s3c2410_usb_set_power(struct s3c2410_hcd_info *info, * request. */ -static int ohci_s3c2410_hub_control ( +static int ohci_s3c2410_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, @@ -199,9 +197,8 @@ static int ohci_s3c2410_hub_control ( dev_dbg(hcd->self.controller, "ClearPortFeature: OVER_CURRENT\n"); - if (valid_port(wIndex)) { + if (valid_port(wIndex)) info->port[wIndex-1].oc_status = 0; - } goto out; @@ -242,8 +239,11 @@ static int ohci_s3c2410_hub_control ( desc->wHubCharacteristics |= cpu_to_le16(0x0001); if (info->enable_oc) { - desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM); - desc->wHubCharacteristics |= cpu_to_le16(0x0008|0x0001); + desc->wHubCharacteristics &= ~cpu_to_le16( + HUB_CHAR_OCPM); + desc->wHubCharacteristics |= cpu_to_le16( + 0x0008 | + 0x0001); } dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n", @@ -257,13 +257,11 @@ static int ohci_s3c2410_hub_control ( dev_dbg(hcd->self.controller, "GetPortStatus(%d)\n", wIndex); if (valid_port(wIndex)) { - if (info->port[wIndex-1].oc_changed) { + if (info->port[wIndex-1].oc_changed) *data |= cpu_to_le32(RH_PS_OCIC); - } - if (info->port[wIndex-1].oc_status) { + if (info->port[wIndex-1].oc_status) *data |= cpu_to_le32(RH_PS_POCI); - } } } @@ -321,7 +319,7 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc) */ static void -usb_hcd_s3c2410_remove (struct usb_hcd *hcd, struct platform_device *dev) +usb_hcd_s3c2410_remove(struct usb_hcd *hcd, struct platform_device *dev) { usb_remove_hcd(hcd); s3c2410_stop_hc(dev); @@ -339,7 +337,7 @@ usb_hcd_s3c2410_remove (struct usb_hcd *hcd, struct platform_device *dev) * through the hotplug entry's driver_data. * */ -static int usb_hcd_s3c2410_probe (const struct hc_driver *driver, +static int usb_hcd_s3c2410_probe(const struct hc_driver *driver, struct platform_device *dev) { struct usb_hcd *hcd = NULL; @@ -411,17 +409,19 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver, /*-------------------------------------------------------------------------*/ static int -ohci_s3c2410_start (struct usb_hcd *hcd) +ohci_s3c2410_start(struct usb_hcd *hcd) { - struct ohci_hcd *ohci = hcd_to_ohci (hcd); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); int ret; - if ((ret = ohci_init(ohci)) < 0) + ret = ohci_init(ohci); + if (ret < 0) return ret; - if ((ret = ohci_run (ohci)) < 0) { - err ("can't start %s", hcd->self.bus_name); - ohci_stop (hcd); + ret = ohci_run(ohci); + if (ret < 0) { + err("can't start %s", hcd->self.bus_name); + ohci_stop(hcd); return ret; } -- cgit v1.2.3 From 69248d4281fda03dd4da982e1d51f6b22cf1a109 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 5 May 2011 08:46:07 +0900 Subject: USB: ohci-s3c2410: return proper error if clk_get fails Return PTR_ERR(clk) instead of -ENOENT if clk_get fails Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-s3c2410.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index e6e8d98b385..7c9a4d55526 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -362,14 +362,14 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver, clk = clk_get(&dev->dev, "usb-host"); if (IS_ERR(clk)) { dev_err(&dev->dev, "cannot get usb-host clock\n"); - retval = -ENOENT; + retval = PTR_ERR(clk); goto err_mem; } usb_clk = clk_get(&dev->dev, "usb-bus-host"); if (IS_ERR(usb_clk)) { dev_err(&dev->dev, "cannot get usb-bus-host clock\n"); - retval = -ENOENT; + retval = PTR_ERR(usb_clk); goto err_clk; } -- cgit v1.2.3 From ea437f39234f7f991428886f16aae5c264cffe62 Mon Sep 17 00:00:00 2001 From: Ramneek Mehresh Date: Wed, 4 May 2011 19:41:55 +0530 Subject: fsl/usb: Unused endpoint failure for USB gadget Though USB controller works without this most of the time, an issue was faced where USB was configured as printer device and it was dropping first packet(64 bytes) in full speed mode due to DATA PID mismatch. The problem gets resolved once unused endpoints are configured as bulk. As per P1020 RM (Table17-31, bits 19-18, bits 3-2) "When only one endpoint (RX or TX, but not both) of an endpoint pair is used, the unused endpoint should be configured as a bulk type endpoint." So according to the RM, this patch is initializing TX and RX endpoints as bulk type Signed-off-by: Suchit Lepcha Signed-off-by: Ramneek Mehresh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_udc_core.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 999eafe8965..2cd9a60c7f3 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2004-2007 Freescale Semicondutor, Inc. All rights reserved. + * Copyright (C) 2004-2007,2011 Freescale Semiconductor, Inc. + * All rights reserved. * * Author: Li Yang * Jiang Bo @@ -230,7 +231,8 @@ static void nuke(struct fsl_ep *ep, int status) static int dr_controller_setup(struct fsl_udc *udc) { - unsigned int tmp, portctrl; + unsigned int tmp, portctrl, ep_num; + unsigned int max_no_of_ep; #ifndef CONFIG_ARCH_MXC unsigned int ctrl; #endif @@ -298,6 +300,14 @@ static int dr_controller_setup(struct fsl_udc *udc) udc->ep_qh, (int)tmp, fsl_readl(&dr_regs->endpointlistaddr)); + max_no_of_ep = (0x0000001F & fsl_readl(&dr_regs->dccparams)); + for (ep_num = 1; ep_num < max_no_of_ep; ep_num++) { + tmp = fsl_readl(&dr_regs->endptctrl[ep_num]); + tmp &= ~(EPCTRL_TX_TYPE | EPCTRL_RX_TYPE); + tmp |= (EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT) + | (EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT); + fsl_writel(tmp, &dr_regs->endptctrl[ep_num]); + } /* Config control enable i/o output, cpu endian register */ #ifndef CONFIG_ARCH_MXC if (udc->pdata->have_sysif_regs) { @@ -391,12 +401,14 @@ static void dr_ep_setup(unsigned char ep_num, unsigned char dir, if (ep_num) tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; tmp_epctrl |= EPCTRL_TX_ENABLE; + tmp_epctrl &= ~EPCTRL_TX_TYPE; tmp_epctrl |= ((unsigned int)(ep_type) << EPCTRL_TX_EP_TYPE_SHIFT); } else { if (ep_num) tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; tmp_epctrl |= EPCTRL_RX_ENABLE; + tmp_epctrl &= ~EPCTRL_RX_TYPE; tmp_epctrl |= ((unsigned int)(ep_type) << EPCTRL_RX_EP_TYPE_SHIFT); } @@ -619,10 +631,13 @@ static int fsl_ep_disable(struct usb_ep *_ep) /* disable ep on controller */ ep_num = ep_index(ep); epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); - if (ep_is_in(ep)) - epctrl &= ~EPCTRL_TX_ENABLE; - else - epctrl &= ~EPCTRL_RX_ENABLE; + if (ep_is_in(ep)) { + epctrl &= ~(EPCTRL_TX_ENABLE | EPCTRL_TX_TYPE); + epctrl |= EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT; + } else { + epctrl &= ~(EPCTRL_RX_ENABLE | EPCTRL_TX_TYPE); + epctrl |= EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT; + } fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]); udc = (struct fsl_udc *)ep->udc; -- cgit v1.2.3 From dfeca7a8750296a7d065d45257b3cd86aadc3fb9 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Fri, 6 May 2011 12:00:12 +0200 Subject: USB: UHCI: Remove PCI dependencies from uhci-hub This patch is part of a series that extend the UHCI HCD to support non-PCI host controllers. uhci-hub.c contained two PCI vendor checks for silicon quirks. Move these checks into uhci-hcd.c and use bits in uhci_hcd structure to mark that we need to use the quirks. This patch is followed by other patches that will remove PCI dependencies from uhci-hcd.c as well. Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 11 +++++++++++ drivers/usb/host/uhci-hcd.h | 4 ++++ drivers/usb/host/uhci-hub.c | 6 ++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 83344d688ff..214851a6244 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -546,6 +546,17 @@ static int uhci_init(struct usb_hcd *hcd) } uhci->rh_numports = port; + /* Intel controllers report the OverCurrent bit active on. + * VIA controllers report it active off, so we'll adjust the + * bit value. (It's not standardized in the UHCI spec.) + */ + if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_VIA) + uhci->oc_low = 1; + + /* HP's server management chip requires a longer port reset delay. */ + if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP) + uhci->wait_for_hp = 1; + /* Kick BIOS off this hardware and reset if the controller * isn't already safely quiescent. */ diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 49bf2790f9c..f86db61cf08 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -415,6 +415,10 @@ struct uhci_hcd { struct timer_list fsbr_timer; /* For turning off FBSR */ + /* Silicon quirks */ + unsigned int oc_low:1; /* OverCurrent bit active low */ + unsigned int wait_for_hp:1; /* Wait for HP port reset */ + /* Support for port suspend/resume/reset */ unsigned long port_c_suspend; /* Bit-arrays of ports */ unsigned long resuming_ports; diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 6d59c0f77f2..75418265488 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -149,8 +149,7 @@ static void uhci_check_ports(struct uhci_hcd *uhci) /* HP's server management chip requires * a longer delay. */ - if (to_pci_dev(uhci_dev(uhci))->vendor == - PCI_VENDOR_ID_HP) + if (uhci->wait_for_hp) wait_for_HP(port_addr); /* If the port was enabled before, turning @@ -266,8 +265,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * VIA controllers report it active off, so we'll adjust the * bit value. (It's not standardized in the UHCI spec.) */ - if (to_pci_dev(hcd->self.controller)->vendor == - PCI_VENDOR_ID_VIA) + if (uhci->oc_low) status ^= USBPORTSC_OC; /* UHCI doesn't support C_RESET (always false) */ -- cgit v1.2.3 From e7652e1ebc0f5e07929067ece14ca869dad20dd6 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Fri, 6 May 2011 12:00:13 +0200 Subject: USB: UHCI: Allow dynamic assignment of bus specific functions This patch is part of a series that extend the UHCI HCD to support non-PCI controllers. This patch changes calls to uhci_reset_hc, uhci_check_and_reset_hc, configure_hc, resume_detect_interrupts_are_broken and global_suspend_mode_is_broken so that they are made through pointers in the uhci hcd struct. This will allow these functions to be replaced with bus/arch specific functions. Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 87 +++++++++++++++++++++++++++++++++++---------- drivers/usb/host/uhci-hcd.h | 10 ++++++ 2 files changed, 78 insertions(+), 19 deletions(-) diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 214851a6244..683e87e49a0 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -142,6 +142,15 @@ static void finish_reset(struct uhci_hcd *uhci) clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); } +/* + * Make sure the controller is completely inactive, unable to + * generate interrupts or do DMA. + */ +static void uhci_pci_reset_hc(struct uhci_hcd *uhci) +{ + uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); +} + /* * Last rites for a defunct/nonfunctional controller * or one we don't want to use any more. @@ -149,7 +158,7 @@ static void finish_reset(struct uhci_hcd *uhci) static void uhci_hc_died(struct uhci_hcd *uhci) { uhci_get_current_frame_number(uhci); - uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); + uhci->reset_hc(uhci); finish_reset(uhci); uhci->dead = 1; @@ -157,6 +166,18 @@ static void uhci_hc_died(struct uhci_hcd *uhci) ++uhci->frame_number; } +/* + * Initialize a controller that was newly discovered or has just been + * resumed. In either case we can't be sure of its previous state. + * + * Returns: 1 if the controller was reset, 0 otherwise. + */ +static int uhci_pci_check_and_reset_hc(struct uhci_hcd *uhci) +{ + return uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), + uhci->io_addr); +} + /* * Initialize a controller that was newly discovered or has lost power * or otherwise been reset while it was suspended. In none of these cases @@ -164,17 +185,27 @@ static void uhci_hc_died(struct uhci_hcd *uhci) */ static void check_and_reset_hc(struct uhci_hcd *uhci) { - if (uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr)) + if (uhci->check_and_reset_hc(uhci)) finish_reset(uhci); } +static void uhci_pci_configure_hc(struct uhci_hcd *uhci) +{ + struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); + + /* Enable PIRQ */ + pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT); + + /* Disable platform-specific non-PME# wakeup */ + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + pci_write_config_byte(pdev, USBRES_INTEL, 0); +} + /* * Store the basic register settings needed by the controller. */ static void configure_hc(struct uhci_hcd *uhci) { - struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); - /* Set the frame length to the default: 1 ms exactly */ outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF); @@ -185,24 +216,15 @@ static void configure_hc(struct uhci_hcd *uhci) outw(uhci->frame_number & UHCI_MAX_SOF_NUMBER, uhci->io_addr + USBFRNUM); - /* Enable PIRQ */ - pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT); - - /* Disable platform-specific non-PME# wakeup */ - if (pdev->vendor == PCI_VENDOR_ID_INTEL) - pci_write_config_byte(pdev, USBRES_INTEL, 0); + /* perform any arch/bus specific configuration */ + if (uhci->configure_hc) + uhci->configure_hc(uhci); } - -static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) +static int uhci_pci_resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) { int port; - /* If we have to ignore overcurrent events then almost by definition - * we can't depend on resume-detect interrupts. */ - if (ignore_oc) - return 1; - switch (to_pci_dev(uhci_dev(uhci))->vendor) { default: break; @@ -231,7 +253,18 @@ static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) return 0; } -static int global_suspend_mode_is_broken(struct uhci_hcd *uhci) +static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) +{ + /* If we have to ignore overcurrent events then almost by definition + * we can't depend on resume-detect interrupts. */ + if (ignore_oc) + return 1; + + return uhci->resume_detect_interrupts_are_broken ? + uhci->resume_detect_interrupts_are_broken(uhci) : 0; +} + +static int uhci_pci_global_suspend_mode_is_broken(struct uhci_hcd *uhci) { int port; const char *sys_info; @@ -253,6 +286,12 @@ static int global_suspend_mode_is_broken(struct uhci_hcd *uhci) return 0; } +static int global_suspend_mode_is_broken(struct uhci_hcd *uhci) +{ + return uhci->global_suspend_mode_is_broken ? + uhci->global_suspend_mode_is_broken(uhci) : 0; +} + static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state) __releases(uhci->lock) __acquires(uhci->lock) @@ -557,6 +596,16 @@ static int uhci_init(struct usb_hcd *hcd) if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP) uhci->wait_for_hp = 1; + /* Set up pointers to PCI-specific functions */ + uhci->reset_hc = uhci_pci_reset_hc; + uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc; + uhci->configure_hc = uhci_pci_configure_hc; + uhci->resume_detect_interrupts_are_broken = + uhci_pci_resume_detect_interrupts_are_broken; + uhci->global_suspend_mode_is_broken = + uhci_pci_global_suspend_mode_is_broken; + + /* Kick BIOS off this hardware and reset if the controller * isn't already safely quiescent. */ @@ -847,7 +896,7 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) /* Make sure resume from hibernation re-enumerates everything */ if (hibernated) { - uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); + uhci->reset_hc(uhci); finish_reset(uhci); } diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index f86db61cf08..56943795457 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -433,6 +433,16 @@ struct uhci_hcd { int total_load; /* Sum of array values */ short load[MAX_PHASE]; /* Periodic allocations */ + + /* Reset host controller */ + void (*reset_hc) (struct uhci_hcd *uhci); + int (*check_and_reset_hc) (struct uhci_hcd *uhci); + /* configure_hc should perform arch specific settings, if needed */ + void (*configure_hc) (struct uhci_hcd *uhci); + /* Check for broken resume detect interrupts */ + int (*resume_detect_interrupts_are_broken) (struct uhci_hcd *uhci); + /* Check for broken global suspend */ + int (*global_suspend_mode_is_broken) (struct uhci_hcd *uhci); }; /* Convert between a usb_hcd pointer and the corresponding uhci_hcd */ -- cgit v1.2.3 From e4d235d800c8d75750a46331298e3473c10651b2 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Fri, 6 May 2011 12:00:14 +0200 Subject: USB: UHCI: Codingstyle fixes This patch is part of a series that extend the UHCI HCD to support non-PCI host controllers. This patch fixes the following warnings from checkpatch: ERROR: switch and case should be at the same indent + switch (to_pci_dev(uhci_dev(uhci))->vendor) { + default: [...] + case PCI_VENDOR_ID_GENESYS: [...] + case PCI_VENDOR_ID_INTEL: WARNING: static char array declaration should probably be static const char + static char bad_Asus_board[] = "A7V8X"; WARNING: Use DEFINE_PCI_DEVICE_TABLE for struct pci_device_id +static const struct pci_device_id uhci_pci_ids[] = { { Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 683e87e49a0..348174fdfae 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -226,16 +226,16 @@ static int uhci_pci_resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) int port; switch (to_pci_dev(uhci_dev(uhci))->vendor) { - default: + default: break; - case PCI_VENDOR_ID_GENESYS: + case PCI_VENDOR_ID_GENESYS: /* Genesys Logic's GL880S controllers don't generate * resume-detect interrupts. */ return 1; - case PCI_VENDOR_ID_INTEL: + case PCI_VENDOR_ID_INTEL: /* Some of Intel's USB controllers have a bug that causes * resume-detect interrupts if any port has an over-current * condition. To make matters worse, some motherboards @@ -268,7 +268,7 @@ static int uhci_pci_global_suspend_mode_is_broken(struct uhci_hcd *uhci) { int port; const char *sys_info; - static char bad_Asus_board[] = "A7V8X"; + static const char bad_Asus_board[] = "A7V8X"; /* One of Asus's motherboards has a bug which causes it to * wake up immediately from suspend-to-RAM if any of the ports @@ -998,7 +998,7 @@ static const struct hc_driver uhci_driver = { .hub_control = uhci_hub_control, }; -static const struct pci_device_id uhci_pci_ids[] = { { +static DEFINE_PCI_DEVICE_TABLE(uhci_pci_ids) = { { /* handle any USB UHCI controller */ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0), .driver_data = (unsigned long) &uhci_driver, -- cgit v1.2.3 From c31a65f869f7b8a7039007411c76d7b6f9a63323 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Fri, 6 May 2011 12:00:15 +0200 Subject: USB: UHCI: Move PCI specific functions to uhci-pci.c This patch is part of a series that extend the UHCI HCD to support non-PCI controllers. This patch moves PCI specific functions to uhci-pci.c and includes this file in uhci-hcd.c. It also renames the function uhci_init to uhci_pci_init. uhci_init/uhci_pci_init is modified so that the port-detection logic is kept in a new separate function uhci_count_ports() in uhci-hcd.c. Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 326 +++++--------------------------------------- drivers/usb/host/uhci-pci.c | 301 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 333 insertions(+), 294 deletions(-) create mode 100644 drivers/usb/host/uhci-pci.c diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 348174fdfae..3d2a10563be 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -48,7 +48,6 @@ #include #include "uhci-hcd.h" -#include "pci-quirks.h" /* * Version Information @@ -142,15 +141,6 @@ static void finish_reset(struct uhci_hcd *uhci) clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); } -/* - * Make sure the controller is completely inactive, unable to - * generate interrupts or do DMA. - */ -static void uhci_pci_reset_hc(struct uhci_hcd *uhci) -{ - uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); -} - /* * Last rites for a defunct/nonfunctional controller * or one we don't want to use any more. @@ -166,18 +156,6 @@ static void uhci_hc_died(struct uhci_hcd *uhci) ++uhci->frame_number; } -/* - * Initialize a controller that was newly discovered or has just been - * resumed. In either case we can't be sure of its previous state. - * - * Returns: 1 if the controller was reset, 0 otherwise. - */ -static int uhci_pci_check_and_reset_hc(struct uhci_hcd *uhci) -{ - return uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), - uhci->io_addr); -} - /* * Initialize a controller that was newly discovered or has lost power * or otherwise been reset while it was suspended. In none of these cases @@ -189,18 +167,6 @@ static void check_and_reset_hc(struct uhci_hcd *uhci) finish_reset(uhci); } -static void uhci_pci_configure_hc(struct uhci_hcd *uhci) -{ - struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); - - /* Enable PIRQ */ - pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT); - - /* Disable platform-specific non-PME# wakeup */ - if (pdev->vendor == PCI_VENDOR_ID_INTEL) - pci_write_config_byte(pdev, USBRES_INTEL, 0); -} - /* * Store the basic register settings needed by the controller. */ @@ -221,38 +187,6 @@ static void configure_hc(struct uhci_hcd *uhci) uhci->configure_hc(uhci); } -static int uhci_pci_resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) -{ - int port; - - switch (to_pci_dev(uhci_dev(uhci))->vendor) { - default: - break; - - case PCI_VENDOR_ID_GENESYS: - /* Genesys Logic's GL880S controllers don't generate - * resume-detect interrupts. - */ - return 1; - - case PCI_VENDOR_ID_INTEL: - /* Some of Intel's USB controllers have a bug that causes - * resume-detect interrupts if any port has an over-current - * condition. To make matters worse, some motherboards - * hardwire unused USB ports' over-current inputs active! - * To prevent problems, we will not enable resume-detect - * interrupts if any ports are OC. - */ - for (port = 0; port < uhci->rh_numports; ++port) { - if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & - USBPORTSC_OC) - return 1; - } - break; - } - return 0; -} - static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) { /* If we have to ignore overcurrent events then almost by definition @@ -264,28 +198,6 @@ static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) uhci->resume_detect_interrupts_are_broken(uhci) : 0; } -static int uhci_pci_global_suspend_mode_is_broken(struct uhci_hcd *uhci) -{ - int port; - const char *sys_info; - static const char bad_Asus_board[] = "A7V8X"; - - /* One of Asus's motherboards has a bug which causes it to - * wake up immediately from suspend-to-RAM if any of the ports - * are connected. In such cases we will not set EGSM. - */ - sys_info = dmi_get_system_info(DMI_BOARD_NAME); - if (sys_info && !strcmp(sys_info, bad_Asus_board)) { - for (port = 0; port < uhci->rh_numports; ++port) { - if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & - USBPORTSC_CCS) - return 1; - } - } - - return 0; -} - static int global_suspend_mode_is_broken(struct uhci_hcd *uhci) { return uhci->global_suspend_mode_is_broken ? @@ -551,82 +463,6 @@ static void release_uhci(struct uhci_hcd *uhci) uhci->frame, uhci->frame_dma_handle); } -static int uhci_init(struct usb_hcd *hcd) -{ - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - unsigned io_size = (unsigned) hcd->rsrc_len; - int port; - - uhci->io_addr = (unsigned long) hcd->rsrc_start; - - /* The UHCI spec says devices must have 2 ports, and goes on to say - * they may have more but gives no way to determine how many there - * are. However according to the UHCI spec, Bit 7 of the port - * status and control register is always set to 1. So we try to - * use this to our advantage. Another common failure mode when - * a nonexistent register is addressed is to return all ones, so - * we test for that also. - */ - for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) { - unsigned int portstatus; - - portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2)); - if (!(portstatus & 0x0080) || portstatus == 0xffff) - break; - } - if (debug) - dev_info(uhci_dev(uhci), "detected %d ports\n", port); - - /* Anything greater than 7 is weird so we'll ignore it. */ - if (port > UHCI_RH_MAXCHILD) { - dev_info(uhci_dev(uhci), "port count misdetected? " - "forcing to 2 ports\n"); - port = 2; - } - uhci->rh_numports = port; - - /* Intel controllers report the OverCurrent bit active on. - * VIA controllers report it active off, so we'll adjust the - * bit value. (It's not standardized in the UHCI spec.) - */ - if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_VIA) - uhci->oc_low = 1; - - /* HP's server management chip requires a longer port reset delay. */ - if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP) - uhci->wait_for_hp = 1; - - /* Set up pointers to PCI-specific functions */ - uhci->reset_hc = uhci_pci_reset_hc; - uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc; - uhci->configure_hc = uhci_pci_configure_hc; - uhci->resume_detect_interrupts_are_broken = - uhci_pci_resume_detect_interrupts_are_broken; - uhci->global_suspend_mode_is_broken = - uhci_pci_global_suspend_mode_is_broken; - - - /* Kick BIOS off this hardware and reset if the controller - * isn't already safely quiescent. - */ - check_and_reset_hc(uhci); - return 0; -} - -/* Make sure the controller is quiescent and that we're not using it - * any more. This is mainly for the benefit of programs which, like kexec, - * expect the hardware to be idle: not doing DMA or generating IRQs. - * - * This routine may be called in a damaged or failing kernel. Hence we - * do not acquire the spinlock before shutting down the controller. - */ -static void uhci_shutdown(struct pci_dev *pdev) -{ - struct usb_hcd *hcd = pci_get_drvdata(pdev); - - uhci_hc_died(hcd_to_uhci(hcd)); -} - /* * Allocate a frame list, and then setup the skeleton * @@ -843,87 +679,6 @@ static int uhci_rh_resume(struct usb_hcd *hcd) return rc; } -static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) -{ - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); - int rc = 0; - - dev_dbg(uhci_dev(uhci), "%s\n", __func__); - - spin_lock_irq(&uhci->lock); - if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) - goto done_okay; /* Already suspended or dead */ - - if (uhci->rh_state > UHCI_RH_SUSPENDED) { - dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n"); - rc = -EBUSY; - goto done; - }; - - /* All PCI host controllers are required to disable IRQ generation - * at the source, so we must turn off PIRQ. - */ - pci_write_config_word(pdev, USBLEGSUP, 0); - clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); - - /* Enable platform-specific non-PME# wakeup */ - if (do_wakeup) { - if (pdev->vendor == PCI_VENDOR_ID_INTEL) - pci_write_config_byte(pdev, USBRES_INTEL, - USBPORT1EN | USBPORT2EN); - } - -done_okay: - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); -done: - spin_unlock_irq(&uhci->lock); - return rc; -} - -static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) -{ - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - - dev_dbg(uhci_dev(uhci), "%s\n", __func__); - - /* Since we aren't in D3 any more, it's safe to set this flag - * even if the controller was dead. - */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - spin_lock_irq(&uhci->lock); - - /* Make sure resume from hibernation re-enumerates everything */ - if (hibernated) { - uhci->reset_hc(uhci); - finish_reset(uhci); - } - - /* The firmware may have changed the controller settings during - * a system wakeup. Check it and reconfigure to avoid problems. - */ - else { - check_and_reset_hc(uhci); - } - configure_hc(uhci); - - /* Tell the core if the controller had to be reset */ - if (uhci->rh_state == UHCI_RH_RESET) - usb_root_hub_lost_power(hcd->self.root_hub); - - spin_unlock_irq(&uhci->lock); - - /* If interrupts don't work and remote wakeup is enabled then - * the suspended root hub needs to be polled. - */ - if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) - set_bit(HCD_FLAG_POLL_RH, &hcd->flags); - - /* Does the root hub have a port wakeup pending? */ - usb_hcd_poll_rh_status(hcd); - return 0; -} #endif /* Wait until a particular device/endpoint's QH is idle, and free it */ @@ -966,62 +721,45 @@ static int uhci_hcd_get_frame_number(struct usb_hcd *hcd) return frame_number + delta; } -static const char hcd_name[] = "uhci_hcd"; - -static const struct hc_driver uhci_driver = { - .description = hcd_name, - .product_desc = "UHCI Host Controller", - .hcd_priv_size = sizeof(struct uhci_hcd), - - /* Generic hardware linkage */ - .irq = uhci_irq, - .flags = HCD_USB11, - - /* Basic lifecycle operations */ - .reset = uhci_init, - .start = uhci_start, -#ifdef CONFIG_PM - .pci_suspend = uhci_pci_suspend, - .pci_resume = uhci_pci_resume, - .bus_suspend = uhci_rh_suspend, - .bus_resume = uhci_rh_resume, -#endif - .stop = uhci_stop, - - .urb_enqueue = uhci_urb_enqueue, - .urb_dequeue = uhci_urb_dequeue, +/* Determines number of ports on controller */ +static int uhci_count_ports(struct usb_hcd *hcd) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + unsigned io_size = (unsigned) hcd->rsrc_len; + int port; - .endpoint_disable = uhci_hcd_endpoint_disable, - .get_frame_number = uhci_hcd_get_frame_number, + /* The UHCI spec says devices must have 2 ports, and goes on to say + * they may have more but gives no way to determine how many there + * are. However according to the UHCI spec, Bit 7 of the port + * status and control register is always set to 1. So we try to + * use this to our advantage. Another common failure mode when + * a nonexistent register is addressed is to return all ones, so + * we test for that also. + */ + for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) { + unsigned int portstatus; - .hub_status_data = uhci_hub_status_data, - .hub_control = uhci_hub_control, -}; + portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2)); + if (!(portstatus & 0x0080) || portstatus == 0xffff) + break; + } + if (debug) + dev_info(uhci_dev(uhci), "detected %d ports\n", port); -static DEFINE_PCI_DEVICE_TABLE(uhci_pci_ids) = { { - /* handle any USB UHCI controller */ - PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0), - .driver_data = (unsigned long) &uhci_driver, - }, { /* end: all zeroes */ } -}; + /* Anything greater than 7 is weird so we'll ignore it. */ + if (port > UHCI_RH_MAXCHILD) { + dev_info(uhci_dev(uhci), "port count misdetected? " + "forcing to 2 ports\n"); + port = 2; + } -MODULE_DEVICE_TABLE(pci, uhci_pci_ids); + return port; +} -static struct pci_driver uhci_pci_driver = { - .name = (char *)hcd_name, - .id_table = uhci_pci_ids, +static const char hcd_name[] = "uhci_hcd"; - .probe = usb_hcd_pci_probe, - .remove = usb_hcd_pci_remove, - .shutdown = uhci_shutdown, +#include "uhci-pci.c" -#ifdef CONFIG_PM_SLEEP - .driver = { - .pm = &usb_hcd_pci_pm_ops - }, -#endif -}; - static int __init uhci_hcd_init(void) { int retval = -ENOMEM; diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c new file mode 100644 index 00000000000..c300bd2f7d1 --- /dev/null +++ b/drivers/usb/host/uhci-pci.c @@ -0,0 +1,301 @@ +/* + * UHCI HCD (Host Controller Driver) PCI Bus Glue. + * + * Extracted from uhci-hcd.c: + * Maintainer: Alan Stern + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Georg Acher, acher@in.tum.de + * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de + * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch + * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at + * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface + * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). + * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) + * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu + */ + +#include "pci-quirks.h" + +/* + * Make sure the controller is completely inactive, unable to + * generate interrupts or do DMA. + */ +static void uhci_pci_reset_hc(struct uhci_hcd *uhci) +{ + uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); +} + +/* + * Initialize a controller that was newly discovered or has just been + * resumed. In either case we can't be sure of its previous state. + * + * Returns: 1 if the controller was reset, 0 otherwise. + */ +static int uhci_pci_check_and_reset_hc(struct uhci_hcd *uhci) +{ + return uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), + uhci->io_addr); +} + +/* + * Store the basic register settings needed by the controller. + * This function is called at the end of configure_hc in uhci-hcd.c. + */ +static void uhci_pci_configure_hc(struct uhci_hcd *uhci) +{ + struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); + + /* Enable PIRQ */ + pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT); + + /* Disable platform-specific non-PME# wakeup */ + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + pci_write_config_byte(pdev, USBRES_INTEL, 0); +} + +static int uhci_pci_resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) +{ + int port; + + switch (to_pci_dev(uhci_dev(uhci))->vendor) { + default: + break; + + case PCI_VENDOR_ID_GENESYS: + /* Genesys Logic's GL880S controllers don't generate + * resume-detect interrupts. + */ + return 1; + + case PCI_VENDOR_ID_INTEL: + /* Some of Intel's USB controllers have a bug that causes + * resume-detect interrupts if any port has an over-current + * condition. To make matters worse, some motherboards + * hardwire unused USB ports' over-current inputs active! + * To prevent problems, we will not enable resume-detect + * interrupts if any ports are OC. + */ + for (port = 0; port < uhci->rh_numports; ++port) { + if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & + USBPORTSC_OC) + return 1; + } + break; + } + return 0; +} + +static int uhci_pci_global_suspend_mode_is_broken(struct uhci_hcd *uhci) +{ + int port; + const char *sys_info; + static const char bad_Asus_board[] = "A7V8X"; + + /* One of Asus's motherboards has a bug which causes it to + * wake up immediately from suspend-to-RAM if any of the ports + * are connected. In such cases we will not set EGSM. + */ + sys_info = dmi_get_system_info(DMI_BOARD_NAME); + if (sys_info && !strcmp(sys_info, bad_Asus_board)) { + for (port = 0; port < uhci->rh_numports; ++port) { + if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & + USBPORTSC_CCS) + return 1; + } + } + + return 0; +} + +static int uhci_pci_init(struct usb_hcd *hcd) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + + uhci->io_addr = (unsigned long) hcd->rsrc_start; + + uhci->rh_numports = uhci_count_ports(hcd); + + /* Intel controllers report the OverCurrent bit active on. + * VIA controllers report it active off, so we'll adjust the + * bit value. (It's not standardized in the UHCI spec.) + */ + if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_VIA) + uhci->oc_low = 1; + + /* HP's server management chip requires a longer port reset delay. */ + if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP) + uhci->wait_for_hp = 1; + + /* Set up pointers to PCI-specific functions */ + uhci->reset_hc = uhci_pci_reset_hc; + uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc; + uhci->configure_hc = uhci_pci_configure_hc; + uhci->resume_detect_interrupts_are_broken = + uhci_pci_resume_detect_interrupts_are_broken; + uhci->global_suspend_mode_is_broken = + uhci_pci_global_suspend_mode_is_broken; + + + /* Kick BIOS off this hardware and reset if the controller + * isn't already safely quiescent. + */ + check_and_reset_hc(uhci); + return 0; +} + +/* Make sure the controller is quiescent and that we're not using it + * any more. This is mainly for the benefit of programs which, like kexec, + * expect the hardware to be idle: not doing DMA or generating IRQs. + * + * This routine may be called in a damaged or failing kernel. Hence we + * do not acquire the spinlock before shutting down the controller. + */ +static void uhci_shutdown(struct pci_dev *pdev) +{ + struct usb_hcd *hcd = pci_get_drvdata(pdev); + + uhci_hc_died(hcd_to_uhci(hcd)); +} + +#ifdef CONFIG_PM + +static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); + int rc = 0; + + dev_dbg(uhci_dev(uhci), "%s\n", __func__); + + spin_lock_irq(&uhci->lock); + if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) + goto done_okay; /* Already suspended or dead */ + + if (uhci->rh_state > UHCI_RH_SUSPENDED) { + dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n"); + rc = -EBUSY; + goto done; + }; + + /* All PCI host controllers are required to disable IRQ generation + * at the source, so we must turn off PIRQ. + */ + pci_write_config_word(pdev, USBLEGSUP, 0); + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); + + /* Enable platform-specific non-PME# wakeup */ + if (do_wakeup) { + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + pci_write_config_byte(pdev, USBRES_INTEL, + USBPORT1EN | USBPORT2EN); + } + +done_okay: + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); +done: + spin_unlock_irq(&uhci->lock); + return rc; +} + +static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + + dev_dbg(uhci_dev(uhci), "%s\n", __func__); + + /* Since we aren't in D3 any more, it's safe to set this flag + * even if the controller was dead. + */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + spin_lock_irq(&uhci->lock); + + /* Make sure resume from hibernation re-enumerates everything */ + if (hibernated) { + uhci->reset_hc(uhci); + finish_reset(uhci); + } + + /* The firmware may have changed the controller settings during + * a system wakeup. Check it and reconfigure to avoid problems. + */ + else { + check_and_reset_hc(uhci); + } + configure_hc(uhci); + + /* Tell the core if the controller had to be reset */ + if (uhci->rh_state == UHCI_RH_RESET) + usb_root_hub_lost_power(hcd->self.root_hub); + + spin_unlock_irq(&uhci->lock); + + /* If interrupts don't work and remote wakeup is enabled then + * the suspended root hub needs to be polled. + */ + if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); + + /* Does the root hub have a port wakeup pending? */ + usb_hcd_poll_rh_status(hcd); + return 0; +} + +#endif + +static const struct hc_driver uhci_driver = { + .description = hcd_name, + .product_desc = "UHCI Host Controller", + .hcd_priv_size = sizeof(struct uhci_hcd), + + /* Generic hardware linkage */ + .irq = uhci_irq, + .flags = HCD_USB11, + + /* Basic lifecycle operations */ + .reset = uhci_pci_init, + .start = uhci_start, +#ifdef CONFIG_PM + .pci_suspend = uhci_pci_suspend, + .pci_resume = uhci_pci_resume, + .bus_suspend = uhci_rh_suspend, + .bus_resume = uhci_rh_resume, +#endif + .stop = uhci_stop, + + .urb_enqueue = uhci_urb_enqueue, + .urb_dequeue = uhci_urb_dequeue, + + .endpoint_disable = uhci_hcd_endpoint_disable, + .get_frame_number = uhci_hcd_get_frame_number, + + .hub_status_data = uhci_hub_status_data, + .hub_control = uhci_hub_control, +}; + +static DEFINE_PCI_DEVICE_TABLE(uhci_pci_ids) = { { + /* handle any USB UHCI controller */ + PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0), + .driver_data = (unsigned long) &uhci_driver, + }, { /* end: all zeroes */ } +}; + +MODULE_DEVICE_TABLE(pci, uhci_pci_ids); + +static struct pci_driver uhci_pci_driver = { + .name = (char *)hcd_name, + .id_table = uhci_pci_ids, + + .probe = usb_hcd_pci_probe, + .remove = usb_hcd_pci_remove, + .shutdown = uhci_shutdown, + +#ifdef CONFIG_PM_SLEEP + .driver = { + .pm = &usb_hcd_pci_pm_ops + }, +#endif +}; -- cgit v1.2.3 From 9faa091a409851ac6b3812164d53644074bc89b1 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Fri, 6 May 2011 12:00:16 +0200 Subject: USB: UHCI: Wrap I/O register accesses This patch is part of a series that extend the UHCI HCD to support non-PCI controllers. This patch replaces in{b,w,l} and out{b,wl} with calls to local inline functions. This is done so that the register access functions can be extended to support register areas not mapped in PCI I/O space. Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-debug.c | 17 ++++++++--------- drivers/usb/host/uhci-hcd.c | 42 +++++++++++++++++++++--------------------- drivers/usb/host/uhci-hcd.h | 30 ++++++++++++++++++++++++++++++ drivers/usb/host/uhci-hub.c | 35 ++++++++++++++++++----------------- 4 files changed, 77 insertions(+), 47 deletions(-) diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index ee60cd3ea64..f882a84b1bb 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -285,7 +285,6 @@ static int uhci_show_root_hub_state(struct uhci_hcd *uhci, char *buf, int len) static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) { char *out = buf; - unsigned long io_addr = uhci->io_addr; unsigned short usbcmd, usbstat, usbint, usbfrnum; unsigned int flbaseadd; unsigned char sof; @@ -295,14 +294,14 @@ static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) if (len < 80 * 9) return 0; - usbcmd = inw(io_addr + 0); - usbstat = inw(io_addr + 2); - usbint = inw(io_addr + 4); - usbfrnum = inw(io_addr + 6); - flbaseadd = inl(io_addr + 8); - sof = inb(io_addr + 12); - portsc1 = inw(io_addr + 16); - portsc2 = inw(io_addr + 18); + usbcmd = uhci_readw(uhci, 0); + usbstat = uhci_readw(uhci, 2); + usbint = uhci_readw(uhci, 4); + usbfrnum = uhci_readw(uhci, 6); + flbaseadd = uhci_readl(uhci, 8); + sof = uhci_readb(uhci, 12); + portsc1 = uhci_readw(uhci, 16); + portsc2 = uhci_readw(uhci, 18); out += sprintf(out, " usbcmd = %04x %s%s%s%s%s%s%s%s\n", usbcmd, diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 3d2a10563be..5176c537b95 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -133,7 +133,7 @@ static void finish_reset(struct uhci_hcd *uhci) * We have to clear them by hand. */ for (port = 0; port < uhci->rh_numports; ++port) - outw(0, uhci->io_addr + USBPORTSC1 + (port * 2)); + uhci_writew(uhci, 0, USBPORTSC1 + (port * 2)); uhci->port_c_suspend = uhci->resuming_ports = 0; uhci->rh_state = UHCI_RH_RESET; @@ -173,14 +173,14 @@ static void check_and_reset_hc(struct uhci_hcd *uhci) static void configure_hc(struct uhci_hcd *uhci) { /* Set the frame length to the default: 1 ms exactly */ - outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF); + uhci_writeb(uhci, USBSOF_DEFAULT, USBSOF); /* Store the frame list base address */ - outl(uhci->frame_dma_handle, uhci->io_addr + USBFLBASEADD); + uhci_writel(uhci, uhci->frame_dma_handle, USBFLBASEADD); /* Set the current frame number */ - outw(uhci->frame_number & UHCI_MAX_SOF_NUMBER, - uhci->io_addr + USBFRNUM); + uhci_writew(uhci, uhci->frame_number & UHCI_MAX_SOF_NUMBER, + USBFRNUM); /* perform any arch/bus specific configuration */ if (uhci->configure_hc) @@ -264,8 +264,8 @@ __acquires(uhci->lock) !int_enable) uhci->RD_enable = int_enable = 0; - outw(int_enable, uhci->io_addr + USBINTR); - outw(egsm_enable | USBCMD_CF, uhci->io_addr + USBCMD); + uhci_writew(uhci, int_enable, USBINTR); + uhci_writew(uhci, egsm_enable | USBCMD_CF, USBCMD); mb(); udelay(5); @@ -274,7 +274,7 @@ __acquires(uhci->lock) * controller should stop after a few microseconds. Otherwise * we will give the controller one frame to stop. */ - if (!auto_stop && !(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) { + if (!auto_stop && !(uhci_readw(uhci, USBSTS) & USBSTS_HCH)) { uhci->rh_state = UHCI_RH_SUSPENDING; spin_unlock_irq(&uhci->lock); msleep(1); @@ -282,7 +282,7 @@ __acquires(uhci->lock) if (uhci->dead) return; } - if (!(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) + if (!(uhci_readw(uhci, USBSTS) & USBSTS_HCH)) dev_warn(uhci_dev(uhci), "Controller not stopped yet!\n"); uhci_get_current_frame_number(uhci); @@ -309,9 +309,9 @@ static void start_rh(struct uhci_hcd *uhci) /* Mark it configured and running with a 64-byte max packet. * All interrupts are enabled, even though RESUME won't do anything. */ - outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, uhci->io_addr + USBCMD); - outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, - uhci->io_addr + USBINTR); + uhci_writew(uhci, USBCMD_RS | USBCMD_CF | USBCMD_MAXP, USBCMD); + uhci_writew(uhci, USBINTR_TIMEOUT | USBINTR_RESUME | + USBINTR_IOC | USBINTR_SP, USBINTR); mb(); uhci->rh_state = UHCI_RH_RUNNING; set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); @@ -334,9 +334,9 @@ __acquires(uhci->lock) unsigned egsm; /* Keep EGSM on if it was set before */ - egsm = inw(uhci->io_addr + USBCMD) & USBCMD_EGSM; + egsm = uhci_readw(uhci, USBCMD) & USBCMD_EGSM; uhci->rh_state = UHCI_RH_RESUMING; - outw(USBCMD_FGR | USBCMD_CF | egsm, uhci->io_addr + USBCMD); + uhci_writew(uhci, USBCMD_FGR | USBCMD_CF | egsm, USBCMD); spin_unlock_irq(&uhci->lock); msleep(20); spin_lock_irq(&uhci->lock); @@ -344,10 +344,10 @@ __acquires(uhci->lock) return; /* End Global Resume and wait for EOP to be sent */ - outw(USBCMD_CF, uhci->io_addr + USBCMD); + uhci_writew(uhci, USBCMD_CF, USBCMD); mb(); udelay(4); - if (inw(uhci->io_addr + USBCMD) & USBCMD_FGR) + if (uhci_readw(uhci, USBCMD) & USBCMD_FGR) dev_warn(uhci_dev(uhci), "FGR not stopped yet!\n"); } @@ -367,10 +367,10 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd) * interrupt cause. Contrary to the UHCI specification, the * "HC Halted" status bit is persistent: it is RO, not R/WC. */ - status = inw(uhci->io_addr + USBSTS); + status = uhci_readw(uhci, USBSTS); if (!(status & ~USBSTS_HCH)) /* shared interrupt, not mine */ return IRQ_NONE; - outw(status, uhci->io_addr + USBSTS); /* Clear it */ + uhci_writew(uhci, status, USBSTS); /* Clear it */ if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) { if (status & USBSTS_HSE) @@ -426,7 +426,7 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci) if (!uhci->is_stopped) { unsigned delta; - delta = (inw(uhci->io_addr + USBFRNUM) - uhci->frame_number) & + delta = (uhci_readw(uhci, USBFRNUM) - uhci->frame_number) & (UHCI_NUMFRAMES - 1); uhci->frame_number += delta; } @@ -716,7 +716,7 @@ static int uhci_hcd_get_frame_number(struct usb_hcd *hcd) /* Minimize latency by avoiding the spinlock */ frame_number = uhci->frame_number; barrier(); - delta = (inw(uhci->io_addr + USBFRNUM) - frame_number) & + delta = (uhci_readw(uhci, USBFRNUM) - frame_number) & (UHCI_NUMFRAMES - 1); return frame_number + delta; } @@ -739,7 +739,7 @@ static int uhci_count_ports(struct usb_hcd *hcd) for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) { unsigned int portstatus; - portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2)); + portstatus = uhci_readw(uhci, USBPORTSC1 + (port * 2)); if (!(portstatus & 0x0080) || portstatus == 0xffff) break; } diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 56943795457..a6de241bf96 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -481,4 +481,34 @@ struct urb_priv { #define PCI_VENDOR_ID_GENESYS 0x17a0 #define PCI_DEVICE_ID_GL880S_UHCI 0x8083 +static inline u32 uhci_readl(struct uhci_hcd *uhci, int reg) +{ + return inl(uhci->io_addr + reg); +} + +static inline void uhci_writel(struct uhci_hcd *uhci, u32 val, int reg) +{ + outl(val, uhci->io_addr + reg); +} + +static inline u16 uhci_readw(struct uhci_hcd *uhci, int reg) +{ + return inw(uhci->io_addr + reg); +} + +static inline void uhci_writew(struct uhci_hcd *uhci, u16 val, int reg) +{ + outw(val, uhci->io_addr + reg); +} + +static inline u8 uhci_readb(struct uhci_hcd *uhci, int reg) +{ + return inb(uhci->io_addr + reg); +} + +static inline void uhci_writeb(struct uhci_hcd *uhci, u8 val, int reg) +{ + outb(val, uhci->io_addr + reg); +} + #endif diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 75418265488..045cde4cbc3 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -44,7 +44,7 @@ static int any_ports_active(struct uhci_hcd *uhci) int port; for (port = 0; port < uhci->rh_numports; ++port) { - if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & + if ((uhci_readw(uhci, USBPORTSC1 + port * 2) & (USBPORTSC_CCS | RWC_BITS)) || test_bit(port, &uhci->port_c_suspend)) return 1; @@ -68,7 +68,7 @@ static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf) *buf = 0; for (port = 0; port < uhci->rh_numports; ++port) { - if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & mask) || + if ((uhci_readw(uhci, USBPORTSC1 + port * 2) & mask) || test_bit(port, &uhci->port_c_suspend)) *buf |= (1 << (port + 1)); } @@ -78,17 +78,17 @@ static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf) #define OK(x) len = (x); break #define CLR_RH_PORTSTAT(x) \ - status = inw(port_addr); \ + status = uhci_readw(uhci, port_addr); \ status &= ~(RWC_BITS|WZ_BITS); \ status &= ~(x); \ status |= RWC_BITS & (x); \ - outw(status, port_addr) + uhci_writew(uhci, status, port_addr) #define SET_RH_PORTSTAT(x) \ - status = inw(port_addr); \ + status = uhci_readw(uhci, port_addr); \ status |= (x); \ status &= ~(RWC_BITS|WZ_BITS); \ - outw(status, port_addr) + uhci_writew(uhci, status, port_addr) /* UHCI controllers don't automatically stop resume signalling after 20 msec, * so we have to poll and check timeouts in order to take care of it. @@ -99,7 +99,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, int status; int i; - if (inw(port_addr) & SUSPEND_BITS) { + if (uhci_readw(uhci, port_addr) & SUSPEND_BITS) { CLR_RH_PORTSTAT(SUSPEND_BITS); if (test_bit(port, &uhci->resuming_ports)) set_bit(port, &uhci->port_c_suspend); @@ -110,7 +110,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, * Experiments show that some controllers take longer, so * we'll poll for completion. */ for (i = 0; i < 10; ++i) { - if (!(inw(port_addr) & SUSPEND_BITS)) + if (!(uhci_readw(uhci, port_addr) & SUSPEND_BITS)) break; udelay(1); } @@ -121,12 +121,12 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, /* Wait for the UHCI controller in HP's iLO2 server management chip. * It can take up to 250 us to finish a reset and set the CSC bit. */ -static void wait_for_HP(unsigned long port_addr) +static void wait_for_HP(struct uhci_hcd *uhci, unsigned long port_addr) { int i; for (i = 10; i < 250; i += 10) { - if (inw(port_addr) & USBPORTSC_CSC) + if (uhci_readw(uhci, port_addr) & USBPORTSC_CSC) return; udelay(10); } @@ -140,8 +140,8 @@ static void uhci_check_ports(struct uhci_hcd *uhci) int status; for (port = 0; port < uhci->rh_numports; ++port) { - port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; - status = inw(port_addr); + port_addr = USBPORTSC1 + 2 * port; + status = uhci_readw(uhci, port_addr); if (unlikely(status & USBPORTSC_PR)) { if (time_after_eq(jiffies, uhci->ports_timeout)) { CLR_RH_PORTSTAT(USBPORTSC_PR); @@ -150,7 +150,7 @@ static void uhci_check_ports(struct uhci_hcd *uhci) /* HP's server management chip requires * a longer delay. */ if (uhci->wait_for_hp) - wait_for_HP(port_addr); + wait_for_HP(uhci, port_addr); /* If the port was enabled before, turning * reset on caused a port enable change. @@ -241,7 +241,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, struct uhci_hcd *uhci = hcd_to_uhci(hcd); int status, lstatus, retval = 0, len = 0; unsigned int port = wIndex - 1; - unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; + unsigned long port_addr = USBPORTSC1 + 2 * port; u16 wPortChange, wPortStatus; unsigned long flags; @@ -259,7 +259,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto err; uhci_check_ports(uhci); - status = inw(port_addr); + status = uhci_readw(uhci, port_addr); /* Intel controllers report the OverCurrent bit active on. * VIA controllers report it active off, so we'll adjust the @@ -356,7 +356,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, CLR_RH_PORTSTAT(USBPORTSC_PEC); OK(0); case USB_PORT_FEAT_SUSPEND: - if (!(inw(port_addr) & USBPORTSC_SUSP)) { + if (!(uhci_readw(uhci, port_addr) & USBPORTSC_SUSP)) { /* Make certain the port isn't suspended */ uhci_finish_suspend(uhci, port, port_addr); @@ -368,7 +368,8 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * if the port is disabled. When this happens * just skip the Resume signalling. */ - if (!(inw(port_addr) & USBPORTSC_RD)) + if (!(uhci_readw(uhci, port_addr) & + USBPORTSC_RD)) uhci_finish_suspend(uhci, port, port_addr); else -- cgit v1.2.3 From d3219d1c4c9ab7cd959f8f294420faf5f936cf55 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Fri, 6 May 2011 12:00:17 +0200 Subject: USB: UHCI: Support non-PCI host controllers This patch is part of a series that extend the UHCI HCD to support non-PCI host controllers. This patch also extends the uhci_{read,write}* functions to allow accesses to registers not mapped into PCI I/O space. This extension also includes the addition of a void __iomem pointer to the uhci structure. A new Kconfig option is added to signal that the system has a non-PCI HC. If this Kconfig option is set, uhci-hcd.c will include generic reset functions for systems that do not make use of keyboard and mouse legacy support. PCI controllers will still always use the reset functions from pci-quirks This patch is followed by a patch that adds bus glue for the first non-PCI UHCI HC. Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 4 +++ drivers/usb/host/uhci-hcd.c | 73 +++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/uhci-hcd.h | 65 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 7dd4c44fabb..8045988f57a 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -410,6 +410,10 @@ config USB_UHCI_HCD To compile this driver as a module, choose M here: the module will be called uhci-hcd. +config USB_UHCI_SUPPORT_NON_PCI_HC + bool + depends on USB_UHCI_HCD + config USB_FHCI_HCD tristate "Freescale QE USB Host Controller support" depends on USB && OF_GPIO && QE_GPIO && QUICC_ENGINE diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 5176c537b95..cd482fcc05d 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -167,6 +167,79 @@ static void check_and_reset_hc(struct uhci_hcd *uhci) finish_reset(uhci); } +#if defined(CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC) +/* + * The two functions below are generic reset functions that are used on systems + * that do not have keyboard and mouse legacy support. We assume that we are + * running on such a system if CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC is defined. + */ + +/* + * Make sure the controller is completely inactive, unable to + * generate interrupts or do DMA. + */ +static void uhci_generic_reset_hc(struct uhci_hcd *uhci) +{ + /* Reset the HC - this will force us to get a + * new notification of any already connected + * ports due to the virtual disconnect that it + * implies. + */ + uhci_writew(uhci, USBCMD_HCRESET, USBCMD); + mb(); + udelay(5); + if (uhci_readw(uhci, USBCMD) & USBCMD_HCRESET) + dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n"); + + /* Just to be safe, disable interrupt requests and + * make sure the controller is stopped. + */ + uhci_writew(uhci, 0, USBINTR); + uhci_writew(uhci, 0, USBCMD); +} + +/* + * Initialize a controller that was newly discovered or has just been + * resumed. In either case we can't be sure of its previous state. + * + * Returns: 1 if the controller was reset, 0 otherwise. + */ +static int uhci_generic_check_and_reset_hc(struct uhci_hcd *uhci) +{ + unsigned int cmd, intr; + + /* + * When restarting a suspended controller, we expect all the + * settings to be the same as we left them: + * + * Controller is stopped and configured with EGSM set; + * No interrupts enabled except possibly Resume Detect. + * + * If any of these conditions are violated we do a complete reset. + */ + + cmd = uhci_readw(uhci, USBCMD); + if ((cmd & USBCMD_RS) || !(cmd & USBCMD_CF) || !(cmd & USBCMD_EGSM)) { + dev_dbg(uhci_dev(uhci), "%s: cmd = 0x%04x\n", + __func__, cmd); + goto reset_needed; + } + + intr = uhci_readw(uhci, USBINTR); + if (intr & (~USBINTR_RESUME)) { + dev_dbg(uhci_dev(uhci), "%s: intr = 0x%04x\n", + __func__, intr); + goto reset_needed; + } + return 0; + +reset_needed: + dev_dbg(uhci_dev(uhci), "Performing full reset\n"); + uhci_generic_reset_hc(uhci); + return 1; +} +#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */ + /* * Store the basic register settings needed by the controller. */ diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index a6de241bf96..a4e64d08f02 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -380,6 +380,9 @@ struct uhci_hcd { /* Grabbed from PCI */ unsigned long io_addr; + /* Used when registers are memory mapped */ + void __iomem *regs; + struct dma_pool *qh_pool; struct dma_pool *td_pool; @@ -481,6 +484,14 @@ struct urb_priv { #define PCI_VENDOR_ID_GENESYS 0x17a0 #define PCI_DEVICE_ID_GL880S_UHCI 0x8083 +/* + * Functions used to access controller registers. The UCHI spec says that host + * controller I/O registers are mapped into PCI I/O space. For non-PCI hosts + * we use memory mapped registers. + */ + +#if !defined(CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC) +/* Support PCI only */ static inline u32 uhci_readl(struct uhci_hcd *uhci, int reg) { return inl(uhci->io_addr + reg); @@ -511,4 +522,58 @@ static inline void uhci_writeb(struct uhci_hcd *uhci, u8 val, int reg) outb(val, uhci->io_addr + reg); } +#else +/* Support PCI and non-PCI host controllers */ + +#define uhci_has_pci_registers(u) ((u)->io_addr != 0) + +static inline u32 uhci_readl(struct uhci_hcd *uhci, int reg) +{ + if (uhci_has_pci_registers(uhci)) + return inl(uhci->io_addr + reg); + else + return readl(uhci->regs + reg); +} + +static inline void uhci_writel(struct uhci_hcd *uhci, u32 val, int reg) +{ + if (uhci_has_pci_registers(uhci)) + outl(val, uhci->io_addr + reg); + else + writel(val, uhci->regs + reg); +} + +static inline u16 uhci_readw(struct uhci_hcd *uhci, int reg) +{ + if (uhci_has_pci_registers(uhci)) + return inw(uhci->io_addr + reg); + else + return readw(uhci->regs + reg); +} + +static inline void uhci_writew(struct uhci_hcd *uhci, u16 val, int reg) +{ + if (uhci_has_pci_registers(uhci)) + outw(val, uhci->io_addr + reg); + else + writew(val, uhci->regs + reg); +} + +static inline u8 uhci_readb(struct uhci_hcd *uhci, int reg) +{ + if (uhci_has_pci_registers(uhci)) + return inb(uhci->io_addr + reg); + else + return readb(uhci->regs + reg); +} + +static inline void uhci_writeb(struct uhci_hcd *uhci, u8 val, int reg) +{ + if (uhci_has_pci_registers(uhci)) + outb(val, uhci->io_addr + reg); + else + writeb(val, uhci->regs + reg); +} +#endif /* !defined(CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC) */ + #endif -- cgit v1.2.3 From 3db7739c80990ef53621f76f6095a91e70d88546 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Fri, 6 May 2011 12:00:18 +0200 Subject: USB: UHCI: Add support for GRLIB GRUSBHC controller This patch adds support for the UHCI part of the GRLIB GRUSBHC controller found on some LEON/GRLIB SoCs. The UHCI HCD previously only supported controllers connected over PCI. This patch adds support for the first non-PCI UHCI HC. I have tried to replicate the solution used in ehci-hcd.c. Tested on GR-LEON4-ITX board (LEON4/GRLIB with GRUSBHC) and x86 with Intel UHCI HC. Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 6 +- drivers/usb/host/uhci-grlib.c | 194 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/uhci-hcd.c | 41 +++++++-- 3 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 drivers/usb/host/uhci-grlib.c diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 8045988f57a..8898505af42 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -396,7 +396,7 @@ config USB_OHCI_LITTLE_ENDIAN config USB_UHCI_HCD tristate "UHCI HCD (most Intel and VIA) support" - depends on USB && PCI + depends on USB && (PCI || SPARC_LEON) ---help--- The Universal Host Controller Interface is a standard by Intel for accessing the USB hardware in the PC (which is also called the USB @@ -405,7 +405,8 @@ config USB_UHCI_HCD with Intel PCI chipsets (like intel 430TX, 440FX, 440LX, 440BX, i810, i820) conform to this standard. Also all VIA PCI chipsets (like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro - 133). If unsure, say Y. + 133) and LEON/GRLIB SoCs with the GRUSBHC controller. + If unsure, say Y. To compile this driver as a module, choose M here: the module will be called uhci-hcd. @@ -413,6 +414,7 @@ config USB_UHCI_HCD config USB_UHCI_SUPPORT_NON_PCI_HC bool depends on USB_UHCI_HCD + default y if SPARC_LEON config USB_FHCI_HCD tristate "Freescale QE USB Host Controller support" diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c new file mode 100644 index 00000000000..b1addd60a1e --- /dev/null +++ b/drivers/usb/host/uhci-grlib.c @@ -0,0 +1,194 @@ +/* + * UHCI HCD (Host Controller Driver) for GRLIB GRUSBHC + * + * Copyright (c) 2011 Jan Andersson + * + * This file is based on UHCI PCI HCD: + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Georg Acher, acher@in.tum.de + * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de + * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch + * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at + * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface + * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). + * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) + * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu + */ + +#include +#include +#include + +static int uhci_grlib_init(struct usb_hcd *hcd) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + + uhci->rh_numports = uhci_count_ports(hcd); + + /* Set up pointers to to generic functions */ + uhci->reset_hc = uhci_generic_reset_hc; + uhci->check_and_reset_hc = uhci_generic_check_and_reset_hc; + /* No special actions need to be taken for the functions below */ + uhci->configure_hc = NULL; + uhci->resume_detect_interrupts_are_broken = NULL; + uhci->global_suspend_mode_is_broken = NULL; + + /* Reset if the controller isn't already safely quiescent. */ + check_and_reset_hc(uhci); + return 0; +} + +static const struct hc_driver uhci_grlib_hc_driver = { + .description = hcd_name, + .product_desc = "GRLIB GRUSBHC UHCI Host Controller", + .hcd_priv_size = sizeof(struct uhci_hcd), + + /* Generic hardware linkage */ + .irq = uhci_irq, + .flags = HCD_MEMORY | HCD_USB11, + + /* Basic lifecycle operations */ + .reset = uhci_grlib_init, + .start = uhci_start, +#ifdef CONFIG_PM + .pci_suspend = NULL, + .pci_resume = NULL, + .bus_suspend = uhci_rh_suspend, + .bus_resume = uhci_rh_resume, +#endif + .stop = uhci_stop, + + .urb_enqueue = uhci_urb_enqueue, + .urb_dequeue = uhci_urb_dequeue, + + .endpoint_disable = uhci_hcd_endpoint_disable, + .get_frame_number = uhci_hcd_get_frame_number, + + .hub_status_data = uhci_hub_status_data, + .hub_control = uhci_hub_control, +}; + + +static int __devinit uhci_hcd_grlib_probe(struct platform_device *op) +{ + struct device_node *dn = op->dev.of_node; + struct usb_hcd *hcd; + struct uhci_hcd *uhci = NULL; + struct resource res; + int irq; + int rv; + + if (usb_disabled()) + return -ENODEV; + + dev_dbg(&op->dev, "initializing GRUSBHC UHCI USB Controller\n"); + + rv = of_address_to_resource(dn, 0, &res); + if (rv) + return rv; + + /* usb_create_hcd requires dma_mask != NULL */ + op->dev.dma_mask = &op->dev.coherent_dma_mask; + hcd = usb_create_hcd(&uhci_grlib_hc_driver, &op->dev, + "GRUSBHC UHCI USB"); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = res.start; + hcd->rsrc_len = res.end - res.start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); + rv = -EBUSY; + goto err_rmr; + } + + irq = irq_of_parse_and_map(dn, 0); + if (irq == NO_IRQ) { + printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); + rv = -EBUSY; + goto err_irq; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + printk(KERN_ERR "%s: ioremap failed\n", __FILE__); + rv = -ENOMEM; + goto err_ioremap; + } + + uhci = hcd_to_uhci(hcd); + + uhci->regs = hcd->regs; + + rv = usb_add_hcd(hcd, irq, 0); + if (rv) + goto err_uhci; + + return 0; + +err_uhci: + iounmap(hcd->regs); +err_ioremap: + irq_dispose_mapping(irq); +err_irq: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_rmr: + usb_put_hcd(hcd); + + return rv; +} + +static int uhci_hcd_grlib_remove(struct platform_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + + dev_set_drvdata(&op->dev, NULL); + + dev_dbg(&op->dev, "stopping GRLIB GRUSBHC UHCI USB Controller\n"); + + usb_remove_hcd(hcd); + + iounmap(hcd->regs); + irq_dispose_mapping(hcd->irq); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + usb_put_hcd(hcd); + + return 0; +} + +/* Make sure the controller is quiescent and that we're not using it + * any more. This is mainly for the benefit of programs which, like kexec, + * expect the hardware to be idle: not doing DMA or generating IRQs. + * + * This routine may be called in a damaged or failing kernel. Hence we + * do not acquire the spinlock before shutting down the controller. + */ +static void uhci_hcd_grlib_shutdown(struct platform_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + + uhci_hc_died(hcd_to_uhci(hcd)); +} + +static const struct of_device_id uhci_hcd_grlib_of_match[] = { + { .name = "GAISLER_UHCI", }, + { .name = "01_027", }, + {}, +}; +MODULE_DEVICE_TABLE(of, uhci_hcd_grlib_of_match); + + +static struct platform_driver uhci_grlib_driver = { + .probe = uhci_hcd_grlib_probe, + .remove = uhci_hcd_grlib_remove, + .shutdown = uhci_hcd_grlib_shutdown, + .driver = { + .name = "grlib-uhci", + .owner = THIS_MODULE, + .of_match_table = uhci_hcd_grlib_of_match, + }, +}; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index cd482fcc05d..79dd822e58d 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -831,7 +831,19 @@ static int uhci_count_ports(struct usb_hcd *hcd) static const char hcd_name[] = "uhci_hcd"; +#ifdef CONFIG_PCI #include "uhci-pci.c" +#define PCI_DRIVER uhci_pci_driver +#endif + +#ifdef CONFIG_SPARC_LEON +#include "uhci-grlib.c" +#define PLATFORM_DRIVER uhci_grlib_driver +#endif + +#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) +#error "missing bus glue for uhci-hcd" +#endif static int __init uhci_hcd_init(void) { @@ -858,13 +870,27 @@ static int __init uhci_hcd_init(void) if (!uhci_up_cachep) goto up_failed; - retval = pci_register_driver(&uhci_pci_driver); - if (retval) - goto init_failed; +#ifdef PLATFORM_DRIVER + retval = platform_driver_register(&PLATFORM_DRIVER); + if (retval < 0) + goto clean0; +#endif + +#ifdef PCI_DRIVER + retval = pci_register_driver(&PCI_DRIVER); + if (retval < 0) + goto clean1; +#endif return 0; -init_failed: +#ifdef PCI_DRIVER +clean1: +#endif +#ifdef PLATFORM_DRIVER + platform_driver_unregister(&PLATFORM_DRIVER); +clean0: +#endif kmem_cache_destroy(uhci_up_cachep); up_failed: @@ -881,7 +907,12 @@ errbuf_failed: static void __exit uhci_hcd_cleanup(void) { - pci_unregister_driver(&uhci_pci_driver); +#ifdef PLATFORM_DRIVER + platform_driver_unregister(&PLATFORM_DRIVER); +#endif +#ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); +#endif kmem_cache_destroy(uhci_up_cachep); debugfs_remove(uhci_debugfs_root); kfree(errbuf); -- cgit v1.2.3 From 0f73cac8e41723d600c91a0f5b481dc3202f4f82 Mon Sep 17 00:00:00 2001 From: Anji jonnala Date: Wed, 4 May 2011 10:19:46 +0530 Subject: USB: OTG: msm: vote for dayatona fabric clock HSUSB core clock is derived from daytona fabric clock and for HSUSB operational require minimum core clock at 55MHz. Since, HSUSB cannot tolerate daytona fabric clock change in the middle of HSUSB operational, vote for maximum Daytona fabric clock while usb is operational Signed-off-by: Anji jonnala Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/msm_otg.c | 36 +++++++++++++++++++++++++++++++++++- include/linux/usb/msm_hsusb.h | 7 +++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index f58b7dab75a..7792fef0be5 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -324,6 +324,9 @@ static int msm_otg_suspend(struct msm_otg *motg) if (motg->core_clk) clk_disable(motg->core_clk); + if (!IS_ERR(motg->pclk_src)) + clk_disable(motg->pclk_src); + if (device_may_wakeup(otg->dev)) enable_irq_wake(motg->irq); if (bus) @@ -347,6 +350,9 @@ static int msm_otg_resume(struct msm_otg *motg) if (!atomic_read(&motg->in_lpm)) return 0; + if (!IS_ERR(motg->pclk_src)) + clk_enable(motg->pclk_src); + clk_enable(motg->pclk); clk_enable(motg->clk); if (motg->core_clk) @@ -862,12 +868,31 @@ static int __init msm_otg_probe(struct platform_device *pdev) ret = PTR_ERR(motg->clk); goto put_phy_reset_clk; } + clk_set_rate(motg->clk, 60000000); + + /* + * If USB Core is running its protocol engine based on CORE CLK, + * CORE CLK must be running at >55Mhz for correct HSUSB + * operation and USB core cannot tolerate frequency changes on + * CORE CLK. For such USB cores, vote for maximum clk frequency + * on pclk source + */ + if (motg->pdata->pclk_src_name) { + motg->pclk_src = clk_get(&pdev->dev, + motg->pdata->pclk_src_name); + if (IS_ERR(motg->pclk_src)) + goto put_clk; + clk_set_rate(motg->pclk_src, INT_MAX); + clk_enable(motg->pclk_src); + } else + motg->pclk_src = ERR_PTR(-ENOENT); + motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); if (IS_ERR(motg->pclk)) { dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); ret = PTR_ERR(motg->pclk); - goto put_clk; + goto put_pclk_src; } /* @@ -955,6 +980,11 @@ put_core_clk: if (motg->core_clk) clk_put(motg->core_clk); clk_put(motg->pclk); +put_pclk_src: + if (!IS_ERR(motg->pclk_src)) { + clk_disable(motg->pclk_src); + clk_put(motg->pclk_src); + } put_clk: clk_put(motg->clk); put_phy_reset_clk: @@ -1004,6 +1034,10 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) clk_disable(motg->clk); if (motg->core_clk) clk_disable(motg->core_clk); + if (!IS_ERR(motg->pclk_src)) { + clk_disable(motg->pclk_src); + clk_put(motg->pclk_src); + } iounmap(motg->regs); pm_runtime_set_suspended(&pdev->dev); diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 3657403eac1..31ef1853f93 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -64,7 +64,8 @@ enum otg_control_type { * @otg_control: OTG switch controlled by user/Id pin * @default_mode: Default operational mode. Applicable only if * OTG switch is controller by user. - * + * @pclk_src_name: pclk is derived from ebi1_usb_clk in case of 7x27 and 8k + * dfab_usb_hs_clk in case of 8660 and 8960. */ struct msm_otg_platform_data { int *phy_init_seq; @@ -74,6 +75,7 @@ struct msm_otg_platform_data { enum otg_control_type otg_control; enum usb_mode_type default_mode; void (*setup_gpio)(enum usb_otg_state state); + char *pclk_src_name; }; /** @@ -83,6 +85,7 @@ struct msm_otg_platform_data { * @irq: IRQ number assigned for HSUSB controller. * @clk: clock struct of usb_hs_clk. * @pclk: clock struct of usb_hs_pclk. + * @pclk_src: pclk source for voting. * @phy_reset_clk: clock struct of usb_phy_clk. * @core_clk: clock struct of usb_hs_core_clk. * @regs: ioremapped register base address. @@ -90,7 +93,6 @@ struct msm_otg_platform_data { * @sm_work: OTG state machine work. * @in_lpm: indicates low power mode (LPM) state. * @async_int: Async interrupt arrived. - * */ struct msm_otg { struct otg_transceiver otg; @@ -98,6 +100,7 @@ struct msm_otg { int irq; struct clk *clk; struct clk *pclk; + struct clk *pclk_src; struct clk *phy_reset_clk; struct clk *core_clk; void __iomem *regs; -- cgit v1.2.3 From d860852e087eed7eadbea64f1a8db9a231c5e9b3 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Wed, 4 May 2011 10:19:47 +0530 Subject: USB: OTG: msm: Implement charger detection Implement good battery algorithm defined in the battery charging V1.2 spec for detecting different charging ports. USB hardware is put into low power mode when connected to a dedicated charging port. vbus_draw and set_power methods are implemented for determining the allowed current from Host in different states (un-configured/suspend/configured). The charger block is implemented using vendor specific registers and the PHY used in MSM8960(28nm PHY) different from older targets like MSM8x60 and MSM7x30(45nm PHY). The PHY vendor and product id registers are not implemented in the above chipsets. Hence PHY type is passed via platform data. Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ci13xxx_udc.c | 10 ++ drivers/usb/otg/msm_otg.c | 380 ++++++++++++++++++++++++++++++++++++++- include/linux/usb/msm_hsusb.h | 72 +++++++- 3 files changed, 457 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 68123ee0139..baaf87ed768 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -2506,6 +2506,15 @@ out: return ret; } +static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA) +{ + struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + + if (udc->transceiver) + return otg_set_power(udc->transceiver, mA); + return -ENOTSUPP; +} + /** * Device operations part of the API to the USB controller hardware, * which don't involve endpoints (or i/o) @@ -2514,6 +2523,7 @@ out: static const struct usb_gadget_ops usb_gadget_ops = { .vbus_session = ci13xxx_vbus_session, .wakeup = ci13xxx_wakeup, + .vbus_draw = ci13xxx_vbus_draw, }; /** diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 7792fef0be5..854b7e3413d 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -409,6 +409,33 @@ skip_phy_resume: } #endif +static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) +{ + if (motg->cur_power == mA) + return; + + /* TODO: Notify PMIC about available current */ + dev_info(motg->otg.dev, "Avail curr from USB = %u\n", mA); + motg->cur_power = mA; +} + +static int msm_otg_set_power(struct otg_transceiver *otg, unsigned mA) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + + /* + * Gadget driver uses set_power method to notify about the + * available current based on suspend/configured states. + * + * IDEV_CHG can be drawn irrespective of suspend/un-configured + * states when CDP/ACA is connected. + */ + if (motg->chg_type == USB_SDP_CHARGER) + msm_otg_notify_charger(motg, mA); + + return 0; +} + static void msm_otg_start_host(struct otg_transceiver *otg, int on) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); @@ -563,6 +590,306 @@ static int msm_otg_set_peripheral(struct otg_transceiver *otg, return 0; } +static bool msm_chg_check_secondary_det(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 chg_det; + bool ret = false; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + ret = chg_det & (1 << 4); + break; + case SNPS_28NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x87); + ret = chg_det & 1; + break; + default: + break; + } + return ret; +} + +static void msm_chg_enable_secondary_det(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + /* Turn off charger block */ + chg_det |= ~(1 << 1); + ulpi_write(otg, chg_det, 0x34); + udelay(20); + /* control chg block via ULPI */ + chg_det &= ~(1 << 3); + ulpi_write(otg, chg_det, 0x34); + /* put it in host mode for enabling D- source */ + chg_det &= ~(1 << 2); + ulpi_write(otg, chg_det, 0x34); + /* Turn on chg detect block */ + chg_det &= ~(1 << 1); + ulpi_write(otg, chg_det, 0x34); + udelay(20); + /* enable chg detection */ + chg_det &= ~(1 << 0); + ulpi_write(otg, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* + * Configure DM as current source, DP as current sink + * and enable battery charging comparators. + */ + ulpi_write(otg, 0x8, 0x85); + ulpi_write(otg, 0x2, 0x85); + ulpi_write(otg, 0x1, 0x85); + break; + default: + break; + } +} + +static bool msm_chg_check_primary_det(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 chg_det; + bool ret = false; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + ret = chg_det & (1 << 4); + break; + case SNPS_28NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x87); + ret = chg_det & 1; + break; + default: + break; + } + return ret; +} + +static void msm_chg_enable_primary_det(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + /* enable chg detection */ + chg_det &= ~(1 << 0); + ulpi_write(otg, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* + * Configure DP as current source, DM as current sink + * and enable battery charging comparators. + */ + ulpi_write(otg, 0x2, 0x85); + ulpi_write(otg, 0x1, 0x85); + break; + default: + break; + } +} + +static bool msm_chg_check_dcd(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 line_state; + bool ret = false; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + line_state = ulpi_read(otg, 0x15); + ret = !(line_state & 1); + break; + case SNPS_28NM_INTEGRATED_PHY: + line_state = ulpi_read(otg, 0x87); + ret = line_state & 2; + break; + default: + break; + } + return ret; +} + +static void msm_chg_disable_dcd(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + chg_det &= ~(1 << 5); + ulpi_write(otg, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + ulpi_write(otg, 0x10, 0x86); + break; + default: + break; + } +} + +static void msm_chg_enable_dcd(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + /* Turn on D+ current source */ + chg_det |= (1 << 5); + ulpi_write(otg, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* Data contact detection enable */ + ulpi_write(otg, 0x10, 0x85); + break; + default: + break; + } +} + +static void msm_chg_block_on(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 func_ctrl, chg_det; + + /* put the controller in non-driving mode */ + func_ctrl = ulpi_read(otg, ULPI_FUNC_CTRL); + func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; + ulpi_write(otg, func_ctrl, ULPI_FUNC_CTRL); + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + /* control chg block via ULPI */ + chg_det &= ~(1 << 3); + ulpi_write(otg, chg_det, 0x34); + /* Turn on chg detect block */ + chg_det &= ~(1 << 1); + ulpi_write(otg, chg_det, 0x34); + udelay(20); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* Clear charger detecting control bits */ + ulpi_write(otg, 0x3F, 0x86); + /* Clear alt interrupt latch and enable bits */ + ulpi_write(otg, 0x1F, 0x92); + ulpi_write(otg, 0x1F, 0x95); + udelay(100); + break; + default: + break; + } +} + +static void msm_chg_block_off(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 func_ctrl, chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + /* Turn off charger block */ + chg_det |= ~(1 << 1); + ulpi_write(otg, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* Clear charger detecting control bits */ + ulpi_write(otg, 0x3F, 0x86); + /* Clear alt interrupt latch and enable bits */ + ulpi_write(otg, 0x1F, 0x92); + ulpi_write(otg, 0x1F, 0x95); + break; + default: + break; + } + + /* put the controller in normal mode */ + func_ctrl = ulpi_read(otg, ULPI_FUNC_CTRL); + func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL; + ulpi_write(otg, func_ctrl, ULPI_FUNC_CTRL); +} + +#define MSM_CHG_DCD_POLL_TIME (100 * HZ/1000) /* 100 msec */ +#define MSM_CHG_DCD_MAX_RETRIES 6 /* Tdcd_tmout = 6 * 100 msec */ +#define MSM_CHG_PRIMARY_DET_TIME (40 * HZ/1000) /* TVDPSRC_ON */ +#define MSM_CHG_SECONDARY_DET_TIME (40 * HZ/1000) /* TVDMSRC_ON */ +static void msm_chg_detect_work(struct work_struct *w) +{ + struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work); + struct otg_transceiver *otg = &motg->otg; + bool is_dcd, tmout, vout; + unsigned long delay; + + dev_dbg(otg->dev, "chg detection work\n"); + switch (motg->chg_state) { + case USB_CHG_STATE_UNDEFINED: + pm_runtime_get_sync(otg->dev); + msm_chg_block_on(motg); + msm_chg_enable_dcd(motg); + motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; + motg->dcd_retries = 0; + delay = MSM_CHG_DCD_POLL_TIME; + break; + case USB_CHG_STATE_WAIT_FOR_DCD: + is_dcd = msm_chg_check_dcd(motg); + tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES; + if (is_dcd || tmout) { + msm_chg_disable_dcd(motg); + msm_chg_enable_primary_det(motg); + delay = MSM_CHG_PRIMARY_DET_TIME; + motg->chg_state = USB_CHG_STATE_DCD_DONE; + } else { + delay = MSM_CHG_DCD_POLL_TIME; + } + break; + case USB_CHG_STATE_DCD_DONE: + vout = msm_chg_check_primary_det(motg); + if (vout) { + msm_chg_enable_secondary_det(motg); + delay = MSM_CHG_SECONDARY_DET_TIME; + motg->chg_state = USB_CHG_STATE_PRIMARY_DONE; + } else { + motg->chg_type = USB_SDP_CHARGER; + motg->chg_state = USB_CHG_STATE_DETECTED; + delay = 0; + } + break; + case USB_CHG_STATE_PRIMARY_DONE: + vout = msm_chg_check_secondary_det(motg); + if (vout) + motg->chg_type = USB_DCP_CHARGER; + else + motg->chg_type = USB_CDP_CHARGER; + motg->chg_state = USB_CHG_STATE_SECONDARY_DONE; + /* fall through */ + case USB_CHG_STATE_SECONDARY_DONE: + motg->chg_state = USB_CHG_STATE_DETECTED; + case USB_CHG_STATE_DETECTED: + msm_chg_block_off(motg); + dev_dbg(otg->dev, "charger = %d\n", motg->chg_type); + schedule_work(&motg->sm_work); + return; + default: + return; + } + + schedule_delayed_work(&motg->chg_work, delay); +} + /* * We support OTG, Peripheral only and Host only configurations. In case * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen @@ -633,9 +960,48 @@ static void msm_otg_sm_work(struct work_struct *w) writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); msm_otg_start_host(otg, 1); otg->state = OTG_STATE_A_HOST; - } else if (test_bit(B_SESS_VLD, &motg->inputs) && otg->gadget) { - msm_otg_start_peripheral(otg, 1); - otg->state = OTG_STATE_B_PERIPHERAL; + } else if (test_bit(B_SESS_VLD, &motg->inputs)) { + switch (motg->chg_state) { + case USB_CHG_STATE_UNDEFINED: + msm_chg_detect_work(&motg->chg_work.work); + break; + case USB_CHG_STATE_DETECTED: + switch (motg->chg_type) { + case USB_DCP_CHARGER: + msm_otg_notify_charger(motg, + IDEV_CHG_MAX); + break; + case USB_CDP_CHARGER: + msm_otg_notify_charger(motg, + IDEV_CHG_MAX); + msm_otg_start_peripheral(otg, 1); + otg->state = OTG_STATE_B_PERIPHERAL; + break; + case USB_SDP_CHARGER: + msm_otg_notify_charger(motg, IUNIT); + msm_otg_start_peripheral(otg, 1); + otg->state = OTG_STATE_B_PERIPHERAL; + break; + default: + break; + } + break; + default: + break; + } + } else { + /* + * If charger detection work is pending, decrement + * the pm usage counter to balance with the one that + * is incremented in charger detection work. + */ + if (cancel_delayed_work_sync(&motg->chg_work)) { + pm_runtime_put_sync(otg->dev); + msm_otg_reset(otg); + } + msm_otg_notify_charger(motg, 0); + motg->chg_state = USB_CHG_STATE_UNDEFINED; + motg->chg_type = USB_INVALID_CHARGER; } pm_runtime_put_sync(otg->dev); break; @@ -643,7 +1009,10 @@ static void msm_otg_sm_work(struct work_struct *w) dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n"); if (!test_bit(B_SESS_VLD, &motg->inputs) || !test_bit(ID, &motg->inputs)) { + msm_otg_notify_charger(motg, 0); msm_otg_start_peripheral(otg, 0); + motg->chg_state = USB_CHG_STATE_UNDEFINED; + motg->chg_type = USB_INVALID_CHARGER; otg->state = OTG_STATE_B_IDLE; msm_otg_reset(otg); schedule_work(w); @@ -935,6 +1304,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) writel(0, USB_OTGSC); INIT_WORK(&motg->sm_work, msm_otg_sm_work); + INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work); ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED, "msm_otg", motg); if (ret) { @@ -945,6 +1315,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) otg->init = msm_otg_reset; otg->set_host = msm_otg_set_host; otg->set_peripheral = msm_otg_set_peripheral; + otg->set_power = msm_otg_set_power; otg->io_ops = &msm_otg_io_ops; @@ -1004,6 +1375,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) return -EBUSY; msm_otg_debugfs_cleanup(); + cancel_delayed_work_sync(&motg->chg_work); cancel_work_sync(&motg->sm_work); pm_runtime_resume(&pdev->dev); diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 31ef1853f93..00311fe9d0d 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -2,7 +2,7 @@ * * Copyright (C) 2008 Google, Inc. * Author: Brian Swetland - * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -53,6 +53,64 @@ enum otg_control_type { OTG_USER_CONTROL, }; +/** + * PHY used in + * + * INVALID_PHY Unsupported PHY + * CI_45NM_INTEGRATED_PHY Chipidea 45nm integrated PHY + * SNPS_28NM_INTEGRATED_PHY Synopsis 28nm integrated PHY + * + */ +enum msm_usb_phy_type { + INVALID_PHY = 0, + CI_45NM_INTEGRATED_PHY, + SNPS_28NM_INTEGRATED_PHY, +}; + +#define IDEV_CHG_MAX 1500 +#define IUNIT 100 + +/** + * Different states involved in USB charger detection. + * + * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection + * process is not yet started. + * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact. + * USB_CHG_STATE_DCD_DONE Data pin contact is detected. + * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects + * between SDP and DCP/CDP). + * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects + * between DCP and CDP). + * USB_CHG_STATE_DETECTED USB charger type is determined. + * + */ +enum usb_chg_state { + USB_CHG_STATE_UNDEFINED = 0, + USB_CHG_STATE_WAIT_FOR_DCD, + USB_CHG_STATE_DCD_DONE, + USB_CHG_STATE_PRIMARY_DONE, + USB_CHG_STATE_SECONDARY_DONE, + USB_CHG_STATE_DETECTED, +}; + +/** + * USB charger types + * + * USB_INVALID_CHARGER Invalid USB charger. + * USB_SDP_CHARGER Standard downstream port. Refers to a downstream port + * on USB2.0 compliant host/hub. + * USB_DCP_CHARGER Dedicated charger port (AC charger/ Wall charger). + * USB_CDP_CHARGER Charging downstream port. Enumeration can happen and + * IDEV_CHG_MAX can be drawn irrespective of USB state. + * + */ +enum usb_chg_type { + USB_INVALID_CHARGER = 0, + USB_SDP_CHARGER, + USB_DCP_CHARGER, + USB_CDP_CHARGER, +}; + /** * struct msm_otg_platform_data - platform device data * for msm_otg driver. @@ -74,6 +132,7 @@ struct msm_otg_platform_data { enum usb_mode_type mode; enum otg_control_type otg_control; enum usb_mode_type default_mode; + enum msm_usb_phy_type phy_type; void (*setup_gpio)(enum usb_otg_state state); char *pclk_src_name; }; @@ -93,6 +152,12 @@ struct msm_otg_platform_data { * @sm_work: OTG state machine work. * @in_lpm: indicates low power mode (LPM) state. * @async_int: Async interrupt arrived. + * @cur_power: The amount of mA available from downstream port. + * @chg_work: Charger detection work. + * @chg_state: The state of charger detection process. + * @chg_type: The type of charger attached. + * @dcd_retires: The retry count used to track Data contact + * detection process. */ struct msm_otg { struct otg_transceiver otg; @@ -110,6 +175,11 @@ struct msm_otg { struct work_struct sm_work; atomic_t in_lpm; int async_int; + unsigned cur_power; + struct delayed_work chg_work; + enum usb_chg_state chg_state; + enum usb_chg_type chg_type; + u8 dcd_retries; }; #endif -- cgit v1.2.3 From 11aa5c478e743712228ff2da881b85100800c1ee Mon Sep 17 00:00:00 2001 From: Anji jonnala Date: Wed, 4 May 2011 10:19:48 +0530 Subject: USB: OTG: msm: Configure PHY Analog and Digital voltage domains Signed-off-by: Anji jonnala Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/msm_otg.c | 189 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 854b7e3413d..628ba7d943d 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -45,6 +46,171 @@ #define DRIVER_NAME "msm_otg" #define ULPI_IO_TIMEOUT_USEC (10 * 1000) + +#define USB_PHY_3P3_VOL_MIN 3050000 /* uV */ +#define USB_PHY_3P3_VOL_MAX 3300000 /* uV */ +#define USB_PHY_3P3_HPM_LOAD 50000 /* uA */ +#define USB_PHY_3P3_LPM_LOAD 4000 /* uA */ + +#define USB_PHY_1P8_VOL_MIN 1800000 /* uV */ +#define USB_PHY_1P8_VOL_MAX 1800000 /* uV */ +#define USB_PHY_1P8_HPM_LOAD 50000 /* uA */ +#define USB_PHY_1P8_LPM_LOAD 4000 /* uA */ + +#define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */ +#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */ + +static struct regulator *hsusb_3p3; +static struct regulator *hsusb_1p8; +static struct regulator *hsusb_vddcx; + +static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) +{ + int ret = 0; + + if (init) { + hsusb_vddcx = regulator_get(motg->otg.dev, "HSUSB_VDDCX"); + if (IS_ERR(hsusb_vddcx)) { + dev_err(motg->otg.dev, "unable to get hsusb vddcx\n"); + return PTR_ERR(hsusb_vddcx); + } + + ret = regulator_set_voltage(hsusb_vddcx, + USB_PHY_VDD_DIG_VOL_MIN, + USB_PHY_VDD_DIG_VOL_MAX); + if (ret) { + dev_err(motg->otg.dev, "unable to set the voltage " + "for hsusb vddcx\n"); + regulator_put(hsusb_vddcx); + return ret; + } + + ret = regulator_enable(hsusb_vddcx); + if (ret) { + dev_err(motg->otg.dev, "unable to enable hsusb vddcx\n"); + regulator_put(hsusb_vddcx); + } + } else { + ret = regulator_set_voltage(hsusb_vddcx, 0, + USB_PHY_VDD_DIG_VOL_MIN); + if (ret) { + dev_err(motg->otg.dev, "unable to set the voltage " + "for hsusb vddcx\n"); + return ret; + } + ret = regulator_disable(hsusb_vddcx); + if (ret) + dev_err(motg->otg.dev, "unable to disable hsusb vddcx\n"); + + regulator_put(hsusb_vddcx); + } + + return ret; +} + +static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) +{ + int rc = 0; + + if (init) { + hsusb_3p3 = regulator_get(motg->otg.dev, "HSUSB_3p3"); + if (IS_ERR(hsusb_3p3)) { + dev_err(motg->otg.dev, "unable to get hsusb 3p3\n"); + return PTR_ERR(hsusb_3p3); + } + + rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN, + USB_PHY_3P3_VOL_MAX); + if (rc) { + dev_err(motg->otg.dev, "unable to set voltage level " + "for hsusb 3p3\n"); + goto put_3p3; + } + rc = regulator_enable(hsusb_3p3); + if (rc) { + dev_err(motg->otg.dev, "unable to enable the hsusb 3p3\n"); + goto put_3p3; + } + hsusb_1p8 = regulator_get(motg->otg.dev, "HSUSB_1p8"); + if (IS_ERR(hsusb_1p8)) { + dev_err(motg->otg.dev, "unable to get hsusb 1p8\n"); + rc = PTR_ERR(hsusb_1p8); + goto disable_3p3; + } + rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN, + USB_PHY_1P8_VOL_MAX); + if (rc) { + dev_err(motg->otg.dev, "unable to set voltage level " + "for hsusb 1p8\n"); + goto put_1p8; + } + rc = regulator_enable(hsusb_1p8); + if (rc) { + dev_err(motg->otg.dev, "unable to enable the hsusb 1p8\n"); + goto put_1p8; + } + + return 0; + } + + regulator_disable(hsusb_1p8); +put_1p8: + regulator_put(hsusb_1p8); +disable_3p3: + regulator_disable(hsusb_3p3); +put_3p3: + regulator_put(hsusb_3p3); + return rc; +} + +static int msm_hsusb_ldo_set_mode(int on) +{ + int ret = 0; + + if (!hsusb_1p8 || IS_ERR(hsusb_1p8)) { + pr_err("%s: HSUSB_1p8 is not initialized\n", __func__); + return -ENODEV; + } + + if (!hsusb_3p3 || IS_ERR(hsusb_3p3)) { + pr_err("%s: HSUSB_3p3 is not initialized\n", __func__); + return -ENODEV; + } + + if (on) { + ret = regulator_set_optimum_mode(hsusb_1p8, + USB_PHY_1P8_HPM_LOAD); + if (ret < 0) { + pr_err("%s: Unable to set HPM of the regulator " + "HSUSB_1p8\n", __func__); + return ret; + } + ret = regulator_set_optimum_mode(hsusb_3p3, + USB_PHY_3P3_HPM_LOAD); + if (ret < 0) { + pr_err("%s: Unable to set HPM of the regulator " + "HSUSB_3p3\n", __func__); + regulator_set_optimum_mode(hsusb_1p8, + USB_PHY_1P8_LPM_LOAD); + return ret; + } + } else { + ret = regulator_set_optimum_mode(hsusb_1p8, + USB_PHY_1P8_LPM_LOAD); + if (ret < 0) + pr_err("%s: Unable to set LPM of the regulator " + "HSUSB_1p8\n", __func__); + ret = regulator_set_optimum_mode(hsusb_3p3, + USB_PHY_3P3_LPM_LOAD); + if (ret < 0) + pr_err("%s: Unable to set LPM of the regulator " + "HSUSB_3p3\n", __func__); + } + + pr_debug("reg (%s)\n", on ? "HPM" : "LPM"); + return ret < 0 ? ret : 0; +} + static int ulpi_read(struct otg_transceiver *otg, u32 reg) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); @@ -1297,6 +1463,24 @@ static int __init msm_otg_probe(struct platform_device *pdev) clk_enable(motg->clk); clk_enable(motg->pclk); + + ret = msm_hsusb_init_vddcx(motg, 1); + if (ret) { + dev_err(&pdev->dev, "hsusb vddcx configuration failed\n"); + goto free_regs; + } + + ret = msm_hsusb_ldo_init(motg, 1); + if (ret) { + dev_err(&pdev->dev, "hsusb vreg configuration failed\n"); + goto vddcx_exit; + } + ret = msm_hsusb_ldo_set_mode(1); + if (ret) { + dev_err(&pdev->dev, "hsusb vreg enable failed\n"); + goto ldo_exit; + } + if (motg->core_clk) clk_enable(motg->core_clk); @@ -1345,6 +1529,10 @@ free_irq: disable_clks: clk_disable(motg->pclk); clk_disable(motg->clk); +ldo_exit: + msm_hsusb_ldo_init(motg, 0); +vddcx_exit: + msm_hsusb_init_vddcx(motg, 0); free_regs: iounmap(motg->regs); put_core_clk: @@ -1410,6 +1598,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) clk_disable(motg->pclk_src); clk_put(motg->pclk_src); } + msm_hsusb_ldo_init(motg, 0); iounmap(motg->regs); pm_runtime_set_suspended(&pdev->dev); -- cgit v1.2.3 From 04aebcbb1b6dccadc8862b2765265f65a946db57 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Wed, 4 May 2011 10:19:49 +0530 Subject: USB: OTG: msm: Add PHY suspend support for MSM8960 Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/msm_otg.c | 64 +++++++++++++++++++++++++++++++++------- include/linux/usb/msm_hsusb_hw.h | 2 ++ 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 628ba7d943d..1cdda6c0523 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -163,6 +163,32 @@ put_3p3: return rc; } +#ifdef CONFIG_PM_SLEEP +#define USB_PHY_SUSP_DIG_VOL 500000 +static int msm_hsusb_config_vddcx(int high) +{ + int max_vol = USB_PHY_VDD_DIG_VOL_MAX; + int min_vol; + int ret; + + if (high) + min_vol = USB_PHY_VDD_DIG_VOL_MIN; + else + min_vol = USB_PHY_SUSP_DIG_VOL; + + ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol); + if (ret) { + pr_err("%s: unable to set the voltage for regulator " + "HSUSB_VDDCX\n", __func__); + return ret; + } + + pr_debug("%s: min_vol:%d max_vol:%d\n", __func__, min_vol, max_vol); + + return ret; +} +#endif + static int msm_hsusb_ldo_set_mode(int on) { int ret = 0; @@ -434,27 +460,28 @@ static int msm_otg_suspend(struct msm_otg *motg) disable_irq(motg->irq); /* + * Chipidea 45-nm PHY suspend sequence: + * * Interrupt Latch Register auto-clear feature is not present * in all PHY versions. Latch register is clear on read type. * Clear latch register to avoid spurious wakeup from * low power mode (LPM). - */ - ulpi_read(otg, 0x14); - - /* + * * PHY comparators are disabled when PHY enters into low power * mode (LPM). Keep PHY comparators ON in LPM only when we expect * VBUS/Id notifications from USB PHY. Otherwise turn off USB * PHY comparators. This save significant amount of power. - */ - if (pdata->otg_control == OTG_PHY_CONTROL) - ulpi_write(otg, 0x01, 0x30); - - /* + * * PLL is not turned off when PHY enters into low power mode (LPM). * Disable PLL for maximum power savings. */ - ulpi_write(otg, 0x08, 0x09); + + if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY) { + ulpi_read(otg, 0x14); + if (pdata->otg_control == OTG_PHY_CONTROL) + ulpi_write(otg, 0x01, 0x30); + ulpi_write(otg, 0x08, 0x09); + } /* * PHY may take some time or even fail to enter into low power @@ -485,6 +512,10 @@ static int msm_otg_suspend(struct msm_otg *motg) */ writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL) + writel(readl(USB_PHY_CTRL) | PHY_RETEN, USB_PHY_CTRL); + clk_disable(motg->pclk); clk_disable(motg->clk); if (motg->core_clk) @@ -493,6 +524,12 @@ static int msm_otg_suspend(struct msm_otg *motg) if (!IS_ERR(motg->pclk_src)) clk_disable(motg->pclk_src); + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL) { + msm_hsusb_ldo_set_mode(0); + msm_hsusb_config_vddcx(0); + } + if (device_may_wakeup(otg->dev)) enable_irq_wake(motg->irq); if (bus) @@ -524,6 +561,13 @@ static int msm_otg_resume(struct msm_otg *motg) if (motg->core_clk) clk_enable(motg->core_clk); + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL) { + msm_hsusb_ldo_set_mode(1); + msm_hsusb_config_vddcx(1); + writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL); + } + temp = readl(USB_USBCMD); temp &= ~ASYNC_INTR_CTRL; temp &= ~ULPI_STP_CTRL; diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index 7d1babbff07..6e97a2d3d39 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -24,6 +24,7 @@ #define USB_PORTSC (MSM_USB_BASE + 0x0184) #define USB_OTGSC (MSM_USB_BASE + 0x01A4) #define USB_USBMODE (MSM_USB_BASE + 0x01A8) +#define USB_PHY_CTRL (MSM_USB_BASE + 0x0240) #define USBCMD_RESET 2 #define USB_USBINTR (MSM_USB_BASE + 0x0148) @@ -42,6 +43,7 @@ #define ASYNC_INTR_CTRL (1 << 29) /* Enable async interrupt */ #define ULPI_STP_CTRL (1 << 30) /* Block communication with PHY */ +#define PHY_RETEN (1 << 1) /* PHY retention enable/disable */ /* OTG definitions */ #define OTGSC_INTSTS_MASK (0x7f << 16) -- cgit v1.2.3 From b83cdc8f4d94a127e9319bef37f384b01ecca72e Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 5 May 2011 18:13:56 +0800 Subject: xHCI 1.0: Setup Stage TRB Transfer Type flag Setup Stage Transfer Type field is added to indicate the presence and the direction of the Data Stage TD, and determines the direction of the Status Stage TD so the wLength length field should be ignored by the xHC. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 11 +++++++++++ drivers/usb/host/xhci.h | 3 +++ 2 files changed, 14 insertions(+) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 396f8d2a2e8..3e759af049b 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3053,6 +3053,17 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field |= TRB_IDT | TRB_TYPE(TRB_SETUP); if (start_cycle == 0) field |= 0x1; + + /* xHCI 1.0 6.4.1.2.1: Transfer Type field */ + if (xhci->hci_version == 0x100) { + if (urb->transfer_buffer_length > 0) { + if (setup->bRequestType & USB_DIR_IN) + field |= TRB_TX_TYPE(TRB_DATA_IN); + else + field |= TRB_TX_TYPE(TRB_DATA_OUT); + } + } + queue_trb(xhci, ep_ring, false, true, setup->bRequestType | setup->bRequest << 8 | le16_to_cpu(setup->wValue) << 16, le16_to_cpu(setup->wIndex) | le16_to_cpu(setup->wLength) << 16, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index db661543a80..af8b66f2fd0 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -967,6 +967,9 @@ struct xhci_event_cmd { /* Control transfer TRB specific fields */ #define TRB_DIR_IN (1<<16) +#define TRB_TX_TYPE(p) ((p) << 16) +#define TRB_DATA_OUT 2 +#define TRB_DATA_IN 3 /* Isochronous TRB specific fields */ #define TRB_SIA (1<<31) -- cgit v1.2.3 From 51eb01a746089f2c3d9b87f870353772d2fb4c37 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 5 May 2011 18:13:58 +0800 Subject: xHCI 1.0: Control endpoint average TRB length field set xHCI 1.0 specification indicates that software should set Average TRB Length to '8' for control endpoints. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-mem.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index a4fc4d92938..543833b9dba 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1246,8 +1246,15 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, * including link TRBs, No-op TRBs, and Event data TRBs. Since we don't * use Event Data TRBs, and we don't chain in a link TRB on short * transfers, we're basically dividing by 1. + * + * xHCI 1.0 specification indicates that the Average TRB Length should + * be set to 8 for control endpoints. */ - ep_ctx->tx_info |= cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(max_esit_payload)); + if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version == 0x100) + ep_ctx->tx_info |= cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(8)); + else + ep_ctx->tx_info |= + cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(max_esit_payload)); /* FIXME Debug endpoint context */ return 0; -- cgit v1.2.3 From 7b1fc2ea8a5fbf9487d83865456cff77d0249ea9 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 5 May 2011 18:14:00 +0800 Subject: xHCI 1.0: Isoch endpoint CErr field set xHCI 1.0 specification specifies that CErr does not apply to Isoch endpoints and shall be set to '0' for Isoch endpoints. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 543833b9dba..04145740686 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1190,12 +1190,12 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, /* FIXME dig Mult and streams info out of ep companion desc */ /* Allow 3 retries for everything but isoc; - * error count = 0 means infinite retries. + * CErr shall be set to 0 for Isoch endpoints. */ if (!usb_endpoint_xfer_isoc(&ep->desc)) ep_ctx->ep_info2 = cpu_to_le32(ERROR_COUNT(3)); else - ep_ctx->ep_info2 = cpu_to_le32(ERROR_COUNT(1)); + ep_ctx->ep_info2 = cpu_to_le32(ERROR_COUNT(0)); ep_ctx->ep_info2 |= cpu_to_le32(xhci_get_endpoint_type(udev, ep)); -- cgit v1.2.3 From ad106f292369d753d5c75751cb9e760726e3cd00 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 5 May 2011 18:14:02 +0800 Subject: xHCI 1.0: Block Interrupts for Isoch transfer Currently an isoc URB is divided into multiple TDs, and every TD will trigger an interrupt when it's processed. However, software can schedule multiple TDs at a time, and it only needs an interrupt every URB. xHCI 1.0 introduces the Block Event Interrupt(BEI) flag which allows Normal and Isoch Transfer TRBs to place an Event TRB on an Event Ring but not assert an intrrupt to the host, and the interrupt rate is significantly reduced and the system performance is improved. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 5 +++++ drivers/usb/host/xhci.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3e759af049b..c35058b94de 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3293,6 +3293,11 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } else { td->last_trb = ep_ring->enqueue; field |= TRB_IOC; + if (xhci->hci_version == 0x100) { + /* Set BEI bit except for the last td */ + if (i < num_tds - 1) + field |= TRB_BEI; + } more_trbs_coming = false; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index af8b66f2fd0..33a49d5d6c2 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -964,6 +964,8 @@ struct xhci_event_cmd { /* The buffer pointer contains immediate data */ #define TRB_IDT (1<<6) +/* Block Event Interrupt */ +#define TRB_BEI (1<<9) /* Control transfer TRB specific fields */ #define TRB_DIR_IN (1<<16) -- cgit v1.2.3 From 700b41736c07f357df0a1b8f342fd237f365b58d Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 5 May 2011 18:14:05 +0800 Subject: xHCI 1.0: TT_THINK_TIME set xHCI 1.0 spec says the TT Think Time field shall be set to zero if the device is not a High-speed hub. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6864759c8d1..3abf33223b1 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2699,11 +2699,16 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, /* Set TT think time - convert from ns to FS bit times. * 0 = 8 FS bit times, 1 = 16 FS bit times, * 2 = 24 FS bit times, 3 = 32 FS bit times. + * + * xHCI 1.0: this field shall be 0 if the device is not a + * High-spped hub. */ think_time = tt->think_time; if (think_time != 0) think_time = (think_time / 666) - 1; - slot_ctx->tt_info |= cpu_to_le32(TT_THINK_TIME(think_time)); + if (xhci->hci_version < 0x100 || hdev->speed == USB_SPEED_HIGH) + slot_ctx->tt_info |= + cpu_to_le32(TT_THINK_TIME(think_time)); } else { xhci_dbg(xhci, "xHCI version %x doesn't need hub " "TT think time or number of ports\n", -- cgit v1.2.3 From 1bb73a88839d473f4f2c529ecf453029439aa837 Mon Sep 17 00:00:00 2001 From: Alex He Date: Thu, 5 May 2011 18:14:12 +0800 Subject: xHCI 1.0: Max Exit Latency Too Large Error This is a new TRB Completion Code of the xHCI spec 1.0. Asserted by the Evalute Context Command if the proposed Max Exit Latency would not allow the periodic endpoints of the Device Slot to be scheduled. Signed-off-by: Alex He Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 5 +++++ drivers/usb/host/xhci.h | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 3abf33223b1..013e113b818 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1560,6 +1560,11 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci, xhci_dbg_ctx(xhci, virt_dev->out_ctx, 1); ret = -EINVAL; break; + case COMP_MEL_ERR: + /* Max Exit Latency too large error */ + dev_warn(&udev->dev, "WARN: Max Exit Latency too large\n"); + ret = -EINVAL; + break; case COMP_SUCCESS: dev_dbg(&udev->dev, "Successful evaluate context command\n"); ret = 0; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 33a49d5d6c2..e12db7cfb9b 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -881,7 +881,9 @@ struct xhci_transfer_event { #define COMP_STOP_INVAL 27 /* Control Abort Error - Debug Capability - control pipe aborted */ #define COMP_DBG_ABORT 28 -/* TRB type 29 and 30 reserved */ +/* Max Exit Latency Too Large Error */ +#define COMP_MEL_ERR 29 +/* TRB type 30 reserved */ /* Isoc Buffer Overrun - an isoc IN ep sent more data than could fit in TD */ #define COMP_BUFF_OVER 31 /* Event Lost Error - xHC has an "internal event overrun condition" */ -- cgit v1.2.3 From 1b9ba000177ee47bcc5b44c7c34e48e735f5f9b1 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 9 May 2011 13:08:06 +0300 Subject: usb: gadget: composite: Allow function drivers to pause control transfers Some USB function drivers (e.g. f_mass_storage.c) need to delay or defer the data/status stages of standard control requests like SET_CONFIGURATION or SET_INTERFACE till they are done with their bookkeeping and are actually ready for accepting new commands to their interface. They can now achieve this functionality by returning USB_GADGET_DELAYED_STATUS in their setup handlers (e.g. set_alt()). The composite framework will then defer completion of the control transfer by not completing the data/status stages. This ensures that the host does not send new packets to the interface till the function driver is ready to take them. When the function driver that requested for USB_GADGET_DELAYED_STATUS is done with its bookkeeping, it should signal the composite framework to continue with the data/status stages of the control transfer. It can do so by invoking the new API usb_composite_setup_continue(). This is where the control transfer's data/status stages are completed and host can initiate new transfers. The DELAYED_STATUS mechanism is currently only supported if the expected data phase is 0 bytes (i.e. w_length == 0). Since SET_CONFIGURATION and SET_INTERFACE are the only cases that will use this mechanism, this is not a limitation. Signed-off-by: Roger Quadros Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 62 +++++++++++++++++++++++++++++++++++++++++- include/linux/usb/composite.h | 16 ++++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 82314ed2250..5cbb1a41c22 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -461,12 +461,23 @@ static int set_config(struct usb_composite_dev *cdev, reset_config(cdev); goto done; } + + if (result == USB_GADGET_DELAYED_STATUS) { + DBG(cdev, + "%s: interface %d (%s) requested delayed status\n", + __func__, tmp, f->name); + cdev->delayed_status++; + DBG(cdev, "delayed_status count %d\n", + cdev->delayed_status); + } } /* when we return, be sure our power usage is valid */ power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW; done: usb_gadget_vbus_draw(gadget, power); + if (result >= 0 && cdev->delayed_status) + result = USB_GADGET_DELAYED_STATUS; return result; } @@ -895,6 +906,14 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (w_value && !f->set_alt) break; value = f->set_alt(f, w_index, w_value); + if (value == USB_GADGET_DELAYED_STATUS) { + DBG(cdev, + "%s: interface %d (%s) requested delayed status\n", + __func__, intf, f->name); + cdev->delayed_status++; + DBG(cdev, "delayed_status count %d\n", + cdev->delayed_status); + } break; case USB_REQ_GET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) @@ -958,7 +977,7 @@ unknown: } /* respond with data transfer before status phase? */ - if (value >= 0) { + if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) { req->length = value; req->zero = value < w_length; value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); @@ -967,6 +986,10 @@ unknown: req->status = 0; composite_setup_complete(gadget->ep0, req); } + } else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) { + WARN(cdev, + "%s: Delayed status not supported for w_length != 0", + __func__); } done: @@ -1289,3 +1312,40 @@ void usb_composite_unregister(struct usb_composite_driver *driver) return; usb_gadget_unregister_driver(&composite_driver); } + +/** + * usb_composite_setup_continue() - Continue with the control transfer + * @cdev: the composite device who's control transfer was kept waiting + * + * This function must be called by the USB function driver to continue + * with the control transfer's data/status stage in case it had requested to + * delay the data/status stages. A USB function's setup handler (e.g. set_alt()) + * can request the composite framework to delay the setup request's data/status + * stages by returning USB_GADGET_DELAYED_STATUS. + */ +void usb_composite_setup_continue(struct usb_composite_dev *cdev) +{ + int value; + struct usb_request *req = cdev->req; + unsigned long flags; + + DBG(cdev, "%s\n", __func__); + spin_lock_irqsave(&cdev->lock, flags); + + if (cdev->delayed_status == 0) { + WARN(cdev, "%s: Unexpected call\n", __func__); + + } else if (--cdev->delayed_status == 0) { + DBG(cdev, "%s: Completing delayed status\n", __func__); + req->length = 0; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DBG(cdev, "ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(cdev->gadget->ep0, req); + } + } + + spin_unlock_irqrestore(&cdev->lock, flags); +} + diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 882a084a841..b78cba466d3 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -37,6 +37,14 @@ #include #include +/* + * USB function drivers should return USB_GADGET_DELAYED_STATUS if they + * wish to delay the data/status stages of the control transfer till they + * are ready. The control transfer will then be kept from completing till + * all the function drivers that requested for USB_GADGET_DELAYED_STAUS + * invoke usb_composite_setup_continue(). + */ +#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */ struct usb_configuration; @@ -285,6 +293,7 @@ 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); +extern void usb_composite_setup_continue(struct usb_composite_dev *cdev); /** @@ -342,7 +351,12 @@ struct usb_composite_dev { */ unsigned deactivations; - /* protects at least deactivation count */ + /* the composite driver won't complete the control transfer's + * data/status stages till delayed_status is zero. + */ + int delayed_status; + + /* protects deactivations and delayed_status counts*/ spinlock_t lock; }; -- cgit v1.2.3 From 95ed32366748e2034e82c9e738c312df8fb3d3a9 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 9 May 2011 13:08:07 +0300 Subject: usb: gadget: f_mass_storage: Make us pass USBCV MSC Compliance tests Defer the SET_CONFIG and SET_INTERFACE control transfer's data/status stages till we are ready to process new CBW from the host. This way we ensure that we don't loose any CBW during MSC compliance tests and cause lock up. Signed-off-by: Roger Quadros Acked-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 01ae27b60d4..ad96be808b2 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -347,6 +347,7 @@ struct fsg_operations { /* Data shared by all the FSG instances. */ struct fsg_common { struct usb_gadget *gadget; + struct usb_composite_dev *cdev; struct fsg_dev *fsg, *new_fsg; wait_queue_head_t fsg_wait; @@ -2441,7 +2442,7 @@ static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) struct fsg_dev *fsg = fsg_from_func(f); fsg->common->new_fsg = fsg; raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); - return 0; + return USB_GADGET_DELAYED_STATUS; } static void fsg_disable(struct usb_function *f) @@ -2577,6 +2578,8 @@ static void handle_exception(struct fsg_common *common) case FSG_STATE_CONFIG_CHANGE: do_set_interface(common, common->new_fsg); + if (common->new_fsg) + usb_composite_setup_continue(common->cdev); break; case FSG_STATE_EXIT: @@ -2747,6 +2750,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, common->gadget = gadget; common->ep0 = gadget->ep0; common->ep0req = cdev->req; + common->cdev = cdev; /* Maybe allocate device-global string IDs, and patch descriptors */ if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { -- cgit v1.2.3 From c851e83f00f6d343ff7e7405f6f50eb3ba288a2c Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 10 May 2011 11:17:33 +0200 Subject: USB: cp210x: fix typo, Telegesys should have been Telegesis CC: David Chalmers Reported-by: Xavier Carcelle Signed-off-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index ebeccb75558..fd67cc53545 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -102,7 +102,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ { USB_DEVICE(0x10C4, 0x822B) }, /* Modem EDGE(GSM) Comander 2 */ { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */ - { USB_DEVICE(0x10C4, 0x8293) }, /* Telegesys ETRX2USB */ + { USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */ { USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ { USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */ -- cgit v1.2.3 From 27362d467b3d9955f6ae1737002dac8c0f99fdc7 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 9 May 2011 15:28:39 +0900 Subject: USB: ehci-s5p : use __devinit and __devexit macros for probe and remove The __devinit and __devexit macros were added to probe and remove functions. The macros move the probe and remove functions to the devinit and devexit sections Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-s5p.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 321a03301ad..e3374c8f7b3 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -56,7 +56,7 @@ static const struct hc_driver s5p_ehci_hc_driver = { .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; -static int s5p_ehci_probe(struct platform_device *pdev) +static int __devinit s5p_ehci_probe(struct platform_device *pdev) { struct s5p_ehci_platdata *pdata; struct s5p_ehci_hcd *s5p_ehci; @@ -158,7 +158,7 @@ fail_hcd: return err; } -static int s5p_ehci_remove(struct platform_device *pdev) +static int __devexit s5p_ehci_remove(struct platform_device *pdev) { struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev); @@ -191,7 +191,7 @@ static void s5p_ehci_shutdown(struct platform_device *pdev) static struct platform_driver s5p_ehci_driver = { .probe = s5p_ehci_probe, - .remove = s5p_ehci_remove, + .remove = __devexit_p(s5p_ehci_remove), .shutdown = s5p_ehci_shutdown, .driver = { .name = "s5p-ehci", -- cgit v1.2.3 From 304b0b572c66bcd89df13e856db16503609c1a24 Mon Sep 17 00:00:00 2001 From: Greg Dietsche Date: Sun, 8 May 2011 22:51:43 -0500 Subject: usb: fix warning in usbtest module v2 On amd64 unsigned is not as wide as pointer and this causes a compiler warning. Switching to unsigned long corrects the problem. Signed-off-by: Greg Dietsche Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 7e0bb2af8e6..c5676202d74 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -268,9 +268,9 @@ static inline void simple_fill_buf(struct urb *urb) } } -static inline unsigned buffer_offset(void *buf) +static inline unsigned long buffer_offset(void *buf) { - return (unsigned)buf & (ARCH_KMALLOC_MINALIGN - 1); + return (unsigned long)buf & (ARCH_KMALLOC_MINALIGN - 1); } static int check_guard_bytes(struct usbtest_dev *tdev, struct urb *urb) @@ -329,7 +329,7 @@ static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb) static void simple_free_urb(struct urb *urb) { - unsigned offset = buffer_offset(urb->transfer_buffer); + unsigned long offset = buffer_offset(urb->transfer_buffer); if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) usb_free_coherent( -- cgit v1.2.3 From 849426c3a430a6fb4613b0e5dbb81bcd6b10a075 Mon Sep 17 00:00:00 2001 From: Maxin B John Date: Sun, 8 May 2011 15:56:17 +0300 Subject: usb: gadget: Remove the LUN checks which are always true Comparing an unsigned integer with greater than or equal to zero is always true. So, it is safe to remove similar checks from 'f_mass_storage.c' and 'file_storage.c' Signed-off-by: Maxin B. John Acked-by: Michal Nazarewicz Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 2 +- drivers/usb/gadget/file_storage.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index ad96be808b2..efb58f9f5aa 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -1884,7 +1884,7 @@ static int check_command(struct fsg_common *common, int cmnd_size, common->lun, lun); /* Check the LUN */ - if (common->lun >= 0 && common->lun < common->nluns) { + if (common->lun < common->nluns) { curlun = &common->luns[common->lun]; common->curlun = curlun; if (common->cmnd[0] != REQUEST_SENSE) { diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index fcfc77c7ad7..0360f56221e 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -2285,7 +2285,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, fsg->lun = lun; // Use LUN from the command /* Check the LUN */ - if (fsg->lun >= 0 && fsg->lun < fsg->nluns) { + if (fsg->lun < fsg->nluns) { fsg->curlun = curlun = &fsg->luns[fsg->lun]; if (fsg->cmnd[0] != REQUEST_SENSE) { curlun->sense_data = SS_NO_SENSE; -- cgit v1.2.3 From 0a94c6b121c58692a9b6cbe2cd9a26ffdc2c4a82 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 7 May 2011 22:24:49 +0200 Subject: ARM: S3C2416: Add USB Phy register definitions Add register definitions required to configure the USB Phy. The definitions for PHYCTRL, PHYPWR, URSTCON and UCLKCON registers and corresponding bit field definitions are added. Signed-off-by: Thomas Abraham Signed-off-by: Sangbeom Kim Signed-off-by: Greg Kroah-Hartman --- .../mach-s3c2410/include/mach/regs-s3c2443-clock.h | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h index 44494a56e68..5e06c726583 100644 --- a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h +++ b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h @@ -37,6 +37,10 @@ #define S3C2443_SYSID S3C2443_CLKREG(0x5C) #define S3C2443_PWRCFG S3C2443_CLKREG(0x60) #define S3C2443_RSTCON S3C2443_CLKREG(0x64) +#define S3C2443_PHYCTRL S3C2443_CLKREG(0x80) +#define S3C2443_PHYPWR S3C2443_CLKREG(0x84) +#define S3C2443_URSTCON S3C2443_CLKREG(0x88) +#define S3C2443_UCLKCON S3C2443_CLKREG(0x8C) #define S3C2443_SWRST_RESET (0x533c2443) @@ -121,6 +125,27 @@ #define S3C2443_PWRCFG_SLEEP (1<<15) +#define S3C2443_PWRCFG_USBPHY (1 << 4) + +#define S3C2443_URSTCON_FUNCRST (1 << 2) +#define S3C2443_URSTCON_PHYRST (1 << 0) + +#define S3C2443_PHYCTRL_CLKSEL (1 << 3) +#define S3C2443_PHYCTRL_EXTCLK (1 << 2) +#define S3C2443_PHYCTRL_PLLSEL (1 << 1) +#define S3C2443_PHYCTRL_DSPORT (1 << 0) + +#define S3C2443_PHYPWR_COMMON_ON (1 << 31) +#define S3C2443_PHYPWR_ANALOG_PD (1 << 4) +#define S3C2443_PHYPWR_PLL_REFCLK (1 << 3) +#define S3C2443_PHYPWR_XO_ON (1 << 2) +#define S3C2443_PHYPWR_PLL_PWRDN (1 << 1) +#define S3C2443_PHYPWR_FSUSPEND (1 << 0) + +#define S3C2443_UCLKCON_DETECT_VBUS (1 << 31) +#define S3C2443_UCLKCON_FUNC_CLKEN (1 << 2) +#define S3C2443_UCLKCON_TCLKEN (1 << 0) + #include static inline unsigned int -- cgit v1.2.3 From 4a98f590659113f72c021201781d5b83bc743a7c Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 7 May 2011 22:26:53 +0200 Subject: ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller. S3C2416, S3C2443 and S3C2450 includes a USB High-Speed Gadget controller module. This patch adds the following for supporting this controller. 1. Definition for USB High-Speed controller base address. 2. Platform device instantiation. 3. Declaration for platform data structure. 4. Functionality to setup platform data for the controller. Signed-off-by: Thomas Abraham Signed-off-by: Sangbeom Kim Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-s3c2410/include/mach/map.h | 4 +++ arch/arm/plat-s3c24xx/devs.c | 41 +++++++++++++++++++++++++++++++ arch/arm/plat-s3c24xx/include/plat/udc.h | 17 +++++++++++++ arch/arm/plat-samsung/include/plat/devs.h | 1 + 4 files changed, 63 insertions(+) diff --git a/arch/arm/mach-s3c2410/include/mach/map.h b/arch/arm/mach-s3c2410/include/mach/map.h index 25bbf5a942d..425552d84b6 100644 --- a/arch/arm/mach-s3c2410/include/mach/map.h +++ b/arch/arm/mach-s3c2410/include/mach/map.h @@ -21,6 +21,10 @@ /* USB host controller */ #define S3C2410_PA_USBHOST (0x49000000) +/* S3C2416/S3C2443/S3C2450 High-Speed USB Gadget */ +#define S3C2416_PA_HSUDC (0x49800000) +#define S3C2416_SZ_HSUDC (SZ_4K) + /* DMA controller */ #define S3C2410_PA_DMA (0x4B000000) #define S3C24XX_SZ_DMA SZ_1M diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c index 268f3ed0a10..73667994518 100644 --- a/arch/arm/plat-s3c24xx/devs.c +++ b/arch/arm/plat-s3c24xx/devs.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -233,6 +234,46 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd) } } +/* USB High Speed 2.0 Device (Gadget) */ +static struct resource s3c_hsudc_resource[] = { + [0] = { + .start = S3C2416_PA_HSUDC, + .end = S3C2416_PA_HSUDC + S3C2416_SZ_HSUDC - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_USBD, + .end = IRQ_USBD, + .flags = IORESOURCE_IRQ, + } +}; + +static u64 s3c_hsudc_dmamask = DMA_BIT_MASK(32); + +struct platform_device s3c_device_usb_hsudc = { + .name = "s3c-hsudc", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_hsudc_resource), + .resource = s3c_hsudc_resource, + .dev = { + .dma_mask = &s3c_hsudc_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd) +{ + struct s3c24xx_hsudc_platdata *npd; + + npd = kmalloc(sizeof(*npd), GFP_KERNEL); + if (npd) { + memcpy(npd, pd, sizeof(*npd)); + s3c_device_usb_hsudc.dev.platform_data = npd; + } else { + printk(KERN_ERR "no memory for udc platform data\n"); + } +} + /* IIS */ static struct resource s3c_iis_resource[] = { diff --git a/arch/arm/plat-s3c24xx/include/plat/udc.h b/arch/arm/plat-s3c24xx/include/plat/udc.h index 80457c6414a..f6388424250 100644 --- a/arch/arm/plat-s3c24xx/include/plat/udc.h +++ b/arch/arm/plat-s3c24xx/include/plat/udc.h @@ -37,4 +37,21 @@ struct s3c2410_udc_mach_info { extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *); +/** + * s3c24xx_hsudc_platdata - Platform data for USB High-Speed gadget controller. + * @epnum: Number of endpoints to be instantiated by the controller driver. + * @gpio_init: Platform specific USB related GPIO initialization. + * @gpio_uninit: Platform specific USB releted GPIO uninitialzation. + * + * Representation of platform data for the S3C24XX USB 2.0 High Speed gadget + * controllers. + */ +struct s3c24xx_hsudc_platdata { + unsigned int epnum; + void (*gpio_init)(void); + void (*gpio_uninit)(void); +}; + +extern void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd); + #endif /* __ASM_ARM_ARCH_UDC_H */ diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h index 3f38debbb10..39818d8da42 100644 --- a/arch/arm/plat-samsung/include/plat/devs.h +++ b/arch/arm/plat-samsung/include/plat/devs.h @@ -88,6 +88,7 @@ extern struct platform_device s3c64xx_device_onenand1; extern struct platform_device s5p_device_onenand; extern struct platform_device s3c_device_usbgadget; +extern struct platform_device s3c_device_usb_hsudc; extern struct platform_device s3c_device_usb_hsotg; extern struct platform_device s5pv210_device_ac97; -- cgit v1.2.3 From f65680455def9eea074fce58b76006a5ce60e28e Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 7 May 2011 22:29:16 +0200 Subject: ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller Add support for USB 2.0 High-Speed gadget controller driver for Samsung's S3C2416 processor. Signed-off-by: Thomas Abraham Signed-off-by: Sangbeom Kim Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-s3c2416/mach-smdk2416.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/arm/mach-s3c2416/mach-smdk2416.c b/arch/arm/mach-s3c2416/mach-smdk2416.c index 3f83177246c..ac27ebb31c9 100644 --- a/arch/arm/mach-s3c2416/mach-smdk2416.c +++ b/arch/arm/mach-s3c2416/mach-smdk2416.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,7 @@ #include #include #include +#include #include #include @@ -47,6 +49,7 @@ #include #include #include +#include #include #include @@ -121,6 +124,27 @@ static struct s3c2410_uartcfg smdk2416_uartcfgs[] __initdata = { } }; +void smdk2416_hsudc_gpio_init(void) +{ + s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_UP); + s3c_gpio_setpull(S3C2410_GPF(2), S3C_GPIO_PULL_NONE); + s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(1)); + s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 0); +} + +void smdk2416_hsudc_gpio_uninit(void) +{ + s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 1); + s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_NONE); + s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(0)); +} + +struct s3c24xx_hsudc_platdata smdk2416_hsudc_platdata = { + .epnum = 9, + .gpio_init = smdk2416_hsudc_gpio_init, + .gpio_uninit = smdk2416_hsudc_gpio_uninit, +}; + struct s3c_fb_pd_win smdk2416_fb_win[] = { [0] = { /* think this is the same as the smdk6410 */ @@ -186,6 +210,7 @@ static struct platform_device *smdk2416_devices[] __initdata = { &s3c_device_i2c0, &s3c_device_hsmmc0, &s3c_device_hsmmc1, + &s3c_device_usb_hsudc, }; static void __init smdk2416_map_io(void) @@ -203,6 +228,8 @@ static void __init smdk2416_machine_init(void) s3c_sdhci0_set_platdata(&smdk2416_hsmmc0_pdata); s3c_sdhci1_set_platdata(&smdk2416_hsmmc1_pdata); + s3c24xx_hsudc_set_platdata(&smdk2416_hsudc_platdata); + gpio_request(S3C2410_GPB(4), "USBHost Power"); gpio_direction_output(S3C2410_GPB(4), 1); -- cgit v1.2.3 From a9df304cf78d76108196da1ff1dad4d9a5737c2e Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 7 May 2011 22:28:04 +0200 Subject: USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed device controller module. This driver enables support for USB high-speed gadget functionality for the Samsung S3C24xx SoC's that include this controller. Signed-off-by: Thomas Abraham Signed-off-by: Sangbeom Kim Signed-off-by: Kukjin Kim Signed-off-by: Alexander Neumann Signed-off-by: Heiko Stuebner Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 17 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/gadget_chips.h | 8 + drivers/usb/gadget/s3c-hsudc.c | 1349 +++++++++++++++++++++++++++++++++++++ 4 files changed, 1375 insertions(+) create mode 100644 drivers/usb/gadget/s3c-hsudc.c diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 4c02b9f1597..58456d1aec2 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -356,6 +356,23 @@ config USB_S3C2410_DEBUG boolean "S3C2410 udc debug messages" depends on USB_GADGET_S3C2410 +config USB_GADGET_S3C_HSUDC + boolean "S3C2416, S3C2443 and S3C2450 USB Device Controller" + depends on ARCH_S3C2410 + select USB_GADGET_DUALSPEED + help + Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC + integrated with dual speed USB 2.0 device controller. It has + 8 endpoints, as well as endpoint zero. + + This driver has been tested on S3C2416 and S3C2450 processors. + +config USB_S3C_HSUDC + tristate + depends on USB_GADGET_S3C_HSUDC + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_PXA_U2O boolean "PXA9xx Processor USB2.0 controller" select USB_GADGET_DUALSPEED diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1ea15ee74fd..4fe92b18a05 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o obj-$(CONFIG_USB_CI13XXX_PCI) += ci13xxx_pci.o obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o +obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o obj-$(CONFIG_USB_EG20T) += pch_udc.o obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index ec3fd979c71..bcdac7c73e8 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -136,6 +136,12 @@ #define gadget_is_s3c_hsotg(g) 0 #endif +#ifdef CONFIG_USB_S3C_HSUDC +#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name)) +#else +#define gadget_is_s3c_hsudc(g) 0 +#endif + #ifdef CONFIG_USB_GADGET_EG20T #define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name)) #else @@ -215,6 +221,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x28; else if (gadget_is_renesas_usbhs(gadget)) return 0x29; + else if (gadget_is_s3c_hsudc(gadget)) + return 0x30; return -ENOENT; } diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c new file mode 100644 index 00000000000..d26901932a5 --- /dev/null +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -0,0 +1,1349 @@ +/* linux/drivers/usb/gadget/s3c-hsudc.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S3C24XX USB 2.0 High-speed USB controller gadget driver + * + * The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints. + * Each endpoint can be configured as either in or out endpoint. Endpoints + * can be configured for Bulk or Interrupt transfer mode. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define S3C_HSUDC_REG(x) (x) + +/* Non-Indexed Registers */ +#define S3C_IR S3C_HSUDC_REG(0x00) /* Index Register */ +#define S3C_EIR S3C_HSUDC_REG(0x04) /* EP Intr Status */ +#define S3C_EIR_EP0 (1<<0) +#define S3C_EIER S3C_HSUDC_REG(0x08) /* EP Intr Enable */ +#define S3C_FAR S3C_HSUDC_REG(0x0c) /* Gadget Address */ +#define S3C_FNR S3C_HSUDC_REG(0x10) /* Frame Number */ +#define S3C_EDR S3C_HSUDC_REG(0x14) /* EP Direction */ +#define S3C_TR S3C_HSUDC_REG(0x18) /* Test Register */ +#define S3C_SSR S3C_HSUDC_REG(0x1c) /* System Status */ +#define S3C_SSR_DTZIEN_EN (0xff8f) +#define S3C_SSR_ERR (0xff80) +#define S3C_SSR_VBUSON (1 << 8) +#define S3C_SSR_HSP (1 << 4) +#define S3C_SSR_SDE (1 << 3) +#define S3C_SSR_RESUME (1 << 2) +#define S3C_SSR_SUSPEND (1 << 1) +#define S3C_SSR_RESET (1 << 0) +#define S3C_SCR S3C_HSUDC_REG(0x20) /* System Control */ +#define S3C_SCR_DTZIEN_EN (1 << 14) +#define S3C_SCR_RRD_EN (1 << 5) +#define S3C_SCR_SUS_EN (1 << 1) +#define S3C_SCR_RST_EN (1 << 0) +#define S3C_EP0SR S3C_HSUDC_REG(0x24) /* EP0 Status */ +#define S3C_EP0SR_EP0_LWO (1 << 6) +#define S3C_EP0SR_STALL (1 << 4) +#define S3C_EP0SR_TX_SUCCESS (1 << 1) +#define S3C_EP0SR_RX_SUCCESS (1 << 0) +#define S3C_EP0CR S3C_HSUDC_REG(0x28) /* EP0 Control */ +#define S3C_BR(_x) S3C_HSUDC_REG(0x60 + (_x * 4)) + +/* Indexed Registers */ +#define S3C_ESR S3C_HSUDC_REG(0x2c) /* EPn Status */ +#define S3C_ESR_FLUSH (1 << 6) +#define S3C_ESR_STALL (1 << 5) +#define S3C_ESR_LWO (1 << 4) +#define S3C_ESR_PSIF_ONE (1 << 2) +#define S3C_ESR_PSIF_TWO (2 << 2) +#define S3C_ESR_TX_SUCCESS (1 << 1) +#define S3C_ESR_RX_SUCCESS (1 << 0) +#define S3C_ECR S3C_HSUDC_REG(0x30) /* EPn Control */ +#define S3C_ECR_DUEN (1 << 7) +#define S3C_ECR_FLUSH (1 << 6) +#define S3C_ECR_STALL (1 << 1) +#define S3C_ECR_IEMS (1 << 0) +#define S3C_BRCR S3C_HSUDC_REG(0x34) /* Read Count */ +#define S3C_BWCR S3C_HSUDC_REG(0x38) /* Write Count */ +#define S3C_MPR S3C_HSUDC_REG(0x3c) /* Max Pkt Size */ + +#define WAIT_FOR_SETUP (0) +#define DATA_STATE_XMIT (1) +#define DATA_STATE_RECV (2) + +/** + * struct s3c_hsudc_ep - Endpoint representation used by driver. + * @ep: USB gadget layer representation of device endpoint. + * @name: Endpoint name (as required by ep autoconfiguration). + * @dev: Reference to the device controller to which this EP belongs. + * @desc: Endpoint descriptor obtained from the gadget driver. + * @queue: Transfer request queue for the endpoint. + * @stopped: Maintains state of endpoint, set if EP is halted. + * @bEndpointAddress: EP address (including direction bit). + * @fifo: Base address of EP FIFO. + */ +struct s3c_hsudc_ep { + struct usb_ep ep; + char name[20]; + struct s3c_hsudc *dev; + const struct usb_endpoint_descriptor *desc; + struct list_head queue; + u8 stopped; + u8 wedge; + u8 bEndpointAddress; + void __iomem *fifo; +}; + +/** + * struct s3c_hsudc_req - Driver encapsulation of USB gadget transfer request. + * @req: Reference to USB gadget transfer request. + * @queue: Used for inserting this request to the endpoint request queue. + */ +struct s3c_hsudc_req { + struct usb_request req; + struct list_head queue; +}; + +/** + * struct s3c_hsudc - Driver's abstraction of the device controller. + * @gadget: Instance of usb_gadget which is referenced by gadget driver. + * @driver: Reference to currenty active gadget driver. + * @dev: The device reference used by probe function. + * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed). + * @regs: Remapped base address of controller's register space. + * @mem_rsrc: Device memory resource used for remapping device register space. + * irq: IRQ number used by the controller. + * uclk: Reference to the controller clock. + * ep0state: Current state of EP0. + * ep: List of endpoints supported by the controller. + */ +struct s3c_hsudc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct device *dev; + struct s3c24xx_hsudc_platdata *pd; + spinlock_t lock; + void __iomem *regs; + struct resource *mem_rsrc; + int irq; + struct clk *uclk; + int ep0state; + struct s3c_hsudc_ep ep[]; +}; + +#define ep_maxpacket(_ep) ((_ep)->ep.maxpacket) +#define ep_is_in(_ep) ((_ep)->bEndpointAddress & USB_DIR_IN) +#define ep_index(_ep) ((_ep)->bEndpointAddress & \ + USB_ENDPOINT_NUMBER_MASK) + +static struct s3c_hsudc *the_controller; +static const char driver_name[] = "s3c-udc"; +static const char ep0name[] = "ep0-control"; + +static inline struct s3c_hsudc_req *our_req(struct usb_request *req) +{ + return container_of(req, struct s3c_hsudc_req, req); +} + +static inline struct s3c_hsudc_ep *our_ep(struct usb_ep *ep) +{ + return container_of(ep, struct s3c_hsudc_ep, ep); +} + +static inline struct s3c_hsudc *to_hsudc(struct usb_gadget *gadget) +{ + return container_of(gadget, struct s3c_hsudc, gadget); +} + +static inline void set_index(struct s3c_hsudc *hsudc, int ep_addr) +{ + ep_addr &= USB_ENDPOINT_NUMBER_MASK; + writel(ep_addr, hsudc->regs + S3C_IR); +} + +static inline void __orr32(void __iomem *ptr, u32 val) +{ + writel(readl(ptr) | val, ptr); +} + +static void s3c_hsudc_init_phy(void) +{ + u32 cfg; + + cfg = readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY; + writel(cfg, S3C2443_PWRCFG); + + cfg = readl(S3C2443_URSTCON); + cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); + writel(cfg, S3C2443_URSTCON); + mdelay(1); + + cfg = readl(S3C2443_URSTCON); + cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); + writel(cfg, S3C2443_URSTCON); + + cfg = readl(S3C2443_PHYCTRL); + cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT); + cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL); + writel(cfg, S3C2443_PHYCTRL); + + cfg = readl(S3C2443_PHYPWR); + cfg &= ~(S3C2443_PHYPWR_FSUSPEND | S3C2443_PHYPWR_PLL_PWRDN | + S3C2443_PHYPWR_XO_ON | S3C2443_PHYPWR_PLL_REFCLK | + S3C2443_PHYPWR_ANALOG_PD); + cfg |= S3C2443_PHYPWR_COMMON_ON; + writel(cfg, S3C2443_PHYPWR); + + cfg = readl(S3C2443_UCLKCON); + cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN | + S3C2443_UCLKCON_TCLKEN); + writel(cfg, S3C2443_UCLKCON); +} + +static void s3c_hsudc_uninit_phy(void) +{ + u32 cfg; + + cfg = readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY; + writel(cfg, S3C2443_PWRCFG); + + writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR); + + cfg = readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN; + writel(cfg, S3C2443_UCLKCON); +} + +/** + * s3c_hsudc_complete_request - Complete a transfer request. + * @hsep: Endpoint to which the request belongs. + * @hsreq: Transfer request to be completed. + * @status: Transfer completion status for the transfer request. + */ +static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep, + struct s3c_hsudc_req *hsreq, int status) +{ + unsigned int stopped = hsep->stopped; + struct s3c_hsudc *hsudc = hsep->dev; + + list_del_init(&hsreq->queue); + hsreq->req.status = status; + + if (!ep_index(hsep)) { + hsudc->ep0state = WAIT_FOR_SETUP; + hsep->bEndpointAddress &= ~USB_DIR_IN; + } + + hsep->stopped = 1; + spin_unlock(&hsudc->lock); + if (hsreq->req.complete != NULL) + hsreq->req.complete(&hsep->ep, &hsreq->req); + spin_lock(&hsudc->lock); + hsep->stopped = stopped; +} + +/** + * s3c_hsudc_nuke_ep - Terminate all requests queued for a endpoint. + * @hsep: Endpoint for which queued requests have to be terminated. + * @status: Transfer completion status for the transfer request. + */ +static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status) +{ + struct s3c_hsudc_req *hsreq; + + while (!list_empty(&hsep->queue)) { + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + s3c_hsudc_complete_request(hsep, hsreq, status); + } +} + +/** + * s3c_hsudc_stop_activity - Stop activity on all endpoints. + * @hsudc: Device controller for which EP activity is to be stopped. + * @driver: Reference to the gadget driver which is currently active. + * + * All the endpoints are stopped and any pending transfer requests if any on + * the endpoint are terminated. + */ +static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc, + struct usb_gadget_driver *driver) +{ + struct s3c_hsudc_ep *hsep; + int epnum; + + hsudc->gadget.speed = USB_SPEED_UNKNOWN; + + for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) { + hsep = &hsudc->ep[epnum]; + hsep->stopped = 1; + s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN); + } + + spin_unlock(&hsudc->lock); + driver->disconnect(&hsudc->gadget); + spin_lock(&hsudc->lock); +} + +/** + * s3c_hsudc_read_setup_pkt - Read the received setup packet from EP0 fifo. + * @hsudc: Device controller from which setup packet is to be read. + * @buf: The buffer into which the setup packet is read. + * + * The setup packet received in the EP0 fifo is read and stored into a + * given buffer address. + */ + +static void s3c_hsudc_read_setup_pkt(struct s3c_hsudc *hsudc, u16 *buf) +{ + int count; + + count = readl(hsudc->regs + S3C_BRCR); + while (count--) + *buf++ = (u16)readl(hsudc->regs + S3C_BR(0)); + + writel(S3C_EP0SR_RX_SUCCESS, hsudc->regs + S3C_EP0SR); +} + +/** + * s3c_hsudc_write_fifo - Write next chunk of transfer data to EP fifo. + * @hsep: Endpoint to which the data is to be written. + * @hsreq: Transfer request from which the next chunk of data is written. + * + * Write the next chunk of data from a transfer request to the endpoint FIFO. + * If the transfer request completes, 1 is returned, otherwise 0 is returned. + */ +static int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep, + struct s3c_hsudc_req *hsreq) +{ + u16 *buf; + u32 max = ep_maxpacket(hsep); + u32 count, length; + bool is_last; + void __iomem *fifo = hsep->fifo; + + buf = hsreq->req.buf + hsreq->req.actual; + prefetch(buf); + + length = hsreq->req.length - hsreq->req.actual; + length = min(length, max); + hsreq->req.actual += length; + + writel(length, hsep->dev->regs + S3C_BWCR); + for (count = 0; count < length; count += 2) + writel(*buf++, fifo); + + if (count != max) { + is_last = true; + } else { + if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero) + is_last = false; + else + is_last = true; + } + + if (is_last) { + s3c_hsudc_complete_request(hsep, hsreq, 0); + return 1; + } + + return 0; +} + +/** + * s3c_hsudc_read_fifo - Read the next chunk of data from EP fifo. + * @hsep: Endpoint from which the data is to be read. + * @hsreq: Transfer request to which the next chunk of data read is written. + * + * Read the next chunk of data from the endpoint FIFO and a write it to the + * transfer request buffer. If the transfer request completes, 1 is returned, + * otherwise 0 is returned. + */ +static int s3c_hsudc_read_fifo(struct s3c_hsudc_ep *hsep, + struct s3c_hsudc_req *hsreq) +{ + struct s3c_hsudc *hsudc = hsep->dev; + u32 csr, offset; + u16 *buf, word; + u32 buflen, rcnt, rlen; + void __iomem *fifo = hsep->fifo; + u32 is_short = 0; + + offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; + csr = readl(hsudc->regs + offset); + if (!(csr & S3C_ESR_RX_SUCCESS)) + return -EINVAL; + + buf = hsreq->req.buf + hsreq->req.actual; + prefetchw(buf); + buflen = hsreq->req.length - hsreq->req.actual; + + rcnt = readl(hsudc->regs + S3C_BRCR); + rlen = (csr & S3C_ESR_LWO) ? (rcnt * 2 - 1) : (rcnt * 2); + + hsreq->req.actual += min(rlen, buflen); + is_short = (rlen < hsep->ep.maxpacket); + + while (rcnt-- != 0) { + word = (u16)readl(fifo); + if (buflen) { + *buf++ = word; + buflen--; + } else { + hsreq->req.status = -EOVERFLOW; + } + } + + writel(S3C_ESR_RX_SUCCESS, hsudc->regs + offset); + + if (is_short || hsreq->req.actual == hsreq->req.length) { + s3c_hsudc_complete_request(hsep, hsreq, 0); + return 1; + } + + return 0; +} + +/** + * s3c_hsudc_epin_intr - Handle in-endpoint interrupt. + * @hsudc - Device controller for which the interrupt is to be handled. + * @ep_idx - Endpoint number on which an interrupt is pending. + * + * Handles interrupt for a in-endpoint. The interrupts that are handled are + * stall and data transmit complete interrupt. + */ +static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx) +{ + struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx]; + struct s3c_hsudc_req *hsreq; + u32 csr; + + csr = readl((u32)hsudc->regs + S3C_ESR); + if (csr & S3C_ESR_STALL) { + writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR); + return; + } + + if (csr & S3C_ESR_TX_SUCCESS) { + writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR); + if (list_empty(&hsep->queue)) + return; + + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) && + (csr & S3C_ESR_PSIF_TWO)) + s3c_hsudc_write_fifo(hsep, hsreq); + } +} + +/** + * s3c_hsudc_epout_intr - Handle out-endpoint interrupt. + * @hsudc - Device controller for which the interrupt is to be handled. + * @ep_idx - Endpoint number on which an interrupt is pending. + * + * Handles interrupt for a out-endpoint. The interrupts that are handled are + * stall, flush and data ready interrupt. + */ +static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx) +{ + struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx]; + struct s3c_hsudc_req *hsreq; + u32 csr; + + csr = readl((u32)hsudc->regs + S3C_ESR); + if (csr & S3C_ESR_STALL) { + writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR); + return; + } + + if (csr & S3C_ESR_FLUSH) { + __orr32(hsudc->regs + S3C_ECR, S3C_ECR_FLUSH); + return; + } + + if (csr & S3C_ESR_RX_SUCCESS) { + if (list_empty(&hsep->queue)) + return; + + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + if (((s3c_hsudc_read_fifo(hsep, hsreq)) == 0) && + (csr & S3C_ESR_PSIF_TWO)) + s3c_hsudc_read_fifo(hsep, hsreq); + } +} + +/** s3c_hsudc_set_halt - Set or clear a endpoint halt. + * @_ep: Endpoint on which halt has to be set or cleared. + * @value: 1 for setting halt on endpoint, 0 to clear halt. + * + * Set or clear endpoint halt. If halt is set, the endpoint is stopped. + * If halt is cleared, for in-endpoints, if there are any pending + * transfer requests, transfers are started. + */ +static int s3c_hsudc_set_halt(struct usb_ep *_ep, int value) +{ + struct s3c_hsudc_ep *hsep = our_ep(_ep); + struct s3c_hsudc *hsudc = hsep->dev; + struct s3c_hsudc_req *hsreq; + unsigned long irqflags; + u32 ecr; + u32 offset; + + if (value && ep_is_in(hsep) && !list_empty(&hsep->queue)) + return -EAGAIN; + + spin_lock_irqsave(&hsudc->lock, irqflags); + set_index(hsudc, ep_index(hsep)); + offset = (ep_index(hsep)) ? S3C_ECR : S3C_EP0CR; + ecr = readl(hsudc->regs + offset); + + if (value) { + ecr |= S3C_ECR_STALL; + if (ep_index(hsep)) + ecr |= S3C_ECR_FLUSH; + hsep->stopped = 1; + } else { + ecr &= ~S3C_ECR_STALL; + hsep->stopped = hsep->wedge = 0; + } + writel(ecr, hsudc->regs + offset); + + if (ep_is_in(hsep) && !list_empty(&hsep->queue) && !value) { + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + if (hsreq) + s3c_hsudc_write_fifo(hsep, hsreq); + } + + spin_unlock_irqrestore(&hsudc->lock, irqflags); + return 0; +} + +/** s3c_hsudc_set_wedge - Sets the halt feature with the clear requests ignored + * @_ep: Endpoint on which wedge has to be set. + * + * Sets the halt feature with the clear requests ignored. + */ +static int s3c_hsudc_set_wedge(struct usb_ep *_ep) +{ + struct s3c_hsudc_ep *hsep = our_ep(_ep); + + if (!hsep) + return -EINVAL; + + hsep->wedge = 1; + return usb_ep_set_halt(_ep); +} + +/** s3c_hsudc_handle_reqfeat - Handle set feature or clear feature requests. + * @_ep: Device controller on which the set/clear feature needs to be handled. + * @ctrl: Control request as received on the endpoint 0. + * + * Handle set feature or clear feature control requests on the control endpoint. + */ +static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc, + struct usb_ctrlrequest *ctrl) +{ + struct s3c_hsudc_ep *hsep; + bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); + u8 ep_num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; + + if (ctrl->bRequestType == USB_RECIP_ENDPOINT) { + hsep = &hsudc->ep[ep_num]; + switch (le16_to_cpu(ctrl->wValue)) { + case USB_ENDPOINT_HALT: + if (set || (!set && !hsep->wedge)) + s3c_hsudc_set_halt(&hsep->ep, set); + return 0; + } + } + + return -ENOENT; +} + +/** + * s3c_hsudc_process_req_status - Handle get status control request. + * @hsudc: Device controller on which get status request has be handled. + * @ctrl: Control request as received on the endpoint 0. + * + * Handle get status control request received on control endpoint. + */ +static void s3c_hsudc_process_req_status(struct s3c_hsudc *hsudc, + struct usb_ctrlrequest *ctrl) +{ + struct s3c_hsudc_ep *hsep0 = &hsudc->ep[0]; + struct s3c_hsudc_req hsreq; + struct s3c_hsudc_ep *hsep; + __le16 reply; + u8 epnum; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + reply = cpu_to_le16(0); + break; + + case USB_RECIP_INTERFACE: + reply = cpu_to_le16(0); + break; + + case USB_RECIP_ENDPOINT: + epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; + hsep = &hsudc->ep[epnum]; + reply = cpu_to_le16(hsep->stopped ? 1 : 0); + break; + } + + INIT_LIST_HEAD(&hsreq.queue); + hsreq.req.length = 2; + hsreq.req.buf = &reply; + hsreq.req.actual = 0; + hsreq.req.complete = NULL; + s3c_hsudc_write_fifo(hsep0, &hsreq); +} + +/** + * s3c_hsudc_process_setup - Process control request received on endpoint 0. + * @hsudc: Device controller on which control request has been received. + * + * Read the control request received on endpoint 0, decode it and handle + * the request. + */ +static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc) +{ + struct s3c_hsudc_ep *hsep = &hsudc->ep[0]; + struct usb_ctrlrequest ctrl = {0}; + int ret; + + s3c_hsudc_nuke_ep(hsep, -EPROTO); + s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl); + + if (ctrl.bRequestType & USB_DIR_IN) { + hsep->bEndpointAddress |= USB_DIR_IN; + hsudc->ep0state = DATA_STATE_XMIT; + } else { + hsep->bEndpointAddress &= ~USB_DIR_IN; + hsudc->ep0state = DATA_STATE_RECV; + } + + switch (ctrl.bRequest) { + case USB_REQ_SET_ADDRESS: + if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + hsudc->ep0state = WAIT_FOR_SETUP; + return; + + case USB_REQ_GET_STATUS: + if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + break; + s3c_hsudc_process_req_status(hsudc, &ctrl); + return; + + case USB_REQ_SET_FEATURE: + case USB_REQ_CLEAR_FEATURE: + if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + break; + s3c_hsudc_handle_reqfeat(hsudc, &ctrl); + hsudc->ep0state = WAIT_FOR_SETUP; + return; + } + + if (hsudc->driver) { + spin_unlock(&hsudc->lock); + ret = hsudc->driver->setup(&hsudc->gadget, &ctrl); + spin_lock(&hsudc->lock); + + if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) { + hsep->bEndpointAddress &= ~USB_DIR_IN; + hsudc->ep0state = WAIT_FOR_SETUP; + } + + if (ret < 0) { + dev_err(hsudc->dev, "setup failed, returned %d\n", + ret); + s3c_hsudc_set_halt(&hsep->ep, 1); + hsudc->ep0state = WAIT_FOR_SETUP; + hsep->bEndpointAddress &= ~USB_DIR_IN; + } + } +} + +/** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt. + * @hsudc: Device controller on which endpoint 0 interrupt has occured. + * + * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur + * when a stall handshake is sent to host or data is sent/received on + * endpoint 0. + */ +static void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc) +{ + struct s3c_hsudc_ep *hsep = &hsudc->ep[0]; + struct s3c_hsudc_req *hsreq; + u32 csr = readl(hsudc->regs + S3C_EP0SR); + u32 ecr; + + if (csr & S3C_EP0SR_STALL) { + ecr = readl(hsudc->regs + S3C_EP0CR); + ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH); + writel(ecr, hsudc->regs + S3C_EP0CR); + + writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR); + hsep->stopped = 0; + + s3c_hsudc_nuke_ep(hsep, -ECONNABORTED); + hsudc->ep0state = WAIT_FOR_SETUP; + hsep->bEndpointAddress &= ~USB_DIR_IN; + return; + } + + if (csr & S3C_EP0SR_TX_SUCCESS) { + writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR); + if (ep_is_in(hsep)) { + if (list_empty(&hsep->queue)) + return; + + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + s3c_hsudc_write_fifo(hsep, hsreq); + } + } + + if (csr & S3C_EP0SR_RX_SUCCESS) { + if (hsudc->ep0state == WAIT_FOR_SETUP) + s3c_hsudc_process_setup(hsudc); + else { + if (!ep_is_in(hsep)) { + if (list_empty(&hsep->queue)) + return; + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + s3c_hsudc_read_fifo(hsep, hsreq); + } + } + } +} + +/** + * s3c_hsudc_ep_enable - Enable a endpoint. + * @_ep: The endpoint to be enabled. + * @desc: Endpoint descriptor. + * + * Enables a endpoint when called from the gadget driver. Endpoint stall if + * any is cleared, transfer type is configured and endpoint interrupt is + * enabled. + */ +static int s3c_hsudc_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct s3c_hsudc_ep *hsep; + struct s3c_hsudc *hsudc; + unsigned long flags; + u32 ecr = 0; + + hsep = container_of(_ep, struct s3c_hsudc_ep, ep); + if (!_ep || !desc || hsep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || hsep->bEndpointAddress != desc->bEndpointAddress + || ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize)) + return -EINVAL; + + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep)) + || !desc->wMaxPacketSize) + return -ERANGE; + + hsudc = hsep->dev; + if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&hsudc->lock, flags); + + set_index(hsudc, hsep->bEndpointAddress); + ecr |= ((usb_endpoint_xfer_int(desc)) ? S3C_ECR_IEMS : S3C_ECR_DUEN); + writel(ecr, hsudc->regs + S3C_ECR); + + hsep->stopped = hsep->wedge = 0; + hsep->desc = desc; + hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + + s3c_hsudc_set_halt(_ep, 0); + __set_bit(ep_index(hsep), hsudc->regs + S3C_EIER); + + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; +} + +/** + * s3c_hsudc_ep_disable - Disable a endpoint. + * @_ep: The endpoint to be disabled. + * @desc: Endpoint descriptor. + * + * Disables a endpoint when called from the gadget driver. + */ +static int s3c_hsudc_ep_disable(struct usb_ep *_ep) +{ + struct s3c_hsudc_ep *hsep = our_ep(_ep); + struct s3c_hsudc *hsudc = hsep->dev; + unsigned long flags; + + if (!_ep || !hsep->desc) + return -EINVAL; + + spin_lock_irqsave(&hsudc->lock, flags); + + set_index(hsudc, hsep->bEndpointAddress); + __clear_bit(ep_index(hsep), hsudc->regs + S3C_EIER); + + s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN); + + hsep->desc = 0; + hsep->stopped = 1; + + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; +} + +/** + * s3c_hsudc_alloc_request - Allocate a new request. + * @_ep: Endpoint for which request is allocated (not used). + * @gfp_flags: Flags used for the allocation. + * + * Allocates a single transfer request structure when called from gadget driver. + */ +static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct s3c_hsudc_req *hsreq; + + hsreq = kzalloc(sizeof *hsreq, gfp_flags); + if (!hsreq) + return 0; + + INIT_LIST_HEAD(&hsreq->queue); + return &hsreq->req; +} + +/** + * s3c_hsudc_free_request - Deallocate a request. + * @ep: Endpoint for which request is deallocated (not used). + * @_req: Request to be deallocated. + * + * Allocates a single transfer request structure when called from gadget driver. + */ +static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct s3c_hsudc_req *hsreq; + + hsreq = container_of(_req, struct s3c_hsudc_req, req); + WARN_ON(!list_empty(&hsreq->queue)); + kfree(hsreq); +} + +/** + * s3c_hsudc_queue - Queue a transfer request for the endpoint. + * @_ep: Endpoint for which the request is queued. + * @_req: Request to be queued. + * @gfp_flags: Not used. + * + * Start or enqueue a request for a endpoint when called from gadget driver. + */ +static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct s3c_hsudc_req *hsreq; + struct s3c_hsudc_ep *hsep; + struct s3c_hsudc *hsudc; + unsigned long flags; + u32 offset; + u32 csr; + + hsreq = container_of(_req, struct s3c_hsudc_req, req); + if ((!_req || !_req->complete || !_req->buf || + !list_empty(&hsreq->queue))) + return -EINVAL; + + hsep = container_of(_ep, struct s3c_hsudc_ep, ep); + hsudc = hsep->dev; + if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&hsudc->lock, flags); + set_index(hsudc, hsep->bEndpointAddress); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + if (!ep_index(hsep) && _req->length == 0) { + hsudc->ep0state = WAIT_FOR_SETUP; + s3c_hsudc_complete_request(hsep, hsreq, 0); + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; + } + + if (list_empty(&hsep->queue) && !hsep->stopped) { + offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; + if (ep_is_in(hsep)) { + csr = readl((u32)hsudc->regs + offset); + if (!(csr & S3C_ESR_TX_SUCCESS) && + (s3c_hsudc_write_fifo(hsep, hsreq) == 1)) + hsreq = 0; + } else { + csr = readl((u32)hsudc->regs + offset); + if ((csr & S3C_ESR_RX_SUCCESS) + && (s3c_hsudc_read_fifo(hsep, hsreq) == 1)) + hsreq = 0; + } + } + + if (hsreq != 0) + list_add_tail(&hsreq->queue, &hsep->queue); + + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; +} + +/** + * s3c_hsudc_dequeue - Dequeue a transfer request from an endpoint. + * @_ep: Endpoint from which the request is dequeued. + * @_req: Request to be dequeued. + * + * Dequeue a request from a endpoint when called from gadget driver. + */ +static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct s3c_hsudc_ep *hsep = our_ep(_ep); + struct s3c_hsudc *hsudc = hsep->dev; + struct s3c_hsudc_req *hsreq; + unsigned long flags; + + hsep = container_of(_ep, struct s3c_hsudc_ep, ep); + if (!_ep || hsep->ep.name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&hsudc->lock, flags); + + list_for_each_entry(hsreq, &hsep->queue, queue) { + if (&hsreq->req == _req) + break; + } + if (&hsreq->req != _req) { + spin_unlock_irqrestore(&hsudc->lock, flags); + return -EINVAL; + } + + set_index(hsudc, hsep->bEndpointAddress); + s3c_hsudc_complete_request(hsep, hsreq, -ECONNRESET); + + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; +} + +static struct usb_ep_ops s3c_hsudc_ep_ops = { + .enable = s3c_hsudc_ep_enable, + .disable = s3c_hsudc_ep_disable, + .alloc_request = s3c_hsudc_alloc_request, + .free_request = s3c_hsudc_free_request, + .queue = s3c_hsudc_queue, + .dequeue = s3c_hsudc_dequeue, + .set_halt = s3c_hsudc_set_halt, + .set_wedge = s3c_hsudc_set_wedge, +}; + +/** + * s3c_hsudc_initep - Initialize a endpoint to default state. + * @hsudc - Reference to the device controller. + * @hsep - Endpoint to be initialized. + * @epnum - Address to be assigned to the endpoint. + * + * Initialize a endpoint with default configuration. + */ +static void s3c_hsudc_initep(struct s3c_hsudc *hsudc, + struct s3c_hsudc_ep *hsep, int epnum) +{ + char *dir; + + if ((epnum % 2) == 0) { + dir = "out"; + } else { + dir = "in"; + hsep->bEndpointAddress = USB_DIR_IN; + } + + hsep->bEndpointAddress |= epnum; + if (epnum) + snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir); + else + snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name); + + INIT_LIST_HEAD(&hsep->queue); + INIT_LIST_HEAD(&hsep->ep.ep_list); + if (epnum) + list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list); + + hsep->dev = hsudc; + hsep->ep.name = hsep->name; + hsep->ep.maxpacket = epnum ? 512 : 64; + hsep->ep.ops = &s3c_hsudc_ep_ops; + hsep->fifo = hsudc->regs + S3C_BR(epnum); + hsep->desc = 0; + hsep->stopped = 0; + hsep->wedge = 0; + + set_index(hsudc, epnum); + writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR); +} + +/** + * s3c_hsudc_setup_ep - Configure all endpoints to default state. + * @hsudc: Reference to device controller. + * + * Configures all endpoints to default state. + */ +static void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc) +{ + int epnum; + + hsudc->ep0state = WAIT_FOR_SETUP; + INIT_LIST_HEAD(&hsudc->gadget.ep_list); + for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) + s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum); +} + +/** + * s3c_hsudc_reconfig - Reconfigure the device controller to default state. + * @hsudc: Reference to device controller. + * + * Reconfigures the device controller registers to a default state. + */ +static void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc) +{ + writel(0xAA, hsudc->regs + S3C_EDR); + writel(1, hsudc->regs + S3C_EIER); + writel(0, hsudc->regs + S3C_TR); + writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN | + S3C_SCR_RST_EN, hsudc->regs + S3C_SCR); + writel(0, hsudc->regs + S3C_EP0CR); + + s3c_hsudc_setup_ep(hsudc); +} + +/** + * s3c_hsudc_irq - Interrupt handler for device controller. + * @irq: Not used. + * @_dev: Reference to the device controller. + * + * Interrupt handler for the device controller. This handler handles controller + * interrupts and endpoint interrupts. + */ +static irqreturn_t s3c_hsudc_irq(int irq, void *_dev) +{ + struct s3c_hsudc *hsudc = _dev; + struct s3c_hsudc_ep *hsep; + u32 ep_intr; + u32 sys_status; + u32 ep_idx; + + spin_lock(&hsudc->lock); + + sys_status = readl(hsudc->regs + S3C_SSR); + ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF; + + if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) { + spin_unlock(&hsudc->lock); + return IRQ_HANDLED; + } + + if (sys_status) { + if (sys_status & S3C_SSR_VBUSON) + writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR); + + if (sys_status & S3C_SSR_ERR) + writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR); + + if (sys_status & S3C_SSR_SDE) { + writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR); + hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ? + USB_SPEED_HIGH : USB_SPEED_FULL; + } + + if (sys_status & S3C_SSR_SUSPEND) { + writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR); + if (hsudc->gadget.speed != USB_SPEED_UNKNOWN + && hsudc->driver && hsudc->driver->suspend) + hsudc->driver->suspend(&hsudc->gadget); + } + + if (sys_status & S3C_SSR_RESUME) { + writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR); + if (hsudc->gadget.speed != USB_SPEED_UNKNOWN + && hsudc->driver && hsudc->driver->resume) + hsudc->driver->resume(&hsudc->gadget); + } + + if (sys_status & S3C_SSR_RESET) { + writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR); + for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) { + hsep = &hsudc->ep[ep_idx]; + hsep->stopped = 1; + s3c_hsudc_nuke_ep(hsep, -ECONNRESET); + } + s3c_hsudc_reconfig(hsudc); + hsudc->ep0state = WAIT_FOR_SETUP; + } + } + + if (ep_intr & S3C_EIR_EP0) { + writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR); + set_index(hsudc, 0); + s3c_hsudc_handle_ep0_intr(hsudc); + } + + ep_intr >>= 1; + ep_idx = 1; + while (ep_intr) { + if (ep_intr & 1) { + hsep = &hsudc->ep[ep_idx]; + set_index(hsudc, ep_idx); + writel(1 << ep_idx, hsudc->regs + S3C_EIR); + if (ep_is_in(hsep)) + s3c_hsudc_epin_intr(hsudc, ep_idx); + else + s3c_hsudc_epout_intr(hsudc, ep_idx); + } + ep_intr >>= 1; + ep_idx++; + } + + spin_unlock(&hsudc->lock); + return IRQ_HANDLED; +} + +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) +{ + struct s3c_hsudc *hsudc = the_controller; + int ret; + + if (!driver + || (driver->speed != USB_SPEED_FULL && + driver->speed != USB_SPEED_HIGH) + || !bind + || !driver->unbind || !driver->disconnect || !driver->setup) + return -EINVAL; + + if (!hsudc) + return -ENODEV; + + if (hsudc->driver) + return -EBUSY; + + hsudc->driver = driver; + hsudc->gadget.dev.driver = &driver->driver; + hsudc->gadget.speed = USB_SPEED_UNKNOWN; + ret = device_add(&hsudc->gadget.dev); + if (ret) { + dev_err(hsudc->dev, "failed to probe gadget device"); + return ret; + } + + ret = bind(&hsudc->gadget); + if (ret) { + dev_err(hsudc->dev, "%s: bind failed\n", hsudc->gadget.name); + device_del(&hsudc->gadget.dev); + + hsudc->driver = NULL; + hsudc->gadget.dev.driver = NULL; + return ret; + } + + enable_irq(hsudc->irq); + dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name); + + s3c_hsudc_reconfig(hsudc); + s3c_hsudc_init_phy(); + if (hsudc->pd->gpio_init) + hsudc->pd->gpio_init(); + + return 0; +} +EXPORT_SYMBOL(usb_gadget_probe_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct s3c_hsudc *hsudc = the_controller; + unsigned long flags; + + if (!hsudc) + return -ENODEV; + + if (!driver || driver != hsudc->driver || !driver->unbind) + return -EINVAL; + + spin_lock_irqsave(&hsudc->lock, flags); + hsudc->driver = 0; + s3c_hsudc_uninit_phy(); + if (hsudc->pd->gpio_uninit) + hsudc->pd->gpio_uninit(); + s3c_hsudc_stop_activity(hsudc, driver); + spin_unlock_irqrestore(&hsudc->lock, flags); + + driver->unbind(&hsudc->gadget); + device_del(&hsudc->gadget.dev); + disable_irq(hsudc->irq); + + dev_info(hsudc->dev, "unregistered gadget driver '%s'\n", + driver->driver.name); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc) +{ + return readl(hsudc->regs + S3C_FNR) & 0x3FF; +} + +static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget) +{ + return s3c_hsudc_read_frameno(to_hsudc(gadget)); +} + +static struct usb_gadget_ops s3c_hsudc_gadget_ops = { + .get_frame = s3c_hsudc_gadget_getframe, +}; + +static int s3c_hsudc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct s3c_hsudc *hsudc; + struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data; + int ret; + + hsudc = kzalloc(sizeof(struct s3c_hsudc) + + sizeof(struct s3c_hsudc_ep) * pd->epnum, + GFP_KERNEL); + if (!hsudc) { + dev_err(dev, "cannot allocate memory\n"); + return -ENOMEM; + } + + the_controller = hsudc; + platform_set_drvdata(pdev, dev); + hsudc->dev = dev; + hsudc->pd = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "unable to obtain driver resource data\n"); + ret = -ENODEV; + goto err_res; + } + + hsudc->mem_rsrc = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!hsudc->mem_rsrc) { + dev_err(dev, "failed to reserve register area\n"); + ret = -ENODEV; + goto err_res; + } + + hsudc->regs = ioremap(res->start, resource_size(res)); + if (!hsudc->regs) { + dev_err(dev, "error mapping device register area\n"); + ret = -EBUSY; + goto err_remap; + } + + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "unable to obtain IRQ number\n"); + goto err_irq; + } + hsudc->irq = ret; + + ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc); + if (ret < 0) { + dev_err(dev, "irq request failed\n"); + goto err_irq; + } + + spin_lock_init(&hsudc->lock); + + device_initialize(&hsudc->gadget.dev); + dev_set_name(&hsudc->gadget.dev, "gadget"); + + hsudc->gadget.is_dualspeed = 1; + hsudc->gadget.ops = &s3c_hsudc_gadget_ops; + hsudc->gadget.name = dev_name(dev); + hsudc->gadget.dev.parent = dev; + hsudc->gadget.dev.dma_mask = dev->dma_mask; + hsudc->gadget.ep0 = &hsudc->ep[0].ep; + + hsudc->gadget.is_otg = 0; + hsudc->gadget.is_a_peripheral = 0; + + s3c_hsudc_setup_ep(hsudc); + + hsudc->uclk = clk_get(&pdev->dev, "usb-device"); + if (hsudc->uclk == NULL) { + dev_err(dev, "failed to find usb-device clock source\n"); + return -ENOENT; + } + clk_enable(hsudc->uclk); + + local_irq_disable(); + + disable_irq(hsudc->irq); + local_irq_enable(); + return 0; + +err_irq: + iounmap(hsudc->regs); + +err_remap: + release_resource(hsudc->mem_rsrc); + kfree(hsudc->mem_rsrc); + +err_res: + kfree(hsudc); + return ret; +} + +static struct platform_driver s3c_hsudc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "s3c-hsudc", + }, + .probe = s3c_hsudc_probe, +}; + +static int __init s3c_hsudc_modinit(void) +{ + return platform_driver_register(&s3c_hsudc_driver); +} + +static void __exit s3c_hsudc_modexit(void) +{ + platform_driver_unregister(&s3c_hsudc_driver); +} + +module_init(s3c_hsudc_modinit); +module_exit(s3c_hsudc_modexit); + +MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver"); +MODULE_AUTHOR("Thomas Abraham "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From fd5054c169d29747a44b4e1419ff47f57ae82dbc Mon Sep 17 00:00:00 2001 From: Erik Slagter Date: Wed, 11 May 2011 12:06:55 +0200 Subject: USB: cdc_acm: Fix oops when Droids MuIn LCD is connected The Droids MuIn LCD operates like a serial remote terminal. Data received are displayed directly on the LCD. This patch fixes the kernel null pointer oops when it is plugged in. Add NO_DATA_INTERFACE quirk to tell the driver that "control" and "data" interfaces are not separated for this device, which prevents dereferencing a null pointer in the device probe code. Signed-off-by: Erik Slagter Signed-off-by: Maxin B. John Tested-by: Erik Slagter Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 13 +++++++++++-- drivers/usb/class/cdc-acm.h | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 58754b508a0..395a347f2eb 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -853,7 +853,7 @@ static int acm_probe(struct usb_interface *intf, u8 ac_management_function = 0; u8 call_management_function = 0; int call_interface_num = -1; - int data_interface_num; + int data_interface_num = -1; unsigned long quirks; int num_rx_buf; int i; @@ -937,7 +937,11 @@ next_desc: if (!union_header) { if (call_interface_num > 0) { dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n"); - data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); + /* quirks for Droids MuIn LCD */ + if (quirks & NO_DATA_INTERFACE) + data_interface = usb_ifnum_to_if(usb_dev, 0); + else + data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); control_interface = intf; } else { if (intf->cur_altsetting->desc.bNumEndpoints != 3) { @@ -1535,6 +1539,11 @@ static const struct usb_device_id acm_ids[] = { .driver_info = NOT_A_MODEM, }, + /* Support for Droids MuIn LCD */ + { USB_DEVICE(0x04d8, 0x000b), + .driver_info = NO_DATA_INTERFACE, + }, + /* control interfaces without any protocol set */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTO_NONE) }, diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 7b5c0bd07f8..ca7937f26e2 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -126,3 +126,4 @@ struct acm { #define SINGLE_RX_URB 2 #define NO_CAP_LINE 4 #define NOT_A_MODEM 8 +#define NO_DATA_INTERFACE 16 -- cgit v1.2.3 From c786e09c35924252ff219241e7027e340b77252d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 11 May 2011 16:00:09 +0900 Subject: usb: renesas_usbhs: fixup fifo disable It was necessary to check pipe condition after disable fifo. Current driver checked it in a wrong place. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/pipe.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 5897ddad05f..bc4521c5426 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -183,7 +183,6 @@ static void usbhsp_pipe_select(struct usbhs_pipe *pipe) static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe) { struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe); - struct device *dev = usbhs_priv_to_dev(priv); int timeout = 1024; u16 val; @@ -206,6 +205,7 @@ static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe) * - "Pipe Control Registers Switching Procedure" */ usbhs_write(priv, CFIFOSEL, 0); + usbhs_fifo_disable(pipe); do { val = usbhsp_pipectrl_get(pipe); @@ -217,21 +217,6 @@ static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe) } while (timeout--); - /* - * force NAK - */ - timeout = 1024; - usbhs_fifo_disable(pipe); - do { - val = usbhsp_pipectrl_get(pipe); - val &= PBUSY; - if (!val) - return 0; - - } while (timeout--); - - dev_err(dev, "pipe barrier failed\n"); - return -EBUSY; } @@ -270,10 +255,22 @@ static void __usbhsp_pid_try_nak_if_stall(struct usbhs_pipe *pipe) void usbhs_fifo_disable(struct usbhs_pipe *pipe) { + int timeout = 1024; + u16 val; + /* see "Pipe n Control Register" - "PID" */ __usbhsp_pid_try_nak_if_stall(pipe); usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK); + + do { + val = usbhsp_pipectrl_get(pipe); + val &= PBUSY; + if (!val) + break; + + udelay(10); + } while (timeout--); } void usbhs_fifo_enable(struct usbhs_pipe *pipe) -- cgit v1.2.3 From 97f93227e646741adf4d7cbf4baf7a087746e93a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 11 May 2011 16:00:15 +0900 Subject: usb: renesas_usbhs: fixup error processing on probe/remove The error processing order was wrong. This patch modify it. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 34e68e0205c..f3664d6af66 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -321,11 +321,11 @@ static int __devinit usbhs_probe(struct platform_device *pdev) /* call pipe and module init */ ret = usbhs_pipe_probe(priv); if (ret < 0) - goto probe_end_mod_exit; + goto probe_end_iounmap; ret = usbhs_mod_probe(priv); if (ret < 0) - goto probe_end_iounmap; + goto probe_end_pipe_exit; /* dev_set_drvdata should be called after usbhs_mod_init */ dev_set_drvdata(&pdev->dev, priv); @@ -346,7 +346,7 @@ static int __devinit usbhs_probe(struct platform_device *pdev) ret = usbhs_platform_call(priv, hardware_init, pdev); if (ret < 0) { dev_err(&pdev->dev, "platform prove failed.\n"); - goto probe_end_pipe_exit; + goto probe_end_mod_exit; } /* reset phy for connection */ @@ -372,10 +372,10 @@ static int __devinit usbhs_probe(struct platform_device *pdev) probe_end_call_remove: usbhs_platform_call(priv, hardware_exit, pdev); -probe_end_pipe_exit: - usbhs_pipe_remove(priv); probe_end_mod_exit: usbhs_mod_remove(priv); +probe_end_pipe_exit: + usbhs_pipe_remove(priv); probe_end_iounmap: iounmap(priv->base); probe_end_kfree: @@ -403,8 +403,8 @@ static int __devexit usbhs_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); usbhs_platform_call(priv, hardware_exit, pdev); - usbhs_pipe_remove(priv); usbhs_mod_remove(priv); + usbhs_pipe_remove(priv); iounmap(priv->base); kfree(priv); -- cgit v1.2.3 From 3abeca998a44205cfd837fa0bf1f7c24f8294acb Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 5 May 2011 19:08:09 -0700 Subject: xhci: Fix bug in control transfer cancellation. When the xHCI driver attempts to cancel a transfer, it issues a Stop Endpoint command and waits for the host controller to indicate which TRB it was in the middle of processing. The host will put an event TRB with completion code COMP_STOP on the event ring if it stops on a control transfer TRB (or other types of transfer TRBs). The ring handling code is supposed to set ep->stopped_trb to the TRB that the host stopped on when this happens. Unfortunately, there is a long-standing bug in the control transfer completion code. It doesn't actually check to see if COMP_STOP is set before attempting to process the transfer based on which part of the control TD completed. So when we get an event on the data phase of the control TRB with COMP_STOP set, it thinks it's a normal completion of the transfer and doesn't set ep->stopped_td or ep->stopped_trb. When the ring handling code goes on to process the completion of the Stop Endpoint command, it sees that ep->stopped_trb is not a part of the TD it's trying to cancel. It thinks the hardware has its enqueue pointer somewhere further up in the ring, and thinks it's safe to turn the control TRBs into no-op TRBs. Since the hardware was in the middle of the control TRBs to be cancelled, the proper software behavior is to issue a Set TR dequeue pointer command. It turns out that the NEC host controllers can handle active TRBs being set to no-op TRBs after a stop endpoint command, but other host controllers have issues with this out-of-spec software behavior. Fix this behavior. This patch should be backported to kernels as far back as 2.6.31, but it may be a bit challenging, since process_ctrl_td() was introduced in some refactoring done in 2.6.36, and some endian-safe patches added in 2.6.40 that touch the same lines. Signed-off-by: Sarah Sharp Cc: Dmitry Torokhov Cc: stable@kernel.org --- drivers/usb/host/xhci-ring.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index c35058b94de..237a765f8d1 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1641,6 +1641,9 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, else *status = 0; break; + case COMP_STOP_INVAL: + case COMP_STOP: + return finish_td(xhci, td, event_trb, event, ep, status, false); default: if (!xhci_requires_manual_halt_cleanup(xhci, ep_ctx, trb_comp_code)) @@ -1685,15 +1688,12 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, } } else { /* Maybe the event was for the data stage? */ - if (trb_comp_code != COMP_STOP_INVAL) { - /* We didn't stop on a link TRB in the middle */ - td->urb->actual_length = - td->urb->transfer_buffer_length - - TRB_LEN(le32_to_cpu(event->transfer_len)); - xhci_dbg(xhci, "Waiting for status " - "stage event\n"); - return 0; - } + td->urb->actual_length = + td->urb->transfer_buffer_length - + TRB_LEN(le32_to_cpu(event->transfer_len)); + xhci_dbg(xhci, "Waiting for status " + "stage event\n"); + return 0; } } -- cgit v1.2.3 From 5c853013dcdadb60724268bf860d372fba71694c Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Wed, 11 May 2011 15:15:51 -0700 Subject: ehci: pci quirk cleanup Factor the handoff code out from quirk_usb_disable_ehci Signed-off-by: Andy Ross Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/pci-quirks.c | 136 ++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 70 deletions(-) diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 9b166d70ae9..e300509faa6 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -503,14 +503,70 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) iounmap(base); } +static void __devinit ehci_bios_handoff(struct pci_dev *pdev, + void __iomem *op_reg_base, + u32 cap, u8 offset) +{ + int msec, tried_handoff = 0; + + if (cap & EHCI_USBLEGSUP_BIOS) { + dev_dbg(&pdev->dev, "EHCI: BIOS handoff\n"); + +#if 0 +/* aleksey_gorelov@phoenix.com reports that some systems need SMI forced on, + * but that seems dubious in general (the BIOS left it off intentionally) + * and is known to prevent some systems from booting. so we won't do this + * unless maybe we can determine when we're on a system that needs SMI forced. + */ + /* BIOS workaround (?): be sure the pre-Linux code + * receives the SMI + */ + pci_read_config_dword(pdev, offset + EHCI_USBLEGCTLSTS, &val); + pci_write_config_dword(pdev, offset + EHCI_USBLEGCTLSTS, + val | EHCI_USBLEGCTLSTS_SOOE); +#endif + + /* some systems get upset if this semaphore is + * set for any other reason than forcing a BIOS + * handoff.. + */ + pci_write_config_byte(pdev, offset + 3, 1); + } + + /* if boot firmware now owns EHCI, spin till it hands it over. */ + msec = 1000; + while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { + tried_handoff = 1; + msleep(10); + msec -= 10; + pci_read_config_dword(pdev, offset, &cap); + } + + if (cap & EHCI_USBLEGSUP_BIOS) { + /* well, possibly buggy BIOS... try to shut it down, + * and hope nothing goes too wrong + */ + dev_warn(&pdev->dev, "EHCI: BIOS handoff failed" + " (BIOS bug?) %08x\n", cap); + pci_write_config_byte(pdev, offset + 2, 0); + } + + /* just in case, always disable EHCI SMIs */ + pci_write_config_dword(pdev, offset + EHCI_USBLEGCTLSTS, 0); + + /* If the BIOS ever owned the controller then we can't expect + * any power sessions to remain intact. + */ + if (tried_handoff) + writel(0, op_reg_base + EHCI_CONFIGFLAG); +} + static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) { - int wait_time, delta; void __iomem *base, *op_reg_base; - u32 hcc_params, val; + u32 hcc_params, cap, val; u8 offset, cap_length; - int count = 256/4; - int tried_handoff = 0; + int wait_time, delta, count = 256/4; if (!mmio_resource_enabled(pdev, 0)) return; @@ -529,77 +585,17 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) hcc_params = readl(base + EHCI_HCC_PARAMS); offset = (hcc_params >> 8) & 0xff; while (offset && --count) { - u32 cap; - int msec; - pci_read_config_dword(pdev, offset, &cap); - switch (cap & 0xff) { - case 1: /* BIOS/SMM/... handoff support */ - if ((cap & EHCI_USBLEGSUP_BIOS)) { - dev_dbg(&pdev->dev, "EHCI: BIOS handoff\n"); - -#if 0 -/* aleksey_gorelov@phoenix.com reports that some systems need SMI forced on, - * but that seems dubious in general (the BIOS left it off intentionally) - * and is known to prevent some systems from booting. so we won't do this - * unless maybe we can determine when we're on a system that needs SMI forced. - */ - /* BIOS workaround (?): be sure the - * pre-Linux code receives the SMI - */ - pci_read_config_dword(pdev, - offset + EHCI_USBLEGCTLSTS, - &val); - pci_write_config_dword(pdev, - offset + EHCI_USBLEGCTLSTS, - val | EHCI_USBLEGCTLSTS_SOOE); -#endif - - /* some systems get upset if this semaphore is - * set for any other reason than forcing a BIOS - * handoff.. - */ - pci_write_config_byte(pdev, offset + 3, 1); - } - - /* if boot firmware now owns EHCI, spin till - * it hands it over. - */ - msec = 1000; - while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { - tried_handoff = 1; - msleep(10); - msec -= 10; - pci_read_config_dword(pdev, offset, &cap); - } - if (cap & EHCI_USBLEGSUP_BIOS) { - /* well, possibly buggy BIOS... try to shut - * it down, and hope nothing goes too wrong - */ - dev_warn(&pdev->dev, "EHCI: BIOS handoff failed" - " (BIOS bug?) %08x\n", cap); - pci_write_config_byte(pdev, offset + 2, 0); - } - - /* just in case, always disable EHCI SMIs */ - pci_write_config_dword(pdev, - offset + EHCI_USBLEGCTLSTS, - 0); - - /* If the BIOS ever owned the controller then we - * can't expect any power sessions to remain intact. - */ - if (tried_handoff) - writel(0, op_reg_base + EHCI_CONFIGFLAG); + switch (cap & 0xff) { + case 1: + ehci_bios_handoff(pdev, op_reg_base, cap, offset); break; - case 0: /* illegal reserved capability */ - cap = 0; - /* FALLTHROUGH */ + case 0: /* Illegal reserved cap, set cap=0 so we exit */ + cap = 0; /* then fallthrough... */ default: dev_warn(&pdev->dev, "EHCI: unrecognized capability " - "%02x\n", cap & 0xff); - break; + "%02x\n", cap & 0xff); } offset = (cap >> 8) & 0xff; } -- cgit v1.2.3 From 3610ea5397b80822e417aaa0e706fd803fb05680 Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Wed, 11 May 2011 15:52:38 -0700 Subject: ehci: workaround for pci quirk timeout on ExoPC The BIOS handoff for the unused EHCI controller on the ExoPC tablet hangs for 90 seconds on boot. Detect that device, skip negotiation and force the handoff. Signed-off-by: Andy Ross Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/pci-quirks.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index e300509faa6..f16c59d5f48 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "pci-quirks.h" #include "xhci-ext-caps.h" @@ -507,9 +508,20 @@ static void __devinit ehci_bios_handoff(struct pci_dev *pdev, void __iomem *op_reg_base, u32 cap, u8 offset) { - int msec, tried_handoff = 0; + int try_handoff = 1, tried_handoff = 0; + + /* The Pegatron Lucid (ExoPC) tablet sporadically waits for 90 + * seconds trying the handoff on its unused controller. Skip + * it. */ + if (pdev->vendor == 0x8086 && pdev->device == 0x283a) { + const char *dmi_bn = dmi_get_system_info(DMI_BOARD_NAME); + const char *dmi_bv = dmi_get_system_info(DMI_BIOS_VERSION); + if (dmi_bn && !strcmp(dmi_bn, "EXOPG06411") && + dmi_bv && !strcmp(dmi_bv, "Lucid-CE-133")) + try_handoff = 0; + } - if (cap & EHCI_USBLEGSUP_BIOS) { + if (try_handoff && (cap & EHCI_USBLEGSUP_BIOS)) { dev_dbg(&pdev->dev, "EHCI: BIOS handoff\n"); #if 0 @@ -534,20 +546,23 @@ static void __devinit ehci_bios_handoff(struct pci_dev *pdev, } /* if boot firmware now owns EHCI, spin till it hands it over. */ - msec = 1000; - while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { - tried_handoff = 1; - msleep(10); - msec -= 10; - pci_read_config_dword(pdev, offset, &cap); + if (try_handoff) { + int msec = 1000; + while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { + tried_handoff = 1; + msleep(10); + msec -= 10; + pci_read_config_dword(pdev, offset, &cap); + } } if (cap & EHCI_USBLEGSUP_BIOS) { /* well, possibly buggy BIOS... try to shut it down, * and hope nothing goes too wrong */ - dev_warn(&pdev->dev, "EHCI: BIOS handoff failed" - " (BIOS bug?) %08x\n", cap); + if (try_handoff) + dev_warn(&pdev->dev, "EHCI: BIOS handoff failed" + " (BIOS bug?) %08x\n", cap); pci_write_config_byte(pdev, offset + 2, 0); } -- cgit v1.2.3 From a0885924326f79e157847010a9aaf49b058b30dc Mon Sep 17 00:00:00 2001 From: huajun li Date: Tue, 3 May 2011 21:11:00 +0800 Subject: xhci: move the common code to a function to get max ports and port array There are several functions using same code to get max ports and port array, this patch moves the common code to a function in order to reuse them easily. Signed-off-by: Huajun Li Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 66 +++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index a5696373601..0be788cc2fd 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -376,11 +376,27 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, port_change_bit, wIndex, port_status); } +static int xhci_get_ports(struct usb_hcd *hcd, __le32 __iomem ***port_array) +{ + int max_ports; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + if (hcd->speed == HCD_USB3) { + max_ports = xhci->num_usb3_ports; + *port_array = xhci->usb3_ports; + } else { + max_ports = xhci->num_usb2_ports; + *port_array = xhci->usb2_ports; + } + + return max_ports; +} + int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - int ports; + int max_ports; unsigned long flags; u32 temp, temp1, status; int retval = 0; @@ -389,13 +405,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, struct xhci_bus_state *bus_state; u16 link_state = 0; - if (hcd->speed == HCD_USB3) { - ports = xhci->num_usb3_ports; - port_array = xhci->usb3_ports; - } else { - ports = xhci->num_usb2_ports; - port_array = xhci->usb2_ports; - } + max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; spin_lock_irqsave(&xhci->lock, flags); @@ -420,7 +430,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, (struct usb_hub_descriptor *) buf); break; case GetPortStatus: - if (!wIndex || wIndex > ports) + if (!wIndex || wIndex > max_ports) goto error; wIndex--; status = 0; @@ -519,7 +529,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (wValue == USB_PORT_FEAT_LINK_STATE) link_state = (wIndex & 0xff00) >> 3; wIndex &= 0xff; - if (!wIndex || wIndex > ports) + if (!wIndex || wIndex > max_ports) goto error; wIndex--; temp = xhci_readl(xhci, port_array[wIndex]); @@ -637,7 +647,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_readl(xhci, port_array[wIndex]); break; case ClearPortFeature: - if (!wIndex || wIndex > ports) + if (!wIndex || wIndex > max_ports) goto error; wIndex--; temp = xhci_readl(xhci, port_array[wIndex]); @@ -730,21 +740,15 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) u32 mask; int i, retval; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - int ports; + int max_ports; __le32 __iomem **port_array; struct xhci_bus_state *bus_state; - if (hcd->speed == HCD_USB3) { - ports = xhci->num_usb3_ports; - port_array = xhci->usb3_ports; - } else { - ports = xhci->num_usb2_ports; - port_array = xhci->usb2_ports; - } + max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; /* Initial status is no changes */ - retval = (ports + 8) / 8; + retval = (max_ports + 8) / 8; memset(buf, 0, retval); status = 0; @@ -752,7 +756,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) spin_lock_irqsave(&xhci->lock, flags); /* For each port, did anything change? If so, set that bit in buf. */ - for (i = 0; i < ports; i++) { + for (i = 0; i < max_ports; i++) { temp = xhci_readl(xhci, port_array[i]); if (temp == 0xffffffff) { retval = -ENODEV; @@ -780,15 +784,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) struct xhci_bus_state *bus_state; unsigned long flags; - if (hcd->speed == HCD_USB3) { - max_ports = xhci->num_usb3_ports; - port_array = xhci->usb3_ports; - xhci_dbg(xhci, "suspend USB 3.0 root hub\n"); - } else { - max_ports = xhci->num_usb2_ports; - port_array = xhci->usb2_ports; - xhci_dbg(xhci, "suspend USB 2.0 root hub\n"); - } + max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; spin_lock_irqsave(&xhci->lock, flags); @@ -873,15 +869,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) u32 temp; unsigned long flags; - if (hcd->speed == HCD_USB3) { - max_ports = xhci->num_usb3_ports; - port_array = xhci->usb3_ports; - xhci_dbg(xhci, "resume USB 3.0 root hub\n"); - } else { - max_ports = xhci->num_usb2_ports; - port_array = xhci->usb2_ports; - xhci_dbg(xhci, "resume USB 2.0 root hub\n"); - } + max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; if (time_before(jiffies, bus_state->next_statechange)) -- cgit v1.2.3 From 5c8a86e10a7c164f44537fabdc169fd8b4e7a440 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 11 May 2011 12:44:08 +0300 Subject: usb: musb: drop unneeded musb_debug trickery We have a generic way of enabling/disabling different debug messages on a driver called DYNAMIC_PRINTK. Anyone interested in enabling just part of the debug messages, please read the documentation under: Documentation/dynamic-debug-howto.txt for information on how to use that great infrastructure. Signed-off-by: Felipe Balbi --- drivers/usb/musb/Kconfig | 8 --- drivers/usb/musb/Makefile | 2 - drivers/usb/musb/am35x.c | 10 ++-- drivers/usb/musb/blackfin.c | 11 ++-- drivers/usb/musb/cppi_dma.c | 34 ++++++------ drivers/usb/musb/da8xx.c | 14 ++--- drivers/usb/musb/davinci.c | 10 ++-- drivers/usb/musb/musb_core.c | 96 +++++++++++++++----------------- drivers/usb/musb/musb_debug.h | 12 ---- drivers/usb/musb/musb_gadget.c | 110 +++++++++++++++++++------------------ drivers/usb/musb/musb_gadget_ep0.c | 22 ++++---- drivers/usb/musb/musb_host.c | 106 ++++++++++++++++------------------- drivers/usb/musb/musb_virthub.c | 22 ++++---- drivers/usb/musb/musbhsdma.c | 11 ++-- drivers/usb/musb/omap2430.c | 20 +++---- drivers/usb/musb/tusb6010.c | 48 ++++++++-------- drivers/usb/musb/tusb6010_omap.c | 32 +++++------ 17 files changed, 267 insertions(+), 301 deletions(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 74073b363c3..f90123f4ce5 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -176,11 +176,3 @@ config USB_TUSB_OMAP_DMA help Enable DMA transfers on TUSB 6010 when OMAP DMA is available. -config USB_MUSB_DEBUG - depends on USB_MUSB_HDRC - bool "Enable debugging messages" - default n - help - This enables musb debugging. To set the logging level use the debug - module parameter. Starting at level 3, per-transfer (urb, usb_request, - packet, or dma transfer) tracing may kick in. diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 74df5284894..4e15281b9e0 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -2,8 +2,6 @@ # for USB OTG silicon based on Mentor Graphics INVENTRA designs # -ccflags-$(CONFIG_USB_MUSB_DEBUG) := -DDEBUG - obj-$(CONFIG_USB_MUSB_HDRC) += musb_hdrc.o musb_hdrc-y := musb_core.o diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 9e6209f87d3..23ac28f98d9 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -151,7 +151,7 @@ static void otg_timer(unsigned long _musb) * status change events (from the transceiver) otherwise. */ devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(7, "Poll devctl %02x (%s)\n", devctl, + dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); @@ -203,7 +203,7 @@ static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { - DBG(4, "%s active, deleting timer\n", + dev_dbg(musb->controller, "%s active, deleting timer\n", otg_state_string(musb->xceiv->state)); del_timer(&otg_workaround); last_timer = jiffies; @@ -211,12 +211,12 @@ static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout) } if (time_after(last_timer, timeout) && timer_pending(&otg_workaround)) { - DBG(4, "Longer idle timer already pending, ignoring...\n"); + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n"); return; } last_timer = timeout; - DBG(4, "%s inactive, starting idle timer for %u ms\n", + dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", otg_state_string(musb->xceiv->state), jiffies_to_msecs(timeout - jiffies)); mod_timer(&otg_workaround, timeout); @@ -305,7 +305,7 @@ static irqreturn_t am35x_musb_interrupt(int irq, void *hci) } /* NOTE: this must complete power-on within 100 ms. */ - DBG(2, "VBUS %s (%s)%s, devctl %02x\n", + dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", otg_state_string(musb->xceiv->state), err ? " ERROR" : "", diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index e141d89656e..95d75c00404 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -43,7 +43,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) musb_writew(epio, MUSB_TXCOUNT, len); - DBG(4, "TX ep%d fifo %p count %d buf %p, epio %p\n", + dev_dbg(musb->controller, "TX ep%d fifo %p count %d buf %p, epio %p\n", hw_ep->epnum, fifo, len, src, epio); dump_fifo_data(src, len); @@ -154,7 +154,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) *(dst + len - 1) = (u8)inw((unsigned long)fifo + 4); } } - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'R', hw_ep->epnum, fifo, len, dst); dump_fifo_data(dst, len); @@ -279,13 +279,14 @@ static void musb_conn_timer_handler(unsigned long _musb) } break; default: - DBG(1, "%s state not handled\n", + dev_dbg(musb->controller, "%s state not handled\n", otg_state_string(musb->xceiv->state)); break; } spin_unlock_irqrestore(&musb->lock, flags); - DBG(4, "state is %s\n", otg_state_string(musb->xceiv->state)); + dev_dbg(musb->controller, "state is %s\n", + otg_state_string(musb->xceiv->state)); } static void bfin_musb_enable(struct musb *musb) @@ -307,7 +308,7 @@ static void bfin_musb_set_vbus(struct musb *musb, int is_on) value = !value; gpio_set_value(musb->config->gpio_vrsel, value); - DBG(1, "VBUS %s, devctl %02x " + dev_dbg(musb->controller, "VBUS %s, devctl %02x " /* otg %3x conf %08x prcm %08x */ "\n", otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL)); diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index ab434fbd8c3..149f3f310a0 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -236,7 +236,7 @@ static int cppi_controller_stop(struct dma_controller *c) musb_writel(tibase, DAVINCI_RXCPPI_INTCLR_REG, DAVINCI_DMA_ALL_CHANNELS_ENABLE); - DBG(1, "Tearing down RX and TX Channels\n"); + dev_dbg(musb->controller, "Tearing down RX and TX Channels\n"); for (i = 0; i < ARRAY_SIZE(controller->tx); i++) { /* FIXME restructure of txdma to use bds like rxdma */ controller->tx[i].last_processed = NULL; @@ -301,13 +301,13 @@ cppi_channel_allocate(struct dma_controller *c, */ if (transmit) { if (index >= ARRAY_SIZE(controller->tx)) { - DBG(1, "no %cX%d CPPI channel\n", 'T', index); + dev_dbg(musb->controller, "no %cX%d CPPI channel\n", 'T', index); return NULL; } cppi_ch = controller->tx + index; } else { if (index >= ARRAY_SIZE(controller->rx)) { - DBG(1, "no %cX%d CPPI channel\n", 'R', index); + dev_dbg(musb->controller, "no %cX%d CPPI channel\n", 'R', index); return NULL; } cppi_ch = controller->rx + index; @@ -318,13 +318,13 @@ cppi_channel_allocate(struct dma_controller *c, * with the other DMA engine too */ if (cppi_ch->hw_ep) - DBG(1, "re-allocating DMA%d %cX channel %p\n", + dev_dbg(musb->controller, "re-allocating DMA%d %cX channel %p\n", index, transmit ? 'T' : 'R', cppi_ch); cppi_ch->hw_ep = ep; cppi_ch->channel.status = MUSB_DMA_STATUS_FREE; cppi_ch->channel.max_len = 0x7fffffff; - DBG(4, "Allocate CPPI%d %cX\n", index, transmit ? 'T' : 'R'); + dev_dbg(musb->controller, "Allocate CPPI%d %cX\n", index, transmit ? 'T' : 'R'); return &cppi_ch->channel; } @@ -339,7 +339,7 @@ static void cppi_channel_release(struct dma_channel *channel) c = container_of(channel, struct cppi_channel, channel); tibase = c->controller->tibase; if (!c->hw_ep) - DBG(1, "releasing idle DMA channel %p\n", c); + dev_dbg(musb->controller, "releasing idle DMA channel %p\n", c); else if (!c->transmit) core_rxirq_enable(tibase, c->index + 1); @@ -597,7 +597,7 @@ cppi_next_tx_segment(struct musb *musb, struct cppi_channel *tx) length = min(n_bds * maxpacket, length); } - DBG(4, "TX DMA%d, pktSz %d %s bds %d dma 0x%llx len %u\n", + dev_dbg(musb->controller, "TX DMA%d, pktSz %d %s bds %d dma 0x%llx len %u\n", tx->index, maxpacket, rndis ? "rndis" : "transparent", @@ -654,7 +654,7 @@ cppi_next_tx_segment(struct musb *musb, struct cppi_channel *tx) bd->hw_options |= CPPI_ZERO_SET; } - DBG(5, "TXBD %p: nxt %08x buf %08x len %04x opt %08x\n", + dev_dbg(musb->controller, "TXBD %p: nxt %08x buf %08x len %04x opt %08x\n", bd, bd->hw_next, bd->hw_bufp, bd->hw_off_len, bd->hw_options); @@ -819,7 +819,7 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket) length = min(n_bds * maxpacket, length); - DBG(4, "RX DMA%d seg, maxp %d %s bds %d (cnt %d) " + dev_dbg(musb->controller, "RX DMA%d seg, maxp %d %s bds %d (cnt %d) " "dma 0x%llx len %u %u/%u\n", rx->index, maxpacket, onepacket @@ -936,7 +936,7 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket) DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4)) & 0xffff; if (i < (2 + n_bds)) { - DBG(2, "bufcnt%d underrun - %d (for %d)\n", + dev_dbg(musb->controller, "bufcnt%d underrun - %d (for %d)\n", rx->index, i, n_bds); musb_writel(tibase, DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4), @@ -985,7 +985,7 @@ static int cppi_channel_program(struct dma_channel *ch, /* WARN_ON(1); */ break; case MUSB_DMA_STATUS_UNKNOWN: - DBG(1, "%cX DMA%d not allocated!\n", + dev_dbg(musb->controller, "%cX DMA%d not allocated!\n", cppi_ch->transmit ? 'T' : 'R', cppi_ch->index); /* FALLTHROUGH */ @@ -1040,7 +1040,7 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch) if (!completed && (bd->hw_options & CPPI_OWN_SET)) break; - DBG(5, "C/RXBD %llx: nxt %08x buf %08x " + dev_dbg(musb->controller, "C/RXBD %llx: nxt %08x buf %08x " "off.len %08x opt.len %08x (%d)\n", (unsigned long long)bd->dma, bd->hw_next, bd->hw_bufp, bd->hw_off_len, bd->hw_options, @@ -1062,7 +1062,7 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch) * CPPI ignores those BDs even though OWN is still set. */ completed = true; - DBG(3, "rx short %d/%d (%d)\n", + dev_dbg(musb->controller, "rx short %d/%d (%d)\n", len, bd->buflen, rx->channel.actual_len); } @@ -1112,7 +1112,7 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch) musb_ep_select(cppi->mregs, rx->index + 1); csr = musb_readw(regs, MUSB_RXCSR); if (csr & MUSB_RXCSR_DMAENAB) { - DBG(4, "list%d %p/%p, last %llx%s, csr %04x\n", + dev_dbg(musb->controller, "list%d %p/%p, last %llx%s, csr %04x\n", rx->index, rx->head, rx->tail, rx->last_processed @@ -1175,7 +1175,7 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id) return IRQ_NONE; } - DBG(4, "CPPI IRQ Tx%x Rx%x\n", tx, rx); + dev_dbg(musb->controller, "CPPI IRQ Tx%x Rx%x\n", tx, rx); /* process TX channels */ for (index = 0; tx; tx = tx >> 1, index++) { @@ -1203,7 +1203,7 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id) * that needs to be acknowledged. */ if (NULL == bd) { - DBG(1, "null BD\n"); + dev_dbg(musb->controller, "null BD\n"); musb_writel(&tx_ram->tx_complete, 0, 0); continue; } @@ -1218,7 +1218,7 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id) if (bd->hw_options & CPPI_OWN_SET) break; - DBG(5, "C/TXBD %p n %x b %x off %x opt %x\n", + dev_dbg(musb->controller, "C/TXBD %p n %x b %x off %x opt %x\n", bd, bd->hw_next, bd->hw_bufp, bd->hw_off_len, bd->hw_options); diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 0d8984993a3..662ed34980b 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -199,7 +199,7 @@ static void otg_timer(unsigned long _musb) * status change events (from the transceiver) otherwise. */ devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(7, "Poll devctl %02x (%s)\n", devctl, + dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); @@ -274,7 +274,7 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { - DBG(4, "%s active, deleting timer\n", + dev_dbg(musb->controller, "%s active, deleting timer\n", otg_state_string(musb->xceiv->state)); del_timer(&otg_workaround); last_timer = jiffies; @@ -282,12 +282,12 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout) } if (time_after(last_timer, timeout) && timer_pending(&otg_workaround)) { - DBG(4, "Longer idle timer already pending, ignoring...\n"); + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n"); return; } last_timer = timeout; - DBG(4, "%s inactive, starting idle timer for %u ms\n", + dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", otg_state_string(musb->xceiv->state), jiffies_to_msecs(timeout - jiffies)); mod_timer(&otg_workaround, timeout); @@ -314,7 +314,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) goto eoi; musb_writel(reg_base, DA8XX_USB_INTR_SRC_CLEAR_REG, status); - DBG(4, "USB IRQ %08x\n", status); + dev_dbg(musb->controller, "USB IRQ %08x\n", status); musb->int_rx = (status & DA8XX_INTR_RX_MASK) >> DA8XX_INTR_RX_SHIFT; musb->int_tx = (status & DA8XX_INTR_TX_MASK) >> DA8XX_INTR_TX_SHIFT; @@ -366,7 +366,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) portstate(musb->port1_status &= ~USB_PORT_STAT_POWER); } - DBG(2, "VBUS %s (%s)%s, devctl %02x\n", + dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", otg_state_string(musb->xceiv->state), err ? " ERROR" : "", @@ -413,7 +413,7 @@ static int da8xx_musb_set_mode(struct musb *musb, u8 musb_mode) break; #endif default: - DBG(2, "Trying to set unsupported mode %u\n", musb_mode); + dev_dbg(musb->controller, "Trying to set unsupported mode %u\n", musb_mode); } __raw_writel(cfgchip2, CFGCHIP2); diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 3661b81a957..2a2adf6492c 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -220,7 +220,7 @@ static void otg_timer(unsigned long _musb) * status change events (from the transceiver) otherwise. */ devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(7, "poll devctl %02x (%s)\n", devctl, + dev_dbg(musb->controller, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); @@ -298,7 +298,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci) /* ack and handle non-CPPI interrupts */ tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG); musb_writel(tibase, DAVINCI_USB_INT_SRC_CLR_REG, tmp); - DBG(4, "IRQ %08x\n", tmp); + dev_dbg(musb->controller, "IRQ %08x\n", tmp); musb->int_rx = (tmp & DAVINCI_USB_RXINT_MASK) >> DAVINCI_USB_RXINT_SHIFT; @@ -355,7 +355,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci) * (OTG_TIME_A_WAIT_VRISE) but we don't check for that. */ davinci_musb_source_power(musb, drvvbus, 0); - DBG(2, "VBUS %s (%s)%s, devctl %02x\n", + dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", otg_state_string(musb->xceiv->state), err ? " ERROR" : "", @@ -485,7 +485,7 @@ static int davinci_musb_exit(struct musb *musb) break; if ((devctl & MUSB_DEVCTL_VBUS) != warn) { warn = devctl & MUSB_DEVCTL_VBUS; - DBG(1, "VBUS %d\n", + dev_dbg(musb->controller, "VBUS %d\n", warn >> MUSB_DEVCTL_VBUS_SHIFT); } msleep(1000); @@ -494,7 +494,7 @@ static int davinci_musb_exit(struct musb *musb) /* in OTG mode, another host might be connected */ if (devctl & MUSB_DEVCTL_VBUS) - DBG(1, "VBUS off timeout (devctl %02x)\n", devctl); + dev_dbg(musb->controller, "VBUS off timeout (devctl %02x)\n", devctl); } phy_off(); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 9a280872c2b..6e3988cb6e8 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -104,10 +104,6 @@ #define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON) -unsigned musb_debug; -module_param_named(debug, musb_debug, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug message level. Default = 0"); - #define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia" #define DRIVER_DESC "Inventra Dual-Role USB Controller Driver" @@ -157,10 +153,8 @@ static int musb_ulpi_read(struct otg_transceiver *otg, u32 offset) while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) & MUSB_ULPI_REG_CMPLT)) { i++; - if (i == 10000) { - DBG(3, "ULPI read timed out\n"); + if (i == 10000) return -ETIMEDOUT; - } } r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); @@ -190,10 +184,8 @@ static int musb_ulpi_write(struct otg_transceiver *otg, while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) & MUSB_ULPI_REG_CMPLT)) { i++; - if (i == 10000) { - DBG(3, "ULPI write timed out\n"); + if (i == 10000) return -ETIMEDOUT; - } } r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); @@ -221,11 +213,12 @@ static struct otg_io_access_ops musb_ulpi_access = { */ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) { + struct musb *musb = hw_ep->musb; void __iomem *fifo = hw_ep->fifo; prefetch((u8 *)src); - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'T', hw_ep->epnum, fifo, len, src); /* we can't assume unaligned reads work */ @@ -262,9 +255,10 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) */ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) { + struct musb *musb = hw_ep->musb; void __iomem *fifo = hw_ep->fifo; - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'R', hw_ep->epnum, fifo, len, dst); /* we can't assume unaligned writes work */ @@ -346,20 +340,20 @@ void musb_otg_timer_func(unsigned long data) spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { case OTG_STATE_B_WAIT_ACON: - DBG(1, "HNP: b_wait_acon timeout; back to b_peripheral\n"); + dev_dbg(musb->controller, "HNP: b_wait_acon timeout; back to b_peripheral\n"); musb_g_disconnect(musb); musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb->is_active = 0; break; case OTG_STATE_A_SUSPEND: case OTG_STATE_A_WAIT_BCON: - DBG(1, "HNP: %s timeout\n", + dev_dbg(musb->controller, "HNP: %s timeout\n", otg_state_string(musb->xceiv->state)); musb_platform_set_vbus(musb, 0); musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; break; default: - DBG(1, "HNP: Unhandled mode %s\n", + dev_dbg(musb->controller, "HNP: Unhandled mode %s\n", otg_state_string(musb->xceiv->state)); } musb->ignore_disconnect = 0; @@ -375,16 +369,16 @@ void musb_hnp_stop(struct musb *musb) void __iomem *mbase = musb->mregs; u8 reg; - DBG(1, "HNP: stop from %s\n", otg_state_string(musb->xceiv->state)); + dev_dbg(musb->controller, "HNP: stop from %s\n", otg_state_string(musb->xceiv->state)); switch (musb->xceiv->state) { case OTG_STATE_A_PERIPHERAL: musb_g_disconnect(musb); - DBG(1, "HNP: back to %s\n", + dev_dbg(musb->controller, "HNP: back to %s\n", otg_state_string(musb->xceiv->state)); break; case OTG_STATE_B_HOST: - DBG(1, "HNP: Disabling HR\n"); + dev_dbg(musb->controller, "HNP: Disabling HR\n"); hcd->self.is_b_host = 0; musb->xceiv->state = OTG_STATE_B_PERIPHERAL; MUSB_DEV_MODE(musb); @@ -394,7 +388,7 @@ void musb_hnp_stop(struct musb *musb) /* REVISIT: Start SESSION_REQUEST here? */ break; default: - DBG(1, "HNP: Stopping in unknown state %s\n", + dev_dbg(musb->controller, "HNP: Stopping in unknown state %s\n", otg_state_string(musb->xceiv->state)); } @@ -425,7 +419,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, { irqreturn_t handled = IRQ_NONE; - DBG(3, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl, + dev_dbg(musb->controller, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl, int_usb); /* in host mode, the peripheral may issue remote wakeup. @@ -434,7 +428,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, */ if (int_usb & MUSB_INTR_RESUME) { handled = IRQ_HANDLED; - DBG(3, "RESUME (%s)\n", otg_state_string(musb->xceiv->state)); + dev_dbg(musb->controller, "RESUME (%s)\n", otg_state_string(musb->xceiv->state)); if (devctl & MUSB_DEVCTL_HM) { #ifdef CONFIG_USB_MUSB_HDRC_HCD @@ -449,7 +443,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (power & MUSB_POWER_SUSPENDM) { /* spurious */ musb->int_usb &= ~MUSB_INTR_SUSPEND; - DBG(2, "Spurious SUSPENDM\n"); + dev_dbg(musb->controller, "Spurious SUSPENDM\n"); break; } @@ -521,11 +515,11 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS && (devctl & MUSB_DEVCTL_BDEVICE)) { - DBG(3, "SessReq while on B state\n"); + dev_dbg(musb->controller, "SessReq while on B state\n"); return IRQ_HANDLED; } - DBG(1, "SESSION_REQUEST (%s)\n", + dev_dbg(musb->controller, "SESSION_REQUEST (%s)\n", otg_state_string(musb->xceiv->state)); /* IRQ arrives from ID pin sense or (later, if VBUS power @@ -590,7 +584,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, break; } - DBG(1, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", + dev_dbg(musb->controller, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", otg_state_string(musb->xceiv->state), devctl, ({ char *s; @@ -616,7 +610,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, #endif if (int_usb & MUSB_INTR_SUSPEND) { - DBG(1, "SUSPEND (%s) devctl %02x power %02x\n", + dev_dbg(musb->controller, "SUSPEND (%s) devctl %02x power %02x\n", otg_state_string(musb->xceiv->state), devctl, power); handled = IRQ_HANDLED; @@ -649,7 +643,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (musb->is_active) { #ifdef CONFIG_USB_MUSB_OTG musb->xceiv->state = OTG_STATE_B_WAIT_ACON; - DBG(1, "HNP: Setting timer for b_ase0_brst\n"); + dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n"); mod_timer(&musb->otg_timer, jiffies + msecs_to_jiffies( OTG_TIME_B_ASE0_BRST)); @@ -668,7 +662,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, break; case OTG_STATE_B_HOST: /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ - DBG(1, "REVISIT: SUSPEND as B_HOST\n"); + dev_dbg(musb->controller, "REVISIT: SUSPEND as B_HOST\n"); break; default: /* "should not happen" */ @@ -711,14 +705,14 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, switch (musb->xceiv->state) { case OTG_STATE_B_PERIPHERAL: if (int_usb & MUSB_INTR_SUSPEND) { - DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n"); + dev_dbg(musb->controller, "HNP: SUSPEND+CONNECT, now b_host\n"); int_usb &= ~MUSB_INTR_SUSPEND; goto b_host; } else - DBG(1, "CONNECT as b_peripheral???\n"); + dev_dbg(musb->controller, "CONNECT as b_peripheral???\n"); break; case OTG_STATE_B_WAIT_ACON: - DBG(1, "HNP: CONNECT, now b_host\n"); + dev_dbg(musb->controller, "HNP: CONNECT, now b_host\n"); b_host: musb->xceiv->state = OTG_STATE_B_HOST; hcd->self.is_b_host = 1; @@ -741,13 +735,13 @@ b_host: else usb_hcd_resume_root_hub(hcd); - DBG(1, "CONNECT (%s) devctl %02x\n", + dev_dbg(musb->controller, "CONNECT (%s) devctl %02x\n", otg_state_string(musb->xceiv->state), devctl); } #endif /* CONFIG_USB_MUSB_HDRC_HCD */ if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) { - DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n", + dev_dbg(musb->controller, "DISCONNECT (%s) as %s, devctl %02x\n", otg_state_string(musb->xceiv->state), MUSB_MODE(musb), devctl); handled = IRQ_HANDLED; @@ -810,13 +804,13 @@ b_host: * stop the session. */ if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV)) - DBG(1, "BABBLE devctl: %02x\n", devctl); + dev_dbg(musb->controller, "BABBLE devctl: %02x\n", devctl); else { ERR("Stopping host session -- babble\n"); musb_writeb(musb->mregs, MUSB_DEVCTL, 0); } } else if (is_peripheral_capable()) { - DBG(1, "BUS RESET as %s\n", + dev_dbg(musb->controller, "BUS RESET as %s\n", otg_state_string(musb->xceiv->state)); switch (musb->xceiv->state) { #ifdef CONFIG_USB_OTG @@ -830,7 +824,7 @@ b_host: /* FALLTHROUGH */ case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */ /* never use invalid T(a_wait_bcon) */ - DBG(1, "HNP: in %s, %d msec timeout\n", + dev_dbg(musb->controller, "HNP: in %s, %d msec timeout\n", otg_state_string(musb->xceiv->state), TA_WAIT_BCON(musb)); mod_timer(&musb->otg_timer, jiffies @@ -842,7 +836,7 @@ b_host: musb_g_reset(musb); break; case OTG_STATE_B_WAIT_ACON: - DBG(1, "HNP: RESET (%s), to b_peripheral\n", + dev_dbg(musb->controller, "HNP: RESET (%s), to b_peripheral\n", otg_state_string(musb->xceiv->state)); musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb_g_reset(musb); @@ -855,7 +849,7 @@ b_host: musb_g_reset(musb); break; default: - DBG(1, "Unhandled BUS RESET as %s\n", + dev_dbg(musb->controller, "Unhandled BUS RESET as %s\n", otg_state_string(musb->xceiv->state)); } } @@ -879,7 +873,7 @@ b_host: u8 epnum; u16 frame; - DBG(6, "START_OF_FRAME\n"); + dev_dbg(musb->controller, "START_OF_FRAME\n"); handled = IRQ_HANDLED; /* start any periodic Tx transfers waiting for current frame */ @@ -921,7 +915,7 @@ void musb_start(struct musb *musb) void __iomem *regs = musb->mregs; u8 devctl = musb_readb(regs, MUSB_DEVCTL); - DBG(2, "<== devctl %02x\n", devctl); + dev_dbg(musb->controller, "<== devctl %02x\n", devctl); /* Set INT enable registers, enable interrupts */ musb_writew(regs, MUSB_INTRTXE, musb->epmask); @@ -998,7 +992,7 @@ void musb_stop(struct musb *musb) /* stop IRQs, timers, ... */ musb_platform_disable(musb); musb_generic_disable(musb); - DBG(3, "HDRC disabled\n"); + dev_dbg(musb->controller, "HDRC disabled\n"); /* FIXME * - mark host and/or peripheral drivers unusable/inactive @@ -1344,7 +1338,7 @@ static int __init ep_config_from_hw(struct musb *musb) void *mbase = musb->mregs; int ret = 0; - DBG(2, "<== static silicon ep config\n"); + dev_dbg(musb->controller, "<== static silicon ep config\n"); /* FIXME pick up ep0 maxpacket size */ @@ -1491,7 +1485,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) #endif if (hw_ep->max_packet_sz_tx) { - DBG(1, + dev_dbg(musb->controller, "%s: hw_ep %d%s, %smax %d\n", musb_driver_name, i, hw_ep->is_shared_fifo ? "shared" : "tx", @@ -1500,7 +1494,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) hw_ep->max_packet_sz_tx); } if (hw_ep->max_packet_sz_rx && !hw_ep->is_shared_fifo) { - DBG(1, + dev_dbg(musb->controller, "%s: hw_ep %d%s, %smax %d\n", musb_driver_name, i, "rx", @@ -1509,7 +1503,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) hw_ep->max_packet_sz_rx); } if (!(hw_ep->max_packet_sz_tx || hw_ep->max_packet_sz_rx)) - DBG(1, "hw_ep %d not configured\n", i); + dev_dbg(musb->controller, "hw_ep %d not configured\n", i); } return 0; @@ -1562,14 +1556,14 @@ irqreturn_t musb_interrupt(struct musb *musb) devctl = musb_readb(musb->mregs, MUSB_DEVCTL); power = musb_readb(musb->mregs, MUSB_POWER); - DBG(4, "** IRQ %s usb%04x tx%04x rx%04x\n", + dev_dbg(musb->controller, "** IRQ %s usb%04x tx%04x rx%04x\n", (devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral", musb->int_usb, musb->int_tx, musb->int_rx); #ifdef CONFIG_USB_GADGET_MUSB_HDRC if (is_otg_enabled(musb) || is_peripheral_enabled(musb)) if (!musb->gadget_driver) { - DBG(5, "No gadget driver loaded\n"); + dev_dbg(musb->controller, "No gadget driver loaded\n"); return IRQ_HANDLED; } #endif @@ -2060,7 +2054,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) status = usb_add_hcd(musb_to_hcd(musb), -1, 0); hcd->self.uses_pio_for_control = 1; - DBG(1, "%s mode, status %d, devctl %02x %c\n", + dev_dbg(musb->controller, "%s mode, status %d, devctl %02x %c\n", "HOST", status, musb_readb(musb->mregs, MUSB_DEVCTL), (musb_readb(musb->mregs, MUSB_DEVCTL) @@ -2074,7 +2068,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) status = musb_gadget_setup(musb); - DBG(1, "%s mode, status %d, dev%02x\n", + dev_dbg(musb->controller, "%s mode, status %d, dev%02x\n", is_otg_enabled(musb) ? "OTG" : "PERIPHERAL", status, musb_readb(musb->mregs, MUSB_DEVCTL)); @@ -2456,8 +2450,8 @@ static int __init musb_init(void) #elif defined(CONFIG_USB_MUSB_HDRC_HCD) "host" #endif - ", debug=%d\n", - musb_driver_name, musb_debug); + , + musb_driver_name); return platform_driver_probe(&musb_driver, musb_probe); } diff --git a/drivers/usb/musb/musb_debug.h b/drivers/usb/musb/musb_debug.h index f958a49a012..742eada5002 100644 --- a/drivers/usb/musb/musb_debug.h +++ b/drivers/usb/musb/musb_debug.h @@ -42,18 +42,6 @@ #define INFO(fmt, args...) yprintk(KERN_INFO, fmt, ## args) #define ERR(fmt, args...) yprintk(KERN_ERR, fmt, ## args) -#define DBG(level, format, args...) do { \ - if (_dbg_level(level)) \ - pr_debug("%s %d: " format, __func__, __LINE__, ## args); \ - } while (0) - -extern unsigned musb_debug; - -static inline int _dbg_level(unsigned l) -{ - return musb_debug >= l; -} - #ifdef CONFIG_DEBUG_FS extern int musb_init_debugfs(struct musb *musb); extern void musb_exit_debugfs(struct musb *musb); diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index cfbee4dcfc1..d34ff408c81 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -147,7 +147,8 @@ static inline void unmap_dma_buffer(struct musb_request *request, return; if (request->request.dma == DMA_ADDR_INVALID) { - DBG(20, "not unmapping a never mapped buffer\n"); + dev_vdbg(musb->controller, + "not unmapping a never mapped buffer\n"); return; } if (request->map_state == MUSB_MAPPED) { @@ -198,11 +199,11 @@ __acquires(ep->musb->lock) spin_unlock(&musb->lock); unmap_dma_buffer(req, musb); if (request->status == 0) - DBG(5, "%s done request %p, %d/%d\n", + dev_dbg(musb->controller, "%s done request %p, %d/%d\n", ep->end_point.name, request, req->request.actual, req->request.length); else - DBG(2, "%s request %p, %d/%d fault %d\n", + dev_dbg(musb->controller, "%s request %p, %d/%d fault %d\n", ep->end_point.name, request, req->request.actual, req->request.length, request->status); @@ -219,6 +220,7 @@ __acquires(ep->musb->lock) */ static void nuke(struct musb_ep *ep, const int status) { + struct musb *musb = ep->musb; struct musb_request *req = NULL; void __iomem *epio = ep->musb->endpoints[ep->current_epnum].regs; @@ -246,7 +248,8 @@ static void nuke(struct musb_ep *ep, const int status) } value = c->channel_abort(ep->dma); - DBG(value ? 1 : 6, "%s: abort DMA --> %d\n", ep->name, value); + dev_dbg(musb->controller, "%s: abort DMA --> %d\n", + ep->name, value); c->channel_release(ep->dma); ep->dma = NULL; } @@ -329,7 +332,7 @@ static void txstate(struct musb *musb, struct musb_request *req) /* we shouldn't get here while DMA is active ... but we do ... */ if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) { - DBG(4, "dma pending...\n"); + dev_dbg(musb->controller, "dma pending...\n"); return; } @@ -341,18 +344,18 @@ static void txstate(struct musb *musb, struct musb_request *req) (int)(request->length - request->actual)); if (csr & MUSB_TXCSR_TXPKTRDY) { - DBG(5, "%s old packet still ready , txcsr %03x\n", + dev_dbg(musb->controller, "%s old packet still ready , txcsr %03x\n", musb_ep->end_point.name, csr); return; } if (csr & MUSB_TXCSR_P_SENDSTALL) { - DBG(5, "%s stalling, txcsr %03x\n", + dev_dbg(musb->controller, "%s stalling, txcsr %03x\n", musb_ep->end_point.name, csr); return; } - DBG(4, "hw_ep%d, maxpacket %d, fifo count %d, txcsr %03x\n", + dev_dbg(musb->controller, "hw_ep%d, maxpacket %d, fifo count %d, txcsr %03x\n", epnum, musb_ep->packet_sz, fifo_count, csr); @@ -469,7 +472,7 @@ static void txstate(struct musb *musb, struct musb_request *req) } /* host may already have the data when this message shows... */ - DBG(3, "%s TX/IN %s len %d/%d, txcsr %04x, fifo %d/%d\n", + dev_dbg(musb->controller, "%s TX/IN %s len %d/%d, txcsr %04x, fifo %d/%d\n", musb_ep->end_point.name, use_dma ? "dma" : "pio", request->actual, request->length, musb_readw(epio, MUSB_TXCSR), @@ -496,7 +499,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) request = &req->request; csr = musb_readw(epio, MUSB_TXCSR); - DBG(4, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr); + dev_dbg(musb->controller, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr); dma = is_dma_capable() ? musb_ep->dma : NULL; @@ -516,7 +519,8 @@ void musb_g_tx(struct musb *musb, u8 epnum) csr |= MUSB_TXCSR_P_WZC_BITS; csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); musb_writew(epio, MUSB_TXCSR, csr); - DBG(20, "underrun on ep%d, req %p\n", epnum, request); + dev_vdbg(musb->controller, "underrun on ep%d, req %p\n", + epnum, request); } if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { @@ -524,7 +528,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) * SHOULD NOT HAPPEN... has with CPPI though, after * changing SENDSTALL (and other cases); harmless? */ - DBG(5, "%s dma still busy?\n", musb_ep->end_point.name); + dev_dbg(musb->controller, "%s dma still busy?\n", musb_ep->end_point.name); return; } @@ -540,7 +544,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) /* Ensure writebuffer is empty. */ csr = musb_readw(epio, MUSB_TXCSR); request->actual += musb_ep->dma->actual_len; - DBG(4, "TXCSR%d %04x, DMA off, len %zu, req %p\n", + dev_dbg(musb->controller, "TXCSR%d %04x, DMA off, len %zu, req %p\n", epnum, csr, musb_ep->dma->actual_len, request); } @@ -564,7 +568,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) if (csr & MUSB_TXCSR_TXPKTRDY) return; - DBG(4, "sending zero pkt\n"); + dev_dbg(musb->controller, "sending zero pkt\n"); musb_writew(epio, MUSB_TXCSR, MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY); request->zero = 0; @@ -574,7 +578,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) musb_g_giveback(musb_ep, request, 0); req = musb_ep->desc ? next_request(musb_ep) : NULL; if (!req) { - DBG(4, "%s idle now\n", + dev_dbg(musb->controller, "%s idle now\n", musb_ep->end_point.name); return; } @@ -640,12 +644,12 @@ static void rxstate(struct musb *musb, struct musb_request *req) /* We shouldn't get here while DMA is active, but we do... */ if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) { - DBG(4, "DMA pending...\n"); + dev_dbg(musb->controller, "DMA pending...\n"); return; } if (csr & MUSB_RXCSR_P_SENDSTALL) { - DBG(5, "%s stalling, RXCSR %04x\n", + dev_dbg(musb->controller, "%s stalling, RXCSR %04x\n", musb_ep->end_point.name, csr); return; } @@ -757,7 +761,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) #endif /* Mentor's DMA */ fifo_count = request->length - request->actual; - DBG(3, "%s OUT/RX pio fifo %d/%d, maxpacket %d\n", + dev_dbg(musb->controller, "%s OUT/RX pio fifo %d/%d, maxpacket %d\n", musb_ep->end_point.name, len, fifo_count, musb_ep->packet_sz); @@ -846,7 +850,7 @@ void musb_g_rx(struct musb *musb, u8 epnum) csr = musb_readw(epio, MUSB_RXCSR); dma = is_dma_capable() ? musb_ep->dma : NULL; - DBG(4, "<== %s, rxcsr %04x%s %p\n", musb_ep->end_point.name, + dev_dbg(musb->controller, "<== %s, rxcsr %04x%s %p\n", musb_ep->end_point.name, csr, dma ? " (dma)" : "", request); if (csr & MUSB_RXCSR_P_SENTSTALL) { @@ -861,19 +865,18 @@ void musb_g_rx(struct musb *musb, u8 epnum) csr &= ~MUSB_RXCSR_P_OVERRUN; musb_writew(epio, MUSB_RXCSR, csr); - DBG(3, "%s iso overrun on %p\n", musb_ep->name, request); + dev_dbg(musb->controller, "%s iso overrun on %p\n", musb_ep->name, request); if (request->status == -EINPROGRESS) request->status = -EOVERFLOW; } if (csr & MUSB_RXCSR_INCOMPRX) { /* REVISIT not necessarily an error */ - DBG(4, "%s, incomprx\n", musb_ep->end_point.name); + dev_dbg(musb->controller, "%s, incomprx\n", musb_ep->end_point.name); } if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { /* "should not happen"; likely RXPKTRDY pending for DMA */ - DBG((csr & MUSB_RXCSR_DMAENAB) ? 4 : 1, - "%s busy, csr %04x\n", + dev_dbg(musb->controller, "%s busy, csr %04x\n", musb_ep->end_point.name, csr); return; } @@ -887,7 +890,7 @@ void musb_g_rx(struct musb *musb, u8 epnum) request->actual += musb_ep->dma->actual_len; - DBG(4, "RXCSR%d %04x, dma off, %04x, len %zu, req %p\n", + dev_dbg(musb->controller, "RXCSR%d %04x, dma off, %04x, len %zu, req %p\n", epnum, csr, musb_readw(epio, MUSB_RXCSR), musb_ep->dma->actual_len, request); @@ -978,7 +981,7 @@ static int musb_gadget_enable(struct usb_ep *ep, ok = musb->hb_iso_rx; if (!ok) { - DBG(4, "no support for high bandwidth ISO\n"); + dev_dbg(musb->controller, "no support for high bandwidth ISO\n"); goto fail; } musb_ep->hb_mult = (tmp >> 11) & 3; @@ -1002,7 +1005,7 @@ static int musb_gadget_enable(struct usb_ep *ep, goto fail; if (tmp > hw_ep->max_packet_sz_tx) { - DBG(4, "packet size beyond hardware FIFO size\n"); + dev_dbg(musb->controller, "packet size beyond hardware FIFO size\n"); goto fail; } @@ -1042,7 +1045,7 @@ static int musb_gadget_enable(struct usb_ep *ep, goto fail; if (tmp > hw_ep->max_packet_sz_rx) { - DBG(4, "packet size beyond hardware FIFO size\n"); + dev_dbg(musb->controller, "packet size beyond hardware FIFO size\n"); goto fail; } @@ -1155,7 +1158,7 @@ static int musb_gadget_disable(struct usb_ep *ep) spin_unlock_irqrestore(&(musb->lock), flags); - DBG(2, "%s\n", musb_ep->end_point.name); + dev_dbg(musb->controller, "%s\n", musb_ep->end_point.name); return status; } @@ -1167,11 +1170,12 @@ static int musb_gadget_disable(struct usb_ep *ep) struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { struct musb_ep *musb_ep = to_musb_ep(ep); + struct musb *musb = musb_ep->musb; struct musb_request *request = NULL; request = kzalloc(sizeof *request, gfp_flags); if (!request) { - DBG(4, "not enough memory\n"); + dev_dbg(musb->controller, "not enough memory\n"); return NULL; } @@ -1205,7 +1209,7 @@ struct free_record { */ void musb_ep_restart(struct musb *musb, struct musb_request *req) { - DBG(3, "<== %s request %p len %u on hw_ep%d\n", + dev_dbg(musb->controller, "<== %s request %p len %u on hw_ep%d\n", req->tx ? "TX/IN" : "RX/OUT", &req->request, req->request.length, req->epnum); @@ -1239,7 +1243,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, if (request->ep != musb_ep) return -EINVAL; - DBG(4, "<== to %s request=%p\n", ep->name, req); + dev_dbg(musb->controller, "<== to %s request=%p\n", ep->name, req); /* request is mine now... */ request->request.actual = 0; @@ -1253,7 +1257,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, /* don't queue if the ep is down */ if (!musb_ep->desc) { - DBG(4, "req %p queued to %s while ep %s\n", + dev_dbg(musb->controller, "req %p queued to %s while ep %s\n", req, ep->name, "disabled"); status = -ESHUTDOWN; goto cleanup; @@ -1290,7 +1294,7 @@ static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request) break; } if (r != req) { - DBG(3, "request %p not queued to %s\n", request, ep->name); + dev_dbg(musb->controller, "request %p not queued to %s\n", request, ep->name); status = -EINVAL; goto done; } @@ -1356,7 +1360,7 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value) request = next_request(musb_ep); if (value) { if (request) { - DBG(3, "request in progress, cannot halt %s\n", + dev_dbg(musb->controller, "request in progress, cannot halt %s\n", ep->name); status = -EAGAIN; goto done; @@ -1365,7 +1369,7 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value) if (musb_ep->is_in) { csr = musb_readw(epio, MUSB_TXCSR); if (csr & MUSB_TXCSR_FIFONOTEMPTY) { - DBG(3, "FIFO busy, cannot halt %s\n", ep->name); + dev_dbg(musb->controller, "FIFO busy, cannot halt %s\n", ep->name); status = -EAGAIN; goto done; } @@ -1374,7 +1378,7 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value) musb_ep->wedged = 0; /* set/clear the stall and toggle bits */ - DBG(2, "%s: %s stall\n", ep->name, value ? "set" : "clear"); + dev_dbg(musb->controller, "%s: %s stall\n", ep->name, value ? "set" : "clear"); if (musb_ep->is_in) { csr = musb_readw(epio, MUSB_TXCSR); csr |= MUSB_TXCSR_P_WZC_BITS @@ -1401,7 +1405,7 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value) /* maybe start the first request in the queue */ if (!musb_ep->busy && !value && request) { - DBG(3, "restarting the request\n"); + dev_dbg(musb->controller, "restarting the request\n"); musb_ep_restart(musb, request); } @@ -1532,7 +1536,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) case OTG_STATE_B_IDLE: /* Start SRP ... OTG not required. */ devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(2, "Sending SRP: devctl: %02x\n", devctl); + dev_dbg(musb->controller, "Sending SRP: devctl: %02x\n", devctl); devctl |= MUSB_DEVCTL_SESSION; musb_writeb(mregs, MUSB_DEVCTL, devctl); devctl = musb_readb(mregs, MUSB_DEVCTL); @@ -1556,7 +1560,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) status = 0; goto done; default: - DBG(2, "Unhandled wake: %s\n", + dev_dbg(musb->controller, "Unhandled wake: %s\n", otg_state_string(musb->xceiv->state)); goto done; } @@ -1566,7 +1570,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) power = musb_readb(mregs, MUSB_POWER); power |= MUSB_POWER_RESUME; musb_writeb(mregs, MUSB_POWER, power); - DBG(2, "issue wakeup\n"); + dev_dbg(musb->controller, "issue wakeup\n"); /* FIXME do this next chunk in a timer callback, no udelay */ mdelay(2); @@ -1600,7 +1604,7 @@ static void musb_pullup(struct musb *musb, int is_on) /* FIXME if on, HdrcStart; if off, HdrcStop */ - DBG(3, "gadget %s D+ pullup %s\n", + dev_dbg(musb->controller, "gadget %s D+ pullup %s\n", musb->gadget_driver->function, is_on ? "on" : "off"); musb_writeb(musb->mregs, MUSB_POWER, power); } @@ -1608,7 +1612,7 @@ static void musb_pullup(struct musb *musb, int is_on) #if 0 static int musb_gadget_vbus_session(struct usb_gadget *gadget, int is_active) { - DBG(2, "<= %s =>\n", __func__); + dev_dbg(musb->controller, "<= %s =>\n", __func__); /* * FIXME iff driver's softconnect flag is set (as it is during probe, @@ -1817,17 +1821,17 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, /* driver must be initialized to support peripheral mode */ if (!musb) { - DBG(1, "no dev??\n"); + dev_dbg(musb->controller, "no dev??\n"); retval = -ENODEV; goto err0; } pm_runtime_get_sync(musb->controller); - DBG(3, "registering driver %s\n", driver->function); + dev_dbg(musb->controller, "registering driver %s\n", driver->function); if (musb->gadget_driver) { - DBG(1, "%s is already bound to %s\n", + dev_dbg(musb->controller, "%s is already bound to %s\n", musb_driver_name, musb->gadget_driver->driver.name); retval = -EBUSY; @@ -1843,7 +1847,7 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, retval = bind(&musb->g); if (retval) { - DBG(3, "bind to driver %s failed --> %d\n", + dev_dbg(musb->controller, "bind to driver %s failed --> %d\n", driver->driver.name, retval); goto err1; } @@ -1871,7 +1875,7 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, if (is_otg_enabled(musb)) { struct usb_hcd *hcd = musb_to_hcd(musb); - DBG(3, "OTG startup...\n"); + dev_dbg(musb->controller, "OTG startup...\n"); /* REVISIT: funcall to other code, which also * handles power budgeting ... this way also @@ -1879,7 +1883,7 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, */ retval = usb_add_hcd(musb_to_hcd(musb), -1, 0); if (retval < 0) { - DBG(1, "add_hcd failed, %d\n", retval); + dev_dbg(musb->controller, "add_hcd failed, %d\n", retval); goto err2; } @@ -1986,7 +1990,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) stop_activity(musb, driver); otg_set_peripheral(musb->xceiv, NULL); - DBG(3, "unregistering driver %s\n", driver->function); + dev_dbg(musb->controller, "unregistering driver %s\n", driver->function); spin_unlock_irqrestore(&musb->lock, flags); driver->unbind(&musb->g); @@ -2048,7 +2052,7 @@ void musb_g_suspend(struct musb *musb) u8 devctl; devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - DBG(3, "devctl %02x\n", devctl); + dev_dbg(musb->controller, "devctl %02x\n", devctl); switch (musb->xceiv->state) { case OTG_STATE_B_IDLE: @@ -2084,7 +2088,7 @@ void musb_g_disconnect(struct musb *musb) void __iomem *mregs = musb->mregs; u8 devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(3, "devctl %02x\n", devctl); + dev_dbg(musb->controller, "devctl %02x\n", devctl); /* clear HR */ musb_writeb(mregs, MUSB_DEVCTL, devctl & MUSB_DEVCTL_SESSION); @@ -2102,7 +2106,7 @@ void musb_g_disconnect(struct musb *musb) switch (musb->xceiv->state) { default: #ifdef CONFIG_USB_MUSB_OTG - DBG(2, "Unhandled disconnect %s, setting a_idle\n", + dev_dbg(musb->controller, "Unhandled disconnect %s, setting a_idle\n", otg_state_string(musb->xceiv->state)); musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); @@ -2133,7 +2137,7 @@ __acquires(musb->lock) u8 devctl = musb_readb(mbase, MUSB_DEVCTL); u8 power; - DBG(3, "<== %s addr=%x driver '%s'\n", + dev_dbg(musb->controller, "<== %s addr=%x driver '%s'\n", (devctl & MUSB_DEVCTL_BDEVICE) ? "B-Device" : "A-Device", musb_readb(mbase, MUSB_FADDR), diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 75a542e42fd..b2faff23550 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -209,7 +209,7 @@ static inline void musb_try_b_hnp_enable(struct musb *musb) void __iomem *mbase = musb->mregs; u8 devctl; - DBG(1, "HNP: Setting HR\n"); + dev_dbg(musb->controller, "HNP: Setting HR\n"); devctl = musb_readb(mbase, MUSB_DEVCTL); musb_writeb(mbase, MUSB_DEVCTL, devctl | MUSB_DEVCTL_HR); } @@ -306,7 +306,7 @@ __acquires(musb->lock) /* Maybe start the first request in the queue */ request = next_request(musb_ep); if (!musb_ep->busy && request) { - DBG(3, "restarting the request\n"); + dev_dbg(musb->controller, "restarting the request\n"); musb_ep_restart(musb, request); } @@ -553,7 +553,7 @@ static void ep0_txstate(struct musb *musb) if (!req) { /* WARN_ON(1); */ - DBG(2, "odd; csr0 %04x\n", musb_readw(regs, MUSB_CSR0)); + dev_dbg(musb->controller, "odd; csr0 %04x\n", musb_readw(regs, MUSB_CSR0)); return; } @@ -610,7 +610,7 @@ musb_read_setup(struct musb *musb, struct usb_ctrlrequest *req) /* NOTE: earlier 2.6 versions changed setup packets to host * order, but now USB packets always stay in USB byte order. */ - DBG(3, "SETUP req%02x.%02x v%04x i%04x l%d\n", + dev_dbg(musb->controller, "SETUP req%02x.%02x v%04x i%04x l%d\n", req->bRequestType, req->bRequest, le16_to_cpu(req->wValue), @@ -678,7 +678,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) csr = musb_readw(regs, MUSB_CSR0); len = musb_readb(regs, MUSB_COUNT0); - DBG(4, "csr %04x, count %d, myaddr %d, ep0stage %s\n", + dev_dbg(musb->controller, "csr %04x, count %d, myaddr %d, ep0stage %s\n", csr, len, musb_readb(mbase, MUSB_FADDR), decode_ep0stage(musb->ep0_state)); @@ -749,7 +749,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) /* enter test mode if needed (exit by reset) */ else if (musb->test_mode) { - DBG(1, "entering TESTMODE\n"); + dev_dbg(musb->controller, "entering TESTMODE\n"); if (MUSB_TEST_PACKET == musb->test_mode_nr) musb_load_testpacket(musb); @@ -861,7 +861,7 @@ setup: break; } - DBG(3, "handled %d, csr %04x, ep0stage %s\n", + dev_dbg(musb->controller, "handled %d, csr %04x, ep0stage %s\n", handled, csr, decode_ep0stage(musb->ep0_state)); @@ -878,7 +878,7 @@ setup: if (handled < 0) { musb_ep_select(mbase, 0); stall: - DBG(3, "stall (%d)\n", handled); + dev_dbg(musb->controller, "stall (%d)\n", handled); musb->ackpend |= MUSB_CSR0_P_SENDSTALL; musb->ep0_state = MUSB_EP0_STAGE_IDLE; finish: @@ -958,7 +958,7 @@ musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags) status = 0; break; default: - DBG(1, "ep0 request queued in state %d\n", + dev_dbg(musb->controller, "ep0 request queued in state %d\n", musb->ep0_state); status = -EINVAL; goto cleanup; @@ -967,7 +967,7 @@ musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags) /* add request to the list */ list_add_tail(&req->list, &ep->req_list); - DBG(3, "queue to %s (%s), length=%d\n", + dev_dbg(musb->controller, "queue to %s (%s), length=%d\n", ep->name, ep->is_in ? "IN/TX" : "OUT/RX", req->request.length); @@ -1060,7 +1060,7 @@ static int musb_g_ep0_halt(struct usb_ep *e, int value) musb->ackpend = 0; break; default: - DBG(1, "ep0 can't halt in state %d\n", musb->ep0_state); + dev_dbg(musb->controller, "ep0 can't halt in state %d\n", musb->ep0_state); status = -EINVAL; } diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 07b106c7de8..75aa01e0036 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -106,6 +106,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum, */ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) { + struct musb *musb = ep->musb; void __iomem *epio = ep->regs; u16 csr; u16 lastcsr = 0; @@ -114,7 +115,7 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) csr = musb_readw(epio, MUSB_TXCSR); while (csr & MUSB_TXCSR_FIFONOTEMPTY) { if (csr != lastcsr) - DBG(3, "Host TX FIFONOTEMPTY csr: %02x\n", csr); + dev_dbg(musb->controller, "Host TX FIFONOTEMPTY csr: %02x\n", csr); lastcsr = csr; csr |= MUSB_TXCSR_FLUSHFIFO; musb_writew(epio, MUSB_TXCSR, csr); @@ -240,7 +241,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) len = urb->transfer_buffer_length - urb->actual_length; } - DBG(4, "qh %p urb %p dev%d ep%d%s%s, hw_ep %d, %p/%d\n", + dev_dbg(musb->controller, "qh %p urb %p dev%d ep%d%s%s, hw_ep %d, %p/%d\n", qh, urb, address, qh->epnum, is_in ? "in" : "out", ({char *s; switch (qh->type) { @@ -263,7 +264,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) switch (qh->type) { case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: - DBG(3, "check whether there's still time for periodic Tx\n"); + dev_dbg(musb->controller, "check whether there's still time for periodic Tx\n"); frame = musb_readw(mbase, MUSB_FRAME); /* FIXME this doesn't implement that scheduling policy ... * or handle framecounter wrapping @@ -278,7 +279,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) } else { qh->frame = urb->start_frame; /* enable SOF interrupt so we can count down */ - DBG(1, "SOF for %d\n", epnum); + dev_dbg(musb->controller, "SOF for %d\n", epnum); #if 1 /* ifndef CONFIG_ARCH_DAVINCI */ musb_writeb(mbase, MUSB_INTRUSBE, 0xff); #endif @@ -286,7 +287,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) break; default: start: - DBG(4, "Start TX%d %s\n", epnum, + dev_dbg(musb->controller, "Start TX%d %s\n", epnum, hw_ep->tx_channel ? "dma" : "pio"); if (!hw_ep->tx_channel) @@ -301,21 +302,7 @@ static void musb_giveback(struct musb *musb, struct urb *urb, int status) __releases(musb->lock) __acquires(musb->lock) { - DBG(({ int level; switch (status) { - case 0: - level = 4; - break; - /* common/boring faults */ - case -EREMOTEIO: - case -ESHUTDOWN: - case -ECONNRESET: - case -EPIPE: - level = 3; - break; - default: - level = 2; - break; - }; level; }), + dev_dbg(musb->controller, "complete %p %pF (%d), dev%d ep%d%s, %d/%d\n", urb, urb->complete, status, usb_pipedevice(urb->pipe), @@ -426,7 +413,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, } if (qh != NULL && qh->is_ready) { - DBG(4, "... next ep%d %cX urb %p\n", + dev_dbg(musb->controller, "... next ep%d %cX urb %p\n", hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh)); musb_start_urb(musb, is_in, qh); } @@ -471,7 +458,7 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err) /* musb_ep_select(mbase, epnum); */ rx_count = musb_readw(epio, MUSB_RXCOUNT); - DBG(3, "RX%d count %d, buffer %p len %d/%d\n", epnum, rx_count, + dev_dbg(musb->controller, "RX%d count %d, buffer %p len %d/%d\n", epnum, rx_count, urb->transfer_buffer, qh->offset, urb->transfer_buffer_length); @@ -493,7 +480,7 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err) status = -EOVERFLOW; urb->error_count++; } - DBG(2, "** OVERFLOW %d into %d\n", rx_count, length); + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n", rx_count, length); do_flush = 1; } else length = rx_count; @@ -511,7 +498,7 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err) if (rx_count > length) { if (urb->status == -EINPROGRESS) urb->status = -EOVERFLOW; - DBG(2, "** OVERFLOW %d into %d\n", rx_count, length); + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n", rx_count, length); do_flush = 1; } else length = rx_count; @@ -697,7 +684,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum, struct musb_qh *qh = musb_ep_get_qh(hw_ep, !is_out); u16 packet_sz = qh->maxpacket; - DBG(3, "%s hw%d urb %p spd%d dev%d ep%d%s " + dev_dbg(musb->controller, "%s hw%d urb %p spd%d dev%d ep%d%s " "h_addr%02x h_port%02x bytes %d\n", is_out ? "-->" : "<--", epnum, urb, urb->dev->speed, @@ -880,7 +867,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum, } csr |= MUSB_RXCSR_H_REQPKT; - DBG(7, "RXCSR%d := %04x\n", epnum, csr); + dev_dbg(musb->controller, "RXCSR%d := %04x\n", epnum, csr); musb_writew(hw_ep->regs, MUSB_RXCSR, csr); csr = musb_readw(hw_ep->regs, MUSB_RXCSR); } @@ -923,15 +910,15 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) request = (struct usb_ctrlrequest *) urb->setup_packet; if (!request->wLength) { - DBG(4, "start no-DATA\n"); + dev_dbg(musb->controller, "start no-DATA\n"); break; } else if (request->bRequestType & USB_DIR_IN) { - DBG(4, "start IN-DATA\n"); + dev_dbg(musb->controller, "start IN-DATA\n"); musb->ep0_stage = MUSB_EP0_IN; more = true; break; } else { - DBG(4, "start OUT-DATA\n"); + dev_dbg(musb->controller, "start OUT-DATA\n"); musb->ep0_stage = MUSB_EP0_OUT; more = true; } @@ -943,7 +930,7 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) if (fifo_count) { fifo_dest = (u8 *) (urb->transfer_buffer + urb->actual_length); - DBG(3, "Sending %d byte%s to ep0 fifo %p\n", + dev_dbg(musb->controller, "Sending %d byte%s to ep0 fifo %p\n", fifo_count, (fifo_count == 1) ? "" : "s", fifo_dest); @@ -988,7 +975,7 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb) ? musb_readb(epio, MUSB_COUNT0) : 0; - DBG(4, "<== csr0 %04x, qh %p, count %d, urb %p, stage %d\n", + dev_dbg(musb->controller, "<== csr0 %04x, qh %p, count %d, urb %p, stage %d\n", csr, qh, len, urb, musb->ep0_stage); /* if we just did status stage, we are done */ @@ -999,15 +986,15 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb) /* prepare status */ if (csr & MUSB_CSR0_H_RXSTALL) { - DBG(6, "STALLING ENDPOINT\n"); + dev_dbg(musb->controller, "STALLING ENDPOINT\n"); status = -EPIPE; } else if (csr & MUSB_CSR0_H_ERROR) { - DBG(2, "no response, csr0 %04x\n", csr); + dev_dbg(musb->controller, "no response, csr0 %04x\n", csr); status = -EPROTO; } else if (csr & MUSB_CSR0_H_NAKTIMEOUT) { - DBG(2, "control NAK timeout\n"); + dev_dbg(musb->controller, "control NAK timeout\n"); /* NOTE: this code path would be a good place to PAUSE a * control transfer, if another one is queued, so that @@ -1022,7 +1009,7 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb) } if (status) { - DBG(6, "aborting\n"); + dev_dbg(musb->controller, "aborting\n"); retval = IRQ_HANDLED; if (urb) urb->status = status; @@ -1072,7 +1059,7 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb) /* flag status stage */ musb->ep0_stage = MUSB_EP0_STATUS; - DBG(5, "ep0 STATUS, csr %04x\n", csr); + dev_dbg(musb->controller, "ep0 STATUS, csr %04x\n", csr); } musb_writew(epio, MUSB_CSR0, csr); @@ -1126,31 +1113,31 @@ void musb_host_tx(struct musb *musb, u8 epnum) /* with CPPI, DMA sometimes triggers "extra" irqs */ if (!urb) { - DBG(4, "extra TX%d ready, csr %04x\n", epnum, tx_csr); + dev_dbg(musb->controller, "extra TX%d ready, csr %04x\n", epnum, tx_csr); return; } pipe = urb->pipe; dma = is_dma_capable() ? hw_ep->tx_channel : NULL; - DBG(4, "OUT/TX%d end, csr %04x%s\n", epnum, tx_csr, + dev_dbg(musb->controller, "OUT/TX%d end, csr %04x%s\n", epnum, tx_csr, dma ? ", dma" : ""); /* check for errors */ if (tx_csr & MUSB_TXCSR_H_RXSTALL) { /* dma was disabled, fifo flushed */ - DBG(3, "TX end %d stall\n", epnum); + dev_dbg(musb->controller, "TX end %d stall\n", epnum); /* stall; record URB status */ status = -EPIPE; } else if (tx_csr & MUSB_TXCSR_H_ERROR) { /* (NON-ISO) dma was disabled, fifo flushed */ - DBG(3, "TX 3strikes on ep=%d\n", epnum); + dev_dbg(musb->controller, "TX 3strikes on ep=%d\n", epnum); status = -ETIMEDOUT; } else if (tx_csr & MUSB_TXCSR_H_NAKTIMEOUT) { - DBG(6, "TX end=%d device not responding\n", epnum); + dev_dbg(musb->controller, "TX end=%d device not responding\n", epnum); /* NOTE: this code path would be a good place to PAUSE a * transfer, if there's some other (nonperiodic) tx urb @@ -1195,7 +1182,7 @@ void musb_host_tx(struct musb *musb, u8 epnum) /* second cppi case */ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { - DBG(4, "extra TX%d ready, csr %04x\n", epnum, tx_csr); + dev_dbg(musb->controller, "extra TX%d ready, csr %04x\n", epnum, tx_csr); return; } @@ -1254,7 +1241,7 @@ void musb_host_tx(struct musb *musb, u8 epnum) * FIFO mode too... */ if (tx_csr & (MUSB_TXCSR_FIFONOTEMPTY | MUSB_TXCSR_TXPKTRDY)) { - DBG(2, "DMA complete but packet still in FIFO, " + dev_dbg(musb->controller, "DMA complete but packet still in FIFO, " "CSR %04x\n", tx_csr); return; } @@ -1321,7 +1308,7 @@ void musb_host_tx(struct musb *musb, u8 epnum) return; } } else if (tx_csr & MUSB_TXCSR_DMAENAB) { - DBG(1, "not complete, but DMA enabled?\n"); + dev_dbg(musb->controller, "not complete, but DMA enabled?\n"); return; } @@ -1462,7 +1449,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) * usbtest #11 (unlinks) triggers it regularly, sometimes * with fifo full. (Only with DMA??) */ - DBG(3, "BOGUS RX%d ready, csr %04x, count %d\n", epnum, val, + dev_dbg(musb->controller, "BOGUS RX%d ready, csr %04x, count %d\n", epnum, val, musb_readw(epio, MUSB_RXCOUNT)); musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG); return; @@ -1470,20 +1457,20 @@ void musb_host_rx(struct musb *musb, u8 epnum) pipe = urb->pipe; - DBG(5, "<== hw %d rxcsr %04x, urb actual %d (+dma %zu)\n", + dev_dbg(musb->controller, "<== hw %d rxcsr %04x, urb actual %d (+dma %zu)\n", epnum, rx_csr, urb->actual_length, dma ? dma->actual_len : 0); /* check for errors, concurrent stall & unlink is not really * handled yet! */ if (rx_csr & MUSB_RXCSR_H_RXSTALL) { - DBG(3, "RX end %d STALL\n", epnum); + dev_dbg(musb->controller, "RX end %d STALL\n", epnum); /* stall; record URB status */ status = -EPIPE; } else if (rx_csr & MUSB_RXCSR_H_ERROR) { - DBG(3, "end %d RX proto error\n", epnum); + dev_dbg(musb->controller, "end %d RX proto error\n", epnum); status = -EPROTO; musb_writeb(epio, MUSB_RXINTERVAL, 0); @@ -1491,7 +1478,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) } else if (rx_csr & MUSB_RXCSR_DATAERROR) { if (USB_ENDPOINT_XFER_ISOC != qh->type) { - DBG(6, "RX end %d NAK timeout\n", epnum); + dev_dbg(musb->controller, "RX end %d NAK timeout\n", epnum); /* NOTE: NAKing is *NOT* an error, so we want to * continue. Except ... if there's a request for @@ -1514,12 +1501,12 @@ void musb_host_rx(struct musb *musb, u8 epnum) goto finish; } else { - DBG(4, "RX end %d ISO data error\n", epnum); + dev_dbg(musb->controller, "RX end %d ISO data error\n", epnum); /* packet error reported later */ iso_err = true; } } else if (rx_csr & MUSB_RXCSR_INCOMPRX) { - DBG(3, "end %d high bandwidth incomplete ISO packet RX\n", + dev_dbg(musb->controller, "end %d high bandwidth incomplete ISO packet RX\n", epnum); status = -EPROTO; } @@ -1565,7 +1552,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) done = true; } - DBG(2, "RXCSR%d %04x, reqpkt, len %zu%s\n", epnum, rx_csr, + dev_dbg(musb->controller, "RXCSR%d %04x, reqpkt, len %zu%s\n", epnum, rx_csr, xfer_len, dma ? ", dma" : ""); rx_csr &= ~MUSB_RXCSR_H_REQPKT; @@ -1615,7 +1602,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) MUSB_RXCSR_H_WZC_BITS | val); } - DBG(4, "ep %d dma %s, rxcsr %04x, rxcount %d\n", epnum, + dev_dbg(musb->controller, "ep %d dma %s, rxcsr %04x, rxcount %d\n", epnum, done ? "off" : "reset", musb_readw(epio, MUSB_RXCSR), musb_readw(epio, MUSB_RXCOUNT)); @@ -1648,7 +1635,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) rx_count = musb_readw(epio, MUSB_RXCOUNT); - DBG(2, "RX%d count %d, buffer 0x%x len %d/%d\n", + dev_dbg(musb->controller, "RX%d count %d, buffer 0x%x len %d/%d\n", epnum, rx_count, urb->transfer_dma + urb->actual_length, @@ -1672,7 +1659,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) d_status = -EOVERFLOW; urb->error_count++; } - DBG(2, "** OVERFLOW %d into %d\n",\ + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n",\ rx_count, d->length); length = d->length; @@ -1760,7 +1747,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) usb_hcd_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 " : ""); + dev_dbg(musb->controller, "read %spacket\n", done ? "last " : ""); } } @@ -1881,7 +1868,7 @@ static int musb_schedule( idle = 1; qh->mux = 0; hw_ep = musb->endpoints + best_end; - DBG(4, "qh %p periodic slot %d\n", qh, best_end); + dev_dbg(musb->controller, "qh %p periodic slot %d\n", qh, best_end); success: if (head) { idle = list_empty(head); @@ -2087,6 +2074,7 @@ done: static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) { struct musb_hw_ep *ep = qh->hw_ep; + struct musb *musb = ep->musb; void __iomem *epio = ep->regs; unsigned hw_end = ep->epnum; void __iomem *regs = ep->musb->mregs; @@ -2102,7 +2090,7 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) dma = is_in ? ep->rx_channel : ep->tx_channel; if (dma) { status = ep->musb->dma_controller->channel_abort(dma); - DBG(status ? 1 : 3, + dev_dbg(musb->controller, "abort %cX%d DMA for urb %p --> %d\n", is_in ? 'R' : 'T', ep->epnum, urb, status); @@ -2149,7 +2137,7 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) int is_in = usb_pipein(urb->pipe); int ret; - DBG(4, "urb=%p, dev%d ep%d%s\n", urb, + dev_dbg(musb->controller, "urb=%p, dev%d ep%d%s\n", urb, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), is_in ? "in" : "out"); diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index bb1247d5fd0..2d80a575883 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -74,7 +74,7 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend) break; } - DBG(3, "Root port suspended, power %02x\n", power); + dev_dbg(musb->controller, "Root port suspended, power %02x\n", power); musb->port1_status |= USB_PORT_STAT_SUSPEND; switch (musb->xceiv->state) { @@ -97,7 +97,7 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend) break; #endif default: - DBG(1, "bogus rh suspend? %s\n", + dev_dbg(musb->controller, "bogus rh suspend? %s\n", otg_state_string(musb->xceiv->state)); } } else if (power & MUSB_POWER_SUSPENDM) { @@ -105,7 +105,7 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend) power |= MUSB_POWER_RESUME; musb_writeb(mbase, MUSB_POWER, power); - DBG(3, "Root port resuming, power %02x\n", power); + dev_dbg(musb->controller, "Root port resuming, power %02x\n", power); /* later, GetPortStatus will stop RESUME signaling */ musb->port1_status |= MUSB_PORT_STAT_RESUME; @@ -120,7 +120,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset) #ifdef CONFIG_USB_MUSB_OTG if (musb->xceiv->state == OTG_STATE_B_IDLE) { - DBG(2, "HNP: Returning from HNP; no hub reset from b_idle\n"); + dev_dbg(musb->controller, "HNP: Returning from HNP; no hub reset from b_idle\n"); musb->port1_status &= ~USB_PORT_STAT_RESET; return; } @@ -159,7 +159,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset) musb->port1_status &= ~USB_PORT_STAT_ENABLE; musb->rh_timer = jiffies + msecs_to_jiffies(50); } else { - DBG(4, "root port reset stopped\n"); + dev_dbg(musb->controller, "root port reset stopped\n"); musb_writeb(mbase, MUSB_POWER, power & ~MUSB_POWER_RESET); @@ -167,7 +167,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset) power = musb_readb(mbase, MUSB_POWER); if (power & MUSB_POWER_HSMODE) { - DBG(4, "high-speed device connected\n"); + dev_dbg(musb->controller, "high-speed device connected\n"); musb->port1_status |= USB_PORT_STAT_HIGH_SPEED; } @@ -208,7 +208,7 @@ void musb_root_disconnect(struct musb *musb) musb->xceiv->state = OTG_STATE_B_IDLE; break; default: - DBG(1, "host disconnect (%s)\n", + dev_dbg(musb->controller, "host disconnect (%s)\n", otg_state_string(musb->xceiv->state)); } } @@ -288,7 +288,7 @@ int musb_hub_control( default: goto error; } - DBG(5, "clear feature %d\n", wValue); + dev_dbg(musb->controller, "clear feature %d\n", wValue); musb->port1_status &= ~(1 << wValue); break; case GetHubDescriptor: @@ -330,7 +330,7 @@ int musb_hub_control( power = musb_readb(musb->mregs, MUSB_POWER); power &= ~MUSB_POWER_RESUME; - DBG(4, "root port resume stopped, power %02x\n", + dev_dbg(musb->controller, "root port resume stopped, power %02x\n", power); musb_writeb(musb->mregs, MUSB_POWER, power); @@ -353,7 +353,7 @@ int musb_hub_control( (__le32 *) buf); /* port change status is more interesting */ - DBG(get_unaligned((u16 *)(buf+2)) ? 2 : 5, "port status %08x\n", + dev_dbg(musb->controller, "port status %08x\n", musb->port1_status); break; case SetPortFeature: @@ -424,7 +424,7 @@ int musb_hub_control( default: goto error; } - DBG(5, "set feature %d\n", wValue); + dev_dbg(musb->controller, "set feature %d\n", wValue); musb->port1_status |= 1 << wValue; break; diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index d281792db05..f70c5a57773 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -122,11 +122,12 @@ static void configure_channel(struct dma_channel *channel, { struct musb_dma_channel *musb_channel = channel->private_data; struct musb_dma_controller *controller = musb_channel->controller; + struct musb *musb = controller->private_data; void __iomem *mbase = controller->base; u8 bchannel = musb_channel->idx; u16 csr = 0; - DBG(4, "%p, pkt_sz %d, addr 0x%x, len %d, mode %d\n", + dev_dbg(musb->controller, "%p, pkt_sz %d, addr 0x%x, len %d, mode %d\n", channel, packet_sz, dma_addr, len, mode); if (mode) { @@ -161,7 +162,7 @@ static int dma_channel_program(struct dma_channel *channel, struct musb_dma_controller *controller = musb_channel->controller; struct musb *musb = controller->private_data; - DBG(2, "ep%d-%s pkt_sz %d, dma_addr 0x%x length %d, mode %d\n", + dev_dbg(musb->controller, "ep%d-%s pkt_sz %d, dma_addr 0x%x length %d, mode %d\n", musb_channel->epnum, musb_channel->transmit ? "Tx" : "Rx", packet_sz, dma_addr, len, mode); @@ -274,7 +275,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) #endif if (!int_hsdma) { - DBG(2, "spurious DMA irq\n"); + dev_dbg(musb->controller, "spurious DMA irq\n"); for (bchannel = 0; bchannel < MUSB_HSDMA_CHANNELS; bchannel++) { musb_channel = (struct musb_dma_channel *) @@ -288,7 +289,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) } } - DBG(2, "int_hsdma = 0x%x\n", int_hsdma); + dev_dbg(musb->controller, "int_hsdma = 0x%x\n", int_hsdma); if (!int_hsdma) goto done; @@ -315,7 +316,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) channel->actual_len = addr - musb_channel->start_addr; - DBG(2, "ch %p, 0x%x -> 0x%x (%zu / %d) %s\n", + dev_dbg(musb->controller, "ch %p, 0x%x -> 0x%x (%zu / %d) %s\n", channel, musb_channel->start_addr, addr, channel->actual_len, musb_channel->len, diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index b996fec84c2..c5d4c44d0ff 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -76,7 +76,7 @@ static void musb_do_idle(unsigned long _musb) if (musb->port1_status & MUSB_PORT_STAT_RESUME) { power = musb_readb(musb->mregs, MUSB_POWER); power &= ~MUSB_POWER_RESUME; - DBG(1, "root port resume stopped, power %02x\n", power); + dev_dbg(musb->controller, "root port resume stopped, power %02x\n", power); musb_writeb(musb->mregs, MUSB_POWER, power); musb->is_active = 1; musb->port1_status &= ~(USB_PORT_STAT_SUSPEND @@ -114,7 +114,7 @@ static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || ((musb->a_wait_bcon == 0) && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { - DBG(4, "%s active, deleting timer\n", + dev_dbg(musb->controller, "%s active, deleting timer\n", otg_state_string(musb->xceiv->state)); del_timer(&musb_idle_timer); last_timer = jiffies; @@ -125,13 +125,13 @@ static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) if (!timer_pending(&musb_idle_timer)) last_timer = timeout; else { - DBG(4, "Longer idle timer already pending, ignoring\n"); + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring\n"); return; } } last_timer = timeout; - DBG(4, "%s inactive, for idle timer for %lu ms\n", + dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n", otg_state_string(musb->xceiv->state), (unsigned long)jiffies_to_msecs(timeout - jiffies)); mod_timer(&musb_idle_timer, timeout); @@ -194,7 +194,7 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on) } musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - DBG(1, "VBUS %s, devctl %02x " + dev_dbg(musb->controller, "VBUS %s, devctl %02x " /* otg %3x conf %08x prcm %08x */ "\n", otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL)); @@ -240,7 +240,7 @@ static int musb_otg_notifications(struct notifier_block *nb, switch (event) { case USB_EVENT_ID: - DBG(4, "ID GND\n"); + dev_dbg(musb->controller, "ID GND\n"); if (is_otg_enabled(musb)) { #ifdef CONFIG_USB_GADGET_MUSB_HDRC @@ -258,7 +258,7 @@ static int musb_otg_notifications(struct notifier_block *nb, break; case USB_EVENT_VBUS: - DBG(4, "VBUS Connect\n"); + dev_dbg(musb->controller, "VBUS Connect\n"); #ifdef CONFIG_USB_GADGET_MUSB_HDRC if (musb->gadget_driver) @@ -268,7 +268,7 @@ static int musb_otg_notifications(struct notifier_block *nb, break; case USB_EVENT_NONE: - DBG(4, "VBUS Disconnect\n"); + dev_dbg(musb->controller, "VBUS Disconnect\n"); #ifdef CONFIG_USB_GADGET_MUSB_HDRC if (is_otg_enabled(musb) || is_peripheral_enabled(musb)) @@ -286,7 +286,7 @@ static int musb_otg_notifications(struct notifier_block *nb, otg_shutdown(musb->xceiv); break; default: - DBG(4, "ID float\n"); + dev_dbg(musb->controller, "ID float\n"); return NOTIFY_DONE; } @@ -340,7 +340,7 @@ static int omap2430_musb_init(struct musb *musb) status = otg_register_notifier(musb->xceiv, &musb->nb); if (status) - DBG(1, "notification register failed\n"); + dev_dbg(musb->controller, "notification register failed\n"); setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 221feaaded7..2f683a67ebb 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -106,7 +106,7 @@ static void tusb_wbus_quirk(struct musb *musb, int enabled) tmp = phy_otg_ena & ~WBUS_QUIRK_MASK; tmp |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_TESTM2; musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp); - DBG(2, "Enabled tusb wbus quirk ctrl %08x ena %08x\n", + dev_dbg(musb->controller, "Enabled tusb wbus quirk ctrl %08x ena %08x\n", musb_readl(tbase, TUSB_PHY_OTG_CTRL), musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE)); } else if (musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE) @@ -115,7 +115,7 @@ static void tusb_wbus_quirk(struct musb *musb, int enabled) musb_writel(tbase, TUSB_PHY_OTG_CTRL, tmp); tmp = TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena; musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp); - DBG(2, "Disabled tusb wbus quirk ctrl %08x ena %08x\n", + dev_dbg(musb->controller, "Disabled tusb wbus quirk ctrl %08x ena %08x\n", musb_readl(tbase, TUSB_PHY_OTG_CTRL), musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE)); phy_otg_ctrl = 0; @@ -178,7 +178,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf) prefetch(buf); - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'T', epnum, fifo, len, buf); if (epnum) @@ -225,7 +225,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf) void __iomem *fifo = hw_ep->fifo; u8 epnum = hw_ep->epnum; - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'R', epnum, fifo, len, buf); if (epnum) @@ -304,7 +304,7 @@ static int tusb_draw_power(struct otg_transceiver *x, unsigned mA) } musb_writel(tbase, TUSB_PRCM_MNGMT, reg); - DBG(2, "draw max %d mA VBUS\n", mA); + dev_dbg(musb->controller, "draw max %d mA VBUS\n", mA); return 0; } @@ -374,7 +374,7 @@ static void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) reg |= TUSB_PRCM_MNGMT_PM_IDLE | TUSB_PRCM_MNGMT_DEV_IDLE; musb_writel(tbase, TUSB_PRCM_MNGMT, reg); - DBG(6, "idle, wake on %02x\n", wakeup_enables); + dev_dbg(musb->controller, "idle, wake on %02x\n", wakeup_enables); } /* @@ -421,7 +421,7 @@ static void musb_do_idle(unsigned long _musb) if ((musb->a_wait_bcon != 0) && (musb->idle_timeout == 0 || time_after(jiffies, musb->idle_timeout))) { - DBG(4, "Nothing connected %s, turning off VBUS\n", + dev_dbg(musb->controller, "Nothing connected %s, turning off VBUS\n", otg_state_string(musb->xceiv->state)); } /* FALLTHROUGH */ @@ -481,7 +481,7 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || ((musb->a_wait_bcon == 0) && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { - DBG(4, "%s active, deleting timer\n", + dev_dbg(musb->controller, "%s active, deleting timer\n", otg_state_string(musb->xceiv->state)); del_timer(&musb_idle_timer); last_timer = jiffies; @@ -492,13 +492,13 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) if (!timer_pending(&musb_idle_timer)) last_timer = timeout; else { - DBG(4, "Longer idle timer already pending, ignoring\n"); + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring\n"); return; } } last_timer = timeout; - DBG(4, "%s inactive, for idle timer for %lu ms\n", + dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n", otg_state_string(musb->xceiv->state), (unsigned long)jiffies_to_msecs(timeout - jiffies)); mod_timer(&musb_idle_timer, timeout); @@ -573,7 +573,7 @@ static void tusb_musb_set_vbus(struct musb *musb, int is_on) musb_writel(tbase, TUSB_DEV_CONF, conf); musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - DBG(1, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n", + dev_dbg(musb->controller, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n", otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL), musb_readl(tbase, TUSB_DEV_OTG_STAT), @@ -634,7 +634,7 @@ static int tusb_musb_set_mode(struct musb *musb, u8 musb_mode) #endif default: - DBG(2, "Trying to set mode %i\n", musb_mode); + dev_dbg(musb->controller, "Trying to set mode %i\n", musb_mode); return -EINVAL; } @@ -667,7 +667,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) default_a = !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS); else default_a = is_host_enabled(musb); - DBG(2, "Default-%c\n", default_a ? 'A' : 'B'); + dev_dbg(musb->controller, "Default-%c\n", default_a ? 'A' : 'B'); musb->xceiv->default_a = default_a; tusb_musb_set_vbus(musb, default_a); @@ -694,7 +694,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) #endif if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) { - DBG(1, "Forcing disconnect (no interrupt)\n"); + dev_dbg(musb->controller, "Forcing disconnect (no interrupt)\n"); if (musb->xceiv->state != OTG_STATE_B_IDLE) { /* INTR_DISCONNECT can hide... */ musb->xceiv->state = OTG_STATE_B_IDLE; @@ -702,18 +702,18 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) } musb->is_active = 0; } - DBG(2, "vbus change, %s, otg %03x\n", + dev_dbg(musb->controller, "vbus change, %s, otg %03x\n", otg_state_string(musb->xceiv->state), otg_stat); idle_timeout = jiffies + (1 * HZ); schedule_work(&musb->irq_work); } else /* A-dev state machine */ { - DBG(2, "vbus change, %s, otg %03x\n", + dev_dbg(musb->controller, "vbus change, %s, otg %03x\n", otg_state_string(musb->xceiv->state), otg_stat); switch (musb->xceiv->state) { case OTG_STATE_A_IDLE: - DBG(2, "Got SRP, turning on VBUS\n"); + dev_dbg(musb->controller, "Got SRP, turning on VBUS\n"); musb_platform_set_vbus(musb, 1); /* CONNECT can wake if a_wait_bcon is set */ @@ -757,7 +757,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) if (int_src & TUSB_INT_SRC_OTG_TIMEOUT) { u8 devctl; - DBG(4, "%s timer, %03x\n", + dev_dbg(musb->controller, "%s timer, %03x\n", otg_state_string(musb->xceiv->state), otg_stat); switch (musb->xceiv->state) { @@ -769,7 +769,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) { if ((devctl & MUSB_DEVCTL_VBUS) != MUSB_DEVCTL_VBUS) { - DBG(2, "devctl %02x\n", devctl); + dev_dbg(musb->controller, "devctl %02x\n", devctl); break; } musb->xceiv->state = OTG_STATE_A_WAIT_BCON; @@ -814,7 +814,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS); int_src = musb_readl(tbase, TUSB_INT_SRC) & ~TUSB_INT_SRC_RESERVED_BITS; - DBG(3, "TUSB IRQ %08x\n", int_src); + dev_dbg(musb->controller, "TUSB IRQ %08x\n", int_src); musb->int_usb = (u8) int_src; @@ -835,7 +835,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) reg = musb_readl(tbase, TUSB_SCRATCH_PAD); if (reg == i) break; - DBG(6, "TUSB NOR not ready\n"); + dev_dbg(musb->controller, "TUSB NOR not ready\n"); } /* work around issue 13 (2nd half) */ @@ -847,7 +847,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) musb->is_active = 1; schedule_work(&musb->irq_work); } - DBG(3, "wake %sactive %02x\n", + dev_dbg(musb->controller, "wake %sactive %02x\n", musb->is_active ? "" : "in", reg); /* REVISIT host side TUSB_PRCM_WHOSTDISCON, TUSB_PRCM_WBUS */ @@ -869,7 +869,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) u32 dma_src = musb_readl(tbase, TUSB_DMA_INT_SRC); u32 real_dma_src = musb_readl(tbase, TUSB_DMA_INT_MASK); - DBG(3, "DMA IRQ %08x\n", dma_src); + dev_dbg(musb->controller, "DMA IRQ %08x\n", dma_src); real_dma_src = ~real_dma_src & dma_src; if (tusb_dma_omap() && real_dma_src) { int tx_source = (real_dma_src & 0xffff); @@ -877,7 +877,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) for (i = 1; i <= 15; i++) { if (tx_source & (1 << i)) { - DBG(3, "completing ep%i %s\n", i, "tx"); + dev_dbg(musb->controller, "completing ep%i %s\n", i, "tx"); musb_dma_completion(musb, i, 1); } } diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 99cb541e4ef..c784e6c03aa 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -65,7 +65,7 @@ static int tusb_omap_dma_start(struct dma_controller *c) tusb_dma = container_of(c, struct tusb_omap_dma, controller); - /* DBG(3, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */ + /* dev_dbg(musb->controller, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */ return 0; } @@ -76,7 +76,7 @@ static int tusb_omap_dma_stop(struct dma_controller *c) tusb_dma = container_of(c, struct tusb_omap_dma, controller); - /* DBG(3, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */ + /* dev_dbg(musb->controller, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */ return 0; } @@ -89,7 +89,7 @@ static inline int tusb_omap_use_shared_dmareq(struct tusb_omap_dma_ch *chdat) u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP); if (reg != 0) { - DBG(3, "ep%i dmareq0 is busy for ep%i\n", + dev_dbg(musb->controller, "ep%i dmareq0 is busy for ep%i\n", chdat->epnum, reg & 0xf); return -EAGAIN; } @@ -143,7 +143,7 @@ static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data) if (ch_status != OMAP_DMA_BLOCK_IRQ) printk(KERN_ERR "TUSB DMA error status: %i\n", ch_status); - DBG(3, "ep%i %s dma callback ch: %i status: %x\n", + dev_dbg(musb->controller, "ep%i %s dma callback ch: %i status: %x\n", chdat->epnum, chdat->tx ? "tx" : "rx", ch, ch_status); @@ -156,7 +156,7 @@ static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data) /* HW issue #10: XFR_SIZE may get corrupt on DMA (both async & sync) */ if (unlikely(remaining > chdat->transfer_len)) { - DBG(2, "Corrupt %s dma ch%i XFR_SIZE: 0x%08lx\n", + dev_dbg(musb->controller, "Corrupt %s dma ch%i XFR_SIZE: 0x%08lx\n", chdat->tx ? "tx" : "rx", chdat->ch, remaining); remaining = 0; @@ -165,13 +165,13 @@ static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data) channel->actual_len = chdat->transfer_len - remaining; pio = chdat->len - channel->actual_len; - DBG(3, "DMA remaining %lu/%u\n", remaining, chdat->transfer_len); + dev_dbg(musb->controller, "DMA remaining %lu/%u\n", remaining, chdat->transfer_len); /* Transfer remaining 1 - 31 bytes */ if (pio > 0 && pio < 32) { u8 *buf; - DBG(3, "Using PIO for remaining %lu bytes\n", pio); + dev_dbg(musb->controller, "Using PIO for remaining %lu bytes\n", pio); buf = phys_to_virt((u32)chdat->dma_addr) + chdat->transfer_len; if (chdat->tx) { dma_unmap_single(dev, chdat->dma_addr, @@ -209,7 +209,7 @@ static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data) u16 csr; if (chdat->tx) { - DBG(3, "terminating short tx packet\n"); + dev_dbg(musb->controller, "terminating short tx packet\n"); musb_ep_select(mbase, chdat->epnum); csr = musb_readw(hw_ep->regs, MUSB_TXCSR); csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY @@ -264,7 +264,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, dma_remaining = TUSB_EP_CONFIG_XFR_SIZE(dma_remaining); if (dma_remaining) { - DBG(2, "Busy %s dma ch%i, not using: %08x\n", + dev_dbg(musb->controller, "Busy %s dma ch%i, not using: %08x\n", chdat->tx ? "tx" : "rx", chdat->ch, dma_remaining); return false; @@ -283,7 +283,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, sync_dev = chdat->sync_dev; } else { if (tusb_omap_use_shared_dmareq(chdat) != 0) { - DBG(3, "could not get dma for ep%i\n", chdat->epnum); + dev_dbg(musb->controller, "could not get dma for ep%i\n", chdat->epnum); return false; } if (tusb_dma->ch < 0) { @@ -326,7 +326,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, dma_params.frame_count = chdat->transfer_len / 32; /* Burst sz frame */ - DBG(3, "ep%i %s dma ch%i dma: %08x len: %u(%u) packet_sz: %i(%i)\n", + dev_dbg(musb->controller, "ep%i %s dma ch%i dma: %08x len: %u(%u) packet_sz: %i(%i)\n", chdat->epnum, chdat->tx ? "tx" : "rx", ch, dma_addr, chdat->transfer_len, len, chdat->transfer_packet_sz, packet_sz); @@ -370,7 +370,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, dst_burst = OMAP_DMA_DATA_BURST_16; /* 16x32 write */ } - DBG(3, "ep%i %s using %i-bit %s dma from 0x%08lx to 0x%08lx\n", + dev_dbg(musb->controller, "ep%i %s using %i-bit %s dma from 0x%08lx to 0x%08lx\n", chdat->epnum, chdat->tx ? "tx" : "rx", (dma_params.data_type == OMAP_DMA_DATA_TYPE_S32) ? 32 : 16, ((dma_addr & 0x3) == 0) ? "sync" : "async", @@ -525,7 +525,7 @@ tusb_omap_dma_allocate(struct dma_controller *c, /* REVISIT: Why does dmareq5 not work? */ if (hw_ep->epnum == 0) { - DBG(3, "Not allowing DMA for ep0 %s\n", tx ? "tx" : "rx"); + dev_dbg(musb->controller, "Not allowing DMA for ep0 %s\n", tx ? "tx" : "rx"); return NULL; } @@ -585,7 +585,7 @@ tusb_omap_dma_allocate(struct dma_controller *c, chdat->ch = -1; } - DBG(3, "ep%i %s dma: %s dma%i dmareq%i sync%i\n", + dev_dbg(musb->controller, "ep%i %s dma: %s dma%i dmareq%i sync%i\n", chdat->epnum, chdat->tx ? "tx" : "rx", chdat->ch >= 0 ? "dedicated" : "shared", @@ -598,7 +598,7 @@ tusb_omap_dma_allocate(struct dma_controller *c, free_dmareq: tusb_omap_dma_free_dmareq(chdat); - DBG(3, "ep%i: Could not get a DMA channel\n", chdat->epnum); + dev_dbg(musb->controller, "ep%i: Could not get a DMA channel\n", chdat->epnum); channel->status = MUSB_DMA_STATUS_UNKNOWN; return NULL; @@ -611,7 +611,7 @@ static void tusb_omap_dma_release(struct dma_channel *channel) void __iomem *tbase = musb->ctrl_base; u32 reg; - DBG(3, "ep%i ch%i\n", chdat->epnum, chdat->ch); + dev_dbg(musb->controller, "ep%i ch%i\n", chdat->epnum, chdat->ch); reg = musb_readl(tbase, TUSB_DMA_INT_MASK); if (chdat->tx) -- cgit v1.2.3 From 1376d92f9e14209e8c0d549ed143edc70ba6dbb7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 11 May 2011 12:47:59 +0300 Subject: usb: musb: allow musb and glue layers to be modules This in part reverts commit 7a180e70cfc56e131bfe4796773df2acfc7d4180. (usb: musb: temporarily make it bool) and while at that we also allow glue layers to be compiled as modules. There are still some other changes needed until we can have a fully functional build with this setup, but we're getting there. Signed-off-by: Felipe Balbi --- drivers/usb/musb/Kconfig | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index f90123f4ce5..dbb13bd8cb0 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -14,7 +14,7 @@ config USB_MUSB_HDRC select TWL4030_USB if MACH_OMAP_3430SDP select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA select USB_OTG_UTILS - bool 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' + tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' help Say Y here if your system has a dual role high speed USB controller based on the Mentor Graphics silicon IP. Then @@ -30,39 +30,39 @@ config USB_MUSB_HDRC If you do not know what this is, please say N. -# To compile this driver as a module, choose M here; the -# module will be called "musb-hdrc". + To compile this driver as a module, choose M here; the + module will be called "musb-hdrc". choice prompt "Platform Glue Layer" depends on USB_MUSB_HDRC config USB_MUSB_DAVINCI - bool "DaVinci" + tristate "DaVinci" depends on ARCH_DAVINCI_DMx config USB_MUSB_DA8XX - bool "DA8xx/OMAP-L1x" + tristate "DA8xx/OMAP-L1x" depends on ARCH_DAVINCI_DA8XX config USB_MUSB_TUSB6010 - bool "TUSB6010" + tristate "TUSB6010" depends on ARCH_OMAP config USB_MUSB_OMAP2PLUS - bool "OMAP2430 and onwards" + tristate "OMAP2430 and onwards" depends on ARCH_OMAP2PLUS config USB_MUSB_AM35X - bool "AM35x" + tristate "AM35x" depends on ARCH_OMAP config USB_MUSB_BLACKFIN - bool "Blackfin" + tristate "Blackfin" depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523) config USB_MUSB_UX500 - bool "U8500 and U5500" + tristate "U8500 and U5500" depends on (ARCH_U8500 && AB8500_USB) || (ARCH_U5500) endchoice -- cgit v1.2.3 From 981430a1704123b569865997bdde4f1de1afca7e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 11 May 2011 13:02:23 +0300 Subject: usb: musb: export musb_interrupt symbol currently that's used by another module (am35x) which, granted, it shouldn't be using that, but in order to avoid compile errors, let's export that symbol temporarily until re-factoring work is done on that driver. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 6e3988cb6e8..92aeec1f727 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1628,7 +1628,7 @@ irqreturn_t musb_interrupt(struct musb *musb) return retval; } - +EXPORT_SYMBOL_GPL(musb_interrupt); #ifndef CONFIG_MUSB_PIO_ONLY static int __initdata use_dma = 1; -- cgit v1.2.3 From c51e36dc11e44aa960744ec1c36fb2ab8d68b218 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 7 May 2011 19:44:13 +0400 Subject: usb: musb: host: remove duplicate check in musb_ep_program() musb_ep_program() contains obviously duplicate check for 'dma_channel' in its IN/receive path -- removing it allows to save one level of indentation. While at it, improve the comment style... Signed-off-by: Sergei Shtylyov Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_host.c | 49 ++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 75aa01e0036..7295e316bdf 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -837,33 +837,28 @@ static void musb_ep_program(struct musb *musb, u8 epnum, /* kick things off */ if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) { - /* candidate for DMA */ - if (dma_channel) { - dma_channel->actual_len = 0L; - qh->segsize = len; - - /* AUTOREQ is in a DMA register */ - musb_writew(hw_ep->regs, MUSB_RXCSR, csr); - csr = musb_readw(hw_ep->regs, - MUSB_RXCSR); - - /* unless caller treats short rx transfers as - * errors, we dare not queue multiple transfers. - */ - dma_ok = dma_controller->channel_program( - dma_channel, packet_sz, - !(urb->transfer_flags - & URB_SHORT_NOT_OK), - urb->transfer_dma + offset, - qh->segsize); - if (!dma_ok) { - dma_controller->channel_release( - dma_channel); - hw_ep->rx_channel = NULL; - dma_channel = NULL; - } else - csr |= MUSB_RXCSR_DMAENAB; - } + /* Candidate for DMA */ + dma_channel->actual_len = 0L; + qh->segsize = len; + + /* AUTOREQ is in a DMA register */ + musb_writew(hw_ep->regs, MUSB_RXCSR, csr); + csr = musb_readw(hw_ep->regs, MUSB_RXCSR); + + /* + * Unless caller treats short RX transfers as + * errors, we dare not queue multiple transfers. + */ + dma_ok = dma_controller->channel_program(dma_channel, + packet_sz, !(urb->transfer_flags & + URB_SHORT_NOT_OK), + urb->transfer_dma + offset, + qh->segsize); + if (!dma_ok) { + dma_controller->channel_release(dma_channel); + hw_ep->rx_channel = dma_channel = NULL; + } else + csr |= MUSB_RXCSR_DMAENAB; } csr |= MUSB_RXCSR_H_REQPKT; -- cgit v1.2.3 From 603ab524ed95f566b0f25566484d4f449e433d74 Mon Sep 17 00:00:00 2001 From: Hema HK Date: Tue, 22 Mar 2011 16:54:21 +0530 Subject: usb: otg: TWL6030: OMAP4430: Adding SRP VBUS pulsing API Implement the start_srp API to generate the VBUS pulsing and assign it to otg_transciever function pointer. This will be used by the link driver when there is SRP initiation from user. Signed-off-by: Hema HK Signed-off-by: Felipe Balbi --- drivers/usb/otg/twl6030-usb.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index 6e920de64ef..3f2e07011a4 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -31,6 +31,7 @@ #include #include #include +#include /* usb register definitions */ #define USB_VENDOR_ID_LSB 0x00 @@ -188,6 +189,19 @@ static int twl6030_phy_suspend(struct otg_transceiver *x, int suspend) return 0; } +static int twl6030_start_srp(struct otg_transceiver *x) +{ + struct twl6030_usb *twl = xceiv_to_twl(x); + + twl6030_writeb(twl, TWL_MODULE_USB, 0x24, USB_VBUS_CTRL_SET); + twl6030_writeb(twl, TWL_MODULE_USB, 0x84, USB_VBUS_CTRL_SET); + + mdelay(100); + twl6030_writeb(twl, TWL_MODULE_USB, 0xa0, USB_VBUS_CTRL_CLR); + + return 0; +} + static int twl6030_usb_ldo_init(struct twl6030_usb *twl) { @@ -403,6 +417,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) twl->otg.init = twl6030_phy_init; twl->otg.shutdown = twl6030_phy_shutdown; twl->otg.set_suspend = twl6030_phy_suspend; + twl->otg.start_srp = twl6030_start_srp; /* init spinlock for workqueue */ spin_lock_init(&twl->lock); -- cgit v1.2.3 From 8620543eda01a2d26ea1dd831f159e06da8163ad Mon Sep 17 00:00:00 2001 From: Hema HK Date: Tue, 22 Mar 2011 16:54:22 +0530 Subject: usb: musb: Calling VBUS pulsing API when SRP is initiated. Call VBUS pulsing API when there is SRP initiation from user space. Signed-off-by: Hema HK Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index d34ff408c81..6f3cf4ce8bd 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1553,6 +1553,10 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) break; } + spin_unlock_irqrestore(&musb->lock, flags); + otg_start_srp(musb->xceiv); + spin_lock_irqsave(&musb->lock, flags); + /* Block idling for at least 1s */ musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(1 * HZ)); -- cgit v1.2.3 From 43b416e5f6a3ce269571cfe25c8f1e8e9316c5b0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2011 15:21:47 +0300 Subject: MAINTAINERS: tree moved to kernel.org Thanks for kernel.org to give me an account on that server. Signed-off-by: Felipe Balbi --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 16a5c5f2c6a..8031e4f142b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4247,7 +4247,7 @@ F: include/linux/isicom.h MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER M: Felipe Balbi L: linux-usb@vger.kernel.org -T: git git://gitorious.org/usb/usb.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git S: Maintained F: drivers/usb/musb/ @@ -4574,6 +4574,7 @@ M: Felipe Balbi M: David Brownell L: linux-usb@vger.kernel.org L: linux-omap@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git S: Maintained F: drivers/usb/*/*omap* F: arch/arm/*omap*/usb* -- cgit v1.2.3 From 004c127ef071e33df99708c2778fdb15564962ce Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 13 May 2011 21:27:24 +0900 Subject: USB: s3c-hsudc: use IS_ERR() instead of NULL check clk_get() returns ERR_PTR() on error, not NULL. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsudc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index d26901932a5..c339e171eac 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1299,9 +1299,9 @@ static int s3c_hsudc_probe(struct platform_device *pdev) s3c_hsudc_setup_ep(hsudc); hsudc->uclk = clk_get(&pdev->dev, "usb-device"); - if (hsudc->uclk == NULL) { + if (IS_ERR(hsudc->uclk)) { dev_err(dev, "failed to find usb-device clock source\n"); - return -ENOENT; + return PTR_ERR(hsudc->uclk); } clk_enable(hsudc->uclk); -- cgit v1.2.3 From d6167660b284447c710d9067754c69feab229892 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 13 May 2011 21:27:13 +0900 Subject: USB: s3c-hsudc: fix checkpatch error and warning This patch fixes the checkpatch error and warning listed below: ERROR: code indent should use tabs where possible WARNING: please, no spaces at the start of a line Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsudc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index c339e171eac..cfe3cf56d6b 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1133,7 +1133,7 @@ static irqreturn_t s3c_hsudc_irq(int irq, void *_dev) } int usb_gadget_probe_driver(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) + int (*bind)(struct usb_gadget *)) { struct s3c_hsudc *hsudc = the_controller; int ret; -- cgit v1.2.3 From 66e5c643488a26d2a6b737bdbd92fc72f13cdeac Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 13 May 2011 21:26:15 +0900 Subject: USB: s3c-hsotg: fix checkpatch warnings This patch fixes the checkpatch warnings listed below: WARNING: braces {} are not necessary for any arm of this statement WARNING: please, no space before tabs Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index baf96ce16b6..1d332f5da3c 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -616,11 +616,10 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep) maxpkt = S3C_DxEPTSIZ_PktCnt_LIMIT + 1; } else { maxsize = 64+64; - if (hs_ep->dir_in) { + if (hs_ep->dir_in) maxpkt = S3C_DIEPTSIZ0_PktCnt_LIMIT + 1; - } else { + else maxpkt = 2; - } } /* we made the constant loading easier above by using +1 */ @@ -2568,7 +2567,7 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg) if (!(grstctl & S3C_GRSTCTL_AHBIdle)) continue; - break; /* reset done */ + break; /* reset done */ } dev_dbg(hsotg->dev, "reset successful\n"); -- cgit v1.2.3 From 2328ceaea4fb917f8b861b18151b2245233b083f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 13 May 2011 21:26:23 +0900 Subject: USB: s3c-hsotg: return proper error if clk_get fails Return PTR_ERR(hsotg->clk) instead of -EINVAL if clk_get fails Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 1d332f5da3c..acb9cc418df 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -3318,7 +3318,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev) hsotg->clk = clk_get(&pdev->dev, "otg"); if (IS_ERR(hsotg->clk)) { dev_err(dev, "cannot get otg clock\n"); - ret = -EINVAL; + ret = PTR_ERR(hsotg->clk); goto err_mem; } -- cgit v1.2.3 From b0795bbf6dc6bd0a7a37d9d1ef4558e9e2b0acd6 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 13 May 2011 17:30:46 +0200 Subject: drivers/usb/serial/opticon.c: Release resources on kmalloc failure Several resources have been allocated before this kmalloc failure, and thus they should be released in this error handling code, as done in nearby error handling code. The semantic match that finds this problem is: (http://coccinelle.lip6.fr/) // @r exists@ local idexpression urb; statement S; position p1,p2; @@ urb = usb_alloc_urb@p1(...); ... when != urb if (urb == NULL) S ... when != urb ( return <+...urb...+>; | return@p2 ...; ) @script:python@ p1 << r.p1; p2 << r.p2; @@ cocci.print_main("",p1) cocci.print_secs("",p2) // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/opticon.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 1b5633f4698..96423f3c8ef 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -289,8 +289,11 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, /* The conncected devices do not have a bulk write endpoint, * to transmit data to de barcode device the control endpoint is used */ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); - if (!dr) - return -ENOMEM; + if (!dr) { + dev_err(&port->dev, "out of memory\n"); + count = -ENOMEM; + goto error; + } dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT; dr->bRequest = 0x01; -- cgit v1.2.3 From bf1f0a05d472e33dda8e5e69525be1584cdbd03a Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 13 May 2011 17:03:02 +0200 Subject: usb/gadget: at91sam9g20 fix end point max packet size on 9g20 they are the same as the 9260 Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/at91_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 9b7cdb16f26..41dc093c0a1 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1767,7 +1767,7 @@ static int __init at91udc_probe(struct platform_device *pdev) } /* newer chips have more FIFO memory than rm9200 */ - if (cpu_is_at91sam9260()) { + if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) { udc->ep[0].maxpacket = 64; udc->ep[3].maxpacket = 64; udc->ep[4].maxpacket = 512; -- cgit v1.2.3 From 472b91274a6c6857877b5caddb875dcb5ecdfcb8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2011 16:53:48 +0300 Subject: usb: gadget: rndis: don't test against req->length composite.c always sets req->length to zero and expects function driver's setup handlers to return the amount of bytes to be used on req->length. If we test against req->length w_length will always be greater than req->length thus making us always stall that particular SEND_ENCAPSULATED_COMMAND request. Tested against a Windows XP SP3. Signed-off-by: Felipe Balbi Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_rndis.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 882484a4039..fa12ec8364e 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -420,8 +420,7 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) */ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | USB_CDC_SEND_ENCAPSULATED_COMMAND: - if (w_length > req->length || w_value - || w_index != rndis->ctrl_id) + if (w_value || w_index != rndis->ctrl_id) goto invalid; /* read the request; process it later */ value = w_length; -- cgit v1.2.3 From b513d44751bfb609a3c20463f764c8ce822d63e9 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 13 May 2011 13:10:01 -0700 Subject: xhci: Fix full speed bInterval encoding. Dmitry's patch dfa49c4ad120a784ef1ff0717168aa79f55a483a USB: xhci - fix math in xhci_get_endpoint_interval() introduced a bug. The USB 2.0 spec says that full speed isochronous endpoints' bInterval must be decoded as an exponent to a power of two (e.g. interval = 2^(bInterval - 1)). Full speed interrupt endpoints, on the other hand, don't use exponents, and the interval in frames is encoded straight into bInterval. Dmitry's patch was supposed to fix up the full speed isochronous to parse bInterval as an exponent, but instead it changed the *interrupt* endpoint bInterval decoding. The isochronous endpoint encoding was the same. This caused full speed devices with interrupt endpoints (including mice, hubs, and USB to ethernet devices) to fail under NEC 0.96 xHCI host controllers: [ 100.909818] xhci_hcd 0000:06:00.0: add ep 0x83, slot id 1, new drop flags = 0x0, new add flags = 0x99, new slot info = 0x38100000 [ 100.909821] xhci_hcd 0000:06:00.0: xhci_check_bandwidth called for udev ffff88011f0ea000 ... [ 100.910187] xhci_hcd 0000:06:00.0: ERROR: unexpected command completion code 0x11. [ 100.910190] xhci_hcd 0000:06:00.0: xhci_reset_bandwidth called for udev ffff88011f0ea000 When the interrupt endpoint was added and a Configure Endpoint command was issued to the host, the host controller would return a very odd error message (0x11 means "Slot Not Enabled", which isn't true because the slot was enabled). Probably the host controller was getting very confused with the bad encoding. Signed-off-by: Sarah Sharp Cc: Dmitry Torokhov Reported-by: Thomas Lindroth Tested-by: Thomas Lindroth Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 04145740686..6938cc8d848 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1045,12 +1045,12 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, break; case USB_SPEED_FULL: - if (usb_endpoint_xfer_int(&ep->desc)) { + if (usb_endpoint_xfer_isoc(&ep->desc)) { interval = xhci_parse_exponent_interval(udev, ep); break; } /* - * Fall through for isochronous endpoint interval decoding + * Fall through for interrupt endpoint interval decoding * since it uses the same rules as low speed interrupt * endpoints. */ -- cgit v1.2.3 From 30f89ca021c3e584b61bc5a14eede89f74b2e826 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 16 May 2011 13:09:08 -0700 Subject: xhci: Fix memory leak in ring cache deallocation. When an endpoint ring is freed, it is either cached in a per-device ring cache, or simply freed if the ring cache is full. If the ring was added to the cache, then virt_dev->num_rings_cached is incremented. The cache is designed to hold up to 31 endpoint rings, in array indexes 0 to 30. When the device is freed (when the slot was disabled), xhci_free_virt_device() is called, it would free the cached rings in array indexes 0 to virt_dev->num_rings_cached. Unfortunately, the original code in xhci_free_or_cache_endpoint_ring() would put the first entry into the ring cache in array index 1, instead of array index 0. This was caused by the second assignment to rings_cached: rings_cached = virt_dev->num_rings_cached; if (rings_cached < XHCI_MAX_RINGS_CACHED) { virt_dev->num_rings_cached++; rings_cached = virt_dev->num_rings_cached; virt_dev->ring_cache[rings_cached] = virt_dev->eps[ep_index].ring; This meant that when the device was freed, cached rings with indexes 0 to N would be freed, and the last cached ring in index N+1 would not be freed. When the driver was unloaded, this caused interesting messages like: xhci_hcd 0000:06:00.0: dma_pool_destroy xHCI ring segments, ffff880063040000 busy This should be queued to stable kernels back to 2.6.33. Signed-off-by: Sarah Sharp Cc: stable@kernel.org --- drivers/usb/host/xhci-mem.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 6938cc8d848..26caba4c195 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -209,14 +209,13 @@ void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci, rings_cached = virt_dev->num_rings_cached; if (rings_cached < XHCI_MAX_RINGS_CACHED) { - virt_dev->num_rings_cached++; - rings_cached = virt_dev->num_rings_cached; virt_dev->ring_cache[rings_cached] = virt_dev->eps[ep_index].ring; + virt_dev->num_rings_cached++; xhci_dbg(xhci, "Cached old ring, " "%d ring%s cached\n", - rings_cached, - (rings_cached > 1) ? "s" : ""); + virt_dev->num_rings_cached, + (virt_dev->num_rings_cached > 1) ? "s" : ""); } else { xhci_ring_free(xhci, virt_dev->eps[ep_index].ring); xhci_dbg(xhci, "Ring cache full (%d rings), " -- cgit v1.2.3 From 834cb0fc4712a3b21c6b8c5cb55bd13607191311 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 12 May 2011 18:06:37 -0700 Subject: xhci: Fix memory leak bug when dropping endpoints When the USB core wants to change to an alternate interface setting that doesn't include an active endpoint, or de-configuring the device, the xHCI driver needs to issue a Configure Endpoint command to tell the host to drop some endpoints from the schedule. After the command completes, the xHCI driver needs to free rings for any endpoints that were dropped. Unfortunately, the xHCI driver wasn't actually freeing the endpoint rings for dropped endpoints. The rings would be freed if the endpoint's information was simply changed (and a new ring was installed), but dropped endpoints never had their rings freed. This caused errors when the ring segment DMA pool was freed when the xHCI driver was unloaded: [ 5582.883995] xhci_hcd 0000:06:00.0: dma_pool_destroy xHCI ring segments, ffff88003371d000 busy [ 5582.884002] xhci_hcd 0000:06:00.0: dma_pool_destroy xHCI ring segments, ffff880033716000 busy [ 5582.884011] xhci_hcd 0000:06:00.0: dma_pool_destroy xHCI ring segments, ffff880033455000 busy [ 5582.884018] xhci_hcd 0000:06:00.0: Freed segment pool [ 5582.884026] xhci_hcd 0000:06:00.0: Freed device context pool [ 5582.884033] xhci_hcd 0000:06:00.0: Freed small stream array pool [ 5582.884038] xhci_hcd 0000:06:00.0: Freed medium stream array pool [ 5582.884048] xhci_hcd 0000:06:00.0: xhci_stop completed - status = 1 [ 5582.884061] xhci_hcd 0000:06:00.0: USB bus 3 deregistered [ 5582.884193] xhci_hcd 0000:06:00.0: PCI INT A disabled Fix this issue and free endpoint rings when their endpoints are successfully dropped. This patch should be backported to kernels as old as 2.6.31. Signed-off-by: Sarah Sharp Cc: stable@kernel.org --- drivers/usb/host/xhci.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 013e113b818..8f2a56ece44 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1701,8 +1701,17 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) xhci_dbg_ctx(xhci, virt_dev->out_ctx, LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info))); + /* Free any rings that were dropped, but not changed. */ + for (i = 1; i < 31; ++i) { + if ((ctrl_ctx->drop_flags & (1 << (i + 1))) && + !(ctrl_ctx->add_flags & (1 << (i + 1)))) + xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); + } xhci_zero_in_ctx(xhci, virt_dev); - /* Install new rings and free or cache any old rings */ + /* + * Install any rings for completely new endpoints or changed endpoints, + * and free or cache any old rings from changed endpoints. + */ for (i = 1; i < 31; ++i) { if (!virt_dev->eps[i].new_ring) continue; -- cgit v1.2.3 From 610ba42f29c3dfa46a05ff8c2cadc29f544ff76d Mon Sep 17 00:00:00 2001 From: Marcin Gałczyński Date: Sun, 15 May 2011 11:41:54 +0200 Subject: USB: option: add support for Huawei E353 device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I am sharing patch to the devices/usb/serial/option.c. This allows operation of Huawei E353 broadband modem using the “option” driver. The patch simply adds new constant with proper product ID and an entry to usb_device_id. I worked on the 2.6.38.6 sources. Tested on Dell inspiron 1764 (i3 core cpu) and brand new Huawei E353 modem, Fedora 15 beta. Looking at the type of change, i doubt it has potential to introduce problems in other parts of kernel or the driver itself. Signed-off-by: Marcin Galczynski Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 4001f7630a5..318dd00040a 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -149,6 +149,7 @@ static void option_instat_callback(struct urb *urb); #define HUAWEI_PRODUCT_K3765 0x1465 #define HUAWEI_PRODUCT_E14AC 0x14AC #define HUAWEI_PRODUCT_ETS1220 0x1803 +#define HUAWEI_PRODUCT_E353 0x1506 #define QUANTA_VENDOR_ID 0x0408 #define QUANTA_PRODUCT_Q101 0xEA02 @@ -532,6 +533,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x01) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V740) }, -- cgit v1.2.3 From 7b521fcb6bf63a49bb7712d922dd1dd0a90a93cf Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 15 May 2011 09:55:57 -0700 Subject: USB: OTG: msm: Allow the widest possible range for VDDCX when removing When not active the hardware should be able to tolerate voltages within the normal operating range so when removing set the maximum voltage we can use to the maximum rather than minimum of the operating range. This will improve interoperability in case we end up sharing the supply in some design. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/msm_otg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 1cdda6c0523..db69b2ac926 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -92,7 +92,7 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) } } else { ret = regulator_set_voltage(hsusb_vddcx, 0, - USB_PHY_VDD_DIG_VOL_MIN); + USB_PHY_VDD_DIG_VOL_MAX); if (ret) { dev_err(motg->otg.dev, "unable to set the voltage " "for hsusb vddcx\n"); -- cgit v1.2.3 From e99c4309fb064604a957d9c1a8d2d4a9ff19cf5e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 15 May 2011 09:55:58 -0700 Subject: USB: OTG: msm: Free VCCCX regulator even if we can't set the voltage If for some reason we fail to set the voltage range for the VDDCX regulator when removing it's better to still disable and free the regulator as that avoids leaking a reference to it and is likely to ensure that it's turned off completely. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/msm_otg.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index db69b2ac926..b276f8fcdeb 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -93,11 +93,9 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) } else { ret = regulator_set_voltage(hsusb_vddcx, 0, USB_PHY_VDD_DIG_VOL_MAX); - if (ret) { + if (ret) dev_err(motg->otg.dev, "unable to set the voltage " "for hsusb vddcx\n"); - return ret; - } ret = regulator_disable(hsusb_vddcx); if (ret) dev_err(motg->otg.dev, "unable to disable hsusb vddcx\n"); -- cgit v1.2.3 From 2b7aaf503d56216b847c8265421d2a7d9b42df3e Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 16 May 2011 12:15:19 -0400 Subject: OHCI: fix regression caused by nVidia shutdown workaround This patch (as1463) fixes a regression caused by commit 3df7169e73fc1d71a39cffeacc969f6840cdf52b (OHCI: work around for nVidia shutdown problem). The original problem encountered by people using NVIDIA chipsets was that USB devices were not turning off when the system shut down. For example, the LED on an optical mouse would remain on, draining a laptop's battery. The problem was caused by a bug in the chipset; an OHCI controller in the Reset state would continue to drive a bus reset signal even after system shutdown. The workaround was to put the controllers into the Suspend state instead. It turns out that later NVIDIA chipsets do not suffer from this bug. Instead some have the opposite bug: If a system is shut down while an OHCI controller is in the Suspend state, USB devices remain powered! On other systems, shutting down with a Suspended controller causes the system to reboot immediately. Thus, working around the original bug on some machines exposes other bugs on other machines. The best solution seems to be to limit the workaround to OHCI controllers with a low-numbered PCI product ID. I don't know exactly at what point NVIDIA changed their chipsets; the value used here is a guess. So far it was worked out okay for all the people who have tested it. This fixes Bugzilla #35032. Signed-off-by: Alan Stern Tested-by: Andre "Osku" Schmidt Tested-by: Yury Siamashka CC: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-pci.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index d84d6f0314f..ad8166c681e 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -181,10 +181,18 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd) */ static int ohci_quirk_nvidia_shutdown(struct usb_hcd *hcd) { + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); struct ohci_hcd *ohci = hcd_to_ohci(hcd); - ohci->flags |= OHCI_QUIRK_SHUTDOWN; - ohci_dbg(ohci, "enabled nVidia shutdown quirk\n"); + /* Evidently nVidia fixed their later hardware; this is a guess at + * the changeover point. + */ +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_USB 0x026d + + if (pdev->device < PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_USB) { + ohci->flags |= OHCI_QUIRK_SHUTDOWN; + ohci_dbg(ohci, "enabled nVidia shutdown quirk\n"); + } return 0; } -- cgit v1.2.3 From 1e12c910eed82da6971f1c0421a069c680faba2e Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 17 May 2011 10:40:51 -0400 Subject: EHCI: don't rescan interrupt QHs needlessly This patch (as1466) speeds up processing of ehci-hcd's periodic list. The existing code will pointlessly rescan an interrupt endpoint queue each time it encounters the queue's QH in the periodic list, which can happen quite a few times if the endpoint's period is low. On some embedded systems, this useless overhead can waste so much time that the driver falls hopelessly behind and loses events. The patch introduces a "periodic_stamp" variable, which gets incremented each time scan_periodic() runs and each time the scan advances to a new frame. If the corresponding stamp in an interrupt QH is equal to the current periodic_stamp, we assume the QH has already been scanned and skip over it. Otherwise we scan the QH as usual, and if none of its URBs have completed then we store the current periodic_stamp in the QH's stamp, preventing it from being scanned again. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-q.c | 1 + drivers/usb/host/ehci-sched.c | 14 ++++++++++---- drivers/usb/host/ehci.h | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index a46d6a1388c..5d6bc624c96 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -826,6 +826,7 @@ qh_make ( is_input, 0, hb_mult(maxp) * max_packet(maxp))); qh->start = NO_FRAME; + qh->stamp = ehci->periodic_stamp; if (urb->dev->speed == USB_SPEED_HIGH) { qh->c_usecs = 0; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 1543c838b3d..a7408d88fda 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -2287,6 +2287,7 @@ scan_periodic (struct ehci_hcd *ehci) } clock &= mod - 1; clock_frame = clock >> 3; + ++ehci->periodic_stamp; for (;;) { union ehci_shadow q, *q_p; @@ -2315,10 +2316,14 @@ restart: temp.qh = qh_get (q.qh); type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); q = q.qh->qh_next; - modified = qh_completions (ehci, temp.qh); - if (unlikely(list_empty(&temp.qh->qtd_list) || - temp.qh->needs_rescan)) - intr_deschedule (ehci, temp.qh); + if (temp.qh->stamp != ehci->periodic_stamp) { + modified = qh_completions(ehci, temp.qh); + if (!modified) + temp.qh->stamp = ehci->periodic_stamp; + if (unlikely(list_empty(&temp.qh->qtd_list) || + temp.qh->needs_rescan)) + intr_deschedule(ehci, temp.qh); + } qh_put (temp.qh); break; case Q_TYPE_FSTN: @@ -2460,6 +2465,7 @@ restart: if (ehci->clock_frame != clock_frame) { free_cached_lists(ehci); ehci->clock_frame = clock_frame; + ++ehci->periodic_stamp; } } else { now_uframe++; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 829213423de..f68e419cae8 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -118,6 +118,7 @@ struct ehci_hcd { /* one per controller */ struct timer_list watchdog; unsigned long actions; unsigned stamp; + unsigned periodic_stamp; unsigned random_frame; unsigned long next_statechange; ktime_t last_periodic_enable; -- cgit v1.2.3 From e6251a921716fca9c2e3008af2d8d0d5657a59bc Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 17 May 2011 11:02:06 +0200 Subject: USB: gadget: f_audio: Fix invalid dereference of initdata as_out_ep_desc contines to be used during gadget enumeration and thus should not be marked as __initdata Signed-off-by: Martin Jackson Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c index 0111f8a9cf7..8ee330a2ab5 100644 --- a/drivers/usb/gadget/f_audio.c +++ b/drivers/usb/gadget/f_audio.c @@ -177,7 +177,7 @@ static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { }; /* Standard ISO OUT Endpoint Descriptor */ -static struct usb_endpoint_descriptor as_out_ep_desc __initdata = { +static struct usb_endpoint_descriptor as_out_ep_desc = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, -- cgit v1.2.3 From a8e62dd6d91f3bc3687abbb26227e5fc39c4829c Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 17 May 2011 14:45:48 -0400 Subject: usb-storage: fix up the unusual_realtek device list This patch (as1461) fixes the unusual_devs entries for the Realtek USB card reader. They should be ordered by PID, and they should not override the Subclass and Protocol values provided by the device. Otherwise a notification about unnecessary entries gets printed in the kernel log during probing. Signed-off-by: Alan Stern Tested-By: Tony Vroon CC: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_realtek.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/storage/unusual_realtek.h b/drivers/usb/storage/unusual_realtek.h index 3236e032851..e41f50c95ed 100644 --- a/drivers/usb/storage/unusual_realtek.h +++ b/drivers/usb/storage/unusual_realtek.h @@ -23,19 +23,19 @@ #if defined(CONFIG_USB_STORAGE_REALTEK) || \ defined(CONFIG_USB_STORAGE_REALTEK_MODULE) -UNUSUAL_DEV(0x0bda, 0x0159, 0x0000, 0x9999, +UNUSUAL_DEV(0x0bda, 0x0138, 0x0000, 0x9999, "Realtek", "USB Card Reader", - USB_SC_SCSI, USB_PR_BULK, init_realtek_cr, 0), + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), UNUSUAL_DEV(0x0bda, 0x0158, 0x0000, 0x9999, "Realtek", "USB Card Reader", - USB_SC_SCSI, USB_PR_BULK, init_realtek_cr, 0), + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), -UNUSUAL_DEV(0x0bda, 0x0138, 0x0000, 0x9999, +UNUSUAL_DEV(0x0bda, 0x0159, 0x0000, 0x9999, "Realtek", "USB Card Reader", - USB_SC_SCSI, USB_PR_BULK, init_realtek_cr, 0), + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), #endif /* defined(CONFIG_USB_STORAGE_REALTEK) || ... */ -- cgit v1.2.3 From 28e4970555554d12eed286cb47763d1949b931fb Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 18 May 2011 00:25:03 +0300 Subject: usb: musb: fix compile error commit 35a83365da6aa10095c6138cc428c15853409c32 (usb: musb: drop unneeded musb_debug trickery) introduced a compile error for blackfin and tusb6010 glue layers. Fix it. Reported-by: Mike Frysinger Signed-off-by: Felipe Balbi --- drivers/usb/musb/blackfin.c | 2 ++ drivers/usb/musb/tusb6010.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 95d75c00404..ae8c3961774 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -35,6 +35,7 @@ struct bfin_glue { */ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) { + struct musb *musb = hw_ep->musb; void __iomem *fifo = hw_ep->fifo; void __iomem *epio = hw_ep->regs; u8 epnum = hw_ep->epnum; @@ -98,6 +99,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) */ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) { + struct musb *musb = hw_ep->musb; void __iomem *fifo = hw_ep->fifo; u8 epnum = hw_ep->epnum; diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 2f683a67ebb..b410357cf01 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -172,6 +172,7 @@ static inline void tusb_fifo_read_unaligned(void __iomem *fifo, void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf) { + struct musb *musb = hw_ep->musb; void __iomem *ep_conf = hw_ep->conf; void __iomem *fifo = hw_ep->fifo; u8 epnum = hw_ep->epnum; @@ -221,6 +222,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf) void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf) { + struct musb *musb = hw_ep->musb; void __iomem *ep_conf = hw_ep->conf; void __iomem *fifo = hw_ep->fifo; u8 epnum = hw_ep->epnum; -- cgit v1.2.3 From a48ff906788aa2eb692bcb087223645902140554 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Tue, 22 Mar 2011 15:55:56 +0100 Subject: usb: musb: ux500: add ux500 specific code for gadget side Although U8500 and U5500 platforms use paltform dma, Inventra dma specific code can work for them for the most part. Only difference is for the Rx path where this patch is making use of request->short_not_ok to select dma mode. Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 57 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 6f3cf4ce8bd..0a50a35e185 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -372,7 +372,7 @@ static void txstate(struct musb *musb, struct musb_request *req) /* MUSB_TXCSR_P_ISO is still set correctly */ -#ifdef CONFIG_USB_INVENTRA_DMA +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) { if (request_size < musb_ep->packet_sz) musb_ep->dma->desired_mode = 0; @@ -555,7 +555,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) if ((request->zero && request->length && (request->length % musb_ep->packet_sz == 0) && (request->actual == request->length)) -#ifdef CONFIG_USB_INVENTRA_DMA +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || (is_dma && (!dma->desired_mode || (request->actual & (musb_ep->packet_sz - 1)))) @@ -756,6 +756,53 @@ static void rxstate(struct musb *musb, struct musb_request *req) } if (use_dma) + return; + } +#elif defined(CONFIG_USB_UX500_DMA) + if ((is_buffer_mapped(req)) && + (request->actual < request->length)) { + + struct dma_controller *c; + struct dma_channel *channel; + int transfer_size = 0; + + c = musb->dma_controller; + channel = musb_ep->dma; + + /* In case first packet is short */ + if (len < musb_ep->packet_sz) + transfer_size = len; + else if (request->short_not_ok) + transfer_size = min(request->length - + request->actual, + channel->max_len); + else + transfer_size = min(request->length - + request->actual, + (unsigned)len); + + csr &= ~MUSB_RXCSR_DMAMODE; + csr |= (MUSB_RXCSR_DMAENAB | + MUSB_RXCSR_AUTOCLEAR); + + musb_writew(epio, MUSB_RXCSR, csr); + + if (transfer_size <= musb_ep->packet_sz) { + musb_ep->dma->desired_mode = 0; + } else { + musb_ep->dma->desired_mode = 1; + /* Mode must be set after DMAENAB */ + csr |= MUSB_RXCSR_DMAMODE; + musb_writew(epio, MUSB_RXCSR, csr); + } + + if (c->channel_program(channel, + musb_ep->packet_sz, + channel->desired_mode, + request->dma + + request->actual, + transfer_size)) + return; } #endif /* Mentor's DMA */ @@ -895,7 +942,8 @@ void musb_g_rx(struct musb *musb, u8 epnum) musb_readw(epio, MUSB_RXCSR), musb_ep->dma->actual_len, request); -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) || \ + defined(CONFIG_USB_UX500_DMA) /* Autoclear doesn't clear RxPktRdy for short packets */ if ((dma->desired_mode == 0 && !hw_ep->rx_double_buffered) || (dma->actual_len @@ -925,7 +973,8 @@ void musb_g_rx(struct musb *musb, u8 epnum) if (!req) return; } -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) || \ + defined(CONFIG_USB_UX500_DMA) exit: #endif /* Analyze request */ -- cgit v1.2.3 From e3c496f9b31e0e64f59b0c3770a17df7ddbd5ee3 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Tue, 22 Mar 2011 15:55:57 +0100 Subject: usb: musb: ux500: add dma name for ux500 Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 92aeec1f727..ab8e1001e5e 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2439,6 +2439,8 @@ static int __init musb_init(void) "musb-dma" #elif defined(CONFIG_USB_TUSB_OMAP_DMA) "tusb-omap-dma" +#elif defined(CONFIG_USB_UX500_DMA) + "ux500-dma" #else "?dma?" #endif -- cgit v1.2.3 From 8dcc8f72e3f3b7ed1ba1cd5641e15658e2140abe Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Tue, 22 Mar 2011 15:55:58 +0100 Subject: usb: musb: ux500: add dma glue layer for ux500 Unaligned sizes and buffers are not supported and they will be filtered out by is_compatible(). Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/musb/ux500_dma.c | 422 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 422 insertions(+) create mode 100644 drivers/usb/musb/ux500_dma.c diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c new file mode 100644 index 00000000000..cecace41183 --- /dev/null +++ b/drivers/usb/musb/ux500_dma.c @@ -0,0 +1,422 @@ +/* + * drivers/usb/musb/ux500_dma.c + * + * U8500 and U5500 DMA support code + * + * Copyright (C) 2009 STMicroelectronics + * Copyright (C) 2011 ST-Ericsson SA + * Authors: + * Mian Yousaf Kaukab + * Praveena Nadahally + * Rajaram Regupathy + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "musb_core.h" + +struct ux500_dma_channel { + struct dma_channel channel; + struct ux500_dma_controller *controller; + struct musb_hw_ep *hw_ep; + struct work_struct channel_work; + struct dma_chan *dma_chan; + unsigned int cur_len; + dma_cookie_t cookie; + u8 ch_num; + u8 is_tx; + u8 is_allocated; +}; + +struct ux500_dma_controller { + struct dma_controller controller; + struct ux500_dma_channel rx_channel[UX500_MUSB_DMA_NUM_RX_CHANNELS]; + struct ux500_dma_channel tx_channel[UX500_MUSB_DMA_NUM_TX_CHANNELS]; + u32 num_rx_channels; + u32 num_tx_channels; + void *private_data; + dma_addr_t phy_base; +}; + +/* Work function invoked from DMA callback to handle tx transfers. */ +static void ux500_tx_work(struct work_struct *data) +{ + struct ux500_dma_channel *ux500_channel = container_of(data, + struct ux500_dma_channel, channel_work); + struct musb_hw_ep *hw_ep = ux500_channel->hw_ep; + struct musb *musb = hw_ep->musb; + unsigned long flags; + + DBG(4, "DMA tx transfer done on hw_ep=%d\n", hw_ep->epnum); + + spin_lock_irqsave(&musb->lock, flags); + ux500_channel->channel.actual_len = ux500_channel->cur_len; + ux500_channel->channel.status = MUSB_DMA_STATUS_FREE; + musb_dma_completion(musb, hw_ep->epnum, + ux500_channel->is_tx); + spin_unlock_irqrestore(&musb->lock, flags); +} + +/* Work function invoked from DMA callback to handle rx transfers. */ +static void ux500_rx_work(struct work_struct *data) +{ + struct ux500_dma_channel *ux500_channel = container_of(data, + struct ux500_dma_channel, channel_work); + struct musb_hw_ep *hw_ep = ux500_channel->hw_ep; + struct musb *musb = hw_ep->musb; + unsigned long flags; + + DBG(4, "DMA rx transfer done on hw_ep=%d\n", hw_ep->epnum); + + spin_lock_irqsave(&musb->lock, flags); + ux500_channel->channel.actual_len = ux500_channel->cur_len; + ux500_channel->channel.status = MUSB_DMA_STATUS_FREE; + musb_dma_completion(musb, hw_ep->epnum, + ux500_channel->is_tx); + spin_unlock_irqrestore(&musb->lock, flags); +} + +void ux500_dma_callback(void *private_data) +{ + struct dma_channel *channel = (struct dma_channel *)private_data; + struct ux500_dma_channel *ux500_channel = channel->private_data; + + schedule_work(&ux500_channel->channel_work); +} + +static bool ux500_configure_channel(struct dma_channel *channel, + u16 packet_sz, u8 mode, + dma_addr_t dma_addr, u32 len) +{ + struct ux500_dma_channel *ux500_channel = channel->private_data; + struct musb_hw_ep *hw_ep = ux500_channel->hw_ep; + struct dma_chan *dma_chan = ux500_channel->dma_chan; + struct dma_async_tx_descriptor *dma_desc; + enum dma_data_direction direction; + struct scatterlist sg; + struct dma_slave_config slave_conf; + enum dma_slave_buswidth addr_width; + dma_addr_t usb_fifo_addr = (MUSB_FIFO_OFFSET(hw_ep->epnum) + + ux500_channel->controller->phy_base); + + DBG(4, "packet_sz=%d, mode=%d, dma_addr=0x%x, len=%d is_tx=%d\n", + packet_sz, mode, dma_addr, len, ux500_channel->is_tx); + + ux500_channel->cur_len = len; + + sg_init_table(&sg, 1); + sg_set_page(&sg, pfn_to_page(PFN_DOWN(dma_addr)), len, + offset_in_page(dma_addr)); + sg_dma_address(&sg) = dma_addr; + sg_dma_len(&sg) = len; + + direction = ux500_channel->is_tx ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + addr_width = (len & 0x3) ? DMA_SLAVE_BUSWIDTH_1_BYTE : + DMA_SLAVE_BUSWIDTH_4_BYTES; + + slave_conf.direction = direction; + if (direction == DMA_FROM_DEVICE) { + slave_conf.src_addr = usb_fifo_addr; + slave_conf.src_addr_width = addr_width; + slave_conf.src_maxburst = 16; + } else { + slave_conf.dst_addr = usb_fifo_addr; + slave_conf.dst_addr_width = addr_width; + slave_conf.dst_maxburst = 16; + } + dma_chan->device->device_control(dma_chan, DMA_SLAVE_CONFIG, + (unsigned long) &slave_conf); + + dma_desc = dma_chan->device-> + device_prep_slave_sg(dma_chan, &sg, 1, direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!dma_desc) + return false; + + dma_desc->callback = ux500_dma_callback; + dma_desc->callback_param = channel; + ux500_channel->cookie = dma_desc->tx_submit(dma_desc); + + dma_async_issue_pending(dma_chan); + + return true; +} + +static struct dma_channel *ux500_dma_channel_allocate(struct dma_controller *c, + struct musb_hw_ep *hw_ep, u8 is_tx) +{ + struct ux500_dma_controller *controller = container_of(c, + struct ux500_dma_controller, controller); + struct ux500_dma_channel *ux500_channel = NULL; + u8 ch_num = hw_ep->epnum - 1; + u32 max_ch; + + /* Max 8 DMA channels (0 - 7). Each DMA channel can only be allocated + * to specified hw_ep. For example DMA channel 0 can only be allocated + * to hw_ep 1 and 9. + */ + if (ch_num > 7) + ch_num -= 8; + + max_ch = is_tx ? controller->num_tx_channels : + controller->num_rx_channels; + + if (ch_num >= max_ch) + return NULL; + + ux500_channel = is_tx ? &(controller->tx_channel[ch_num]) : + &(controller->rx_channel[ch_num]) ; + + /* Check if channel is already used. */ + if (ux500_channel->is_allocated) + return NULL; + + ux500_channel->hw_ep = hw_ep; + ux500_channel->is_allocated = 1; + + DBG(7, "hw_ep=%d, is_tx=0x%x, channel=%d\n", + hw_ep->epnum, is_tx, ch_num); + + return &(ux500_channel->channel); +} + +static void ux500_dma_channel_release(struct dma_channel *channel) +{ + struct ux500_dma_channel *ux500_channel = channel->private_data; + + DBG(7, "channel=%d\n", ux500_channel->ch_num); + + if (ux500_channel->is_allocated) { + ux500_channel->is_allocated = 0; + channel->status = MUSB_DMA_STATUS_FREE; + channel->actual_len = 0; + } +} + +static int ux500_dma_is_compatible(struct dma_channel *channel, + u16 maxpacket, void *buf, u32 length) +{ + if ((maxpacket & 0x3) || + ((int)buf & 0x3) || + (length < 512) || + (length & 0x3)) + return false; + else + return true; +} + +static int ux500_dma_channel_program(struct dma_channel *channel, + u16 packet_sz, u8 mode, + dma_addr_t dma_addr, u32 len) +{ + int ret; + + BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN || + channel->status == MUSB_DMA_STATUS_BUSY); + + if (!ux500_dma_is_compatible(channel, packet_sz, (void *)dma_addr, len)) + return false; + + channel->status = MUSB_DMA_STATUS_BUSY; + channel->actual_len = 0; + ret = ux500_configure_channel(channel, packet_sz, mode, dma_addr, len); + if (!ret) + channel->status = MUSB_DMA_STATUS_FREE; + + return ret; +} + +static int ux500_dma_channel_abort(struct dma_channel *channel) +{ + struct ux500_dma_channel *ux500_channel = channel->private_data; + struct ux500_dma_controller *controller = ux500_channel->controller; + struct musb *musb = controller->private_data; + void __iomem *epio = musb->endpoints[ux500_channel->hw_ep->epnum].regs; + u16 csr; + + DBG(4, "channel=%d, is_tx=%d\n", ux500_channel->ch_num, + ux500_channel->is_tx); + + if (channel->status == MUSB_DMA_STATUS_BUSY) { + if (ux500_channel->is_tx) { + csr = musb_readw(epio, MUSB_TXCSR); + csr &= ~(MUSB_TXCSR_AUTOSET | + MUSB_TXCSR_DMAENAB | + MUSB_TXCSR_DMAMODE); + musb_writew(epio, MUSB_TXCSR, csr); + } else { + csr = musb_readw(epio, MUSB_RXCSR); + csr &= ~(MUSB_RXCSR_AUTOCLEAR | + MUSB_RXCSR_DMAENAB | + MUSB_RXCSR_DMAMODE); + musb_writew(epio, MUSB_RXCSR, csr); + } + + ux500_channel->dma_chan->device-> + device_control(ux500_channel->dma_chan, + DMA_TERMINATE_ALL, 0); + channel->status = MUSB_DMA_STATUS_FREE; + } + return 0; +} + +static int ux500_dma_controller_stop(struct dma_controller *c) +{ + struct ux500_dma_controller *controller = container_of(c, + struct ux500_dma_controller, controller); + struct ux500_dma_channel *ux500_channel; + struct dma_channel *channel; + u8 ch_num; + + for (ch_num = 0; ch_num < controller->num_rx_channels; ch_num++) { + channel = &controller->rx_channel[ch_num].channel; + ux500_channel = channel->private_data; + + ux500_dma_channel_release(channel); + + if (ux500_channel->dma_chan) + dma_release_channel(ux500_channel->dma_chan); + } + + for (ch_num = 0; ch_num < controller->num_tx_channels; ch_num++) { + channel = &controller->tx_channel[ch_num].channel; + ux500_channel = channel->private_data; + + ux500_dma_channel_release(channel); + + if (ux500_channel->dma_chan) + dma_release_channel(ux500_channel->dma_chan); + } + + return 0; +} + +static int ux500_dma_controller_start(struct dma_controller *c) +{ + struct ux500_dma_controller *controller = container_of(c, + struct ux500_dma_controller, controller); + struct ux500_dma_channel *ux500_channel = NULL; + struct musb *musb = controller->private_data; + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct ux500_musb_board_data *data = plat->board_data; + struct dma_channel *dma_channel = NULL; + u32 ch_num; + u8 dir; + u8 is_tx = 0; + + void **param_array; + struct ux500_dma_channel *channel_array; + u32 ch_count; + void (*musb_channel_work)(struct work_struct *); + dma_cap_mask_t mask; + + if ((data->num_rx_channels > UX500_MUSB_DMA_NUM_RX_CHANNELS) || + (data->num_tx_channels > UX500_MUSB_DMA_NUM_TX_CHANNELS)) + return -EINVAL; + + controller->num_rx_channels = data->num_rx_channels; + controller->num_tx_channels = data->num_tx_channels; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* Prepare the loop for RX channels */ + channel_array = controller->rx_channel; + ch_count = data->num_rx_channels; + param_array = data->dma_rx_param_array; + musb_channel_work = ux500_rx_work; + + for (dir = 0; dir < 2; dir++) { + for (ch_num = 0; ch_num < ch_count; ch_num++) { + ux500_channel = &channel_array[ch_num]; + ux500_channel->controller = controller; + ux500_channel->ch_num = ch_num; + ux500_channel->is_tx = is_tx; + + dma_channel = &(ux500_channel->channel); + dma_channel->private_data = ux500_channel; + dma_channel->status = MUSB_DMA_STATUS_FREE; + dma_channel->max_len = SZ_16M; + + ux500_channel->dma_chan = dma_request_channel(mask, + data->dma_filter, + param_array[ch_num]); + if (!ux500_channel->dma_chan) { + ERR("Dma pipe allocation error dir=%d ch=%d\n", + dir, ch_num); + + /* Release already allocated channels */ + ux500_dma_controller_stop(c); + + return -EBUSY; + } + + INIT_WORK(&ux500_channel->channel_work, + musb_channel_work); + } + + /* Prepare the loop for TX channels */ + channel_array = controller->tx_channel; + ch_count = data->num_tx_channels; + param_array = data->dma_tx_param_array; + musb_channel_work = ux500_tx_work; + is_tx = 1; + } + + return 0; +} + +void dma_controller_destroy(struct dma_controller *c) +{ + struct ux500_dma_controller *controller = container_of(c, + struct ux500_dma_controller, controller); + + kfree(controller); +} + +struct dma_controller *__init +dma_controller_create(struct musb *musb, void __iomem *base) +{ + struct ux500_dma_controller *controller; + struct platform_device *pdev = to_platform_device(musb->controller); + struct resource *iomem; + + controller = kzalloc(sizeof(*controller), GFP_KERNEL); + if (!controller) + return NULL; + + controller->private_data = musb; + + /* Save physical address for DMA controller. */ + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + controller->phy_base = (dma_addr_t) iomem->start; + + controller->controller.start = ux500_dma_controller_start; + controller->controller.stop = ux500_dma_controller_stop; + controller->controller.channel_alloc = ux500_dma_channel_allocate; + controller->controller.channel_release = ux500_dma_channel_release; + controller->controller.channel_program = ux500_dma_channel_program; + controller->controller.channel_abort = ux500_dma_channel_abort; + controller->controller.is_compatible = ux500_dma_is_compatible; + + return &controller->controller; +} -- cgit v1.2.3 From d23894402b33338c51f1863d7f866fdc6f073a02 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Tue, 22 Mar 2011 15:55:59 +0100 Subject: usb: musb: ux500: add configuration and build options for ux500 dma Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/musb/Kconfig | 7 +++++++ drivers/usb/musb/Makefile | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index dbb13bd8cb0..13093481f91 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -153,6 +153,13 @@ config MUSB_PIO_ONLY you can still disable it at run time using the "use_dma=n" module parameter. +config USB_UX500_DMA + bool + depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY + default USB_MUSB_UX500 + help + Enable DMA transfers on UX500 platforms. + config USB_INVENTRA_DMA bool depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 4e15281b9e0..c4d228b6ef8 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -37,6 +37,11 @@ ifneq ($(CONFIG_MUSB_PIO_ONLY),y) ifeq ($(CONFIG_USB_TUSB_OMAP_DMA),y) musb_hdrc-y += tusb6010_omap.o + else + ifeq ($(CONFIG_USB_UX500_DMA),y) + musb_hdrc-y += ux500_dma.o + + endif endif endif endif -- cgit v1.2.3 From 69fff59de4d844f8b4c2454c3c23d32b69dcbfd7 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 17 May 2011 17:27:12 -0400 Subject: USB: remove remaining usages of hcd->state from usbcore and fix regression This patch (as1467) removes the last usages of hcd->state from usbcore. We no longer check to see if an interrupt handler finds that a controller has died; instead we rely on host controller drivers to make an explicit call to usb_hc_died(). This fixes a regression introduced by commit 9b37596a2e860404503a3f2a6513db60c296bfdc (USB: move usbcore away from hcd->state). It used to be that when a controller shared an IRQ with another device and an interrupt arrived while hcd->state was set to HC_STATE_HALT, the interrupt handler would be skipped. The commit removed that test; as a result the current code doesn't skip calling the handler and ends up believing the controller has died, even though it's only temporarily stopped. The solution is to ignore HC_STATE_HALT following the handler's return. As a consequence of this change, several of the host controller drivers need to be modified. They can no longer implicitly rely on usbcore realizing that a controller has died because of hcd->state. The patch adds calls to usb_hc_died() in the appropriate places. The patch also changes a few of the interrupt handlers. They don't expect to be called when hcd->state is equal to HC_STATE_HALT, even if the controller is still alive. Early returns were added to avoid any confusion. Signed-off-by: Alan Stern Tested-by: Manuel Lauss CC: Rodolfo Giometti CC: Olav Kongas CC: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 5 +---- drivers/usb/host/ehci-hcd.c | 4 +++- drivers/usb/host/ehci-sched.c | 8 ++++++-- drivers/usb/host/isp116x-hcd.c | 1 + drivers/usb/host/ohci-hcd.c | 4 +++- drivers/usb/host/oxu210hp-hcd.c | 6 +++++- 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 10936ba9c42..ace9f8442e5 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -986,7 +986,7 @@ static int register_root_hub(struct usb_hcd *hcd) spin_unlock_irq (&hcd_root_hub_lock); /* Did the HC die before the root hub was registered? */ - if (HCD_DEAD(hcd) || hcd->state == HC_STATE_HALT) + if (HCD_DEAD(hcd)) usb_hc_died (hcd); /* This time clean up */ } @@ -2128,9 +2128,6 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); if (hcd->shared_hcd) set_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags); - - if (unlikely(hcd->state == HC_STATE_HALT)) - usb_hc_died(hcd); rc = IRQ_HANDLED; } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index c5719cd258c..b435ed67dd5 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -777,8 +777,9 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) goto dead; } + /* Shared IRQ? */ masked_status = status & INTR_MASK; - if (!masked_status) { /* irq sharing? */ + if (!masked_status || unlikely(hcd->state == HC_STATE_HALT)) { spin_unlock(&ehci->lock); return IRQ_NONE; } @@ -873,6 +874,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) dead: ehci_reset(ehci); ehci_writel(ehci, 0, &ehci->regs->configured_flag); + usb_hc_died(hcd); /* generic layer kills/unlinks all urbs, then * uses ehci_stop to clean up the rest */ diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a7408d88fda..6c9fbe352f7 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -471,8 +471,10 @@ static int enable_periodic (struct ehci_hcd *ehci) */ status = handshake_on_error_set_halt(ehci, &ehci->regs->status, STS_PSS, 0, 9 * 125); - if (status) + if (status) { + usb_hc_died(ehci_to_hcd(ehci)); return status; + } cmd = ehci_readl(ehci, &ehci->regs->command) | CMD_PSE; ehci_writel(ehci, cmd, &ehci->regs->command); @@ -510,8 +512,10 @@ static int disable_periodic (struct ehci_hcd *ehci) */ status = handshake_on_error_set_halt(ehci, &ehci->regs->status, STS_PSS, STS_PSS, 9 * 125); - if (status) + if (status) { + usb_hc_died(ehci_to_hcd(ehci)); return status; + } cmd = ehci_readl(ehci, &ehci->regs->command) & ~CMD_PSE; ehci_writel(ehci, cmd, &ehci->regs->command); diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index c0e22f26da1..baae4ccd16a 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -612,6 +612,7 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd) /* IRQ's are off, we do no DMA, perfectly ready to die ... */ hcd->state = HC_STATE_HALT; + usb_hc_died(hcd); ret = IRQ_HANDLED; goto done; } diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 8c8dc6559ac..9aa10bdf391 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -764,6 +764,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) if (ints == ~(u32)0) { disable (ohci); ohci_dbg (ohci, "device removed!\n"); + usb_hc_died(hcd); return IRQ_HANDLED; } @@ -771,7 +772,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) ints &= ohci_readl(ohci, ®s->intrenable); /* interrupt for some other device? */ - if (ints == 0) + if (ints == 0 || unlikely(hcd->state == HC_STATE_HALT)) return IRQ_NOTMINE; if (ints & OHCI_INTR_UE) { @@ -788,6 +789,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) } else { disable (ohci); ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n"); + usb_hc_died(hcd); } ohci_dump (ohci, 1); diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index 4a771f6cc82..5fbe997dc6d 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -1884,6 +1884,7 @@ static int enable_periodic(struct oxu_hcd *oxu) status = handshake(oxu, &oxu->regs->status, STS_PSS, 0, 9 * 125); if (status != 0) { oxu_to_hcd(oxu)->state = HC_STATE_HALT; + usb_hc_died(oxu_to_hcd(oxu)); return status; } @@ -1909,6 +1910,7 @@ static int disable_periodic(struct oxu_hcd *oxu) status = handshake(oxu, &oxu->regs->status, STS_PSS, STS_PSS, 9 * 125); if (status != 0) { oxu_to_hcd(oxu)->state = HC_STATE_HALT; + usb_hc_died(oxu_to_hcd(oxu)); return status; } @@ -2449,8 +2451,9 @@ static irqreturn_t oxu210_hcd_irq(struct usb_hcd *hcd) goto dead; } + /* Shared IRQ? */ status &= INTR_MASK; - if (!status) { /* irq sharing? */ + if (!status || unlikely(hcd->state == HC_STATE_HALT)) { spin_unlock(&oxu->lock); return IRQ_NONE; } @@ -2516,6 +2519,7 @@ static irqreturn_t oxu210_hcd_irq(struct usb_hcd *hcd) dead: ehci_reset(oxu); writel(0, &oxu->regs->configured_flag); + usb_hc_died(hcd); /* generic layer kills/unlinks all urbs, then * uses oxu_stop to clean up the rest */ -- cgit v1.2.3 From 079cdb0947ce6ae7df0c73a1c82c14920a9b6b6d Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Fri, 20 May 2011 00:17:34 +0200 Subject: usb/isp1760: Move function isp1760_endpoint_disable() within file. Preparation for patch #2. The function isp1760_endpoint_disable() does almost the same thing as urb_dequeue(). In patch #2 I change these to use a common helper function instead of calling each other - for clarity but also to avoid releasing the spinlock while in a "questionable" state. It seemed proper to have these functions close to each other in the code. Signed-off-by: Arvid Brodin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1760-hcd.c | 68 +++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index dd98a966b58..485fc70625a 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -1558,6 +1558,40 @@ out: return retval; } +static void isp1760_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + struct isp1760_qh *qh; + struct isp1760_qtd *qtd; + unsigned long spinflags; + int do_iter; + + spin_lock_irqsave(&priv->lock, spinflags); + qh = ep->hcpriv; + if (!qh) + goto out; + + do_iter = !list_empty(&qh->qtd_list); + while (do_iter) { + do_iter = 0; + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { + if (qtd->urb->ep == ep) { + spin_unlock_irqrestore(&priv->lock, spinflags); + isp1760_urb_dequeue(hcd, qtd->urb, -ECONNRESET); + spin_lock_irqsave(&priv->lock, spinflags); + do_iter = 1; + break; /* Restart iteration */ + } + } + } + ep->hcpriv = NULL; + /* Cannot free qh here since it will be parsed by schedule_ptds() */ + +out: + spin_unlock_irqrestore(&priv->lock, spinflags); +} + static int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf) { struct isp1760_hcd *priv = hcd_to_priv(hcd); @@ -1927,40 +1961,6 @@ error: return retval; } -static void isp1760_endpoint_disable(struct usb_hcd *hcd, - struct usb_host_endpoint *ep) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct isp1760_qh *qh; - struct isp1760_qtd *qtd; - unsigned long spinflags; - int do_iter; - - spin_lock_irqsave(&priv->lock, spinflags); - qh = ep->hcpriv; - if (!qh) - goto out; - - do_iter = !list_empty(&qh->qtd_list); - while (do_iter) { - do_iter = 0; - list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { - if (qtd->urb->ep == ep) { - spin_unlock_irqrestore(&priv->lock, spinflags); - isp1760_urb_dequeue(hcd, qtd->urb, -ECONNRESET); - spin_lock_irqsave(&priv->lock, spinflags); - do_iter = 1; - break; /* Restart iteration */ - } - } - } - ep->hcpriv = NULL; - /* Cannot free qh here since it will be parsed by schedule_ptds() */ - -out: - spin_unlock_irqrestore(&priv->lock, spinflags); -} - static int isp1760_get_frame(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); -- cgit v1.2.3 From d05b6ec01b8186f847ac9e41098e40858926db40 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Fri, 20 May 2011 00:17:45 +0200 Subject: usb/isp1760: Fix possible unlink problems Use skip map to avoid spurious interrupts from unlinked transfers. Also changes to urb_dequeue() and endpoint_disable() to avoid release of spinlock in uncertain state. Signed-off-by: Arvid Brodin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1760-hcd.c | 147 ++++++++++++++++++++++++++--------------- 1 file changed, 95 insertions(+), 52 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 485fc70625a..c9e6e454c62 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -34,7 +34,9 @@ struct isp1760_hcd { u32 hcs_params; spinlock_t lock; struct slotinfo atl_slots[32]; + int atl_done_map; struct slotinfo int_slots[32]; + int int_done_map; struct memory_chunk memory_pool[BLOCKS]; struct list_head controlqhs, bulkqhs, interruptqhs; int active_ptds; @@ -519,9 +521,9 @@ static void isp1760_init_maps(struct usb_hcd *hcd) reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0); - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0); - reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff); reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, ATL_BUF_FILL | INT_BUF_FILL); @@ -803,6 +805,8 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, struct isp1760_qh *qh, struct ptd *ptd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); + int skip_map; + WARN_ON((slot < 0) || (slot > 31)); WARN_ON(qtd->length && !qtd->payload_addr); WARN_ON(slots[slot].qtd); @@ -816,6 +820,25 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, interrupt routine may preempt and expects this value. */ ptd_write(hcd->regs, ptd_offset, slot, ptd); priv->active_ptds++; + + /* Make sure done map has not triggered from some unlinked transfer */ + if (ptd_offset == ATL_PTD_OFFSET) { + priv->atl_done_map |= reg_read32(hcd->regs, + HC_ATL_PTD_DONEMAP_REG); + priv->atl_done_map &= ~(1 << qh->slot); + + skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + skip_map &= ~(1 << qh->slot); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); + } else { + priv->int_done_map |= reg_read32(hcd->regs, + HC_INT_PTD_DONEMAP_REG); + priv->int_done_map &= ~(1 << qh->slot); + + skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); + skip_map &= ~(1 << qh->slot); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); + } } static int is_short_bulk(struct isp1760_qtd *qtd) @@ -1152,7 +1175,6 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) irqreturn_t irqret = IRQ_NONE; struct ptd ptd; struct isp1760_qh *qh; - int int_done_map, atl_done_map; int slot; int state; struct slotinfo *slots; @@ -1160,6 +1182,7 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) struct isp1760_qtd *qtd; int modified; static int last_active_ptds; + int int_skip_map, atl_skip_map; spin_lock(&priv->lock); @@ -1171,29 +1194,42 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) goto leave; reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */ - int_done_map = reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); - atl_done_map = reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); - modified = int_done_map | atl_done_map; + int_skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); + atl_skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); + priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); + priv->int_done_map &= ~int_skip_map; + priv->atl_done_map &= ~atl_skip_map; - while (int_done_map || atl_done_map) { - if (int_done_map) { + modified = priv->int_done_map | priv->atl_done_map; + + while (priv->int_done_map || priv->atl_done_map) { + if (priv->int_done_map) { /* INT ptd */ - slot = __ffs(int_done_map); - int_done_map &= ~(1 << slot); + slot = __ffs(priv->int_done_map); + priv->int_done_map &= ~(1 << slot); slots = priv->int_slots; - if (!slots[slot].qh) + /* This should not trigger, and could be removed if + noone have any problems with it triggering: */ + if (!slots[slot].qh) { + WARN_ON(1); continue; + } ptd_offset = INT_PTD_OFFSET; ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd); state = check_int_transfer(hcd, &ptd, slots[slot].qtd->urb); } else { /* ATL ptd */ - slot = __ffs(atl_done_map); - atl_done_map &= ~(1 << slot); + slot = __ffs(priv->atl_done_map); + priv->atl_done_map &= ~(1 << slot); slots = priv->atl_slots; - if (!slots[slot].qh) + /* This should not trigger, and could be removed if + noone have any problems with it triggering: */ + if (!slots[slot].qh) { + WARN_ON(1); continue; + } ptd_offset = ATL_PTD_OFFSET; ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); state = check_atl_transfer(hcd, &ptd, @@ -1509,14 +1545,41 @@ out: return retval; } +static void kill_transfer(struct usb_hcd *hcd, struct urb *urb, + struct isp1760_qh *qh) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + int skip_map; + + WARN_ON(qh->slot == -1); + + /* We need to forcefully reclaim the slot since some transfers never + return, e.g. interrupt transfers and NAKed bulk transfers. */ + if (usb_pipebulk(urb->pipe)) { + skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + skip_map |= (1 << qh->slot); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); + priv->atl_slots[qh->slot].qh = NULL; + priv->atl_slots[qh->slot].qtd = NULL; + } else { + skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); + skip_map |= (1 << qh->slot); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); + priv->int_slots[qh->slot].qh = NULL; + priv->int_slots[qh->slot].qtd = NULL; + } + + qh->slot = -1; + priv->active_ptds--; +} + static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { struct isp1760_hcd *priv = hcd_to_priv(hcd); + unsigned long spinflags; struct isp1760_qh *qh; struct isp1760_qtd *qtd; - struct ptd ptd; - unsigned long spinflags; int retval = 0; spin_lock_irqsave(&priv->lock, spinflags); @@ -1527,34 +1590,18 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, goto out; } - /* We need to forcefully reclaim the slot since some transfers never - return, e.g. interrupt transfers and NAKed bulk transfers. */ - if (qh->slot > -1) { - memset(&ptd, 0, sizeof(ptd)); - if (usb_pipebulk(urb->pipe)) { - priv->atl_slots[qh->slot].qh = NULL; - priv->atl_slots[qh->slot].qtd = NULL; - ptd_write(hcd->regs, ATL_PTD_OFFSET, qh->slot, &ptd); - } else { - priv->int_slots[qh->slot].qh = NULL; - priv->int_slots[qh->slot].qtd = NULL; - ptd_write(hcd->regs, INT_PTD_OFFSET, qh->slot, &ptd); - } - priv->active_ptds--; - qh->slot = -1; - } - - list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { - if (qtd->urb == urb) + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) + if (qtd->urb == urb) { + if (qtd->status == QTD_XFER_STARTED) + kill_transfer(hcd, urb, qh); qtd->status = QTD_RETIRE; - } + } urb->status = status; schedule_ptds(hcd); out: spin_unlock_irqrestore(&priv->lock, spinflags); - return retval; } @@ -1562,32 +1609,28 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) { struct isp1760_hcd *priv = hcd_to_priv(hcd); + unsigned long spinflags; struct isp1760_qh *qh; struct isp1760_qtd *qtd; - unsigned long spinflags; - int do_iter; spin_lock_irqsave(&priv->lock, spinflags); + qh = ep->hcpriv; if (!qh) goto out; - do_iter = !list_empty(&qh->qtd_list); - while (do_iter) { - do_iter = 0; - list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { - if (qtd->urb->ep == ep) { - spin_unlock_irqrestore(&priv->lock, spinflags); - isp1760_urb_dequeue(hcd, qtd->urb, -ECONNRESET); - spin_lock_irqsave(&priv->lock, spinflags); - do_iter = 1; - break; /* Restart iteration */ - } - } + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { + if (qtd->status == QTD_XFER_STARTED) + kill_transfer(hcd, qtd->urb, qh); + qtd->status = QTD_RETIRE; + qtd->urb->status = -ECONNRESET; } + ep->hcpriv = NULL; /* Cannot free qh here since it will be parsed by schedule_ptds() */ + schedule_ptds(hcd); + out: spin_unlock_irqrestore(&priv->lock, spinflags); } -- cgit v1.2.3 From 5116901d8596a52598364d41581c0a745da003bc Mon Sep 17 00:00:00 2001 From: Karl Relton Date: Wed, 18 May 2011 21:42:34 +0100 Subject: usb-storage: Correct adjust_quirks to include latest flags Commits ae38c78a03e1b77ad45248fcf097e4568e740209 and 00914025cc4e783d4703b4db1d47b41f389e50c8 added quirk flags US_FL_NO_READ_DISC_INFO and US_FL_NO_READ_CAPACITY_16 to the usb-storage driver. However they did not add the corresponding flags to adjust_quirks() in usb.c, so there was no facility for a user to over-ride/add them via the quirks module parameter. Signed-off-by: Karl Relton Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 4 ++++ drivers/usb/storage/usb.c | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index cc85a927819..0e545e044c1 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2581,6 +2581,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. bytes of sense data); c = FIX_CAPACITY (decrease the reported device capacity by one sector); + d = NO_READ_DISC_INFO (don't use + READ_DISC_INFO command); + e = NO_READ_CAPACITY_16 (don't use + READ_CAPACITY_16 command); h = CAPACITY_HEURISTICS (decrease the reported device capacity by one sector if the number is odd); diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 4219c197cb0..5ee7ac42e08 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -439,7 +439,8 @@ static void adjust_quirks(struct us_data *us) US_FL_CAPACITY_HEURISTICS | US_FL_IGNORE_DEVICE | US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 | US_FL_CAPACITY_OK | US_FL_IGNORE_RESIDUE | - US_FL_SINGLE_LUN | US_FL_NO_WP_DETECT); + US_FL_SINGLE_LUN | US_FL_NO_WP_DETECT | + US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16); p = quirks; while (*p) { @@ -471,6 +472,12 @@ static void adjust_quirks(struct us_data *us) case 'c': f |= US_FL_FIX_CAPACITY; break; + case 'd': + f |= US_FL_NO_READ_DISC_INFO; + break; + case 'e': + f |= US_FL_NO_READ_CAPACITY_16; + break; case 'h': f |= US_FL_CAPACITY_HEURISTICS; break; -- cgit v1.2.3 From 8452c6745e74384e7e434144f989ada3eae41170 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Wed, 18 May 2011 10:44:49 +0200 Subject: USB: UHCI: Add support for big endian mmio This patch adds support for big endian mmio to the UHCI HCD. Big endian mmio is supported by adding a flag bit to the UHCI HCD replicating the solution used in the EHCI HCD. When adding big endian support this patch also adds a check to see if we need to support HCs with PCI I/O registers when we support HCs with MMIO. This patch also adds 'const' to the register access functions' uhci_hcd argument. Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 4 +++ drivers/usb/host/uhci-hcd.h | 67 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 8898505af42..ce9e22bf211 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -416,6 +416,10 @@ config USB_UHCI_SUPPORT_NON_PCI_HC depends on USB_UHCI_HCD default y if SPARC_LEON +config USB_UHCI_BIG_ENDIAN_MMIO + bool + depends on USB_UHCI_SUPPORT_NON_PCI_HC + config USB_FHCI_HCD tristate "Freescale QE USB Host Controller support" depends on USB && OF_GPIO && QE_GPIO && QUICC_ENGINE diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index a4e64d08f02..10b68a846f6 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -421,6 +421,7 @@ struct uhci_hcd { /* Silicon quirks */ unsigned int oc_low:1; /* OverCurrent bit active low */ unsigned int wait_for_hp:1; /* Wait for HP port reset */ + unsigned int big_endian_mmio:1; /* Big endian registers */ /* Support for port suspend/resume/reset */ unsigned long port_c_suspend; /* Bit-arrays of ports */ @@ -490,90 +491,126 @@ struct urb_priv { * we use memory mapped registers. */ -#if !defined(CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC) +#ifndef CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC /* Support PCI only */ -static inline u32 uhci_readl(struct uhci_hcd *uhci, int reg) +static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg) { return inl(uhci->io_addr + reg); } -static inline void uhci_writel(struct uhci_hcd *uhci, u32 val, int reg) +static inline void uhci_writel(const struct uhci_hcd *uhci, u32 val, int reg) { outl(val, uhci->io_addr + reg); } -static inline u16 uhci_readw(struct uhci_hcd *uhci, int reg) +static inline u16 uhci_readw(const struct uhci_hcd *uhci, int reg) { return inw(uhci->io_addr + reg); } -static inline void uhci_writew(struct uhci_hcd *uhci, u16 val, int reg) +static inline void uhci_writew(const struct uhci_hcd *uhci, u16 val, int reg) { outw(val, uhci->io_addr + reg); } -static inline u8 uhci_readb(struct uhci_hcd *uhci, int reg) +static inline u8 uhci_readb(const struct uhci_hcd *uhci, int reg) { return inb(uhci->io_addr + reg); } -static inline void uhci_writeb(struct uhci_hcd *uhci, u8 val, int reg) +static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg) { outb(val, uhci->io_addr + reg); } #else +/* Support non-PCI host controllers */ +#ifdef CONFIG_PCI /* Support PCI and non-PCI host controllers */ - #define uhci_has_pci_registers(u) ((u)->io_addr != 0) +#else +/* Support non-PCI host controllers only */ +#define uhci_has_pci_registers(u) 0 +#endif + +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO +/* Support (non-PCI) big endian host controllers */ +#define uhci_big_endian_mmio(u) ((u)->big_endian_mmio) +#else +#define uhci_big_endian_mmio(u) 0 +#endif -static inline u32 uhci_readl(struct uhci_hcd *uhci, int reg) +static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg) { if (uhci_has_pci_registers(uhci)) return inl(uhci->io_addr + reg); +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO + else if (uhci_big_endian_mmio(uhci)) + return readl_be(uhci->regs + reg); +#endif else return readl(uhci->regs + reg); } -static inline void uhci_writel(struct uhci_hcd *uhci, u32 val, int reg) +static inline void uhci_writel(const struct uhci_hcd *uhci, u32 val, int reg) { if (uhci_has_pci_registers(uhci)) outl(val, uhci->io_addr + reg); +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO + else if (uhci_big_endian_mmio(uhci)) + writel_be(val, uhci->regs + reg); +#endif else writel(val, uhci->regs + reg); } -static inline u16 uhci_readw(struct uhci_hcd *uhci, int reg) +static inline u16 uhci_readw(const struct uhci_hcd *uhci, int reg) { if (uhci_has_pci_registers(uhci)) return inw(uhci->io_addr + reg); +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO + else if (uhci_big_endian_mmio(uhci)) + return readw_be(uhci->regs + reg); +#endif else return readw(uhci->regs + reg); } -static inline void uhci_writew(struct uhci_hcd *uhci, u16 val, int reg) +static inline void uhci_writew(const struct uhci_hcd *uhci, u16 val, int reg) { if (uhci_has_pci_registers(uhci)) outw(val, uhci->io_addr + reg); +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO + else if (uhci_big_endian_mmio(uhci)) + writew_be(val, uhci->regs + reg); +#endif else writew(val, uhci->regs + reg); } -static inline u8 uhci_readb(struct uhci_hcd *uhci, int reg) +static inline u8 uhci_readb(const struct uhci_hcd *uhci, int reg) { if (uhci_has_pci_registers(uhci)) return inb(uhci->io_addr + reg); +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO + else if (uhci_big_endian_mmio(uhci)) + return readb_be(uhci->regs + reg); +#endif else return readb(uhci->regs + reg); } -static inline void uhci_writeb(struct uhci_hcd *uhci, u8 val, int reg) +static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg) { if (uhci_has_pci_registers(uhci)) outb(val, uhci->io_addr + reg); +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO + else if (uhci_big_endian_mmio(uhci)) + writeb_be(val, uhci->regs + reg); +#endif else writeb(val, uhci->regs + reg); } -#endif /* !defined(CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC) */ +#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */ #endif -- cgit v1.2.3 From bab1ff1bda27e654dfd382a1fbdfcda1f7ed0a37 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 18 May 2011 10:44:50 +0200 Subject: USB: UHCI: Use ACCESS_ONCE rather than using a full compiler barrier This patch (as1462) updates the special accessor functions defined in uhci-hcd.h. Rather than using a full compiler barrier, all we really need is the ACCESS_ONCE() mechanism, because the idea is to force the compiler to store a fixed copy of a possibly changing value. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.h | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 10b68a846f6..0deeab6c9e5 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -168,12 +168,7 @@ struct uhci_qh { * We need a special accessor for the element pointer because it is * subject to asynchronous updates by the controller. */ -static inline __le32 qh_element(struct uhci_qh *qh) { - __le32 element = qh->element; - - barrier(); - return element; -} +#define qh_element(qh) ACCESS_ONCE((qh)->element) #define LINK_TO_QH(qh) (UHCI_PTR_QH | cpu_to_le32((qh)->dma_handle)) @@ -263,12 +258,7 @@ struct uhci_td { * We need a special accessor for the control/status word because it is * subject to asynchronous updates by the controller. */ -static inline u32 td_status(struct uhci_td *td) { - __le32 status = td->status; - - barrier(); - return le32_to_cpu(status); -} +#define td_status(td) le32_to_cpu(ACCESS_ONCE((td)->status)) #define LINK_TO_TD(td) (cpu_to_le32((td)->dma_handle)) -- cgit v1.2.3 From 51e2f62fe79651e7ed8e16ba126a163b116fe3d7 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Wed, 18 May 2011 10:44:51 +0200 Subject: USB: UHCI: Add support for big endian descriptors This patch adds support for universal host controllers that use big endian descriptors. Support for BE descriptors requires a non-PCI host controller. For kernels with PCI-only UHCI there should be no change in behaviour. This patch tries to replicate the technique used to support BE descriptors in the EHCI HCD. Parts added to uhci-hcd.h are basically copy'n'paste from ehci.h. Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 4 ++ drivers/usb/host/uhci-debug.c | 71 ++++++++++++----------- drivers/usb/host/uhci-hcd.c | 16 +++--- drivers/usb/host/uhci-hcd.h | 90 +++++++++++++++++++++++------ drivers/usb/host/uhci-q.c | 131 +++++++++++++++++++++--------------------- 5 files changed, 192 insertions(+), 120 deletions(-) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index ce9e22bf211..584424804d0 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -420,6 +420,10 @@ config USB_UHCI_BIG_ENDIAN_MMIO bool depends on USB_UHCI_SUPPORT_NON_PCI_HC +config USB_UHCI_BIG_ENDIAN_DESC + bool + depends on USB_UHCI_SUPPORT_NON_PCI_HC + config USB_FHCI_HCD tristate "Freescale QE USB Host Controller support" depends on USB && OF_GPIO && QE_GPIO && QUICC_ENGINE diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index f882a84b1bb..fc0b0daac93 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -37,7 +37,8 @@ static void lprintk(char *buf) } } -static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space) +static int uhci_show_td(struct uhci_hcd *uhci, struct uhci_td *td, char *buf, + int len, int space) { char *out = buf; char *spid; @@ -47,8 +48,9 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space) if (len < 160) return 0; - status = td_status(td); - out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, le32_to_cpu(td->link)); + status = td_status(uhci, td); + out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, + hc32_to_cpu(uhci, td->link)); out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ", ((status >> 27) & 3), (status & TD_CTRL_SPD) ? "SPD " : "", @@ -63,7 +65,7 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space) (status & TD_CTRL_BITSTUFF) ? "BitStuff " : "", status & 0x7ff); - token = td_token(td); + token = td_token(uhci, td); switch (uhci_packetid(token)) { case USB_PID_SETUP: spid = "SETUP"; @@ -86,12 +88,13 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space) (token >> 8) & 127, (token & 0xff), spid); - out += sprintf(out, "(buf=%08x)\n", le32_to_cpu(td->buffer)); + out += sprintf(out, "(buf=%08x)\n", hc32_to_cpu(uhci, td->buffer)); return out - buf; } -static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space) +static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, + char *buf, int len, int space) { char *out = buf; struct uhci_td *td; @@ -130,9 +133,10 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space) if (urbp->qh->type != USB_ENDPOINT_XFER_ISOC && (++i <= 10 || debug > 2)) { out += sprintf(out, "%*s%d: ", space + 2, "", i); - out += uhci_show_td(td, out, len - (out - buf), 0); + out += uhci_show_td(uhci, td, out, + len - (out - buf), 0); } else { - if (td_status(td) & TD_CTRL_ACTIVE) + if (td_status(uhci, td) & TD_CTRL_ACTIVE) ++nactive; else ++ninactive; @@ -151,7 +155,7 @@ static int uhci_show_qh(struct uhci_hcd *uhci, { char *out = buf; int i, nurbs; - __le32 element = qh_element(qh); + __hc32 element = qh_element(qh); char *qtype; /* Try to make sure there's enough memory */ @@ -168,7 +172,8 @@ static int uhci_show_qh(struct uhci_hcd *uhci, out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n", space, "", qh, qtype, - le32_to_cpu(qh->link), le32_to_cpu(element)); + hc32_to_cpu(uhci, qh->link), + hc32_to_cpu(uhci, element)); if (qh->type == USB_ENDPOINT_XFER_ISOC) out += sprintf(out, "%*s period %d phase %d load %d us, " "frame %x desc [%p]\n", @@ -178,22 +183,22 @@ static int uhci_show_qh(struct uhci_hcd *uhci, out += sprintf(out, "%*s period %d phase %d load %d us\n", space, "", qh->period, qh->phase, qh->load); - if (element & UHCI_PTR_QH) + if (element & UHCI_PTR_QH(uhci)) out += sprintf(out, "%*s Element points to QH (bug?)\n", space, ""); - if (element & UHCI_PTR_DEPTH) + if (element & UHCI_PTR_DEPTH(uhci)) out += sprintf(out, "%*s Depth traverse\n", space, ""); - if (element & cpu_to_le32(8)) + if (element & cpu_to_hc32(uhci, 8)) out += sprintf(out, "%*s Bit 3 set (bug?)\n", space, ""); - if (!(element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH))) + if (!(element & ~(UHCI_PTR_QH(uhci) | UHCI_PTR_DEPTH(uhci)))) out += sprintf(out, "%*s Element is NULL (bug?)\n", space, ""); if (list_empty(&qh->queue)) { out += sprintf(out, "%*s queue is empty\n", space, ""); if (qh == uhci->skel_async_qh) - out += uhci_show_td(uhci->term_td, out, + out += uhci_show_td(uhci, uhci->term_td, out, len - (out - buf), 0); } else { struct urb_priv *urbp = list_entry(qh->queue.next, @@ -201,13 +206,13 @@ static int uhci_show_qh(struct uhci_hcd *uhci, struct uhci_td *td = list_entry(urbp->td_list.next, struct uhci_td, list); - if (element != LINK_TO_TD(td)) + if (element != LINK_TO_TD(uhci, td)) out += sprintf(out, "%*s Element != First TD\n", space, ""); i = nurbs = 0; list_for_each_entry(urbp, &qh->queue, node) { if (++i <= 10) - out += uhci_show_urbp(urbp, out, + out += uhci_show_urbp(uhci, urbp, out, len - (out - buf), space + 2); else ++nurbs; @@ -219,7 +224,8 @@ static int uhci_show_qh(struct uhci_hcd *uhci, if (qh->dummy_td) { out += sprintf(out, "%*s Dummy TD\n", space, ""); - out += uhci_show_td(qh->dummy_td, out, len - (out - buf), 0); + out += uhci_show_td(uhci, qh->dummy_td, out, + len - (out - buf), 0); } return out - buf; @@ -346,8 +352,8 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) struct uhci_td *td; struct list_head *tmp, *head; int nframes, nerrs; - __le32 link; - __le32 fsbr_link; + __hc32 link; + __hc32 fsbr_link; static const char * const qh_names[] = { "unlink", "iso", "int128", "int64", "int32", "int16", @@ -375,7 +381,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) nframes = 10; nerrs = 0; for (i = 0; i < UHCI_NUMFRAMES; ++i) { - __le32 qh_dma; + __hc32 qh_dma; j = 0; td = uhci->frame_cpu[i]; @@ -385,7 +391,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) if (nframes > 0) { out += sprintf(out, "- Frame %d -> (%08x)\n", - i, le32_to_cpu(link)); + i, hc32_to_cpu(uhci, link)); j = 1; } @@ -394,7 +400,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) do { td = list_entry(tmp, struct uhci_td, fl_list); tmp = tmp->next; - if (link != LINK_TO_TD(td)) { + if (link != LINK_TO_TD(uhci, td)) { if (nframes > 0) out += sprintf(out, " link does " "not match list entry!\n"); @@ -402,7 +408,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) ++nerrs; } if (nframes > 0) - out += uhci_show_td(td, out, + out += uhci_show_td(uhci, td, out, len - (out - buf), 4); link = td->link; } while (tmp != head); @@ -414,11 +420,12 @@ check_link: if (!j) { out += sprintf(out, "- Frame %d -> (%08x)\n", - i, le32_to_cpu(link)); + i, hc32_to_cpu(uhci, link)); j = 1; } out += sprintf(out, " link does not match " - "QH (%08x)!\n", le32_to_cpu(qh_dma)); + "QH (%08x)!\n", + hc32_to_cpu(uhci, qh_dma)); } else ++nerrs; } @@ -439,11 +446,11 @@ check_link: /* Last QH is the Terminating QH, it's different */ if (i == SKEL_TERM) { - if (qh_element(qh) != LINK_TO_TD(uhci->term_td)) + if (qh_element(qh) != LINK_TO_TD(uhci, uhci->term_td)) out += sprintf(out, " skel_term_qh element is not set to term_td!\n"); link = fsbr_link; if (!link) - link = LINK_TO_QH(uhci->skel_term_qh); + link = LINK_TO_QH(uhci, uhci->skel_term_qh); goto check_qh_link; } @@ -457,20 +464,20 @@ check_link: out += uhci_show_qh(uhci, qh, out, len - (out - buf), 4); if (!fsbr_link && qh->skel >= SKEL_FSBR) - fsbr_link = LINK_TO_QH(qh); + fsbr_link = LINK_TO_QH(uhci, qh); } if ((cnt -= 10) > 0) out += sprintf(out, " Skipped %d QHs\n", cnt); - link = UHCI_PTR_TERM; + link = UHCI_PTR_TERM(uhci); if (i <= SKEL_ISO) ; else if (i < SKEL_ASYNC) - link = LINK_TO_QH(uhci->skel_async_qh); + link = LINK_TO_QH(uhci, uhci->skel_async_qh); else if (!uhci->fsbr_is_on) ; else - link = LINK_TO_QH(uhci->skel_term_qh); + link = LINK_TO_QH(uhci, uhci->skel_term_qh); check_qh_link: if (qh->link != link) out += sprintf(out, " last QH not linked to next skeleton!\n"); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 79dd822e58d..32dec7c7458 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -92,7 +92,7 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci); /* * Calculate the link pointer DMA value for the first Skeleton QH in a frame. */ -static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame) +static __hc32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame) { int skelnum; @@ -114,7 +114,7 @@ static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame) skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES); if (skelnum <= 1) skelnum = 9; - return LINK_TO_QH(uhci->skelqh[skelnum]); + return LINK_TO_QH(uhci, uhci->skelqh[skelnum]); } #include "uhci-debug.c" @@ -630,16 +630,16 @@ static int uhci_start(struct usb_hcd *hcd) * 8 Interrupt queues; link all higher int queues to int1 = async */ for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i) - uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh); - uhci->skel_async_qh->link = UHCI_PTR_TERM; - uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh); + uhci->skelqh[i]->link = LINK_TO_QH(uhci, uhci->skel_async_qh); + uhci->skel_async_qh->link = UHCI_PTR_TERM(uhci); + uhci->skel_term_qh->link = LINK_TO_QH(uhci, uhci->skel_term_qh); /* This dummy TD is to work around a bug in Intel PIIX controllers */ - uhci_fill_td(uhci->term_td, 0, uhci_explen(0) | + uhci_fill_td(uhci, uhci->term_td, 0, uhci_explen(0) | (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0); - uhci->term_td->link = UHCI_PTR_TERM; + uhci->term_td->link = UHCI_PTR_TERM(uhci); uhci->skel_async_qh->element = uhci->skel_term_qh->element = - LINK_TO_TD(uhci->term_td); + LINK_TO_TD(uhci, uhci->term_td); /* * Fill the frame list: make all entries point to the proper diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 0deeab6c9e5..7af2b705204 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -78,11 +78,11 @@ #define USBPORT1EN 0x01 #define USBPORT2EN 0x02 -#define UHCI_PTR_BITS cpu_to_le32(0x000F) -#define UHCI_PTR_TERM cpu_to_le32(0x0001) -#define UHCI_PTR_QH cpu_to_le32(0x0002) -#define UHCI_PTR_DEPTH cpu_to_le32(0x0004) -#define UHCI_PTR_BREADTH cpu_to_le32(0x0000) +#define UHCI_PTR_BITS(uhci) cpu_to_hc32((uhci), 0x000F) +#define UHCI_PTR_TERM(uhci) cpu_to_hc32((uhci), 0x0001) +#define UHCI_PTR_QH(uhci) cpu_to_hc32((uhci), 0x0002) +#define UHCI_PTR_DEPTH(uhci) cpu_to_hc32((uhci), 0x0004) +#define UHCI_PTR_BREADTH(uhci) cpu_to_hc32((uhci), 0x0000) #define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ #define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ @@ -98,6 +98,22 @@ #define QH_WAIT_TIMEOUT msecs_to_jiffies(200) +/* + * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to + * __leXX (normally) or __beXX (given UHCI_BIG_ENDIAN_DESC), depending on + * the host controller implementation. + * + * To facilitate the strongest possible byte-order checking from "sparse" + * and so on, we use __leXX unless that's not practical. + */ +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_DESC +typedef __u32 __bitwise __hc32; +typedef __u16 __bitwise __hc16; +#else +#define __hc32 __le32 +#define __hc16 __le16 +#endif + /* * Queue Headers */ @@ -130,8 +146,8 @@ struct uhci_qh { /* Hardware fields */ - __le32 link; /* Next QH in the schedule */ - __le32 element; /* Queue element (TD) pointer */ + __hc32 link; /* Next QH in the schedule */ + __hc32 element; /* Queue element (TD) pointer */ /* Software fields */ dma_addr_t dma_handle; @@ -170,7 +186,8 @@ struct uhci_qh { */ #define qh_element(qh) ACCESS_ONCE((qh)->element) -#define LINK_TO_QH(qh) (UHCI_PTR_QH | cpu_to_le32((qh)->dma_handle)) +#define LINK_TO_QH(uhci, qh) (UHCI_PTR_QH((uhci)) | \ + cpu_to_hc32((uhci), (qh)->dma_handle)) /* @@ -207,7 +224,7 @@ struct uhci_qh { /* * for TD : (a.k.a. Token) */ -#define td_token(td) le32_to_cpu((td)->token) +#define td_token(uhci, td) hc32_to_cpu((uhci), (td)->token) #define TD_TOKEN_DEVADDR_SHIFT 8 #define TD_TOKEN_TOGGLE_SHIFT 19 #define TD_TOKEN_TOGGLE (1 << 19) @@ -240,10 +257,10 @@ struct uhci_qh { */ struct uhci_td { /* Hardware fields */ - __le32 link; - __le32 status; - __le32 token; - __le32 buffer; + __hc32 link; + __hc32 status; + __hc32 token; + __hc32 buffer; /* Software fields */ dma_addr_t dma_handle; @@ -258,9 +275,10 @@ struct uhci_td { * We need a special accessor for the control/status word because it is * subject to asynchronous updates by the controller. */ -#define td_status(td) le32_to_cpu(ACCESS_ONCE((td)->status)) +#define td_status(uhci, td) hc32_to_cpu((uhci), \ + ACCESS_ONCE((td)->status)) -#define LINK_TO_TD(td) (cpu_to_le32((td)->dma_handle)) +#define LINK_TO_TD(uhci, td) (cpu_to_hc32((uhci), (td)->dma_handle)) /* @@ -383,7 +401,7 @@ struct uhci_hcd { spinlock_t lock; dma_addr_t frame_dma_handle; /* Hardware frame list */ - __le32 *frame; + __hc32 *frame; void **frame_cpu; /* CPU's frame list */ enum uhci_rh_state rh_state; @@ -412,6 +430,7 @@ struct uhci_hcd { unsigned int oc_low:1; /* OverCurrent bit active low */ unsigned int wait_for_hp:1; /* Wait for HP port reset */ unsigned int big_endian_mmio:1; /* Big endian registers */ + unsigned int big_endian_desc:1; /* Big endian descriptors */ /* Support for port suspend/resume/reset */ unsigned long port_c_suspend; /* Bit-arrays of ports */ @@ -603,4 +622,43 @@ static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg) } #endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */ +/* + * The GRLIB GRUSBHC controller can use big endian format for its descriptors. + * + * UHCI controllers accessed through PCI work normally (little-endian + * everywhere), so we don't bother supporting a BE-only mode. + */ +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_DESC +#define uhci_big_endian_desc(u) ((u)->big_endian_desc) + +/* cpu to uhci */ +static inline __hc32 cpu_to_hc32(const struct uhci_hcd *uhci, const u32 x) +{ + return uhci_big_endian_desc(uhci) + ? (__force __hc32)cpu_to_be32(x) + : (__force __hc32)cpu_to_le32(x); +} + +/* uhci to cpu */ +static inline u32 hc32_to_cpu(const struct uhci_hcd *uhci, const __hc32 x) +{ + return uhci_big_endian_desc(uhci) + ? be32_to_cpu((__force __be32)x) + : le32_to_cpu((__force __le32)x); +} + +#else +/* cpu to uhci */ +static inline __hc32 cpu_to_hc32(const struct uhci_hcd *uhci, const u32 x) +{ + return cpu_to_le32(x); +} + +/* uhci to cpu */ +static inline u32 hc32_to_cpu(const struct uhci_hcd *uhci, const __hc32 x) +{ + return le32_to_cpu(x); +} +#endif + #endif diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index af77abb5c68..84ed28b34f9 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -29,12 +29,12 @@ static void uhci_set_next_interrupt(struct uhci_hcd *uhci) { if (uhci->is_stopped) mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies); - uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); + uhci->term_td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC); } static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci) { - uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC); + uhci->term_td->status &= ~cpu_to_hc32(uhci, TD_CTRL_IOC); } @@ -53,7 +53,7 @@ static void uhci_fsbr_on(struct uhci_hcd *uhci) uhci->fsbr_is_on = 1; lqh = list_entry(uhci->skel_async_qh->node.prev, struct uhci_qh, node); - lqh->link = LINK_TO_QH(uhci->skel_term_qh); + lqh->link = LINK_TO_QH(uhci, uhci->skel_term_qh); } static void uhci_fsbr_off(struct uhci_hcd *uhci) @@ -65,7 +65,7 @@ static void uhci_fsbr_off(struct uhci_hcd *uhci) uhci->fsbr_is_on = 0; lqh = list_entry(uhci->skel_async_qh->node.prev, struct uhci_qh, node); - lqh->link = UHCI_PTR_TERM; + lqh->link = UHCI_PTR_TERM(uhci); } static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb) @@ -131,12 +131,12 @@ static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) dma_pool_free(uhci->td_pool, td, td->dma_handle); } -static inline void uhci_fill_td(struct uhci_td *td, u32 status, - u32 token, u32 buffer) +static inline void uhci_fill_td(struct uhci_hcd *uhci, struct uhci_td *td, + u32 status, u32 token, u32 buffer) { - td->status = cpu_to_le32(status); - td->token = cpu_to_le32(token); - td->buffer = cpu_to_le32(buffer); + td->status = cpu_to_hc32(uhci, status); + td->token = cpu_to_hc32(uhci, token); + td->buffer = cpu_to_hc32(uhci, buffer); } static void uhci_add_td_to_urbp(struct uhci_td *td, struct urb_priv *urbp) @@ -170,11 +170,11 @@ static inline void uhci_insert_td_in_frame_list(struct uhci_hcd *uhci, td->link = ltd->link; wmb(); - ltd->link = LINK_TO_TD(td); + ltd->link = LINK_TO_TD(uhci, td); } else { td->link = uhci->frame[framenum]; wmb(); - uhci->frame[framenum] = LINK_TO_TD(td); + uhci->frame[framenum] = LINK_TO_TD(uhci, td); uhci->frame_cpu[framenum] = td; } } @@ -198,7 +198,7 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci, ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list); - uhci->frame[td->frame] = LINK_TO_TD(ntd); + uhci->frame[td->frame] = LINK_TO_TD(uhci, ntd); uhci->frame_cpu[td->frame] = ntd; } } else { @@ -255,8 +255,8 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, memset(qh, 0, sizeof(*qh)); qh->dma_handle = dma_handle; - qh->element = UHCI_PTR_TERM; - qh->link = UHCI_PTR_TERM; + qh->element = UHCI_PTR_TERM(uhci); + qh->link = UHCI_PTR_TERM(uhci); INIT_LIST_HEAD(&qh->queue); INIT_LIST_HEAD(&qh->node); @@ -348,9 +348,9 @@ static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh, /* If the QH element pointer is UHCI_PTR_TERM then then currently * executing URB has already been unlinked, so this one isn't it. */ - if (qh_element(qh) == UHCI_PTR_TERM) + if (qh_element(qh) == UHCI_PTR_TERM(uhci)) goto done; - qh->element = UHCI_PTR_TERM; + qh->element = UHCI_PTR_TERM(uhci); /* Control pipes don't have to worry about toggles */ if (qh->type == USB_ENDPOINT_XFER_CONTROL) @@ -360,7 +360,7 @@ static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh, WARN_ON(list_empty(&urbp->td_list)); td = list_entry(urbp->td_list.next, struct uhci_td, list); qh->needs_fixup = 1; - qh->initial_toggle = uhci_toggle(td_token(td)); + qh->initial_toggle = uhci_toggle(td_token(uhci, td)); done: return ret; @@ -370,7 +370,8 @@ done: * Fix up the data toggles for URBs in a queue, when one of them * terminates early (short transfer, error, or dequeued). */ -static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first) +static void uhci_fixup_toggles(struct uhci_hcd *uhci, struct uhci_qh *qh, + int skip_first) { struct urb_priv *urbp = NULL; struct uhci_td *td; @@ -384,7 +385,7 @@ static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first) /* When starting with the first URB, if the QH element pointer is * still valid then we know the URB's toggles are okay. */ - else if (qh_element(qh) != UHCI_PTR_TERM) + else if (qh_element(qh) != UHCI_PTR_TERM(uhci)) toggle = 2; /* Fix up the toggle for the URBs in the queue. Normally this @@ -396,15 +397,15 @@ static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first) /* If the first TD has the right toggle value, we don't * need to change any toggles in this URB */ td = list_entry(urbp->td_list.next, struct uhci_td, list); - if (toggle > 1 || uhci_toggle(td_token(td)) == toggle) { + if (toggle > 1 || uhci_toggle(td_token(uhci, td)) == toggle) { td = list_entry(urbp->td_list.prev, struct uhci_td, list); - toggle = uhci_toggle(td_token(td)) ^ 1; + toggle = uhci_toggle(td_token(uhci, td)) ^ 1; /* Otherwise all the toggles in the URB have to be switched */ } else { list_for_each_entry(td, &urbp->td_list, list) { - td->token ^= cpu_to_le32( + td->token ^= cpu_to_hc32(uhci, TD_TOKEN_TOGGLE); toggle ^= 1; } @@ -441,7 +442,7 @@ static void link_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh) pqh = list_entry(qh->node.prev, struct uhci_qh, node); qh->link = pqh->link; wmb(); - pqh->link = LINK_TO_QH(qh); + pqh->link = LINK_TO_QH(uhci, qh); } /* @@ -451,7 +452,7 @@ static void link_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh) static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh) { struct uhci_qh *pqh; - __le32 link_to_new_qh; + __hc32 link_to_new_qh; /* Find the predecessor QH for our new one and insert it in the list. * The list of QHs is expected to be short, so linear search won't @@ -465,7 +466,7 @@ static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh) /* Link it into the schedule */ qh->link = pqh->link; wmb(); - link_to_new_qh = LINK_TO_QH(qh); + link_to_new_qh = LINK_TO_QH(uhci, qh); pqh->link = link_to_new_qh; /* If this is now the first FSBR QH, link the terminating skeleton @@ -483,13 +484,13 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) /* Set the element pointer if it isn't set already. * This isn't needed for Isochronous queues, but it doesn't hurt. */ - if (qh_element(qh) == UHCI_PTR_TERM) { + if (qh_element(qh) == UHCI_PTR_TERM(uhci)) { struct urb_priv *urbp = list_entry(qh->queue.next, struct urb_priv, node); struct uhci_td *td = list_entry(urbp->td_list.next, struct uhci_td, list); - qh->element = LINK_TO_TD(td); + qh->element = LINK_TO_TD(uhci, td); } /* Treat the queue as if it has just advanced */ @@ -533,7 +534,7 @@ static void unlink_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh) static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh) { struct uhci_qh *pqh; - __le32 link_to_next_qh = qh->link; + __hc32 link_to_next_qh = qh->link; pqh = list_entry(qh->node.prev, struct uhci_qh, node); pqh->link = link_to_next_qh; @@ -757,8 +758,8 @@ static void uhci_free_urb_priv(struct uhci_hcd *uhci, /* * Map status to standard result codes * - * is (td_status(td) & 0xF60000), a.k.a. - * uhci_status_bits(td_status(td)). + * is (td_status(uhci, td) & 0xF60000), a.k.a. + * uhci_status_bits(td_status(uhci, td)). * Note: does not include the TD_CTRL_NAK bit. * is True for output TDs and False for input TDs. */ @@ -794,7 +795,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); int len = urb->transfer_buffer_length; dma_addr_t data = urb->transfer_dma; - __le32 *plink; + __hc32 *plink; struct urb_priv *urbp = urb->hcpriv; int skel; @@ -811,7 +812,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, */ td = qh->dummy_td; uhci_add_td_to_urbp(td, urbp); - uhci_fill_td(td, status, destination | uhci_explen(8), + uhci_fill_td(uhci, td, status, destination | uhci_explen(8), urb->setup_dma); plink = &td->link; status |= TD_CTRL_ACTIVE; @@ -844,14 +845,14 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = LINK_TO_TD(td); + *plink = LINK_TO_TD(uhci, td); /* Alternate Data0/1 (start with Data1) */ destination ^= TD_TOKEN_TOGGLE; uhci_add_td_to_urbp(td, urbp); - uhci_fill_td(td, status, destination | uhci_explen(pktsze), - data); + uhci_fill_td(uhci, td, status, + destination | uhci_explen(pktsze), data); plink = &td->link; data += pktsze; @@ -864,14 +865,14 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = LINK_TO_TD(td); + *plink = LINK_TO_TD(uhci, td); /* Change direction for the status transaction */ destination ^= (USB_PID_IN ^ USB_PID_OUT); destination |= TD_TOKEN_TOGGLE; /* End in Data1 */ uhci_add_td_to_urbp(td, urbp); - uhci_fill_td(td, status | TD_CTRL_IOC, + uhci_fill_td(uhci, td, status | TD_CTRL_IOC, destination | uhci_explen(0), 0); plink = &td->link; @@ -881,11 +882,11 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = LINK_TO_TD(td); + *plink = LINK_TO_TD(uhci, td); - uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0); + uhci_fill_td(uhci, td, 0, USB_PID_OUT | uhci_explen(0), 0); wmb(); - qh->dummy_td->status |= cpu_to_le32(TD_CTRL_ACTIVE); + qh->dummy_td->status |= cpu_to_hc32(uhci, TD_CTRL_ACTIVE); qh->dummy_td = td; /* Low-speed transfers get a different queue, and won't hog the bus. @@ -921,7 +922,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, int len = urb->transfer_buffer_length; int this_sg_len; dma_addr_t data; - __le32 *plink; + __hc32 *plink; struct urb_priv *urbp = urb->hcpriv; unsigned int toggle; struct scatterlist *sg; @@ -974,10 +975,10 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = LINK_TO_TD(td); + *plink = LINK_TO_TD(uhci, td); } uhci_add_td_to_urbp(td, urbp); - uhci_fill_td(td, status, + uhci_fill_td(uhci, td, status, destination | uhci_explen(pktsze) | (toggle << TD_TOKEN_TOGGLE_SHIFT), data); @@ -1010,10 +1011,10 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = LINK_TO_TD(td); + *plink = LINK_TO_TD(uhci, td); uhci_add_td_to_urbp(td, urbp); - uhci_fill_td(td, status, + uhci_fill_td(uhci, td, status, destination | uhci_explen(0) | (toggle << TD_TOKEN_TOGGLE_SHIFT), data); @@ -1028,7 +1029,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, * fast side but not enough to justify delaying an interrupt * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT * flag setting. */ - td->status |= cpu_to_le32(TD_CTRL_IOC); + td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC); /* * Build the new dummy TD and activate the old one @@ -1036,11 +1037,11 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = LINK_TO_TD(td); + *plink = LINK_TO_TD(uhci, td); - uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0); + uhci_fill_td(uhci, td, 0, USB_PID_OUT | uhci_explen(0), 0); wmb(); - qh->dummy_td->status |= cpu_to_le32(TD_CTRL_ACTIVE); + qh->dummy_td->status |= cpu_to_hc32(uhci, TD_CTRL_ACTIVE); qh->dummy_td = td; usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), @@ -1133,7 +1134,7 @@ static int uhci_fixup_short_transfer(struct uhci_hcd *uhci, * the queue at the status stage transaction, which is * the last TD. */ WARN_ON(list_empty(&urbp->td_list)); - qh->element = LINK_TO_TD(td); + qh->element = LINK_TO_TD(uhci, td); tmp = td->list.prev; ret = -EINPROGRESS; @@ -1142,8 +1143,9 @@ static int uhci_fixup_short_transfer(struct uhci_hcd *uhci, /* When a bulk/interrupt transfer is short, we have to * fix up the toggles of the following URBs on the queue * before restarting the queue at the next URB. */ - qh->initial_toggle = uhci_toggle(td_token(qh->post_td)) ^ 1; - uhci_fixup_toggles(qh, 1); + qh->initial_toggle = + uhci_toggle(td_token(uhci, qh->post_td)) ^ 1; + uhci_fixup_toggles(uhci, qh, 1); if (list_empty(&urbp->td_list)) td = qh->post_td; @@ -1178,7 +1180,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) unsigned int ctrlstat; int len; - ctrlstat = td_status(td); + ctrlstat = td_status(uhci, td); status = uhci_status_bits(ctrlstat); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; @@ -1188,7 +1190,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) if (status) { ret = uhci_map_status(status, - uhci_packetout(td_token(td))); + uhci_packetout(td_token(uhci, td))); if ((debug == 1 && ret != -EPIPE) || debug > 1) { /* Some debugging code */ dev_dbg(&urb->dev->dev, @@ -1204,7 +1206,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) } /* Did we receive a short packet? */ - } else if (len < uhci_expected_length(td_token(td))) { + } else if (len < uhci_expected_length(td_token(uhci, td))) { /* For control transfers, go to the status TD if * this isn't already the last data TD */ @@ -1236,10 +1238,10 @@ err: if (ret < 0) { /* Note that the queue has stopped and save * the next toggle value */ - qh->element = UHCI_PTR_TERM; + qh->element = UHCI_PTR_TERM(uhci); qh->is_stopped = 1; qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL); - qh->initial_toggle = uhci_toggle(td_token(td)) ^ + qh->initial_toggle = uhci_toggle(td_token(uhci, td)) ^ (ret == -EREMOTEIO); } else /* Short packet received */ @@ -1335,14 +1337,14 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, return -ENOMEM; uhci_add_td_to_urbp(td, urbp); - uhci_fill_td(td, status, destination | + uhci_fill_td(uhci, td, status, destination | uhci_explen(urb->iso_frame_desc[i].length), urb->transfer_dma + urb->iso_frame_desc[i].offset); } /* Set the interrupt-on-completion flag on the last packet. */ - td->status |= cpu_to_le32(TD_CTRL_IOC); + td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC); /* Add the TDs to the frame list */ frame = urb->start_frame; @@ -1378,7 +1380,7 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) uhci_remove_tds_from_frame(uhci, qh->iso_frame); - ctrlstat = td_status(td); + ctrlstat = td_status(uhci, td); if (ctrlstat & TD_CTRL_ACTIVE) { status = -EXDEV; /* TD was added too late? */ } else { @@ -1629,7 +1631,7 @@ restart: * queue, the QH can now be re-activated. */ if (!list_empty(&qh->queue)) { if (qh->needs_fixup) - uhci_fixup_toggles(qh, 0); + uhci_fixup_toggles(uhci, qh, 0); /* If the first URB on the queue wants FSBR but its time * limit has expired, set the next TD to interrupt on @@ -1639,7 +1641,7 @@ restart: struct uhci_td *td = list_entry(urbp->td_list.next, struct uhci_td, list); - td->status |= __cpu_to_le32(TD_CTRL_IOC); + td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC); } uhci_activate_qh(uhci, qh); @@ -1686,7 +1688,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh) } else { urbp = list_entry(qh->queue.next, struct urb_priv, node); td = list_entry(urbp->td_list.next, struct uhci_td, list); - status = td_status(td); + status = td_status(uhci, td); if (!(status & TD_CTRL_ACTIVE)) { /* We're okay, the queue has advanced */ @@ -1704,7 +1706,8 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh) if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) { /* Detect the Intel bug and work around it */ - if (qh->post_td && qh_element(qh) == LINK_TO_TD(qh->post_td)) { + if (qh->post_td && qh_element(qh) == + LINK_TO_TD(uhci, qh->post_td)) { qh->element = qh->post_td->link; qh->advance_jiffies = jiffies; ret = 1; -- cgit v1.2.3 From c936503fff31663a8625d23f528d868aa8c22570 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Wed, 18 May 2011 10:44:52 +0200 Subject: sparc: add {read,write}*_be routines This patch adds {read,write}*_be big endian memory access routines to the io.h header used on SPARC32 and SPARC64. Tested on SPARC32 (LEON) Signed-off-by: Jan Andersson Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/include/asm/io.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/sparc/include/asm/io.h b/arch/sparc/include/asm/io.h index a34b2994937..f6902cf3cbe 100644 --- a/arch/sparc/include/asm/io.h +++ b/arch/sparc/include/asm/io.h @@ -5,4 +5,17 @@ #else #include #endif + +/* + * Defines used for both SPARC32 and SPARC64 + */ + +/* Big endian versions of memory read/write routines */ +#define readb_be(__addr) __raw_readb(__addr) +#define readw_be(__addr) __raw_readw(__addr) +#define readl_be(__addr) __raw_readl(__addr) +#define writeb_be(__b, __addr) __raw_writeb(__b, __addr) +#define writel_be(__w, __addr) __raw_writel(__w, __addr) +#define writew_be(__l, __addr) __raw_writew(__l, __addr) + #endif -- cgit v1.2.3 From fda928ac97dbf0359f3dc4c96925b7b422b540d7 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Wed, 18 May 2011 10:44:53 +0200 Subject: USB: UHCI: Support big endian GRUSBHC HC This patch adds support for big endian GRUSBHC UHCI controllers. The HCD bus glue will probe the register interface to determine the endianness of the controller. Tested on GR-LEON4-ITX board which has a controller with little endian interface and on custom LEON3 board with a BE controller. Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 6 ++++-- drivers/usb/host/uhci-grlib.c | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 584424804d0..ab085f12d57 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -418,11 +418,13 @@ config USB_UHCI_SUPPORT_NON_PCI_HC config USB_UHCI_BIG_ENDIAN_MMIO bool - depends on USB_UHCI_SUPPORT_NON_PCI_HC + depends on USB_UHCI_SUPPORT_NON_PCI_HC && SPARC_LEON + default y config USB_UHCI_BIG_ENDIAN_DESC bool - depends on USB_UHCI_SUPPORT_NON_PCI_HC + depends on USB_UHCI_SUPPORT_NON_PCI_HC && SPARC_LEON + default y config USB_FHCI_HCD tristate "Freescale QE USB Host Controller support" diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c index b1addd60a1e..d01c1e22768 100644 --- a/drivers/usb/host/uhci-grlib.c +++ b/drivers/usb/host/uhci-grlib.c @@ -25,6 +25,20 @@ static int uhci_grlib_init(struct usb_hcd *hcd) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); + /* + * Probe to determine the endianness of the controller. + * We know that bit 7 of the PORTSC1 register is always set + * and bit 15 is always clear. If uhci_readw() yields a value + * with bit 7 (0x80) turned on then the current little-endian + * setting is correct. Otherwise we assume the value was + * byte-swapped; hence the register interface and presumably + * also the descriptors are big-endian. + */ + if (!(uhci_readw(uhci, USBPORTSC1) & 0x80)) { + uhci->big_endian_mmio = 1; + uhci->big_endian_desc = 1; + } + uhci->rh_numports = uhci_count_ports(hcd); /* Set up pointers to to generic functions */ -- cgit v1.2.3 From d5f6db9e1aff6ccf1876224f152c0268b0c8a992 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Thu, 19 May 2011 20:47:02 +0200 Subject: USB: EHCI: Remove SPARC_LEON {read,write}_be definitions from ehci.h {read,write}l_be are now defined for SPARC and do not need to be defined for SPARC_LEON in ehci.h. This patch fixes the following warnings: CC drivers/usb/host/ehci-hcd.o In file included from drivers/usb/host/ehci-hcd.c:119: drivers/usb/host/ehci.h:631:1: warning: "readl_be" redefined ... drivers/usb/host/ehci-hcd.c:119: drivers/usb/host/ehci.h:632:1: warning: "writel_be" redefined ... Signed-off-by: Jan Andersson Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index f68e419cae8..bd6ff489baf 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -628,9 +628,6 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) #if defined(CONFIG_ARM) && defined(CONFIG_ARCH_IXP4XX) #define readl_be(addr) __raw_readl((__force unsigned *)addr) #define writel_be(val, addr) __raw_writel(val, (__force unsigned *)addr) -#elif defined(CONFIG_SPARC_LEON) -#define readl_be(addr) __raw_readl(addr) -#define writel_be(val, addr) __raw_writel(val, addr) #endif static inline unsigned int ehci_readl(const struct ehci_hcd *ehci, -- cgit v1.2.3