Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (36 commits)
  USB: Driver for Freescale QUICC Engine USB Host Controller
  USB: option: add QUANTA HSDPA Data Card device ids
  USB: storage: Add another unusual_dev for off-by-one bug
  USB: unusual_dev: usb-storage needs to ignore a device
  USB: GADGET: fix !x & y
  USB: new id for ti_usb_3410_5052 driver
  USB: cdc-acm: Add another conexant modem to the quirks
  USB: 'option' driver - onda device MT503HS has wrong id
  USB: Remove ZTE modem from unusual_devices
  USB: storage: support of Dane-Elec MediaTouch USB device
  USB: usbmon: Implement compat_ioctl
  USB: add kernel-doc for wusb_dev in struct usb_device
  USB: ftdi_sio driver support of bar code scanner from Diebold
  USB: ftdi_sio: added Alti-2 VID and Neptune 3 PID
  USB: cp2101 device
  USB: usblp.c: add USBLP_QUIRK_BIDIR to Brother HL-1440
  USB: remove vernier labpro from ldusb
  USB: CDC-ACM quirk for MTK GPS
  USB: cdc-acm: support some gps data loggers
  USB: composite: Fix bug: low byte of w_index is the usb interface number not the whole 2 bytes of w_index
  ...
This commit is contained in:
Linus Torvalds 2009-01-27 16:08:04 -08:00
commit e4a7ca2903
46 changed files with 4110 additions and 237 deletions

View File

@ -77,29 +77,6 @@ static struct platform_device usb_dev = {
.num_resources = ARRAY_SIZE(usb_resources),
};
#ifdef CONFIG_USB_MUSB_OTG
static struct otg_transceiver *xceiv;
struct otg_transceiver *otg_get_transceiver(void)
{
if (xceiv)
get_device(xceiv->dev);
return xceiv;
}
EXPORT_SYMBOL(otg_get_transceiver);
int otg_set_transceiver(struct otg_transceiver *x)
{
if (xceiv && x)
return -EBUSY;
xceiv = x;
return 0;
}
EXPORT_SYMBOL(otg_set_transceiver);
#endif
void __init setup_usb(unsigned mA, unsigned potpgt_msec)
{
usb_data.power = mA / 2;

View File

@ -13,6 +13,7 @@ obj-$(CONFIG_USB_EHCI_HCD) += host/
obj-$(CONFIG_USB_ISP116X_HCD) += host/
obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_FHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/
obj-$(CONFIG_USB_U132_HCD) += host/
obj-$(CONFIG_USB_R8A66597_HCD) += host/

View File

@ -1349,6 +1349,12 @@ static struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
{ USB_DEVICE(0x0e8d, 0x3329), /* i-blue 747, Qstarz BT-Q1000, Holux M-241 */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
{ USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
@ -1370,6 +1376,9 @@ static struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
{ USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
/* control interfaces with various AT-command sets */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,

View File

@ -226,6 +226,7 @@ static const struct quirk_printer_struct quirk_printers[] = {
{ 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */
{ 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */
{ 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820, by zut <kernel@zut.de> */
{ 0x04f9, 0x000d, USBLP_QUIRK_BIDIR }, /* Brother Industries, Ltd HL-1440 Laser Printer */
{ 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt Printer M129C */
{ 0, 0 }
};

View File

@ -1700,7 +1700,7 @@ const struct file_operations usbdev_file_operations = {
.release = usbdev_release,
};
void usb_fs_classdev_common_remove(struct usb_device *udev)
static void usbdev_remove(struct usb_device *udev)
{
struct dev_state *ps;
struct siginfo sinfo;
@ -1742,10 +1742,15 @@ static void usb_classdev_remove(struct usb_device *dev)
{
if (dev->usb_classdev)
device_unregister(dev->usb_classdev);
usb_fs_classdev_common_remove(dev);
}
static int usb_classdev_notify(struct notifier_block *self,
#else
#define usb_classdev_add(dev) 0
#define usb_classdev_remove(dev) do {} while (0)
#endif
static int usbdev_notify(struct notifier_block *self,
unsigned long action, void *dev)
{
switch (action) {
@ -1755,15 +1760,15 @@ static int usb_classdev_notify(struct notifier_block *self,
break;
case USB_DEVICE_REMOVE:
usb_classdev_remove(dev);
usbdev_remove(dev);
break;
}
return NOTIFY_OK;
}
static struct notifier_block usbdev_nb = {
.notifier_call = usb_classdev_notify,
.notifier_call = usbdev_notify,
};
#endif
static struct cdev usb_device_cdev;
@ -1798,9 +1803,8 @@ int __init usb_devio_init(void)
* to /sys/dev
*/
usb_classdev_class->dev_kobj = NULL;
usb_register_notify(&usbdev_nb);
#endif
usb_register_notify(&usbdev_nb);
out:
return retval;
@ -1811,8 +1815,8 @@ error_cdev:
void usb_devio_cleanup(void)
{
#ifdef CONFIG_USB_DEVICE_CLASS
usb_unregister_notify(&usbdev_nb);
#ifdef CONFIG_USB_DEVICE_CLASS
class_destroy(usb_classdev_class);
#endif
cdev_del(&usb_device_cdev);

View File

@ -284,7 +284,7 @@ static int usb_unbind_interface(struct device *dev)
* supports "soft" unbinding.
*/
if (!driver->soft_unbind)
usb_disable_interface(udev, intf);
usb_disable_interface(udev, intf, false);
driver->disconnect(intf);
usb_cancel_queued_reset(intf);

View File

@ -128,7 +128,6 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
pci_set_master(dev);
device_set_wakeup_enable(&dev->dev, 1);
retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
if (retval != 0)
@ -201,6 +200,7 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
struct usb_hcd *hcd = pci_get_drvdata(dev);
int retval = 0;
int wake, w;
int has_pci_pm;
/* Root hub suspend should have stopped all downstream traffic,
* and all bus master traffic. And done so for both the interface
@ -230,6 +230,15 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
synchronize_irq(dev->irq);
/* Downstream ports from this root hub should already be quiesced, so
* there will be no DMA activity. Now we can shut down the upstream
* link (except maybe for PME# resume signaling) and enter some PCI
* low power state, if the hardware allows.
*/
pci_disable_device(dev);
pci_save_state(dev);
/* Don't fail on error to enable wakeup. We rely on pci code
* to reject requests the hardware can't implement, rather
* than coding the same thing.
@ -241,35 +250,6 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
wake = w;
dev_dbg(&dev->dev, "wakeup: %d\n", wake);
/* Downstream ports from this root hub should already be quiesced, so
* there will be no DMA activity. Now we can shut down the upstream
* link (except maybe for PME# resume signaling) and enter some PCI
* low power state, if the hardware allows.
*/
pci_disable_device(dev);
done:
return retval;
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
/**
* usb_hcd_pci_suspend_late - suspend a PCI-based HCD after IRQs are disabled
* @dev: USB Host Controller being suspended
* @message: Power Management message describing this state transition
*
* Store this function in the HCD's struct pci_driver as .suspend_late.
*/
int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t message)
{
int retval = 0;
int has_pci_pm;
/* We might already be suspended (runtime PM -- not yet written) */
if (dev->current_state != PCI_D0)
goto done;
pci_save_state(dev);
/* Don't change state if we don't need to */
if (message.event == PM_EVENT_FREEZE ||
message.event == PM_EVENT_PRETHAW) {
@ -315,7 +295,7 @@ int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t message)
done:
return retval;
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend_late);
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
/**
* usb_hcd_pci_resume_early - resume a PCI-based HCD before IRQs are enabled
@ -325,65 +305,8 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend_late);
*/
int usb_hcd_pci_resume_early(struct pci_dev *dev)
{
int retval = 0;
pci_power_t state = dev->current_state;
#ifdef CONFIG_PPC_PMAC
/* Reenable ASIC clocks for USB */
if (machine_is(powermac)) {
struct device_node *of_node;
of_node = pci_device_to_OF_node(dev);
if (of_node)
pmac_call_feature(PMAC_FTR_USB_ENABLE,
of_node, 0, 1);
}
#endif
/* NOTE: chip docs cover clean "real suspend" cases (what Linux
* calls "standby", "suspend to RAM", and so on). There are also
* dirty cases when swsusp fakes a suspend in "shutdown" mode.
*/
if (state != PCI_D0) {
#ifdef DEBUG
int pci_pm;
u16 pmcr;
pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
pci_read_config_word(dev, pci_pm + PCI_PM_CTRL, &pmcr);
pmcr &= PCI_PM_CTRL_STATE_MASK;
if (pmcr) {
/* Clean case: power to USB and to HC registers was
* maintained; remote wakeup is easy.
*/
dev_dbg(&dev->dev, "resume from PCI D%d\n", pmcr);
} else {
/* Clean: HC lost Vcc power, D0 uninitialized
* + Vaux may have preserved port and transceiver
* state ... for remote wakeup from D3cold
* + or not; HCD must reinit + re-enumerate
*
* Dirty: D0 semi-initialized cases with swsusp
* + after BIOS init
* + after Linux init (HCD statically linked)
*/
dev_dbg(&dev->dev, "resume from previous PCI D%d\n",
state);
}
#endif
retval = pci_set_power_state(dev, PCI_D0);
} else {
/* Same basic cases: clean (powered/not), dirty */
dev_dbg(&dev->dev, "PCI legacy resume\n");
}
if (retval < 0)
dev_err(&dev->dev, "can't resume: %d\n", retval);
else
pci_restore_state(dev);
return retval;
pci_restore_state(dev);
return 0;
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_resume_early);
@ -398,6 +321,18 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
struct usb_hcd *hcd;
int retval;
#ifdef CONFIG_PPC_PMAC
/* Reenable ASIC clocks for USB */
if (machine_is(powermac)) {
struct device_node *of_node;
of_node = pci_device_to_OF_node(dev);
if (of_node)
pmac_call_feature(PMAC_FTR_USB_ENABLE,
of_node, 0, 1);
}
#endif
hcd = pci_get_drvdata(dev);
if (hcd->state != HC_STATE_SUSPENDED) {
dev_dbg(hcd->self.controller,
@ -405,6 +340,8 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
return 0;
}
pci_enable_wake(dev, PCI_D0, false);
retval = pci_enable_device(dev);
if (retval < 0) {
dev_err(&dev->dev, "can't re-enable after resume, %d!\n",

View File

@ -257,7 +257,6 @@ extern void usb_hcd_pci_remove(struct pci_dev *dev);
#ifdef CONFIG_PM
extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg);
extern int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t msg);
extern int usb_hcd_pci_resume_early(struct pci_dev *dev);
extern int usb_hcd_pci_resume(struct pci_dev *dev);
#endif /* CONFIG_PM */

View File

@ -2382,8 +2382,8 @@ static int hub_port_debounce(struct usb_hub *hub, int port1)
void usb_ep0_reinit(struct usb_device *udev)
{
usb_disable_endpoint(udev, 0 + USB_DIR_IN);
usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
usb_disable_endpoint(udev, 0 + USB_DIR_IN, true);
usb_disable_endpoint(udev, 0 + USB_DIR_OUT, true);
usb_enable_endpoint(udev, &udev->ep0, true);
}
EXPORT_SYMBOL_GPL(usb_ep0_reinit);

View File

@ -717,7 +717,6 @@ static void usbfs_remove_device(struct usb_device *dev)
fs_remove_file (dev->usbfs_dentry);
dev->usbfs_dentry = NULL;
}
usb_fs_classdev_common_remove(dev);
}
static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev)

View File

@ -1039,14 +1039,15 @@ static void remove_intf_ep_devs(struct usb_interface *intf)
* @dev: the device whose endpoint is being disabled
* @epaddr: the endpoint's address. Endpoint number for output,
* endpoint number + USB_DIR_IN for input
* @reset_hardware: flag to erase any endpoint state stored in the
* controller hardware
*
* Deallocates hcd/hardware state for this endpoint ... and nukes all
* pending urbs.
*
* If the HCD hasn't registered a disable() function, this sets the
* endpoint's maxpacket size to 0 to prevent further submissions.
* Disables the endpoint for URB submission and nukes all pending URBs.
* If @reset_hardware is set then also deallocates hcd/hardware state
* for the endpoint.
*/
void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
bool reset_hardware)
{
unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
struct usb_host_endpoint *ep;
@ -1056,15 +1057,18 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
if (usb_endpoint_out(epaddr)) {
ep = dev->ep_out[epnum];
dev->ep_out[epnum] = NULL;
if (reset_hardware)
dev->ep_out[epnum] = NULL;
} else {
ep = dev->ep_in[epnum];
dev->ep_in[epnum] = NULL;
if (reset_hardware)
dev->ep_in[epnum] = NULL;
}
if (ep) {
ep->enabled = 0;
usb_hcd_flush_endpoint(dev, ep);
usb_hcd_disable_endpoint(dev, ep);
if (reset_hardware)
usb_hcd_disable_endpoint(dev, ep);
}
}
@ -1072,17 +1076,21 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
* usb_disable_interface -- Disable all endpoints for an interface
* @dev: the device whose interface is being disabled
* @intf: pointer to the interface descriptor
* @reset_hardware: flag to erase any endpoint state stored in the
* controller hardware
*
* Disables all the endpoints for the interface's current altsetting.
*/
void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf)
void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf,
bool reset_hardware)
{
struct usb_host_interface *alt = intf->cur_altsetting;
int i;
for (i = 0; i < alt->desc.bNumEndpoints; ++i) {
usb_disable_endpoint(dev,
alt->endpoint[i].desc.bEndpointAddress);
alt->endpoint[i].desc.bEndpointAddress,
reset_hardware);
}
}
@ -1103,8 +1111,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__,
skip_ep0 ? "non-ep0" : "all");
for (i = skip_ep0; i < 16; ++i) {
usb_disable_endpoint(dev, i);
usb_disable_endpoint(dev, i + USB_DIR_IN);
usb_disable_endpoint(dev, i, true);
usb_disable_endpoint(dev, i + USB_DIR_IN, true);
}
dev->toggle[0] = dev->toggle[1] = 0;
@ -1274,7 +1282,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
remove_intf_ep_devs(iface);
usb_remove_sysfs_intf_files(iface);
}
usb_disable_interface(dev, iface);
usb_disable_interface(dev, iface, true);
iface->cur_altsetting = alt;
@ -1353,8 +1361,8 @@ int usb_reset_configuration(struct usb_device *dev)
*/
for (i = 1; i < 16; ++i) {
usb_disable_endpoint(dev, i);
usb_disable_endpoint(dev, i + USB_DIR_IN);
usb_disable_endpoint(dev, i, true);
usb_disable_endpoint(dev, i + USB_DIR_IN, true);
}
config = dev->actconfig;

View File

@ -15,9 +15,10 @@ extern void usb_enable_endpoint(struct usb_device *dev,
struct usb_host_endpoint *ep, bool reset_toggle);
extern void usb_enable_interface(struct usb_device *dev,
struct usb_interface *intf, bool reset_toggles);
extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr);
extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
bool reset_hardware);
extern void usb_disable_interface(struct usb_device *dev,
struct usb_interface *intf);
struct usb_interface *intf, bool reset_hardware);
extern void usb_release_interface_cache(struct kref *ref);
extern void usb_disable_device(struct usb_device *dev, int skip_ep0);
extern int usb_deauthorize_device(struct usb_device *);
@ -151,7 +152,6 @@ extern struct usb_driver usbfs_driver;
extern const struct file_operations usbfs_devices_fops;
extern const struct file_operations usbdev_file_operations;
extern void usbfs_conn_disc_event(void);
extern void usb_fs_classdev_common_remove(struct usb_device *udev);
extern int usb_devio_init(void);
extern void usb_devio_cleanup(void);

View File

@ -683,6 +683,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u8 intf = w_index & 0xFF;
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
struct usb_function *f = NULL;
@ -769,10 +770,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
goto unknown;
if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[w_index];
f = cdev->config->interface[intf];
if (!f)
break;
if (w_value && !f->get_alt)
if (w_value && !f->set_alt)
break;
value = f->set_alt(f, w_index, w_value);
break;
@ -781,7 +782,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
goto unknown;
if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[w_index];
f = cdev->config->interface[intf];
if (!f)
break;
/* lots of interfaces only need altsetting zero... */
@ -808,7 +809,7 @@ unknown:
*/
if ((ctrl->bRequestType & USB_RECIP_MASK)
== USB_RECIP_INTERFACE) {
f = cdev->config->interface[w_index];
f = cdev->config->interface[intf];
if (f && f->setup)
value = f->setup(f, ctrl);
else

View File

@ -297,7 +297,7 @@ void imx_ep_stall(struct imx_ep_struct *imx_ep)
for (i = 0; i < 100; i ++) {
temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
if (!temp & EPSTAT_STALL)
if (!(temp & EPSTAT_STALL))
break;
udelay(20);
}

View File

@ -140,6 +140,7 @@ config USB_OHCI_HCD
tristate "OHCI HCD support"
depends on USB && USB_ARCH_HAS_OHCI
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
select USB_OTG_UTILS if ARCH_OMAP
---help---
The Open Host Controller Interface (OHCI) is a standard for accessing
USB 1.1 host controller hardware. It does more in hardware than Intel's
@ -238,6 +239,23 @@ config USB_UHCI_HCD
To compile this driver as a module, choose M here: the
module will be called uhci-hcd.
config USB_FHCI_HCD
tristate "Freescale QE USB Host Controller support"
depends on USB && OF_GPIO && QE_GPIO && QUICC_ENGINE
select FSL_GTM
select QE_USB
help
This driver enables support for Freescale QE USB Host Controller
(as found on MPC8360 and MPC8323 processors), the driver supports
Full and Low Speed USB.
config FHCI_DEBUG
bool "Freescale QE USB Host Controller debug support"
depends on USB_FHCI_HCD && DEBUG_FS
help
Say "y" to see some FHCI debug information and statistics
throught debugfs.
config USB_U132_HCD
tristate "Elan U132 Adapter Host Controller"
depends on USB && USB_FTDI_ELAN

View File

@ -7,6 +7,11 @@ ifeq ($(CONFIG_USB_DEBUG),y)
endif
isp1760-objs := isp1760-hcd.o isp1760-if.o
fhci-objs := fhci-hcd.o fhci-hub.o fhci-q.o fhci-mem.o \
fhci-tds.o fhci-sched.o
ifeq ($(CONFIG_FHCI_DEBUG),y)
fhci-objs += fhci-dbg.o
endif
obj-$(CONFIG_USB_WHCI_HCD) += whci/
@ -17,6 +22,7 @@ obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_FHCI_HCD) += fhci.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o

View File

@ -230,7 +230,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
pci_read_config_word(pdev, 0x62, &port_wake);
if (port_wake & 0x0001) {
dev_warn(&pdev->dev, "Enabling legacy PCI PM\n");
device_init_wakeup(&pdev->dev, 1);
device_set_wakeup_capable(&pdev->dev, 1);
}
}
@ -432,7 +432,6 @@ static struct pci_driver ehci_pci_driver = {
#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.suspend_late = usb_hcd_pci_suspend_late,
.resume_early = usb_hcd_pci_resume_early,
.resume = usb_hcd_pci_resume,
#endif

139
drivers/usb/host/fhci-dbg.c Normal file
View File

@ -0,0 +1,139 @@
/*
* Freescale QUICC Engine USB Host Controller Driver
*
* Copyright (c) Freescale Semicondutor, Inc. 2006.
* Shlomi Gridish <gridish@freescale.com>
* Jerry Huang <Chang-Ming.Huang@freescale.com>
* Copyright (c) Logic Product Development, Inc. 2007
* Peter Barada <peterb@logicpd.com>
* Copyright (c) MontaVista Software, Inc. 2008.
* Anton Vorontsov <avorontsov@ru.mvista.com>
*
* 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 <linux/kernel.h>
#include <linux/errno.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/usb.h>
#include "../core/hcd.h"
#include "fhci.h"
void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er)
{
int i;
if (usb_er == -1) {
fhci->usb_irq_stat[12]++;
return;
}
for (i = 0; i < 12; ++i) {
if (usb_er & (1 << i))
fhci->usb_irq_stat[i]++;
}
}
static int fhci_dfs_regs_show(struct seq_file *s, void *v)
{
struct fhci_hcd *fhci = s->private;
struct fhci_regs __iomem *regs = fhci->regs;
seq_printf(s,
"mode: 0x%x\n" "addr: 0x%x\n"
"command: 0x%x\n" "ep0: 0x%x\n"
"event: 0x%x\n" "mask: 0x%x\n"
"status: 0x%x\n" "SOF timer: %d\n"
"frame number: %d\n"
"lines status: 0x%x\n",
in_8(&regs->usb_mod), in_8(&regs->usb_addr),
in_8(&regs->usb_comm), in_be16(&regs->usb_ep[0]),
in_be16(&regs->usb_event), in_be16(&regs->usb_mask),
in_8(&regs->usb_status), in_be16(&regs->usb_sof_tmr),
in_be16(&regs->usb_frame_num),
fhci_ioports_check_bus_state(fhci));
return 0;
}
static int fhci_dfs_irq_stat_show(struct seq_file *s, void *v)
{
struct fhci_hcd *fhci = s->private;
int *usb_irq_stat = fhci->usb_irq_stat;
seq_printf(s,
"RXB: %d\n" "TXB: %d\n" "BSY: %d\n"
"SOF: %d\n" "TXE0: %d\n" "TXE1: %d\n"
"TXE2: %d\n" "TXE3: %d\n" "IDLE: %d\n"
"RESET: %d\n" "SFT: %d\n" "MSF: %d\n"
"IDLE_ONLY: %d\n",
usb_irq_stat[0], usb_irq_stat[1], usb_irq_stat[2],
usb_irq_stat[3], usb_irq_stat[4], usb_irq_stat[5],
usb_irq_stat[6], usb_irq_stat[7], usb_irq_stat[8],
usb_irq_stat[9], usb_irq_stat[10], usb_irq_stat[11],
usb_irq_stat[12]);
return 0;
}
static int fhci_dfs_regs_open(struct inode *inode, struct file *file)
{
return single_open(file, fhci_dfs_regs_show, inode->i_private);
}
static int fhci_dfs_irq_stat_open(struct inode *inode, struct file *file)
{
return single_open(file, fhci_dfs_irq_stat_show, inode->i_private);
}
static const struct file_operations fhci_dfs_regs_fops = {
.open = fhci_dfs_regs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations fhci_dfs_irq_stat_fops = {
.open = fhci_dfs_irq_stat_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void fhci_dfs_create(struct fhci_hcd *fhci)
{
struct device *dev = fhci_to_hcd(fhci)->self.controller;
fhci->dfs_root = debugfs_create_dir(dev->bus_id, NULL);
if (!fhci->dfs_root) {
WARN_ON(1);
return;
}
fhci->dfs_regs = debugfs_create_file("regs", S_IFREG | S_IRUGO,
fhci->dfs_root, fhci, &fhci_dfs_regs_fops);
fhci->dfs_irq_stat = debugfs_create_file("irq_stat",
S_IFREG | S_IRUGO, fhci->dfs_root, fhci,
&fhci_dfs_irq_stat_fops);
WARN_ON(!fhci->dfs_regs || !fhci->dfs_irq_stat);
}
void fhci_dfs_destroy(struct fhci_hcd *fhci)
{
if (!fhci->dfs_root)
return;
if (fhci->dfs_irq_stat)
debugfs_remove(fhci->dfs_irq_stat);
if (fhci->dfs_regs)
debugfs_remove(fhci->dfs_regs);
debugfs_remove(fhci->dfs_root);
}

836
drivers/usb/host/fhci-hcd.c Normal file
View File

@ -0,0 +1,836 @@
/*
* Freescale QUICC Engine USB Host Controller Driver
*
* Copyright (c) Freescale Semicondutor, Inc. 2006.
* Shlomi Gridish <gridish@freescale.com>
* Jerry Huang <Chang-Ming.Huang@freescale.com>
* Copyright (c) Logic Product Development, Inc. 2007
* Peter Barada <peterb@logicpd.com>
* Copyright (c) MontaVista Software, Inc. 2008.
* Anton Vorontsov <avorontsov@ru.mvista.com>
*
* 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 <linux/module.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/usb.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <asm/qe.h>
#include <asm/fsl_gtm.h>
#include "../core/hcd.h"
#include "fhci.h"
void fhci_start_sof_timer(struct fhci_hcd *fhci)
{
fhci_dbg(fhci, "-> %s\n", __func__);
/* clear frame_n */
out_be16(&fhci->pram->frame_num, 0);
out_be16(&fhci->regs->usb_sof_tmr, 0);
setbits8(&fhci->regs->usb_mod, USB_MODE_SFTE);
fhci_dbg(fhci, "<- %s\n", __func__);
}
void fhci_stop_sof_timer(struct fhci_hcd *fhci)
{
fhci_dbg(fhci, "-> %s\n", __func__);
clrbits8(&fhci->regs->usb_mod, USB_MODE_SFTE);
gtm_stop_timer16(fhci->timer);
fhci_dbg(fhci, "<- %s\n", __func__);
}
u16 fhci_get_sof_timer_count(struct fhci_usb *usb)
{
return be16_to_cpu(in_be16(&usb->fhci->regs->usb_sof_tmr) / 12);
}
/* initialize the endpoint zero */
static u32 endpoint_zero_init(struct fhci_usb *usb,
enum fhci_mem_alloc data_mem,
u32 ring_len)
{
u32 rc;
rc = fhci_create_ep(usb, data_mem, ring_len);
if (rc)
return rc;
/* inilialize endpoint registers */
fhci_init_ep_registers(usb, usb->ep0, data_mem);
return 0;
}
/* enable the USB interrupts */
void fhci_usb_enable_interrupt(struct fhci_usb *usb)
{
struct fhci_hcd *fhci = usb->fhci;
if (usb->intr_nesting_cnt == 1) {
/* initialize the USB interrupt */
enable_irq(fhci_to_hcd(fhci)->irq);
/* initialize the event register and mask register */
out_be16(&usb->fhci->regs->usb_event, 0xffff);
out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
/* enable the timer interrupts */
enable_irq(fhci->timer->irq);
} else if (usb->intr_nesting_cnt > 1)
fhci_info(fhci, "unbalanced USB interrupts nesting\n");
usb->intr_nesting_cnt--;
}
/* diable the usb interrupt */
void fhci_usb_disable_interrupt(struct fhci_usb *usb)
{
struct fhci_hcd *fhci = usb->fhci;
if (usb->intr_nesting_cnt == 0) {
/* diable the timer interrupt */
disable_irq_nosync(fhci->timer->irq);
/* disable the usb interrupt */
disable_irq_nosync(fhci_to_hcd(fhci)->irq);
out_be16(&usb->fhci->regs->usb_mask, 0);
}
usb->intr_nesting_cnt++;
}
/* enable the USB controller */
static u32 fhci_usb_enable(struct fhci_hcd *fhci)
{
struct fhci_usb *usb = fhci->usb_lld;
out_be16(&usb->fhci->regs->usb_event, 0xffff);
out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
setbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN);
mdelay(100);
return 0;
}
/* disable the USB controller */
static u32 fhci_usb_disable(struct fhci_hcd *fhci)
{
struct fhci_usb *usb = fhci->usb_lld;
fhci_usb_disable_interrupt(usb);
fhci_port_disable(fhci);
/* disable the usb controller */
if (usb->port_status == FHCI_PORT_FULL ||
usb->port_status == FHCI_PORT_LOW)
fhci_device_disconnected_interrupt(fhci);
clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN);
return 0;
}
/* check the bus state by polling the QE bit on the IO ports */
int fhci_ioports_check_bus_state(struct fhci_hcd *fhci)
{
u8 bits = 0;
/* check USBOE,if transmitting,exit */
if (!gpio_get_value(fhci->gpios[GPIO_USBOE]))
return -1;
/* check USBRP */
if (gpio_get_value(fhci->gpios[GPIO_USBRP]))
bits |= 0x2;
/* check USBRN */
if (gpio_get_value(fhci->gpios[GPIO_USBRN]))
bits |= 0x1;
return bits;
}
static void fhci_mem_free(struct fhci_hcd *fhci)
{
struct ed *ed;
struct ed *next_ed;
struct td *td;
struct td *next_td;
list_for_each_entry_safe(ed, next_ed, &fhci->empty_eds, node) {
list_del(&ed->node);
kfree(ed);
}
list_for_each_entry_safe(td, next_td, &fhci->empty_tds, node) {
list_del(&td->node);
kfree(td);
}
kfree(fhci->vroot_hub);
fhci->vroot_hub = NULL;
kfree(fhci->hc_list);
fhci->hc_list = NULL;
}
static int fhci_mem_init(struct fhci_hcd *fhci)
{
int i;
fhci->hc_list = kzalloc(sizeof(*fhci->hc_list), GFP_KERNEL);
if (!fhci->hc_list)
goto err;
INIT_LIST_HEAD(&fhci->hc_list->ctrl_list);
INIT_LIST_HEAD(&fhci->hc_list->bulk_list);
INIT_LIST_HEAD(&fhci->hc_list->iso_list);
INIT_LIST_HEAD(&fhci->hc_list->intr_list);
INIT_LIST_HEAD(&fhci->hc_list->done_list);
fhci->vroot_hub = kzalloc(sizeof(*fhci->vroot_hub), GFP_KERNEL);
if (!fhci->vroot_hub)
goto err;
INIT_LIST_HEAD(&fhci->empty_eds);
INIT_LIST_HEAD(&fhci->empty_tds);
/* initialize work queue to handle done list */
fhci_tasklet.data = (unsigned long)fhci;
fhci->process_done_task = &fhci_tasklet;
for (i = 0; i < MAX_TDS; i++) {
struct td *td;
td = kmalloc(sizeof(*td), GFP_KERNEL);
if (!td)
goto err;
fhci_recycle_empty_td(fhci, td);
}
for (i = 0; i < MAX_EDS; i++) {
struct ed *ed;
ed = kmalloc(sizeof(*ed), GFP_KERNEL);
if (!ed)
goto err;
fhci_recycle_empty_ed(fhci, ed);
}
fhci->active_urbs = 0;
return 0;
err:
fhci_mem_free(fhci);
return -ENOMEM;
}
/* destroy the fhci_usb structure */
static void fhci_usb_free(void *lld)
{
struct fhci_usb *usb = lld;
struct fhci_hcd *fhci = usb->fhci;
if (usb) {
fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);
fhci_ep0_free(usb);
kfree(usb->actual_frame);
kfree(usb);
}
}
/* initialize the USB */
static int fhci_usb_init(struct fhci_hcd *fhci)
{
struct fhci_usb *usb = fhci->usb_lld;
memset_io(usb->fhci->pram, 0, FHCI_PRAM_SIZE);
usb->port_status = FHCI_PORT_DISABLED;
usb->max_frame_usage = FRAME_TIME_USAGE;
usb->sw_transaction_time = SW_FIX_TIME_BETWEEN_TRANSACTION;
usb->actual_frame = kzalloc(sizeof(*usb->actual_frame), GFP_KERNEL);
if (!usb->actual_frame) {
fhci_usb_free(usb);
return -ENOMEM;
}
INIT_LIST_HEAD(&usb->actual_frame->tds_list);
/* initializing registers on chip, clear frame number */
out_be16(&fhci->pram->frame_num, 0);
/* clear rx state */
out_be32(&fhci->pram->rx_state, 0);
/* set mask register */
usb->saved_msk = (USB_E_TXB_MASK |
USB_E_TXE1_MASK |
USB_E_IDLE_MASK |
USB_E_RESET_MASK | USB_E_SFT_MASK | USB_E_MSF_MASK);
out_8(&usb->fhci->regs->usb_mod, USB_MODE_HOST | USB_MODE_EN);
/* clearing the mask register */
out_be16(&usb->fhci->regs->usb_mask, 0);
/* initialing the event register */
out_be16(&usb->fhci->regs->usb_event, 0xffff);
if (endpoint_zero_init(usb, DEFAULT_DATA_MEM, DEFAULT_RING_LEN) != 0) {
fhci_usb_free(usb);
return -EINVAL;
}
return 0;
}
/* initialize the fhci_usb struct and the corresponding data staruct */
static struct fhci_usb *fhci_create_lld(struct fhci_hcd *fhci)
{
struct fhci_usb *usb;
/* allocate memory for SCC data structure */
usb = kzalloc(sizeof(*usb), GFP_KERNEL);
if (!usb) {
fhci_err(fhci, "no memory for SCC data struct\n");
return NULL;
}
usb->fhci = fhci;
usb->hc_list = fhci->hc_list;
usb->vroot_hub = fhci->vroot_hub;
usb->transfer_confirm = fhci_transfer_confirm_callback;
return usb;
}
static int fhci_start(struct usb_hcd *hcd)
{
int ret;
struct fhci_hcd *fhci = hcd_to_fhci(hcd);
ret = fhci_mem_init(fhci);
if (ret) {
fhci_err(fhci, "failed to allocate memory\n");
goto err;
}
fhci->usb_lld = fhci_create_lld(fhci);
if (!fhci->usb_lld) {
fhci_err(fhci, "low level driver config failed\n");
ret = -ENOMEM;
goto err;
}
ret = fhci_usb_init(fhci);
if (ret) {
fhci_err(fhci, "low level driver initialize failed\n");
goto err;
}
spin_lock_init(&fhci->lock);
/* connect the virtual root hub */
fhci->vroot_hub->dev_num = 1; /* this field may be needed to fix */
fhci->vroot_hub->hub.wHubStatus = 0;
fhci->vroot_hub->hub.wHubChange = 0;
fhci->vroot_hub->port.wPortStatus = 0;
fhci->vroot_hub->port.wPortChange = 0;
hcd->state = HC_STATE_RUNNING;
/*
* From here on, khubd concurrently accesses the root
* hub; drivers will be talking to enumerated devices.
* (On restart paths, khubd already knows about the root
* hub and could find work as soon as we wrote FLAG_CF.)
*
* Before this point the HC was idle/ready. After, khubd
* and device drivers may start it running.
*/
fhci_usb_enable(fhci);
return 0;
err:
fhci_mem_free(fhci);
return ret;
}
static void fhci_stop(struct usb_hcd *hcd)
{
struct fhci_hcd *fhci = hcd_to_fhci(hcd);
fhci_usb_disable_interrupt(fhci->usb_lld);
fhci_usb_disable(fhci);
fhci_usb_free(fhci->usb_lld);
fhci->usb_lld = NULL;
fhci_mem_free(fhci);
}
static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
{
struct fhci_hcd *fhci = hcd_to_fhci(hcd);
u32 pipe = urb->pipe;
int ret;
int i;
int size = 0;
struct urb_priv *urb_priv;
unsigned long flags;
switch (usb_pipetype(pipe)) {
case PIPE_CONTROL:
/* 1 td fro setup,1 for ack */
size = 2;
case PIPE_BULK:
/* one td for every 4096 bytes(can be upto 8k) */
size += urb->transfer_buffer_length / 4096;
/* ...add for any remaining bytes... */
if ((urb->transfer_buffer_length % 4096) != 0)
size++;
/* ..and maybe a zero length packet to wrap it up */
if (size == 0)
size++;
else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
&& (urb->transfer_buffer_length
% usb_maxpacket(urb->dev, pipe,
usb_pipeout(pipe))) != 0)
size++;
break;
case PIPE_ISOCHRONOUS:
size = urb->number_of_packets;
if (size <= 0)
return -EINVAL;
for (i = 0; i < urb->number_of_packets; i++) {
urb->iso_frame_desc[i].actual_length = 0;
urb->iso_frame_desc[i].status = (u32) (-EXDEV);
}
break;
case PIPE_INTERRUPT:
size = 1;
}
/* allocate the private part of the URB */
urb_priv = kzalloc(sizeof(*urb_priv), mem_flags);
if (!urb_priv)
return -ENOMEM;
/* allocate the private part of the URB */
urb_priv->tds = kzalloc(size * sizeof(struct td), mem_flags);
if (!urb_priv->tds) {
kfree(urb_priv);
return -ENOMEM;
}
spin_lock_irqsave(&fhci->lock, flags);
ret = usb_hcd_link_urb_to_ep(hcd, urb);
if (ret)
goto err;
/* fill the private part of the URB */
urb_priv->num_of_tds = size;
urb->status = -EINPROGRESS;
urb->actual_length = 0;
urb->error_count = 0;
urb->hcpriv = urb_priv;
fhci_queue_urb(fhci, urb);
err:
if (ret) {
kfree(urb_priv->tds);
kfree(urb_priv);
}
spin_unlock_irqrestore(&fhci->lock, flags);
return ret;
}
/* dequeue FHCI URB */
static int fhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{
struct fhci_hcd *fhci = hcd_to_fhci(hcd);
struct fhci_usb *usb = fhci->usb_lld;
int ret = -EINVAL;
unsigned long flags;
if (!urb || !urb->dev || !urb->dev->bus)
goto out;
spin_lock_irqsave(&fhci->lock, flags);
ret = usb_hcd_check_unlink_urb(hcd, urb, status);
if (ret)
goto out2;
if (usb->port_status != FHCI_PORT_DISABLED) {
struct urb_priv *urb_priv;
/*
* flag the urb's data for deletion in some upcoming
* SF interrupt's delete list processing
*/
urb_priv = urb->hcpriv;
if (!urb_priv || (urb_priv->state == URB_DEL))
goto out2;
urb_priv->state = URB_DEL;
/* already pending? */
urb_priv->ed->state = FHCI_ED_URB_DEL;
} else {
fhci_urb_complete_free(fhci, urb);
}
out2:
spin_unlock_irqrestore(&fhci->lock, flags);
out:
return ret;
}
static void fhci_endpoint_disable(struct usb_hcd *hcd,
struct usb_host_endpoint *ep)
{
struct fhci_hcd *fhci;
struct ed *ed;
unsigned long flags;
fhci = hcd_to_fhci(hcd);
spin_lock_irqsave(&fhci->lock, flags);
ed = ep->hcpriv;
if (ed) {
while (ed->td_head != NULL) {
struct td *td = fhci_remove_td_from_ed(ed);
fhci_urb_complete_free(fhci, td->urb);
}
fhci_recycle_empty_ed(fhci, ed);
ep->hcpriv = NULL;
}
spin_unlock_irqrestore(&fhci->lock, flags);
}
static int fhci_get_frame_number(struct usb_hcd *hcd)
{
struct fhci_hcd *fhci = hcd_to_fhci(hcd);
return get_frame_num(fhci);
}
static const struct hc_driver fhci_driver = {
.description = "fsl,usb-fhci",
.product_desc = "FHCI HOST Controller",
.hcd_priv_size = sizeof(struct fhci_hcd),
/* generic hardware linkage */
.irq = fhci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/* basic lifecycle operation */
.start = fhci_start,
.stop = fhci_stop,
/* managing i/o requests and associated device resources */
.urb_enqueue = fhci_urb_enqueue,
.urb_dequeue = fhci_urb_dequeue,
.endpoint_disable = fhci_endpoint_disable,
/* scheduling support */
.get_frame_number = fhci_get_frame_number,
/* root hub support */
.hub_status_data = fhci_hub_status_data,
.hub_control = fhci_hub_control,
};
static int __devinit of_fhci_probe(struct of_device *ofdev,
const struct of_device_id *ofid)
{
struct device *dev = &ofdev->dev;
struct device_node *node = ofdev->node;
struct usb_hcd *hcd;
struct fhci_hcd *fhci;
struct resource usb_regs;
unsigned long pram_addr;
unsigned int usb_irq;
const char *sprop;
const u32 *iprop;
int size;
int ret;
int i;
int j;
if (usb_disabled())
return -ENODEV;
sprop = of_get_property(node, "mode", NULL);
if (sprop && strcmp(sprop, "host"))
return -ENODEV;
hcd = usb_create_hcd(&fhci_driver, dev, dev->bus_id);
if (!hcd) {
dev_err(dev, "could not create hcd\n");
return -ENOMEM;
}
fhci = hcd_to_fhci(hcd);
hcd->self.controller = dev;
dev_set_drvdata(dev, hcd);
iprop = of_get_property(node, "hub-power-budget", &size);
if (iprop && size == sizeof(*iprop))
hcd->power_budget = *iprop;
/* FHCI registers. */
ret = of_address_to_resource(node, 0, &usb_regs);
if (ret) {
dev_err(dev, "could not get regs\n");
goto err_regs;
}
hcd->regs = ioremap(usb_regs.start, usb_regs.end - usb_regs.start + 1);
if (!hcd->regs) {
dev_err(dev, "could not ioremap regs\n");
ret = -ENOMEM;
goto err_regs;
}
fhci->regs = hcd->regs;
/* Parameter RAM. */
iprop = of_get_property(node, "reg", &size);
if (!iprop || size < sizeof(*iprop) * 4) {
dev_err(dev, "can't get pram offset\n");
ret = -EINVAL;
goto err_pram;
}
pram_addr = cpm_muram_alloc_fixed(iprop[2], FHCI_PRAM_SIZE);
if (IS_ERR_VALUE(pram_addr)) {
dev_err(dev, "failed to allocate usb pram\n");
ret = -ENOMEM;
goto err_pram;
}
fhci->pram = cpm_muram_addr(pram_addr);
/* GPIOs and pins */
for (i = 0; i < NUM_GPIOS; i++) {
int gpio;
enum of_gpio_flags flags;
gpio = of_get_gpio_flags(node, i, &flags);
fhci->gpios[i] = gpio;
fhci->alow_gpios[i] = flags & OF_GPIO_ACTIVE_LOW;
if (!gpio_is_valid(gpio)) {
if (i < GPIO_SPEED) {
dev_err(dev, "incorrect GPIO%d: %d\n",
i, gpio);
goto err_gpios;
} else {
dev_info(dev, "assuming board doesn't have "
"%s gpio\n", i == GPIO_SPEED ?
"speed" : "power");
continue;
}
}
ret = gpio_request(gpio, dev->bus_id);
if (ret) {
dev_err(dev, "failed to request gpio %d", i);
goto err_gpios;
}
if (i >= GPIO_SPEED) {
ret = gpio_direction_output(gpio, 0);
if (ret) {
dev_err(dev, "failed to set gpio %d as "
"an output\n", i);
i++;
goto err_gpios;
}
}
}
for (j = 0; j < NUM_PINS; j++) {
fhci->pins[j] = qe_pin_request(ofdev->node, j);
if (IS_ERR(fhci->pins[j])) {
ret = PTR_ERR(fhci->pins[j]);
dev_err(dev, "can't get pin %d: %d\n", j, ret);
goto err_pins;
}
}
/* Frame limit timer and its interrupt. */
fhci->timer = gtm_get_timer16();
if (IS_ERR(fhci->timer)) {
ret = PTR_ERR(fhci->timer);
dev_err(dev, "failed to request qe timer: %i", ret);
goto err_get_timer;
}
ret = request_irq(fhci->timer->irq, fhci_frame_limit_timer_irq,
IRQF_DISABLED, "qe timer (usb)", hcd);
if (ret) {
dev_err(dev, "failed to request timer irq");
goto err_timer_irq;
}
/* USB Host interrupt. */
usb_irq = irq_of_parse_and_map(node, 0);
if (usb_irq == NO_IRQ) {
dev_err(dev, "could not get usb irq\n");
ret = -EINVAL;
goto err_usb_irq;
}
/* Clocks. */
sprop = of_get_property(node, "fsl,fullspeed-clock", NULL);
if (sprop) {
fhci->fullspeed_clk = qe_clock_source(sprop);
if (fhci->fullspeed_clk == QE_CLK_DUMMY) {
dev_err(dev, "wrong fullspeed-clock\n");
ret = -EINVAL;
goto err_clocks;
}
}
sprop = of_get_property(node, "fsl,lowspeed-clock", NULL);
if (sprop) {
fhci->lowspeed_clk = qe_clock_source(sprop);
if (fhci->lowspeed_clk == QE_CLK_DUMMY) {
dev_err(dev, "wrong lowspeed-clock\n");
ret = -EINVAL;
goto err_clocks;
}
}
if (fhci->fullspeed_clk == QE_CLK_NONE &&
fhci->lowspeed_clk == QE_CLK_NONE) {
dev_err(dev, "no clocks specified\n");
ret = -EINVAL;
goto err_clocks;
}
dev_info(dev, "at 0x%p, irq %d\n", hcd->regs, usb_irq);
fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);
/* Start with full-speed, if possible. */
if (fhci->fullspeed_clk != QE_CLK_NONE) {
fhci_config_transceiver(fhci, FHCI_PORT_FULL);
qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK);
} else {
fhci_config_transceiver(fhci, FHCI_PORT_LOW);
qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3);
}
/* Clear and disable any pending interrupts. */
out_be16(&fhci->regs->usb_event, 0xffff);
out_be16(&fhci->regs->usb_mask, 0);
ret = usb_add_hcd(hcd, usb_irq, IRQF_DISABLED);
if (ret < 0)
goto err_add_hcd;
fhci_dfs_create(fhci);
return 0;
err_add_hcd:
err_clocks:
irq_dispose_mapping(usb_irq);
err_usb_irq:
free_irq(fhci->timer->irq, hcd);
err_timer_irq:
gtm_put_timer16(fhci->timer);
err_get_timer:
err_pins:
while (--j >= 0)
qe_pin_free(fhci->pins[j]);
err_gpios:
while (--i >= 0) {
if (gpio_is_valid(fhci->gpios[i]))
gpio_free(fhci->gpios[i]);
}
cpm_muram_free(pram_addr);
err_pram:
iounmap(hcd->regs);
err_regs:
usb_put_hcd(hcd);
return ret;
}
static int __devexit fhci_remove(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct fhci_hcd *fhci = hcd_to_fhci(hcd);
int i;
int j;
usb_remove_hcd(hcd);
free_irq(fhci->timer->irq, hcd);
gtm_put_timer16(fhci->timer);
cpm_muram_free(cpm_muram_offset(fhci->pram));
for (i = 0; i < NUM_GPIOS; i++) {
if (!gpio_is_valid(fhci->gpios[i]))
continue;
gpio_free(fhci->gpios[i]);
}
for (j = 0; j < NUM_PINS; j++)
qe_pin_free(fhci->pins[j]);
fhci_dfs_destroy(fhci);
usb_put_hcd(hcd);
return 0;
}
static int __devexit of_fhci_remove(struct of_device *ofdev)
{
return fhci_remove(&ofdev->dev);
}
static struct of_device_id of_fhci_match[] = {
{ .compatible = "fsl,mpc8323-qe-usb", },
{},
};
MODULE_DEVICE_TABLE(of, of_fhci_match);
static struct of_platform_driver of_fhci_driver = {
.name = "fsl,usb-fhci",
.match_table = of_fhci_match,
.probe = of_fhci_probe,
.remove = __devexit_p(of_fhci_remove),
};
static int __init fhci_module_init(void)
{
return of_register_platform_driver(&of_fhci_driver);
}
module_init(fhci_module_init);
static void __exit fhci_module_exit(void)
{
of_unregister_platform_driver(&of_fhci_driver);
}
module_exit(fhci_module_exit);
MODULE_DESCRIPTION("USB Freescale Host Controller Interface Driver");
MODULE_AUTHOR("Shlomi Gridish <gridish@freescale.com>, "
"Jerry Huang <Chang-Ming.Huang@freescale.com>, "
"Anton Vorontsov <avorontsov@ru.mvista.com>");
MODULE_LICENSE("GPL");

345
drivers/usb/host/fhci-hub.c Normal file
View File

@ -0,0 +1,345 @@
/*
* Freescale QUICC Engine USB Host Controller Driver
*
* Copyright (c) Freescale Semicondutor, Inc. 2006.
* Shlomi Gridish <gridish@freescale.com>
* Jerry Huang <Chang-Ming.Huang@freescale.com>
* Copyright (c) Logic Product Development, Inc. 2007
* Peter Barada <peterb@logicpd.com>
* Copyright (c) MontaVista Software, Inc. 2008.
* Anton Vorontsov <avorontsov@ru.mvista.com>
*
* 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 <linux/kernel.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/usb.h>
#include <linux/gpio.h>
#include <asm/qe.h>
#include "../core/hcd.h"
#include "fhci.h"
/* virtual root hub specific descriptor */
static u8 root_hub_des[] = {
0x09, /* blength */
0x29, /* bDescriptorType;hub-descriptor */
0x01, /* bNbrPorts */
0x00, /* wHubCharacteristics */
0x00,
0x01, /* bPwrOn2pwrGood;2ms */
0x00, /* bHubContrCurrent;0mA */
0x00, /* DeviceRemoveable */
0xff, /* PortPwrCtrlMask */
};
static void fhci_gpio_set_value(struct fhci_hcd *fhci, int gpio_nr, bool on)
{
int gpio = fhci->gpios[gpio_nr];
bool alow = fhci->alow_gpios[gpio_nr];
if (!gpio_is_valid(gpio))
return;
gpio_set_value(gpio, on ^ alow);
mdelay(5);
}
void fhci_config_transceiver(struct fhci_hcd *fhci,
enum fhci_port_status status)
{
fhci_dbg(fhci, "-> %s: %d\n", __func__, status);
switch (status) {
case FHCI_PORT_POWER_OFF:
fhci_gpio_set_value(fhci, GPIO_POWER, false);
break;
case FHCI_PORT_DISABLED:
case FHCI_PORT_WAITING:
fhci_gpio_set_value(fhci, GPIO_POWER, true);
break;
case FHCI_PORT_LOW:
fhci_gpio_set_value(fhci, GPIO_SPEED, false);
break;
case FHCI_PORT_FULL:
fhci_gpio_set_value(fhci, GPIO_SPEED, true);
break;
default:
WARN_ON(1);
break;
}
fhci_dbg(fhci, "<- %s: %d\n", __func__, status);
}
/* disable the USB port by clearing the EN bit in the USBMOD register */
void fhci_port_disable(struct fhci_hcd *fhci)
{
struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld;
enum fhci_port_status port_status;
fhci_dbg(fhci, "-> %s\n", __func__);
fhci_stop_sof_timer(fhci);
fhci_flush_all_transmissions(usb);
fhci_usb_disable_interrupt((struct fhci_usb *)fhci->usb_lld);
port_status = usb->port_status;
usb->port_status = FHCI_PORT_DISABLED;
/* Enable IDLE since we want to know if something comes along */
usb->saved_msk |= USB_E_IDLE_MASK;
out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
/* check if during the disconnection process attached new device */
if (port_status == FHCI_PORT_WAITING)
fhci_device_connected_interrupt(fhci);
usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_ENABLE;
usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld);
fhci_dbg(fhci, "<- %s\n", __func__);
}
/* enable the USB port by setting the EN bit in the USBMOD register */
void fhci_port_enable(void *lld)
{
struct fhci_usb *usb = (struct fhci_usb *)lld;
struct fhci_hcd *fhci = usb->fhci;
fhci_dbg(fhci, "-> %s\n", __func__);
fhci_config_transceiver(fhci, usb->port_status);
if ((usb->port_status != FHCI_PORT_FULL) &&
(usb->port_status != FHCI_PORT_LOW))
fhci_start_sof_timer(fhci);
usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE;
usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
fhci_dbg(fhci, "<- %s\n", __func__);
}
void fhci_io_port_generate_reset(struct fhci_hcd *fhci)
{
fhci_dbg(fhci, "-> %s\n", __func__);
gpio_direction_output(fhci->gpios[GPIO_USBOE], 0);
gpio_direction_output(fhci->gpios[GPIO_USBTP], 0);
gpio_direction_output(fhci->gpios[GPIO_USBTN], 0);
mdelay(5);
qe_pin_set_dedicated(fhci->pins[PIN_USBOE]);
qe_pin_set_dedicated(fhci->pins[PIN_USBTP]);
qe_pin_set_dedicated(fhci->pins[PIN_USBTN]);
fhci_dbg(fhci, "<- %s\n", __func__);
}
/* generate the RESET condition on the bus */
void fhci_port_reset(void *lld)
{
struct fhci_usb *usb = (struct fhci_usb *)lld;
struct fhci_hcd *fhci = usb->fhci;
u8 mode;
u16 mask;
fhci_dbg(fhci, "-> %s\n", __func__);
fhci_stop_sof_timer(fhci);
/* disable the USB controller */
mode = in_8(&fhci->regs->usb_mod);
out_8(&fhci->regs->usb_mod, mode & (~USB_MODE_EN));
/* disable idle interrupts */
mask = in_be16(&fhci->regs->usb_mask);
out_be16(&fhci->regs->usb_mask, mask & (~USB_E_IDLE_MASK));
fhci_io_port_generate_reset(fhci);
/* enable interrupt on this endpoint */
out_be16(&fhci->regs->usb_mask, mask);
/* enable the USB controller */
mode = in_8(&fhci->regs->usb_mod);
out_8(&fhci->regs->usb_mod, mode | USB_MODE_EN);
fhci_start_sof_timer(fhci);
fhci_dbg(fhci, "<- %s\n", __func__);
}
int fhci_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct fhci_hcd *fhci = hcd_to_fhci(hcd);
int ret = 0;
unsigned long flags;
fhci_dbg(fhci, "-> %s\n", __func__);
spin_lock_irqsave(&fhci->lock, flags);
if (fhci->vroot_hub->port.wPortChange & (USB_PORT_STAT_C_CONNECTION |
USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_SUSPEND |
USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_OVERCURRENT)) {
*buf = 1 << 1;
ret = 1;
fhci_dbg(fhci, "-- %s\n", __func__);
}
spin_unlock_irqrestore(&fhci->lock, flags);
fhci_dbg(fhci, "<- %s\n", __func__);
return ret;
}
int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
struct fhci_hcd *fhci = hcd_to_fhci(hcd);
int retval = 0;
int len = 0;
struct usb_hub_status *hub_status;
struct usb_port_status *port_status;
unsigned long flags;
spin_lock_irqsave(&fhci->lock, flags);
fhci_dbg(fhci, "-> %s\n", __func__);
switch (typeReq) {
case ClearHubFeature:
switch (wValue) {
case C_HUB_LOCAL_POWER:
case C_HUB_OVER_CURRENT:
break;
default:
goto error;
}
break;
case ClearPortFeature:
fhci->vroot_hub->feature &= (1 << wValue);
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
fhci->vroot_hub->port.wPortStatus &=
~USB_PORT_STAT_ENABLE;
fhci_port_disable(fhci);
break;
case USB_PORT_FEAT_C_ENABLE:
fhci->vroot_hub->port.wPortChange &=
~USB_PORT_STAT_C_ENABLE;
break;
case USB_PORT_FEAT_SUSPEND:
fhci->vroot_hub->port.wPortStatus &=
~USB_PORT_STAT_SUSPEND;
fhci_stop_sof_timer(fhci);
break;
case USB_PORT_FEAT_C_SUSPEND:
fhci->vroot_hub->port.wPortChange &=
~USB_PORT_STAT_C_SUSPEND;
break;
case USB_PORT_FEAT_POWER:
fhci->vroot_hub->port.wPortStatus &=
~USB_PORT_STAT_POWER;
fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);
break;
case USB_PORT_FEAT_C_CONNECTION:
fhci->vroot_hub->port.wPortChange &=
~USB_PORT_STAT_C_CONNECTION;
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
fhci->vroot_hub->port.wPortChange &=
~USB_PORT_STAT_C_OVERCURRENT;
break;
case USB_PORT_FEAT_C_RESET:
fhci->vroot_hub->port.wPortChange &=
~USB_PORT_STAT_C_RESET;
break;
default:
goto error;
}
break;
case GetHubDescriptor:
memcpy(buf, root_hub_des, sizeof(root_hub_des));
buf[3] = 0x11; /* per-port power, no ovrcrnt */
len = (buf[0] < wLength) ? buf[0] : wLength;
break;
case GetHubStatus:
hub_status = (struct usb_hub_status *)buf;
hub_status->wHubStatus =
cpu_to_le16(fhci->vroot_hub->hub.wHubStatus);
hub_status->wHubChange =
cpu_to_le16(fhci->vroot_hub->hub.wHubChange);
len = 4;
break;
case GetPortStatus:
port_status = (struct usb_port_status *)buf;
port_status->wPortStatus =
cpu_to_le16(fhci->vroot_hub->port.wPortStatus);
port_status->wPortChange =
cpu_to_le16(fhci->vroot_hub->port.wPortChange);
len = 4;
break;
case SetHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
case C_HUB_LOCAL_POWER:
break;
default:
goto error;
}
break;
case SetPortFeature:
fhci->vroot_hub->feature |= (1 << wValue);
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
fhci->vroot_hub->port.wPortStatus |=
USB_PORT_STAT_ENABLE;
fhci_port_enable(fhci->usb_lld);
break;
case USB_PORT_FEAT_SUSPEND:
fhci->vroot_hub->port.wPortStatus |=
USB_PORT_STAT_SUSPEND;
fhci_stop_sof_timer(fhci);
break;
case USB_PORT_FEAT_RESET:
fhci->vroot_hub->port.wPortStatus |=
USB_PORT_STAT_RESET;
fhci_port_reset(fhci->usb_lld);
fhci->vroot_hub->port.wPortStatus |=
USB_PORT_STAT_ENABLE;
fhci->vroot_hub->port.wPortStatus &=
~USB_PORT_STAT_RESET;
break;
case USB_PORT_FEAT_POWER:
fhci->vroot_hub->port.wPortStatus |=
USB_PORT_STAT_POWER;
fhci_config_transceiver(fhci, FHCI_PORT_WAITING);
break;
default:
goto error;
}
break;
default:
error:
retval = -EPIPE;
}
fhci_dbg(fhci, "<- %s\n", __func__);
spin_unlock_irqrestore(&fhci->lock, flags);
return retval;
}

113
drivers/usb/host/fhci-mem.c Normal file
View File

@ -0,0 +1,113 @@
/*
* Freescale QUICC Engine USB Host Controller Driver
*
* Copyright (c) Freescale Semicondutor, Inc. 2006.
* Shlomi Gridish <gridish@freescale.com>
* Jerry Huang <Chang-Ming.Huang@freescale.com>
* Copyright (c) Logic Product Development, Inc. 2007
* Peter Barada <peterb@logicpd.com>
* Copyright (c) MontaVista Software, Inc. 2008.
* Anton Vorontsov <avorontsov@ru.mvista.com>
*
* 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 <linux/kernel.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/usb.h>
#include "../core/hcd.h"
#include "fhci.h"
static void init_td(struct td *td)
{
memset(td, 0, sizeof(*td));
INIT_LIST_HEAD(&td->node);
INIT_LIST_HEAD(&td->frame_lh);
}
static void init_ed(struct ed *ed)
{
memset(ed, 0, sizeof(*ed));
INIT_LIST_HEAD(&ed->td_list);
INIT_LIST_HEAD(&ed->node);
}
static struct td *get_empty_td(struct fhci_hcd *fhci)
{
struct td *td;
if (!list_empty(&fhci->empty_tds)) {
td = list_entry(fhci->empty_tds.next, struct td, node);
list_del(fhci->empty_tds.next);
} else {
td = kmalloc(sizeof(*td), GFP_ATOMIC);
if (!td)
fhci_err(fhci, "No memory to allocate to TD\n");
else
init_td(td);
}
return td;
}
void fhci_recycle_empty_td(struct fhci_hcd *fhci, struct td *td)
{
init_td(td);
list_add(&td->node, &fhci->empty_tds);
}
struct ed *fhci_get_empty_ed(struct fhci_hcd *fhci)
{
struct ed *ed;
if (!list_empty(&fhci->empty_eds)) {
ed = list_entry(fhci->empty_eds.next, struct ed, node);
list_del(fhci->empty_eds.next);
} else {
ed = kmalloc(sizeof(*ed), GFP_ATOMIC);
if (!ed)
fhci_err(fhci, "No memory to allocate to ED\n");
else
init_ed(ed);
}
return ed;
}
void fhci_recycle_empty_ed(struct fhci_hcd *fhci, struct ed *ed)
{
init_ed(ed);
list_add(&ed->node, &fhci->empty_eds);
}
struct td *fhci_td_fill(struct fhci_hcd *fhci, struct urb *urb,
struct urb_priv *urb_priv, struct ed *ed, u16 index,
enum fhci_ta_type type, int toggle, u8 *data, u32 len,
u16 interval, u16 start_frame, bool ioc)
{
struct td *td = get_empty_td(fhci);
if (!td)
return NULL;
td->urb = urb;
td->ed = ed;
td->type = type;
td->toggle = toggle;
td->data = data;
td->len = len;
td->iso_index = index;
td->interval = interval;
td->start_frame = start_frame;
td->ioc = ioc;
td->status = USB_TD_OK;
urb_priv->tds[index] = td;
return td;
}

284
drivers/usb/host/fhci-q.c Normal file
View File

@ -0,0 +1,284 @@
/*
* Freescale QUICC Engine USB Host Controller Driver
*
* Copyright (c) Freescale Semicondutor, Inc. 2006.
* Shlomi Gridish <gridish@freescale.com>
* Jerry Huang <Chang-Ming.Huang@freescale.com>
* Copyright (c) Logic Product Development, Inc. 2007
* Peter Barada <peterb@logicpd.com>
* Copyright (c) MontaVista Software, Inc. 2008.
* Anton Vorontsov <avorontsov@ru.mvista.com>
*
* 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 <linux/kernel.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/usb.h>
#include "../core/hcd.h"
#include "fhci.h"
/* maps the hardware error code to the USB error code */
static int status_to_error(u32 status)
{
if (status == USB_TD_OK)
return 0;
else if (status & USB_TD_RX_ER_CRC)
return -EILSEQ;
else if (status & USB_TD_RX_ER_NONOCT)
return -EPROTO;
else if (status & USB_TD_RX_ER_OVERUN)
return -ECOMM;
else if (status & USB_TD_RX_ER_BITSTUFF)
return -EPROTO;
else if (status & USB_TD_RX_ER_PID)
return -EILSEQ;
else if (status & (USB_TD_TX_ER_NAK | USB_TD_TX_ER_TIMEOUT))
return -ETIMEDOUT;
else if (status & USB_TD_TX_ER_STALL)
return -EPIPE;
else if (status & USB_TD_TX_ER_UNDERUN)
return -ENOSR;
else if (status & USB_TD_RX_DATA_UNDERUN)
return -EREMOTEIO;
else if (status & USB_TD_RX_DATA_OVERUN)
return -EOVERFLOW;
else
return -EINVAL;
}
void fhci_add_td_to_frame(struct fhci_time_frame *frame, struct td *td)
{
list_add_tail(&td->frame_lh, &frame->tds_list);
}
void fhci_add_tds_to_ed(struct ed *ed, struct td **td_list, int number)
{
int i;
for (i = 0; i < number; i++) {
struct td *td = td_list[i];
list_add_tail(&td->node, &ed->td_list);
}
if (ed->td_head == NULL)
ed->td_head = td_list[0];
}
static struct td *peek_td_from_ed(struct ed *ed)
{
struct td *td;
if (!list_empty(&ed->td_list))
td = list_entry(ed->td_list.next, struct td, node);
else
td = NULL;
return td;
}
struct td *fhci_remove_td_from_frame(struct fhci_time_frame *frame)
{
struct td *td;
if (!list_empty(&frame->tds_list)) {
td = list_entry(frame->tds_list.next, struct td, frame_lh);
list_del_init(frame->tds_list.next);
} else
td = NULL;
return td;
}
struct td *fhci_peek_td_from_frame(struct fhci_time_frame *frame)
{
struct td *td;
if (!list_empty(&frame->tds_list))
td = list_entry(frame->tds_list.next, struct td, frame_lh);
else
td = NULL;
return td;
}
struct td *fhci_remove_td_from_ed(struct ed *ed)
{
struct td *td;
if (!list_empty(&ed->td_list)) {
td = list_entry(ed->td_list.next, struct td, node);
list_del_init(ed->td_list.next);
/* if this TD was the ED's head, find next TD */
if (!list_empty(&ed->td_list))
ed->td_head = list_entry(ed->td_list.next, struct td,
node);
else
ed->td_head = NULL;
} else
td = NULL;
return td;
}
struct td *fhci_remove_td_from_done_list(struct fhci_controller_list *p_list)
{
struct td *td;
if (!list_empty(&p_list->done_list)) {
td = list_entry(p_list->done_list.next, struct td, node);
list_del_init(p_list->done_list.next);
} else
td = NULL;
return td;
}
void fhci_move_td_from_ed_to_done_list(struct fhci_usb *usb, struct ed *ed)
{
struct td *td;
td = ed->td_head;
list_del_init(&td->node);
/* If this TD was the ED's head,find next TD */
if (!list_empty(&ed->td_list))
ed->td_head = list_entry(ed->td_list.next, struct td, node);
else {
ed->td_head = NULL;
ed->state = FHCI_ED_SKIP;
}
ed->toggle_carry = td->toggle;
list_add_tail(&td->node, &usb->hc_list->done_list);
if (td->ioc)
usb->transfer_confirm(usb->fhci);
}
/* free done FHCI URB resource such as ED and TD */
static void free_urb_priv(struct fhci_hcd *fhci, struct urb *urb)
{
int i;
struct urb_priv *urb_priv = urb->hcpriv;
struct ed *ed = urb_priv->ed;
for (i = 0; i < urb_priv->num_of_tds; i++) {
list_del_init(&urb_priv->tds[i]->node);
fhci_recycle_empty_td(fhci, urb_priv->tds[i]);
}
/* if this TD was the ED's head,find the next TD */
if (!list_empty(&ed->td_list))
ed->td_head = list_entry(ed->td_list.next, struct td, node);
else
ed->td_head = NULL;
kfree(urb_priv->tds);
kfree(urb_priv);
urb->hcpriv = NULL;
/* if this TD was the ED's head,find next TD */
if (ed->td_head == NULL)
list_del_init(&ed->node);
fhci->active_urbs--;
}
/* this routine called to complete and free done URB */
void fhci_urb_complete_free(struct fhci_hcd *fhci, struct urb *urb)
{
free_urb_priv(fhci, urb);
if (urb->status == -EINPROGRESS) {
if (urb->actual_length != urb->transfer_buffer_length &&
urb->transfer_flags & URB_SHORT_NOT_OK)
urb->status = -EREMOTEIO;
else
urb->status = 0;
}
usb_hcd_unlink_urb_from_ep(fhci_to_hcd(fhci), urb);
spin_unlock(&fhci->lock);
usb_hcd_giveback_urb(fhci_to_hcd(fhci), urb, urb->status);
spin_lock(&fhci->lock);
}
/*
* caculate transfer length/stats and update the urb
* Precondition: irqsafe(only for urb-?status locking)
*/
void fhci_done_td(struct urb *urb, struct td *td)
{
struct ed *ed = td->ed;
u32 cc = td->status;
/* ISO...drivers see per-TD length/status */
if (ed->mode == FHCI_TF_ISO) {
u32 len;
if (!(urb->transfer_flags & URB_SHORT_NOT_OK &&
cc == USB_TD_RX_DATA_UNDERUN))
cc = USB_TD_OK;
if (usb_pipeout(urb->pipe))
len = urb->iso_frame_desc[td->iso_index].length;
else
len = td->actual_len;
urb->actual_length += len;
urb->iso_frame_desc[td->iso_index].actual_length = len;
urb->iso_frame_desc[td->iso_index].status =
status_to_error(cc);
}
/* BULK,INT,CONTROL... drivers see aggregate length/status,
* except that "setup" bytes aren't counted and "short" transfers
* might not be reported as errors.
*/
else {
if (td->error_cnt >= 3)
urb->error_count = 3;
/* control endpoint only have soft stalls */
/* update packet status if needed(short may be ok) */
if (!(urb->transfer_flags & URB_SHORT_NOT_OK) &&
cc == USB_TD_RX_DATA_UNDERUN) {
ed->state = FHCI_ED_OPER;
cc = USB_TD_OK;
}
if (cc != USB_TD_OK) {
if (urb->status == -EINPROGRESS)
urb->status = status_to_error(cc);
}
/* count all non-empty packets except control SETUP packet */
if (td->type != FHCI_TA_SETUP || td->iso_index != 0)
urb->actual_length += td->actual_len;
}
}
/* there are some pedning request to unlink */
void fhci_del_ed_list(struct fhci_hcd *fhci, struct ed *ed)
{
struct td *td = peek_td_from_ed(ed);
struct urb *urb = td->urb;
struct urb_priv *urb_priv = urb->hcpriv;
if (urb_priv->state == URB_DEL) {
td = fhci_remove_td_from_ed(ed);
/* HC may have partly processed this TD */
if (td->status != USB_TD_INPROGRESS)
fhci_done_td(urb, td);
/* URB is done;clean up */
if (++(urb_priv->tds_cnt) == urb_priv->num_of_tds)
fhci_urb_complete_free(fhci, urb);
}
}

View File

@ -0,0 +1,888 @@
/*
* Freescale QUICC Engine USB Host Controller Driver
*
* Copyright (c) Freescale Semicondutor, Inc. 2006.
* Shlomi Gridish <gridish@freescale.com>
* Jerry Huang <Chang-Ming.Huang@freescale.com>
* Copyright (c) Logic Product Development, Inc. 2007
* Peter Barada <peterb@logicpd.com>
* Copyright (c) MontaVista Software, Inc. 2008.
* Anton Vorontsov <avorontsov@ru.mvista.com>
*
* 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 <linux/kernel.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/usb.h>
#include <asm/qe.h>
#include <asm/fsl_gtm.h>
#include "../core/hcd.h"
#include "fhci.h"
static void recycle_frame(struct fhci_usb *usb, struct packet *pkt)
{
pkt->data = NULL;
pkt->len = 0;
pkt->status = USB_TD_OK;
pkt->info = 0;
pkt->priv_data = NULL;
cq_put(usb->ep0->empty_frame_Q, pkt);
}
/* confirm submitted packet */
void fhci_transaction_confirm(struct fhci_usb *usb, struct packet *pkt)
{
struct td *td;
struct packet *td_pkt;
struct ed *ed;
u32 trans_len;
bool td_done = false;
td = fhci_remove_td_from_frame(usb->actual_frame);
td_pkt = td->pkt;
trans_len = pkt->len;
td->status = pkt->status;
if (td->type == FHCI_TA_IN && td_pkt->info & PKT_DUMMY_PACKET) {
if ((td->data + td->actual_len) && trans_len)
memcpy(td->data + td->actual_len, pkt->data,
trans_len);
cq_put(usb->ep0->dummy_packets_Q, pkt->data);
}
recycle_frame(usb, pkt);
ed = td->ed;
if (ed->mode == FHCI_TF_ISO) {
if (ed->td_list.next->next != &ed->td_list) {
struct td *td_next =
list_entry(ed->td_list.next->next, struct td,
node);
td_next->start_frame = usb->actual_frame->frame_num;
}
td->actual_len = trans_len;
td_done = true;
} else if ((td->status & USB_TD_ERROR) &&
!(td->status & USB_TD_TX_ER_NAK)) {
/*
* There was an error on the transaction (but not NAK).
* If it is fatal error (data underrun, stall, bad pid or 3
* errors exceeded), mark this TD as done.
*/
if ((td->status & USB_TD_RX_DATA_UNDERUN) ||
(td->status & USB_TD_TX_ER_STALL) ||
(td->status & USB_TD_RX_ER_PID) ||
(++td->error_cnt >= 3)) {
ed->state = FHCI_ED_HALTED;
td_done = true;
if (td->status & USB_TD_RX_DATA_UNDERUN) {
fhci_dbg(usb->fhci, "td err fu\n");
td->toggle = !td->toggle;
td->actual_len += trans_len;
} else {
fhci_dbg(usb->fhci, "td err f!u\n");
}
} else {
fhci_dbg(usb->fhci, "td err !f\n");
/* it is not a fatal error -retry this transaction */
td->nak_cnt = 0;
td->error_cnt++;
td->status = USB_TD_OK;
}
} else if (td->status & USB_TD_TX_ER_NAK) {
/* there was a NAK response */
fhci_vdbg(usb->fhci, "td nack\n");
td->nak_cnt++;
td->error_cnt = 0;
td->status = USB_TD_OK;
} else {
/* there was no error on transaction */
td->error_cnt = 0;
td->nak_cnt = 0;
td->toggle = !td->toggle;
td->actual_len += trans_len;
if (td->len == td->actual_len)
td_done = true;
}
if (td_done)
fhci_move_td_from_ed_to_done_list(usb, ed);
}
/*
* Flush all transmitted packets from BDs
* This routine is called when disabling the USB port to flush all
* transmissions that are allready scheduled in the BDs
*/
void fhci_flush_all_transmissions(struct fhci_usb *usb)
{
u8 mode;
struct td *td;
mode = in_8(&usb->fhci->regs->usb_mod);
clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN);
fhci_flush_bds(usb);
while ((td = fhci_peek_td_from_frame(usb->actual_frame)) != NULL) {
struct packet *pkt = td->pkt;
pkt->status = USB_TD_TX_ER_TIMEOUT;
fhci_transaction_confirm(usb, pkt);
}
usb->actual_frame->frame_status = FRAME_END_TRANSMISSION;
/* reset the event register */
out_be16(&usb->fhci->regs->usb_event, 0xffff);
/* enable the USB controller */
out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN);
}
/*
* This function forms the packet and transmit the packet. This function
* will handle all endpoint type:ISO,interrupt,control and bulk
*/
static int add_packet(struct fhci_usb *usb, struct ed *ed, struct td *td)
{
u32 fw_transaction_time, len = 0;
struct packet *pkt;
u8 *data = NULL;
/* calcalate data address,len and toggle and then add the transaction */
if (td->toggle == USB_TD_TOGGLE_CARRY)
td->toggle = ed->toggle_carry;
switch (ed->mode) {
case FHCI_TF_ISO:
len = td->len;
if (td->type != FHCI_TA_IN)
data = td->data;
break;
case FHCI_TF_CTRL:
case FHCI_TF_BULK:
len = min(td->len - td->actual_len, ed->max_pkt_size);
if (!((td->type == FHCI_TA_IN) &&
((len + td->actual_len) == td->len)))
data = td->data + td->actual_len;
break;
case FHCI_TF_INTR:
len = min(td->len, ed->max_pkt_size);
if (!((td->type == FHCI_TA_IN) &&
((td->len + CRC_SIZE) >= ed->max_pkt_size)))
data = td->data;
break;
default:
break;
}
if (usb->port_status == FHCI_PORT_FULL)
fw_transaction_time = (((len + PROTOCOL_OVERHEAD) * 11) >> 4);
else
fw_transaction_time = ((len + PROTOCOL_OVERHEAD) * 6);
/* check if there's enough space in this frame to submit this TD */
if (usb->actual_frame->total_bytes + len + PROTOCOL_OVERHEAD >=
usb->max_bytes_per_frame) {
fhci_vdbg(usb->fhci, "not enough space in this frame: "
"%d %d %d\n", usb->actual_frame->total_bytes, len,
usb->max_bytes_per_frame);
return -1;
}
/* check if there's enough time in this frame to submit this TD */
if (usb->actual_frame->frame_status != FRAME_IS_PREPARED &&
(usb->actual_frame->frame_status & FRAME_END_TRANSMISSION ||
(fw_transaction_time + usb->sw_transaction_time >=
1000 - fhci_get_sof_timer_count(usb)))) {
fhci_dbg(usb->fhci, "not enough time in this frame\n");
return -1;
}
/* update frame object fields before transmitting */
pkt = cq_get(usb->ep0->empty_frame_Q);
if (!pkt) {
fhci_dbg(usb->fhci, "there is no empty frame\n");
return -1;
}
td->pkt = pkt;
pkt->info = 0;
if (data == NULL) {
data = cq_get(usb->ep0->dummy_packets_Q);
BUG_ON(!data);
pkt->info = PKT_DUMMY_PACKET;
}
pkt->data = data;
pkt->len = len;
pkt->status = USB_TD_OK;
/* update TD status field before transmitting */
td->status = USB_TD_INPROGRESS;
/* update actual frame time object with the actual transmission */
usb->actual_frame->total_bytes += (len + PROTOCOL_OVERHEAD);
fhci_add_td_to_frame(usb->actual_frame, td);
if (usb->port_status != FHCI_PORT_FULL &&
usb->port_status != FHCI_PORT_LOW) {
pkt->status = USB_TD_TX_ER_TIMEOUT;
pkt->len = 0;
fhci_transaction_confirm(usb, pkt);
} else if (fhci_host_transaction(usb, pkt, td->type, ed->dev_addr,
ed->ep_addr, ed->mode, ed->speed, td->toggle)) {
/* remove TD from actual frame */
list_del_init(&td->frame_lh);
td->status = USB_TD_OK;
if (pkt->info & PKT_DUMMY_PACKET)
cq_put(usb->ep0->dummy_packets_Q, pkt->data);
recycle_frame(usb, pkt);
usb->actual_frame->total_bytes -= (len + PROTOCOL_OVERHEAD);
fhci_err(usb->fhci, "host transaction failed\n");
return -1;
}
return len;
}
static void move_head_to_tail(struct list_head *list)
{
struct list_head *node = list->next;
if (!list_empty(list)) {
list_del(node);
list_add_tail(node, list);
}
}
/*
* This function goes through the endpoint list and schedules the
* transactions within this list
*/
static int scan_ed_list(struct fhci_usb *usb,
struct list_head *list, enum fhci_tf_mode list_type)
{
static const int frame_part[4] = {
[FHCI_TF_CTRL] = MAX_BYTES_PER_FRAME,
[FHCI_TF_ISO] = (MAX_BYTES_PER_FRAME *
MAX_PERIODIC_FRAME_USAGE) / 100,
[FHCI_TF_BULK] = MAX_BYTES_PER_FRAME,
[FHCI_TF_INTR] = (MAX_BYTES_PER_FRAME *
MAX_PERIODIC_FRAME_USAGE) / 100
};
struct ed *ed;
struct td *td;
int ans = 1;
u32 save_transaction_time = usb->sw_transaction_time;
list_for_each_entry(ed, list, node) {
td = ed->td_head;
if (!td || (td && td->status == USB_TD_INPROGRESS))
continue;
if (ed->state != FHCI_ED_OPER) {
if (ed->state == FHCI_ED_URB_DEL) {
td->status = USB_TD_OK;
fhci_move_td_from_ed_to_done_list(usb, ed);
ed->state = FHCI_ED_SKIP;
}
continue;
}
/*
* if it isn't interrupt pipe or it is not iso pipe and the
* interval time passed
*/
if ((list_type == FHCI_TF_INTR || list_type == FHCI_TF_ISO) &&
(((usb->actual_frame->frame_num -
td->start_frame) & 0x7ff) < td->interval))
continue;
if (add_packet(usb, ed, td) < 0)
continue;
/* update time stamps in the TD */
td->start_frame = usb->actual_frame->frame_num;
usb->sw_transaction_time += save_transaction_time;
if (usb->actual_frame->total_bytes >=
usb->max_bytes_per_frame) {
usb->actual_frame->frame_status =
FRAME_DATA_END_TRANSMISSION;
fhci_push_dummy_bd(usb->ep0);
ans = 0;
break;
}
if (usb->actual_frame->total_bytes >= frame_part[list_type])
break;
}
/* be fair to each ED(move list head around) */
move_head_to_tail(list);
usb->sw_transaction_time = save_transaction_time;
return ans;
}
static u32 rotate_frames(struct fhci_usb *usb)
{
struct fhci_hcd *fhci = usb->fhci;
if (!list_empty(&usb->actual_frame->tds_list)) {
if ((((in_be16(&fhci->pram->frame_num) & 0x07ff) -
usb->actual_frame->frame_num) & 0x7ff) > 5)
fhci_flush_actual_frame(usb);
else
return -EINVAL;
}
usb->actual_frame->frame_status = FRAME_IS_PREPARED;
usb->actual_frame->frame_num = in_be16(&fhci->pram->frame_num) & 0x7ff;
usb->actual_frame->total_bytes = 0;
return 0;
}
/*
* This function schedule the USB transaction and will process the
* endpoint in the following order: iso, interrupt, control and bulk.
*/
void fhci_schedule_transactions(struct fhci_usb *usb)
{
int left = 1;
if (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)
if (rotate_frames(usb) != 0)
return;
if (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)
return;
if (usb->actual_frame->total_bytes == 0) {
/*
* schedule the next available ISO transfer
*or next stage of the ISO transfer
*/
scan_ed_list(usb, &usb->hc_list->iso_list, FHCI_TF_ISO);
/*
* schedule the next available interrupt transfer or
* the next stage of the interrupt transfer
*/
scan_ed_list(usb, &usb->hc_list->intr_list, FHCI_TF_INTR);
/*
* schedule the next available control transfer
* or the next stage of the control transfer
*/
left = scan_ed_list(usb, &usb->hc_list->ctrl_list,
FHCI_TF_CTRL);
}
/*
* schedule the next available bulk transfer or the next stage of the
* bulk transfer
*/
if (left > 0)
scan_ed_list(usb, &usb->hc_list->bulk_list, FHCI_TF_BULK);
}
/* Handles SOF interrupt */
static void sof_interrupt(struct fhci_hcd *fhci)
{
struct fhci_usb *usb = fhci->usb_lld;
if ((usb->port_status == FHCI_PORT_DISABLED) &&
(usb->vroot_hub->port.wPortStatus & USB_PORT_STAT_CONNECTION) &&
!(usb->vroot_hub->port.wPortChange & USB_PORT_STAT_C_CONNECTION)) {
if (usb->vroot_hub->port.wPortStatus & USB_PORT_STAT_LOW_SPEED)
usb->port_status = FHCI_PORT_LOW;
else
usb->port_status = FHCI_PORT_FULL;
/* Disable IDLE */
usb->saved_msk &= ~USB_E_IDLE_MASK;
out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
}
gtm_set_exact_timer16(fhci->timer, usb->max_frame_usage, false);
fhci_host_transmit_actual_frame(usb);
usb->actual_frame->frame_status = FRAME_IS_TRANSMITTED;
fhci_schedule_transactions(usb);
}
/* Handles device disconnected interrupt on port */
void fhci_device_disconnected_interrupt(struct fhci_hcd *fhci)
{
struct fhci_usb *usb = fhci->usb_lld;
fhci_dbg(fhci, "-> %s\n", __func__);
fhci_usb_disable_interrupt(usb);
clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS);
usb->port_status = FHCI_PORT_DISABLED;
fhci_stop_sof_timer(fhci);
/* Enable IDLE since we want to know if something comes along */
usb->saved_msk |= USB_E_IDLE_MASK;
out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_CONNECTION;
usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_CONNECTION;
usb->max_bytes_per_frame = 0;
fhci_usb_enable_interrupt(usb);
fhci_dbg(fhci, "<- %s\n", __func__);
}
/* detect a new device connected on the USB port */
void fhci_device_connected_interrupt(struct fhci_hcd *fhci)
{
struct fhci_usb *usb = fhci->usb_lld;
int state;
int ret;
fhci_dbg(fhci, "-> %s\n", __func__);
fhci_usb_disable_interrupt(usb);
state = fhci_ioports_check_bus_state(fhci);
/* low-speed device was connected to the USB port */
if (state == 1) {
ret = qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3);
if (ret) {
fhci_warn(fhci, "Low-Speed device is not supported, "
"try use BRGx\n");
goto out;
}
usb->port_status = FHCI_PORT_LOW;
setbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS);
usb->vroot_hub->port.wPortStatus |=
(USB_PORT_STAT_LOW_SPEED |
USB_PORT_STAT_CONNECTION);
usb->vroot_hub->port.wPortChange |=
USB_PORT_STAT_C_CONNECTION;
usb->max_bytes_per_frame =
(MAX_BYTES_PER_FRAME >> 3) - 7;
fhci_port_enable(usb);
} else if (state == 2) {
ret = qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK);
if (ret) {
fhci_warn(fhci, "Full-Speed device is not supported, "
"try use CLKx\n");
goto out;
}
usb->port_status = FHCI_PORT_FULL;
clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS);
usb->vroot_hub->port.wPortStatus &=
~USB_PORT_STAT_LOW_SPEED;
usb->vroot_hub->port.wPortStatus |=
USB_PORT_STAT_CONNECTION;
usb->vroot_hub->port.wPortChange |=
USB_PORT_STAT_C_CONNECTION;
usb->max_bytes_per_frame = (MAX_BYTES_PER_FRAME - 15);
fhci_port_enable(usb);
}
out:
fhci_usb_enable_interrupt(usb);
fhci_dbg(fhci, "<- %s\n", __func__);
}
irqreturn_t fhci_frame_limit_timer_irq(int irq, void *_hcd)
{
struct usb_hcd *hcd = _hcd;
struct fhci_hcd *fhci = hcd_to_fhci(hcd);
struct fhci_usb *usb = fhci->usb_lld;
spin_lock(&fhci->lock);
gtm_set_exact_timer16(fhci->timer, 1000, false);
if (usb->actual_frame->frame_status == FRAME_IS_TRANSMITTED) {
usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION;
fhci_push_dummy_bd(usb->ep0);
}
fhci_schedule_transactions(usb);
spin_unlock(&fhci->lock);
return IRQ_HANDLED;
}
/* Cancel transmission on the USB endpoint */
static void abort_transmission(struct fhci_usb *usb)
{
fhci_dbg(usb->fhci, "-> %s\n", __func__);
/* issue stop Tx command */
qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0);
/* flush Tx FIFOs */
out_8(&usb->fhci->regs->usb_comm, USB_CMD_FLUSH_FIFO | EP_ZERO);
udelay(1000);
/* reset Tx BDs */
fhci_flush_bds(usb);
/* issue restart Tx command */
qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0);
fhci_dbg(usb->fhci, "<- %s\n", __func__);
}
irqreturn_t fhci_irq(struct usb_hcd *hcd)
{
struct fhci_hcd *fhci = hcd_to_fhci(hcd);
struct fhci_usb *usb;
u16 usb_er = 0;
unsigned long flags;
spin_lock_irqsave(&fhci->lock, flags);
usb = fhci->usb_lld;
usb_er |= in_be16(&usb->fhci->regs->usb_event) &
in_be16(&usb->fhci->regs->usb_mask);
/* clear event bits for next time */
out_be16(&usb->fhci->regs->usb_event, usb_er);
fhci_dbg_isr(fhci, usb_er);
if (usb_er & USB_E_RESET_MASK) {
if ((usb->port_status == FHCI_PORT_FULL) ||
(usb->port_status == FHCI_PORT_LOW)) {
fhci_device_disconnected_interrupt(fhci);
usb_er &= ~USB_E_IDLE_MASK;
} else if (usb->port_status == FHCI_PORT_WAITING) {
usb->port_status = FHCI_PORT_DISCONNECTING;
/* Turn on IDLE since we want to disconnect */
usb->saved_msk |= USB_E_IDLE_MASK;
out_be16(&usb->fhci->regs->usb_event,
usb->saved_msk);
} else if (usb->port_status == FHCI_PORT_DISABLED) {
if (fhci_ioports_check_bus_state(fhci) == 1 &&
usb->port_status != FHCI_PORT_LOW &&
usb->port_status != FHCI_PORT_FULL)
fhci_device_connected_interrupt(fhci);
}
usb_er &= ~USB_E_RESET_MASK;
}
if (usb_er & USB_E_MSF_MASK) {
abort_transmission(fhci->usb_lld);
usb_er &= ~USB_E_MSF_MASK;
}
if (usb_er & (USB_E_SOF_MASK | USB_E_SFT_MASK)) {
sof_interrupt(fhci);
usb_er &= ~(USB_E_SOF_MASK | USB_E_SFT_MASK);
}
if (usb_er & USB_E_TXB_MASK) {
fhci_tx_conf_interrupt(fhci->usb_lld);
usb_er &= ~USB_E_TXB_MASK;
}
if (usb_er & USB_E_TXE1_MASK) {
fhci_tx_conf_interrupt(fhci->usb_lld);
usb_er &= ~USB_E_TXE1_MASK;
}
if (usb_er & USB_E_IDLE_MASK) {
if (usb->port_status == FHCI_PORT_DISABLED &&
usb->port_status != FHCI_PORT_LOW &&
usb->port_status != FHCI_PORT_FULL) {
usb_er &= ~USB_E_RESET_MASK;
fhci_device_connected_interrupt(fhci);
} else if (usb->port_status ==
FHCI_PORT_DISCONNECTING) {
/* XXX usb->port_status = FHCI_PORT_WAITING; */
/* Disable IDLE */
usb->saved_msk &= ~USB_E_IDLE_MASK;
out_be16(&usb->fhci->regs->usb_mask,
usb->saved_msk);
} else {
fhci_dbg_isr(fhci, -1);
}
usb_er &= ~USB_E_IDLE_MASK;
}
spin_unlock_irqrestore(&fhci->lock, flags);
return IRQ_HANDLED;
}
/*
* Process normal completions(error or sucess) and clean the schedule.
*
* This is the main path for handing urbs back to drivers. The only other patth
* is process_del_list(),which unlinks URBs by scanning EDs,instead of scanning
* the (re-reversed) done list as this does.
*/
static void process_done_list(unsigned long data)
{
struct urb *urb;
struct ed *ed;
struct td *td;
struct urb_priv *urb_priv;
struct fhci_hcd *fhci = (struct fhci_hcd *)data;
disable_irq(fhci->timer->irq);
disable_irq(fhci_to_hcd(fhci)->irq);
spin_lock(&fhci->lock);
td = fhci_remove_td_from_done_list(fhci->hc_list);
while (td != NULL) {
urb = td->urb;
urb_priv = urb->hcpriv;
ed = td->ed;
/* update URB's length and status from TD */
fhci_done_td(urb, td);
urb_priv->tds_cnt++;
/*
* if all this urb's TDs are done, call complete()
* Interrupt transfers are the onley special case:
* they are reissued,until "deleted" by usb_unlink_urb
* (real work done in a SOF intr, by process_del_list)
*/
if (urb_priv->tds_cnt == urb_priv->num_of_tds) {
fhci_urb_complete_free(fhci, urb);
} else if (urb_priv->state == URB_DEL &&
ed->state == FHCI_ED_SKIP) {
fhci_del_ed_list(fhci, ed);
ed->state = FHCI_ED_OPER;
} else if (ed->state == FHCI_ED_HALTED) {
urb_priv->state = URB_DEL;
ed->state = FHCI_ED_URB_DEL;
fhci_del_ed_list(fhci, ed);
ed->state = FHCI_ED_OPER;
}
td = fhci_remove_td_from_done_list(fhci->hc_list);
}
spin_unlock(&fhci->lock);
enable_irq(fhci->timer->irq);
enable_irq(fhci_to_hcd(fhci)->irq);
}
DECLARE_TASKLET(fhci_tasklet, process_done_list, 0);
/* transfer complted callback */
u32 fhci_transfer_confirm_callback(struct fhci_hcd *fhci)
{
if (!fhci->process_done_task->state)
tasklet_schedule(fhci->process_done_task);
return 0;
}
/*
* adds urb to the endpoint descriptor list
* arguments:
* fhci data structure for the Low level host controller
* ep USB Host endpoint data structure
* urb USB request block data structure
*/
void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb)
{
struct ed *ed = urb->ep->hcpriv;
struct urb_priv *urb_priv = urb->hcpriv;
u32 data_len = urb->transfer_buffer_length;
int urb_state = 0;
int toggle = 0;
struct td *td;
u8 *data;
u16 cnt = 0;
if (ed == NULL) {
ed = fhci_get_empty_ed(fhci);
ed->dev_addr = usb_pipedevice(urb->pipe);
ed->ep_addr = usb_pipeendpoint(urb->pipe);
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
ed->mode = FHCI_TF_CTRL;
break;
case PIPE_BULK:
ed->mode = FHCI_TF_BULK;
break;
case PIPE_INTERRUPT:
ed->mode = FHCI_TF_INTR;
break;
case PIPE_ISOCHRONOUS:
ed->mode = FHCI_TF_ISO;
break;
default:
break;
}
ed->speed = (urb->dev->speed == USB_SPEED_LOW) ?
FHCI_LOW_SPEED : FHCI_FULL_SPEED;
ed->max_pkt_size = usb_maxpacket(urb->dev,
urb->pipe, usb_pipeout(urb->pipe));
urb->ep->hcpriv = ed;
fhci_dbg(fhci, "new ep speed=%d max_pkt_size=%d\n",
ed->speed, ed->max_pkt_size);
}
/* for ISO transfer calculate start frame index */
if (ed->mode == FHCI_TF_ISO && urb->transfer_flags & URB_ISO_ASAP)
urb->start_frame = ed->td_head ? ed->last_iso + 1 :
get_frame_num(fhci);
/*
* OHCI handles the DATA toggle itself,we just use the USB
* toggle bits
*/
if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe)))
toggle = USB_TD_TOGGLE_CARRY;
else {
toggle = USB_TD_TOGGLE_DATA0;
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe), 1);
}
urb_priv->tds_cnt = 0;
urb_priv->ed = ed;
if (data_len > 0)
data = urb->transfer_buffer;
else
data = NULL;
switch (ed->mode) {
case FHCI_TF_BULK:
if (urb->transfer_flags & URB_ZERO_PACKET &&
urb->transfer_buffer_length > 0 &&
((urb->transfer_buffer_length %
usb_maxpacket(urb->dev, urb->pipe,
usb_pipeout(urb->pipe))) == 0))
urb_state = US_BULK0;
while (data_len > 4096) {
td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
FHCI_TA_IN,
cnt ? USB_TD_TOGGLE_CARRY :
toggle,
data, 4096, 0, 0, true);
data += 4096;
data_len -= 4096;
cnt++;
}
td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN,
cnt ? USB_TD_TOGGLE_CARRY : toggle,
data, data_len, 0, 0, true);
cnt++;
if (urb->transfer_flags & URB_ZERO_PACKET &&
cnt < urb_priv->num_of_tds) {
td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
FHCI_TA_IN,
USB_TD_TOGGLE_CARRY, NULL, 0, 0, 0, true);
cnt++;
}
break;
case FHCI_TF_INTR:
urb->start_frame = get_frame_num(fhci) + 1;
td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN,
USB_TD_TOGGLE_DATA0, data, data_len,
urb->interval, urb->start_frame, true);
break;
case FHCI_TF_CTRL:
ed->dev_addr = usb_pipedevice(urb->pipe);
ed->max_pkt_size = usb_maxpacket(urb->dev, urb->pipe,
usb_pipeout(urb->pipe));
td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, FHCI_TA_SETUP,
USB_TD_TOGGLE_DATA0, urb->setup_packet, 8, 0, 0, true);
if (data_len > 0) {
td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
FHCI_TA_IN,
USB_TD_TOGGLE_DATA1, data, data_len, 0, 0,
true);
}
td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
usb_pipeout(urb->pipe) ? FHCI_TA_IN : FHCI_TA_OUT,
USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true);
urb_state = US_CTRL_SETUP;
break;
case FHCI_TF_ISO:
for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
u16 frame = urb->start_frame;
/*
* FIXME scheduling should handle frame counter
* roll-around ... exotic case (and OHCI has
* a 2^16 iso range, vs other HCs max of 2^10)
*/
frame += cnt * urb->interval;
frame &= 0x07ff;
td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
FHCI_TA_IN,
USB_TD_TOGGLE_DATA0,
data + urb->iso_frame_desc[cnt].offset,
urb->iso_frame_desc[cnt].length,
urb->interval, frame, true);
}
break;
default:
break;
}
/*
* set the state of URB
* control pipe:3 states -- setup,data,status
* interrupt and bulk pipe:1 state -- data
*/
urb->pipe &= ~0x1f;
urb->pipe |= urb_state & 0x1f;
urb_priv->state = URB_INPROGRESS;
if (!ed->td_head) {
ed->state = FHCI_ED_OPER;
switch (ed->mode) {
case FHCI_TF_CTRL:
list_add(&ed->node, &fhci->hc_list->ctrl_list);
break;
case FHCI_TF_BULK:
list_add(&ed->node, &fhci->hc_list->bulk_list);
break;
case FHCI_TF_INTR:
list_add(&ed->node, &fhci->hc_list->intr_list);
break;
case FHCI_TF_ISO:
list_add(&ed->node, &fhci->hc_list->iso_list);
break;
default:
break;
}
}
fhci_add_tds_to_ed(ed, urb_priv->tds, urb_priv->num_of_tds);
fhci->active_urbs++;
}

626
drivers/usb/host/fhci-tds.c Normal file
View File

@ -0,0 +1,626 @@
/*
* Freescale QUICC Engine USB Host Controller Driver
*
* Copyright (c) Freescale Semicondutor, Inc. 2006.
* Shlomi Gridish <gridish@freescale.com>
* Jerry Huang <Chang-Ming.Huang@freescale.com>
* Copyright (c) Logic Product Development, Inc. 2007
* Peter Barada <peterb@logicpd.com>
* Copyright (c) MontaVista Software, Inc. 2008.
* Anton Vorontsov <avorontsov@ru.mvista.com>
*
* 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 <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/usb.h>
#include "../core/hcd.h"
#include "fhci.h"
#define DUMMY_BD_BUFFER 0xdeadbeef
#define DUMMY2_BD_BUFFER 0xbaadf00d
/* Transaction Descriptors bits */
#define TD_R 0x8000 /* ready bit */
#define TD_W 0x2000 /* wrap bit */
#define TD_I 0x1000 /* interrupt on completion */
#define TD_L 0x0800 /* last */
#define TD_TC 0x0400 /* transmit CRC */
#define TD_CNF 0x0200 /* CNF - Must be always 1 */
#define TD_LSP 0x0100 /* Low-speed transaction */
#define TD_PID 0x00c0 /* packet id */
#define TD_RXER 0x0020 /* Rx error or not */
#define TD_NAK 0x0010 /* No ack. */
#define TD_STAL 0x0008 /* Stall recieved */
#define TD_TO 0x0004 /* time out */
#define TD_UN 0x0002 /* underrun */
#define TD_NO 0x0010 /* Rx Non Octet Aligned Packet */
#define TD_AB 0x0008 /* Frame Aborted */
#define TD_CR 0x0004 /* CRC Error */
#define TD_OV 0x0002 /* Overrun */
#define TD_BOV 0x0001 /* Buffer Overrun */
#define TD_ERRORS (TD_NAK | TD_STAL | TD_TO | TD_UN | \
TD_NO | TD_AB | TD_CR | TD_OV | TD_BOV)
#define TD_PID_DATA0 0x0080 /* Data 0 toggle */
#define TD_PID_DATA1 0x00c0 /* Data 1 toggle */
#define TD_PID_TOGGLE 0x00c0 /* Data 0/1 toggle mask */
#define TD_TOK_SETUP 0x0000
#define TD_TOK_OUT 0x4000
#define TD_TOK_IN 0x8000
#define TD_ISO 0x1000
#define TD_ENDP 0x0780
#define TD_ADDR 0x007f
#define TD_ENDP_SHIFT 7
struct usb_td {
__be16 status;
__be16 length;
__be32 buf_ptr;
__be16 extra;
__be16 reserved;
};
static struct usb_td __iomem *next_bd(struct usb_td __iomem *base,
struct usb_td __iomem *td,
u16 status)
{
if (status & TD_W)
return base;
else
return ++td;
}
void fhci_push_dummy_bd(struct endpoint *ep)
{
if (ep->already_pushed_dummy_bd == false) {
u16 td_status = in_be16(&ep->empty_td->status);
out_be32(&ep->empty_td->buf_ptr, DUMMY_BD_BUFFER);
/* get the next TD in the ring */
ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status);
ep->already_pushed_dummy_bd = true;
}
}
/* destroy an USB endpoint */
void fhci_ep0_free(struct fhci_usb *usb)
{
struct endpoint *ep;
int size;
ep = usb->ep0;
if (ep) {
if (ep->td_base)
cpm_muram_free(cpm_muram_offset(ep->td_base));
if (ep->conf_frame_Q) {
size = cq_howmany(ep->conf_frame_Q);
for (; size; size--) {
struct packet *pkt = cq_get(ep->conf_frame_Q);
kfree(pkt);
}
cq_delete(ep->conf_frame_Q);
}
if (ep->empty_frame_Q) {
size = cq_howmany(ep->empty_frame_Q);
for (; size; size--) {
struct packet *pkt = cq_get(ep->empty_frame_Q);
kfree(pkt);
}
cq_delete(ep->empty_frame_Q);
}
if (ep->dummy_packets_Q) {
size = cq_howmany(ep->dummy_packets_Q);
for (; size; size--) {
u8 *buff = cq_get(ep->dummy_packets_Q);
kfree(buff);
}
cq_delete(ep->dummy_packets_Q);
}
kfree(ep);
usb->ep0 = NULL;
}
}
/*
* create the endpoint structure
*
* arguments:
* usb A pointer to the data structure of the USB
* data_mem The data memory partition(BUS)
* ring_len TD ring length
*/
u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
u32 ring_len)
{
struct endpoint *ep;
struct usb_td __iomem *td;
unsigned long ep_offset;
char *err_for = "enpoint PRAM";
int ep_mem_size;
u32 i;
/* we need at least 3 TDs in the ring */
if (!(ring_len > 2)) {
fhci_err(usb->fhci, "illegal TD ring length parameters\n");
return -EINVAL;
}
ep = kzalloc(sizeof(*ep), GFP_KERNEL);
if (!ep)
return -ENOMEM;
ep_mem_size = ring_len * sizeof(*td) + sizeof(struct fhci_ep_pram);
ep_offset = cpm_muram_alloc(ep_mem_size, 32);
if (IS_ERR_VALUE(ep_offset))
goto err;
ep->td_base = cpm_muram_addr(ep_offset);
/* zero all queue pointers */
ep->conf_frame_Q = cq_new(ring_len + 2);
ep->empty_frame_Q = cq_new(ring_len + 2);
ep->dummy_packets_Q = cq_new(ring_len + 2);
if (!ep->conf_frame_Q || !ep->empty_frame_Q || !ep->dummy_packets_Q) {
err_for = "frame_queues";
goto err;
}
for (i = 0; i < (ring_len + 1); i++) {
struct packet *pkt;
u8 *buff;
pkt = kmalloc(sizeof(*pkt), GFP_KERNEL);
if (!pkt) {
err_for = "frame";
goto err;
}
buff = kmalloc(1028 * sizeof(*buff), GFP_KERNEL);
if (!buff) {
kfree(pkt);
err_for = "buffer";
goto err;
}
cq_put(ep->empty_frame_Q, pkt);
cq_put(ep->dummy_packets_Q, buff);
}
/* we put the endpoint parameter RAM right behind the TD ring */
ep->ep_pram_ptr = (void __iomem *)ep->td_base + sizeof(*td) * ring_len;
ep->conf_td = ep->td_base;
ep->empty_td = ep->td_base;
ep->already_pushed_dummy_bd = false;
/* initialize tds */
td = ep->td_base;
for (i = 0; i < ring_len; i++) {
out_be32(&td->buf_ptr, 0);
out_be16(&td->status, 0);
out_be16(&td->length, 0);
out_be16(&td->extra, 0);
td++;
}
td--;
out_be16(&td->status, TD_W); /* for last TD set Wrap bit */
out_be16(&td->length, 0);
/* endpoint structure has been created */
usb->ep0 = ep;
return 0;
err:
fhci_ep0_free(usb);
kfree(ep);
fhci_err(usb->fhci, "no memory for the %s\n", err_for);
return -ENOMEM;
}
/*
* initialize the endpoint register according to the given parameters
*
* artuments:
* usb A pointer to the data strucutre of the USB
* ep A pointer to the endpoint structre
* data_mem The data memory partition(BUS)
*/
void fhci_init_ep_registers(struct fhci_usb *usb, struct endpoint *ep,
enum fhci_mem_alloc data_mem)
{
u8 rt;
/* set the endpoint registers according to the endpoint */
out_be16(&usb->fhci->regs->usb_ep[0],
USB_TRANS_CTR | USB_EP_MF | USB_EP_RTE);
out_be16(&usb->fhci->pram->ep_ptr[0],
cpm_muram_offset(ep->ep_pram_ptr));
rt = (BUS_MODE_BO_BE | BUS_MODE_GBL);
#ifdef MULTI_DATA_BUS
if (data_mem == MEM_SECONDARY)
rt |= BUS_MODE_DTB;
#endif
out_8(&ep->ep_pram_ptr->rx_func_code, rt);
out_8(&ep->ep_pram_ptr->tx_func_code, rt);
out_be16(&ep->ep_pram_ptr->rx_buff_len, 1028);
out_be16(&ep->ep_pram_ptr->rx_base, 0);
out_be16(&ep->ep_pram_ptr->tx_base, cpm_muram_offset(ep->td_base));
out_be16(&ep->ep_pram_ptr->rx_bd_ptr, 0);
out_be16(&ep->ep_pram_ptr->tx_bd_ptr, cpm_muram_offset(ep->td_base));
out_be32(&ep->ep_pram_ptr->tx_state, 0);
}
/*
* Collect the submitted frames and inform the application about them
* It is also prepearing the TDs for new frames. If the Tx interrupts
* are diabled, the application should call that routine to get
* confirmation about the submitted frames. Otherwise, the routine is
* called frome the interrupt service routine during the Tx interrupt.
* In that case the application is informed by calling the application
* specific 'fhci_transaction_confirm' routine
*/
static void fhci_td_transaction_confirm(struct fhci_usb *usb)
{
struct endpoint *ep = usb->ep0;
struct packet *pkt;
struct usb_td __iomem *td;
u16 extra_data;
u16 td_status;
u16 td_length;
u32 buf;
/*
* collect transmitted BDs from the chip. The routine clears all BDs
* with R bit = 0 and the pointer to data buffer is not NULL, that is
* BDs which point to the transmitted data buffer
*/
while (1) {
td = ep->conf_td;
td_status = in_be16(&td->status);
td_length = in_be16(&td->length);
buf = in_be32(&td->buf_ptr);
extra_data = in_be16(&td->extra);
/* check if the TD is empty */
if (!(!(td_status & TD_R) && ((td_status & ~TD_W) || buf)))
break;
/* check if it is a dummy buffer */
else if ((buf == DUMMY_BD_BUFFER) && !(td_status & ~TD_W))
break;
/* mark TD as empty */
clrbits16(&td->status, ~TD_W);
out_be16(&td->length, 0);
out_be32(&td->buf_ptr, 0);
out_be16(&td->extra, 0);
/* advance the TD pointer */
ep->conf_td = next_bd(ep->td_base, ep->conf_td, td_status);
/* check if it is a dummy buffer(type2) */
if ((buf == DUMMY2_BD_BUFFER) && !(td_status & ~TD_W))
continue;
pkt = cq_get(ep->conf_frame_Q);
if (!pkt)
fhci_err(usb->fhci, "no frame to confirm\n");
if (td_status & TD_ERRORS) {
if (td_status & TD_RXER) {
if (td_status & TD_CR)
pkt->status = USB_TD_RX_ER_CRC;
else if (td_status & TD_AB)
pkt->status = USB_TD_RX_ER_BITSTUFF;
else if (td_status & TD_OV)
pkt->status = USB_TD_RX_ER_OVERUN;
else if (td_status & TD_BOV)
pkt->status = USB_TD_RX_DATA_OVERUN;
else if (td_status & TD_NO)
pkt->status = USB_TD_RX_ER_NONOCT;
else
fhci_err(usb->fhci, "illegal error "
"occured\n");
} else if (td_status & TD_NAK)
pkt->status = USB_TD_TX_ER_NAK;
else if (td_status & TD_TO)
pkt->status = USB_TD_TX_ER_TIMEOUT;
else if (td_status & TD_UN)
pkt->status = USB_TD_TX_ER_UNDERUN;
else if (td_status & TD_STAL)
pkt->status = USB_TD_TX_ER_STALL;
else
fhci_err(usb->fhci, "illegal error occured\n");
} else if ((extra_data & TD_TOK_IN) &&
pkt->len > td_length - CRC_SIZE) {
pkt->status = USB_TD_RX_DATA_UNDERUN;
}
if (extra_data & TD_TOK_IN)
pkt->len = td_length - CRC_SIZE;
else if (pkt->info & PKT_ZLP)
pkt->len = 0;
else
pkt->len = td_length;
fhci_transaction_confirm(usb, pkt);
}
}
/*
* Submitting a data frame to a specified endpoint of a USB device
* The frame is put in the driver's transmit queue for this endpoint
*
* Arguments:
* usb A pointer to the USB structure
* pkt A pointer to the user frame structure
* trans_type Transaction tyep - IN,OUT or SETUP
* dest_addr Device address - 0~127
* dest_ep Endpoint number of the device - 0~16
* trans_mode Pipe type - ISO,Interrupt,bulk or control
* dest_speed USB speed - Low speed or FULL speed
* data_toggle Data sequence toggle - 0 or 1
*/
u32 fhci_host_transaction(struct fhci_usb *usb,
struct packet *pkt,
enum fhci_ta_type trans_type,
u8 dest_addr,
u8 dest_ep,
enum fhci_tf_mode trans_mode,
enum fhci_speed dest_speed, u8 data_toggle)
{
struct endpoint *ep = usb->ep0;
struct usb_td __iomem *td;
u16 extra_data;
u16 td_status;
fhci_usb_disable_interrupt(usb);
/* start from the next BD that should be filled */
td = ep->empty_td;
td_status = in_be16(&td->status);
if (td_status & TD_R && in_be16(&td->length)) {
/* if the TD is not free */
fhci_usb_enable_interrupt(usb);
return -1;
}
/* get the next TD in the ring */
ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status);
fhci_usb_enable_interrupt(usb);
pkt->priv_data = td;
out_be32(&td->buf_ptr, virt_to_phys(pkt->data));
/* sets up transaction parameters - addr,endp,dir,and type */
extra_data = (dest_ep << TD_ENDP_SHIFT) | dest_addr;
switch (trans_type) {
case FHCI_TA_IN:
extra_data |= TD_TOK_IN;
break;
case FHCI_TA_OUT:
extra_data |= TD_TOK_OUT;
break;
case FHCI_TA_SETUP:
extra_data |= TD_TOK_SETUP;
break;
}
if (trans_mode == FHCI_TF_ISO)
extra_data |= TD_ISO;
out_be16(&td->extra, extra_data);
/* sets up the buffer descriptor */
td_status = ((td_status & TD_W) | TD_R | TD_L | TD_I | TD_CNF);
if (!(pkt->info & PKT_NO_CRC))
td_status |= TD_TC;
switch (trans_type) {
case FHCI_TA_IN:
if (data_toggle)
pkt->info |= PKT_PID_DATA1;
else
pkt->info |= PKT_PID_DATA0;
break;
default:
if (data_toggle) {
td_status |= TD_PID_DATA1;
pkt->info |= PKT_PID_DATA1;
} else {
td_status |= TD_PID_DATA0;
pkt->info |= PKT_PID_DATA0;
}
break;
}
if ((dest_speed == FHCI_LOW_SPEED) &&
(usb->port_status == FHCI_PORT_FULL))
td_status |= TD_LSP;
out_be16(&td->status, td_status);
/* set up buffer length */
if (trans_type == FHCI_TA_IN)
out_be16(&td->length, pkt->len + CRC_SIZE);
else
out_be16(&td->length, pkt->len);
/* put the frame to the confirmation queue */
cq_put(ep->conf_frame_Q, pkt);
if (cq_howmany(ep->conf_frame_Q) == 1)
out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO);
return 0;
}
/* Reset the Tx BD ring */
void fhci_flush_bds(struct fhci_usb *usb)
{
u16 extra_data;
u16 td_status;
u32 buf;
struct usb_td __iomem *td;
struct endpoint *ep = usb->ep0;
td = ep->td_base;
while (1) {
td_status = in_be16(&td->status);
buf = in_be32(&td->buf_ptr);
extra_data = in_be16(&td->extra);
/* if the TD is not empty - we'll confirm it as Timeout */
if (td_status & TD_R)
out_be16(&td->status, (td_status & ~TD_R) | TD_TO);
/* if this TD is dummy - let's skip this TD */
else if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER)
out_be32(&td->buf_ptr, DUMMY2_BD_BUFFER);
/* if this is the last TD - break */
if (td_status & TD_W)
break;
td++;
}
fhci_td_transaction_confirm(usb);
td = ep->td_base;
do {
out_be16(&td->status, 0);
out_be16(&td->length, 0);
out_be32(&td->buf_ptr, 0);
out_be16(&td->extra, 0);
td++;
} while (!(in_be16(&td->status) & TD_W));
out_be16(&td->status, TD_W); /* for last TD set Wrap bit */
out_be16(&td->length, 0);
out_be32(&td->buf_ptr, 0);
out_be16(&td->extra, 0);
out_be16(&ep->ep_pram_ptr->tx_bd_ptr,
in_be16(&ep->ep_pram_ptr->tx_base));
out_be32(&ep->ep_pram_ptr->tx_state, 0);
out_be16(&ep->ep_pram_ptr->tx_cnt, 0);
ep->empty_td = ep->td_base;
ep->conf_td = ep->td_base;
}
/*
* Flush all transmitted packets from TDs in the actual frame.
* This routine is called when something wrong with the controller and
* we want to get rid of the actual frame and start again next frame
*/
void fhci_flush_actual_frame(struct fhci_usb *usb)
{
u8 mode;
u16 tb_ptr;
u16 extra_data;
u16 td_status;
u32 buf_ptr;
struct usb_td __iomem *td;
struct endpoint *ep = usb->ep0;
/* disable the USB controller */
mode = in_8(&usb->fhci->regs->usb_mod);
out_8(&usb->fhci->regs->usb_mod, mode & ~USB_MODE_EN);
tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr);
td = cpm_muram_addr(tb_ptr);
td_status = in_be16(&td->status);
buf_ptr = in_be32(&td->buf_ptr);
extra_data = in_be16(&td->extra);
do {
if (td_status & TD_R) {
out_be16(&td->status, (td_status & ~TD_R) | TD_TO);
} else {
out_be32(&td->buf_ptr, 0);
ep->already_pushed_dummy_bd = false;
break;
}
/* advance the TD pointer */
td = next_bd(ep->td_base, td, td_status);
td_status = in_be16(&td->status);
buf_ptr = in_be32(&td->buf_ptr);
extra_data = in_be16(&td->extra);
} while ((td_status & TD_R) || buf_ptr);
fhci_td_transaction_confirm(usb);
out_be16(&ep->ep_pram_ptr->tx_bd_ptr,
in_be16(&ep->ep_pram_ptr->tx_base));
out_be32(&ep->ep_pram_ptr->tx_state, 0);
out_be16(&ep->ep_pram_ptr->tx_cnt, 0);
ep->empty_td = ep->td_base;
ep->conf_td = ep->td_base;
usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION;
/* reset the event register */
out_be16(&usb->fhci->regs->usb_event, 0xffff);
/* enable the USB controller */
out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN);
}
/* handles Tx confirm and Tx error interrupt */
void fhci_tx_conf_interrupt(struct fhci_usb *usb)
{
fhci_td_transaction_confirm(usb);
/*
* Schedule another transaction to this frame only if we have
* already confirmed all transaction in the frame.
*/
if (((fhci_get_sof_timer_count(usb) < usb->max_frame_usage) ||
(usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)) &&
(list_empty(&usb->actual_frame->tds_list)))
fhci_schedule_transactions(usb);
}
void fhci_host_transmit_actual_frame(struct fhci_usb *usb)
{
u16 tb_ptr;
u16 td_status;
struct usb_td __iomem *td;
struct endpoint *ep = usb->ep0;
tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr);
td = cpm_muram_addr(tb_ptr);
if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER) {
struct usb_td __iomem *old_td = td;
ep->already_pushed_dummy_bd = false;
td_status = in_be16(&td->status);
/* gets the next TD in the ring */
td = next_bd(ep->td_base, td, td_status);
tb_ptr = cpm_muram_offset(td);
out_be16(&ep->ep_pram_ptr->tx_bd_ptr, tb_ptr);
/* start transmit only if we have something in the TDs */
if (in_be16(&td->status) & TD_R)
out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO);
if (in_be32(&ep->conf_td->buf_ptr) == DUMMY_BD_BUFFER) {
out_be32(&old_td->buf_ptr, 0);
ep->conf_td = next_bd(ep->td_base, ep->conf_td,
td_status);
} else {
out_be32(&old_td->buf_ptr, DUMMY2_BD_BUFFER);
}
}
}

607
drivers/usb/host/fhci.h Normal file
View File

@ -0,0 +1,607 @@
/*
* Freescale QUICC Engine USB Host Controller Driver
*
* Copyright (c) Freescale Semicondutor, Inc. 2006.
* Shlomi Gridish <gridish@freescale.com>
* Jerry Huang <Chang-Ming.Huang@freescale.com>
* Copyright (c) Logic Product Development, Inc. 2007
* Peter Barada <peterb@logicpd.com>
* Copyright (c) MontaVista Software, Inc. 2008.
* Anton Vorontsov <avorontsov@ru.mvista.com>
*
* 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 __FHCI_H
#define __FHCI_H
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/kfifo.h>
#include <linux/io.h>
#include <linux/usb.h>
#include <asm/qe.h>
#include "../core/hcd.h"
#define USB_CLOCK 48000000
#define FHCI_PRAM_SIZE 0x100
#define MAX_EDS 32
#define MAX_TDS 32
/* CRC16 field size */
#define CRC_SIZE 2
/* USB protocol overhead for each frame transmitted from the host */
#define PROTOCOL_OVERHEAD 7
/* Packet structure, info field */
#define PKT_PID_DATA0 0x80000000 /* PID - Data toggle zero */
#define PKT_PID_DATA1 0x40000000 /* PID - Data toggle one */
#define PKT_PID_SETUP 0x20000000 /* PID - Setup bit */
#define PKT_SETUP_STATUS 0x10000000 /* Setup status bit */
#define PKT_SETADDR_STATUS 0x08000000 /* Set address status bit */
#define PKT_SET_HOST_LAST 0x04000000 /* Last data packet */
#define PKT_HOST_DATA 0x02000000 /* Data packet */
#define PKT_FIRST_IN_FRAME 0x01000000 /* First packet in the frame */
#define PKT_TOKEN_FRAME 0x00800000 /* Token packet */
#define PKT_ZLP 0x00400000 /* Zero length packet */
#define PKT_IN_TOKEN_FRAME 0x00200000 /* IN token packet */
#define PKT_OUT_TOKEN_FRAME 0x00100000 /* OUT token packet */
#define PKT_SETUP_TOKEN_FRAME 0x00080000 /* SETUP token packet */
#define PKT_STALL_FRAME 0x00040000 /* STALL packet */
#define PKT_NACK_FRAME 0x00020000 /* NACK packet */
#define PKT_NO_PID 0x00010000 /* No PID */
#define PKT_NO_CRC 0x00008000 /* don't append CRC */
#define PKT_HOST_COMMAND 0x00004000 /* Host command packet */
#define PKT_DUMMY_PACKET 0x00002000 /* Dummy packet, used for mmm */
#define PKT_LOW_SPEED_PACKET 0x00001000 /* Low-Speed packet */
#define TRANS_OK (0)
#define TRANS_INPROGRESS (-1)
#define TRANS_DISCARD (-2)
#define TRANS_FAIL (-3)
#define PS_INT 0
#define PS_DISCONNECTED 1
#define PS_CONNECTED 2
#define PS_READY 3
#define PS_MISSING 4
/* Transfer Descriptor status field */
#define USB_TD_OK 0x00000000 /* TD transmited or received ok */
#define USB_TD_INPROGRESS 0x80000000 /* TD is being transmitted */
#define USB_TD_RX_ER_NONOCT 0x40000000 /* Tx Non Octet Aligned Packet */
#define USB_TD_RX_ER_BITSTUFF 0x20000000 /* Frame Aborted-Received pkt */
#define USB_TD_RX_ER_CRC 0x10000000 /* CRC error */
#define USB_TD_RX_ER_OVERUN 0x08000000 /* Over - run occured */
#define USB_TD_RX_ER_PID 0x04000000 /* wrong PID received */
#define USB_TD_RX_DATA_UNDERUN 0x02000000 /* shorter than expected */
#define USB_TD_RX_DATA_OVERUN 0x01000000 /* longer than expected */
#define USB_TD_TX_ER_NAK 0x00800000 /* NAK handshake */
#define USB_TD_TX_ER_STALL 0x00400000 /* STALL handshake */
#define USB_TD_TX_ER_TIMEOUT 0x00200000 /* transmit time out */
#define USB_TD_TX_ER_UNDERUN 0x00100000 /* transmit underrun */
#define USB_TD_ERROR (USB_TD_RX_ER_NONOCT | USB_TD_RX_ER_BITSTUFF | \
USB_TD_RX_ER_CRC | USB_TD_RX_ER_OVERUN | USB_TD_RX_ER_PID | \
USB_TD_RX_DATA_UNDERUN | USB_TD_RX_DATA_OVERUN | \
USB_TD_TX_ER_NAK | USB_TD_TX_ER_STALL | \
USB_TD_TX_ER_TIMEOUT | USB_TD_TX_ER_UNDERUN)
/* Transfer Descriptor toggle field */
#define USB_TD_TOGGLE_DATA0 0
#define USB_TD_TOGGLE_DATA1 1
#define USB_TD_TOGGLE_CARRY 2
/* #define MULTI_DATA_BUS */
/* Bus mode register RBMR/TBMR */
#define BUS_MODE_GBL 0x20 /* Global snooping */
#define BUS_MODE_BO 0x18 /* Byte ordering */
#define BUS_MODE_BO_BE 0x10 /* Byte ordering - Big-endian */
#define BUS_MODE_DTB 0x02 /* Data bus */
/* FHCI QE USB Register Description */
/* USB Mode Register bit define */
#define USB_MODE_EN 0x01
#define USB_MODE_HOST 0x02
#define USB_MODE_TEST 0x04
#define USB_MODE_SFTE 0x08
#define USB_MODE_RESUME 0x40
#define USB_MODE_LSS 0x80
/* USB Slave Address Register Mask */
#define USB_SLVADDR_MASK 0x7F
/* USB Endpoint register define */
#define USB_EPNUM_MASK 0xF000
#define USB_EPNUM_SHIFT 12
#define USB_TRANS_MODE_SHIFT 8
#define USB_TRANS_CTR 0x0000
#define USB_TRANS_INT 0x0100
#define USB_TRANS_BULK 0x0200
#define USB_TRANS_ISO 0x0300
#define USB_EP_MF 0x0020
#define USB_EP_RTE 0x0010
#define USB_THS_SHIFT 2
#define USB_THS_MASK 0x000c
#define USB_THS_NORMAL 0x0
#define USB_THS_IGNORE_IN 0x0004
#define USB_THS_NACK 0x0008
#define USB_THS_STALL 0x000c
#define USB_RHS_SHIFT 0
#define USB_RHS_MASK 0x0003
#define USB_RHS_NORMAL 0x0
#define USB_RHS_IGNORE_OUT 0x0001
#define USB_RHS_NACK 0x0002
#define USB_RHS_STALL 0x0003
#define USB_RTHS_MASK 0x000f
/* USB Command Register define */
#define USB_CMD_STR_FIFO 0x80
#define USB_CMD_FLUSH_FIFO 0x40
#define USB_CMD_ISFT 0x20
#define USB_CMD_DSFT 0x10
#define USB_CMD_EP_MASK 0x03
/* USB Event and Mask Register define */
#define USB_E_MSF_MASK 0x0800
#define USB_E_SFT_MASK 0x0400
#define USB_E_RESET_MASK 0x0200
#define USB_E_IDLE_MASK 0x0100
#define USB_E_TXE4_MASK 0x0080
#define USB_E_TXE3_MASK 0x0040
#define USB_E_TXE2_MASK 0x0020
#define USB_E_TXE1_MASK 0x0010
#define USB_E_SOF_MASK 0x0008
#define USB_E_BSY_MASK 0x0004
#define USB_E_TXB_MASK 0x0002
#define USB_E_RXB_MASK 0x0001
/* Freescale USB Host controller registers */
struct fhci_regs {
u8 usb_mod; /* mode register */
u8 usb_addr; /* address register */
u8 usb_comm; /* command register */
u8 reserved1[1];
__be16 usb_ep[4]; /* endpoint register */
u8 reserved2[4];
__be16 usb_event; /* event register */
u8 reserved3[2];
__be16 usb_mask; /* mask register */
u8 reserved4[1];
u8 usb_status; /* status register */
__be16 usb_sof_tmr; /* Start Of Frame timer */
u8 reserved5[2];
__be16 usb_frame_num; /* frame number register */
u8 reserved6[1];
};
/* Freescale USB HOST */
struct fhci_pram {
__be16 ep_ptr[4]; /* Endpoint porter reg */
__be32 rx_state; /* Rx internal state */
__be32 rx_ptr; /* Rx internal data pointer */
__be16 frame_num; /* Frame number */
__be16 rx_cnt; /* Rx byte count */
__be32 rx_temp; /* Rx temp */
__be32 rx_data_temp; /* Rx data temp */
__be16 rx_u_ptr; /* Rx microcode return address temp */
u8 reserved1[2]; /* reserved area */
__be32 sof_tbl; /* SOF lookup table pointer */
u8 sof_u_crc_temp; /* SOF micorcode CRC5 temp reg */
u8 reserved2[0xdb];
};
/* Freescale USB Endpoint*/
struct fhci_ep_pram {
__be16 rx_base; /* Rx BD base address */
__be16 tx_base; /* Tx BD base address */
u8 rx_func_code; /* Rx function code */
u8 tx_func_code; /* Tx function code */
__be16 rx_buff_len; /* Rx buffer length */
__be16 rx_bd_ptr; /* Rx BD pointer */
__be16 tx_bd_ptr; /* Tx BD pointer */
__be32 tx_state; /* Tx internal state */
__be32 tx_ptr; /* Tx internal data pointer */
__be16 tx_crc; /* temp transmit CRC */
__be16 tx_cnt; /* Tx byte count */
__be32 tx_temp; /* Tx temp */
__be16 tx_u_ptr; /* Tx microcode return address temp */
__be16 reserved;
};
struct fhci_controller_list {
struct list_head ctrl_list; /* control endpoints */
struct list_head bulk_list; /* bulk endpoints */
struct list_head iso_list; /* isochronous endpoints */
struct list_head intr_list; /* interruput endpoints */
struct list_head done_list; /* done transfers */
};
struct virtual_root_hub {
int dev_num; /* USB address of the root hub */
u32 feature; /* indicates what feature has been set */
struct usb_hub_status hub;
struct usb_port_status port;
};
enum fhci_gpios {
GPIO_USBOE = 0,
GPIO_USBTP,
GPIO_USBTN,
GPIO_USBRP,
GPIO_USBRN,
/* these are optional */
GPIO_SPEED,
GPIO_POWER,
NUM_GPIOS,
};
enum fhci_pins {
PIN_USBOE = 0,
PIN_USBTP,
PIN_USBTN,
NUM_PINS,
};
struct fhci_hcd {
enum qe_clock fullspeed_clk;
enum qe_clock lowspeed_clk;
struct qe_pin *pins[NUM_PINS];
int gpios[NUM_GPIOS];
bool alow_gpios[NUM_GPIOS];
struct fhci_regs __iomem *regs; /* I/O memory used to communicate */
struct fhci_pram __iomem *pram; /* Parameter RAM */
struct gtm_timer *timer;
spinlock_t lock;
struct fhci_usb *usb_lld; /* Low-level driver */
struct virtual_root_hub *vroot_hub; /* the virtual root hub */
int active_urbs;
struct fhci_controller_list *hc_list;
struct tasklet_struct *process_done_task; /* tasklet for done list */
struct list_head empty_eds;
struct list_head empty_tds;
#ifdef CONFIG_FHCI_DEBUG
int usb_irq_stat[13];
struct dentry *dfs_root;
struct dentry *dfs_regs;
struct dentry *dfs_irq_stat;
#endif
};
#define USB_FRAME_USAGE 90
#define FRAME_TIME_USAGE (USB_FRAME_USAGE*10) /* frame time usage */
#define SW_FIX_TIME_BETWEEN_TRANSACTION 150 /* SW */
#define MAX_BYTES_PER_FRAME (USB_FRAME_USAGE*15)
#define MAX_PERIODIC_FRAME_USAGE 90
/* transaction type */
enum fhci_ta_type {
FHCI_TA_IN = 0, /* input transaction */
FHCI_TA_OUT, /* output transaction */
FHCI_TA_SETUP, /* setup transaction */
};
/* transfer mode */
enum fhci_tf_mode {
FHCI_TF_CTRL = 0,
FHCI_TF_ISO,
FHCI_TF_BULK,
FHCI_TF_INTR,
};
enum fhci_speed {
FHCI_FULL_SPEED,
FHCI_LOW_SPEED,
};
/* endpoint state */
enum fhci_ed_state {
FHCI_ED_NEW = 0, /* pipe is new */
FHCI_ED_OPER, /* pipe is operating */
FHCI_ED_URB_DEL, /* pipe is in hold because urb is being deleted */
FHCI_ED_SKIP, /* skip this pipe */
FHCI_ED_HALTED, /* pipe is halted */
};
enum fhci_port_status {
FHCI_PORT_POWER_OFF = 0,
FHCI_PORT_DISABLED,
FHCI_PORT_DISCONNECTING,
FHCI_PORT_WAITING, /* waiting for connection */
FHCI_PORT_FULL, /* full speed connected */
FHCI_PORT_LOW, /* low speed connected */
};
enum fhci_mem_alloc {
MEM_CACHABLE_SYS = 0x00000001, /* primary DDR,cachable */
MEM_NOCACHE_SYS = 0x00000004, /* primary DDR,non-cachable */
MEM_SECONDARY = 0x00000002, /* either secondary DDR or SDRAM */
MEM_PRAM = 0x00000008, /* multi-user RAM identifier */
};
/* USB default parameters*/
#define DEFAULT_RING_LEN 8
#define DEFAULT_DATA_MEM MEM_CACHABLE_SYS
struct ed {
u8 dev_addr; /* device address */
u8 ep_addr; /* endpoint address */
enum fhci_tf_mode mode; /* USB transfer mode */
enum fhci_speed speed;
unsigned int max_pkt_size;
enum fhci_ed_state state;
struct list_head td_list; /* a list of all queued TD to this pipe */
struct list_head node;
/* read only parameters, should be cleared upon initialization */
u8 toggle_carry; /* toggle carry from the last TD submitted */
u32 last_iso; /* time stamp of last queued ISO transfer */
struct td *td_head; /* a pointer to the current TD handled */
};
struct td {
void *data; /* a pointer to the data buffer */
unsigned int len; /* length of the data to be submitted */
unsigned int actual_len; /* actual bytes transfered on this td */
enum fhci_ta_type type; /* transaction type */
u8 toggle; /* toggle for next trans. within this TD */
u16 iso_index; /* ISO transaction index */
u16 start_frame; /* start frame time stamp */
u16 interval; /* interval between trans. (for ISO/Intr) */
u32 status; /* status of the TD */
struct ed *ed; /* a handle to the corresponding ED */
struct urb *urb; /* a handle to the corresponding URB */
bool ioc; /* Inform On Completion */
struct list_head node;
/* read only parameters should be cleared upon initialization */
struct packet *pkt;
int nak_cnt;
int error_cnt;
struct list_head frame_lh;
};
struct packet {
u8 *data; /* packet data */
u32 len; /* packet length */
u32 status; /* status of the packet - equivalent to the status
* field for the corresponding structure td */
u32 info; /* packet information */
void __iomem *priv_data; /* private data of the driver (TDs or BDs) */
};
/* struct for each URB */
#define URB_INPROGRESS 0
#define URB_DEL 1
/* URB states (state field) */
#define US_BULK 0
#define US_BULK0 1
/* three setup states */
#define US_CTRL_SETUP 2
#define US_CTRL_DATA 1
#define US_CTRL_ACK 0
#define EP_ZERO 0
struct urb_priv {
int num_of_tds;
int tds_cnt;
int state;
struct td **tds;
struct ed *ed;
struct timer_list time_out;
};
struct endpoint {
/* Pointer to ep parameter RAM */
struct fhci_ep_pram __iomem *ep_pram_ptr;
/* Host transactions */
struct usb_td __iomem *td_base; /* first TD in the ring */
struct usb_td __iomem *conf_td; /* next TD for confirm after transac */
struct usb_td __iomem *empty_td;/* next TD for new transaction req. */
struct kfifo *empty_frame_Q; /* Empty frames list to use */
struct kfifo *conf_frame_Q; /* frames passed to TDs,waiting for tx */
struct kfifo *dummy_packets_Q;/* dummy packets for the CRC overun */
bool already_pushed_dummy_bd;
};
/* struct for each 1mSec frame time */
#define FRAME_IS_TRANSMITTED 0x00
#define FRAME_TIMER_END_TRANSMISSION 0x01
#define FRAME_DATA_END_TRANSMISSION 0x02
#define FRAME_END_TRANSMISSION 0x03
#define FRAME_IS_PREPARED 0x04
struct fhci_time_frame {
u16 frame_num; /* frame number */
u16 total_bytes; /* total bytes submitted within this frame */
u8 frame_status; /* flag that indicates to stop fill this frame */
struct list_head tds_list; /* all tds of this frame */
};
/* internal driver structure*/
struct fhci_usb {
u16 saved_msk; /* saving of the USB mask register */
struct endpoint *ep0; /* pointer for endpoint0 structure */
int intr_nesting_cnt; /* interrupt nesting counter */
u16 max_frame_usage; /* max frame time usage,in micro-sec */
u16 max_bytes_per_frame; /* max byte can be tx in one time frame */
u32 sw_transaction_time; /* sw complete trans time,in micro-sec */
struct fhci_time_frame *actual_frame;
struct fhci_controller_list *hc_list; /* main structure for hc */
struct virtual_root_hub *vroot_hub;
enum fhci_port_status port_status; /* v_rh port status */
u32 (*transfer_confirm)(struct fhci_hcd *fhci);
struct fhci_hcd *fhci;
};
/*
* Various helpers and prototypes below.
*/
static inline u16 get_frame_num(struct fhci_hcd *fhci)
{
return in_be16(&fhci->pram->frame_num) & 0x07ff;
}
#define fhci_dbg(fhci, fmt, args...) \
dev_dbg(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
#define fhci_vdbg(fhci, fmt, args...) \
dev_vdbg(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
#define fhci_err(fhci, fmt, args...) \
dev_err(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
#define fhci_info(fhci, fmt, args...) \
dev_info(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
#define fhci_warn(fhci, fmt, args...) \
dev_warn(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
static inline struct fhci_hcd *hcd_to_fhci(struct usb_hcd *hcd)
{
return (struct fhci_hcd *)hcd->hcd_priv;
}
static inline struct usb_hcd *fhci_to_hcd(struct fhci_hcd *fhci)
{
return container_of((void *)fhci, struct usb_hcd, hcd_priv);
}
/* fifo of pointers */
static inline struct kfifo *cq_new(int size)
{
return kfifo_alloc(size * sizeof(void *), GFP_KERNEL, NULL);
}
static inline void cq_delete(struct kfifo *kfifo)
{
kfifo_free(kfifo);
}
static inline unsigned int cq_howmany(struct kfifo *kfifo)
{
return __kfifo_len(kfifo) / sizeof(void *);
}
static inline int cq_put(struct kfifo *kfifo, void *p)
{
return __kfifo_put(kfifo, (void *)&p, sizeof(p));
}
static inline void *cq_get(struct kfifo *kfifo)
{
void *p = NULL;
__kfifo_get(kfifo, (void *)&p, sizeof(p));
return p;
}
/* fhci-hcd.c */
void fhci_start_sof_timer(struct fhci_hcd *fhci);
void fhci_stop_sof_timer(struct fhci_hcd *fhci);
u16 fhci_get_sof_timer_count(struct fhci_usb *usb);
void fhci_usb_enable_interrupt(struct fhci_usb *usb);
void fhci_usb_disable_interrupt(struct fhci_usb *usb);
int fhci_ioports_check_bus_state(struct fhci_hcd *fhci);
/* fhci-mem.c */
void fhci_recycle_empty_td(struct fhci_hcd *fhci, struct td *td);
void fhci_recycle_empty_ed(struct fhci_hcd *fhci, struct ed *ed);
struct ed *fhci_get_empty_ed(struct fhci_hcd *fhci);
struct td *fhci_td_fill(struct fhci_hcd *fhci, struct urb *urb,
struct urb_priv *urb_priv, struct ed *ed, u16 index,
enum fhci_ta_type type, int toggle, u8 *data, u32 len,
u16 interval, u16 start_frame, bool ioc);
void fhci_add_tds_to_ed(struct ed *ed, struct td **td_list, int number);
/* fhci-hub.c */
void fhci_config_transceiver(struct fhci_hcd *fhci,
enum fhci_port_status status);
void fhci_port_disable(struct fhci_hcd *fhci);
void fhci_port_enable(void *lld);
void fhci_io_port_generate_reset(struct fhci_hcd *fhci);
void fhci_port_reset(void *lld);
int fhci_hub_status_data(struct usb_hcd *hcd, char *buf);
int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength);
/* fhci-tds.c */
void fhci_flush_bds(struct fhci_usb *usb);
void fhci_flush_actual_frame(struct fhci_usb *usb);
u32 fhci_host_transaction(struct fhci_usb *usb, struct packet *pkt,
enum fhci_ta_type trans_type, u8 dest_addr,
u8 dest_ep, enum fhci_tf_mode trans_mode,
enum fhci_speed dest_speed, u8 data_toggle);
void fhci_host_transmit_actual_frame(struct fhci_usb *usb);
void fhci_tx_conf_interrupt(struct fhci_usb *usb);
void fhci_push_dummy_bd(struct endpoint *ep);
u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
u32 ring_len);
void fhci_init_ep_registers(struct fhci_usb *usb,
struct endpoint *ep,
enum fhci_mem_alloc data_mem);
void fhci_ep0_free(struct fhci_usb *usb);
/* fhci-sched.c */
extern struct tasklet_struct fhci_tasklet;
void fhci_transaction_confirm(struct fhci_usb *usb, struct packet *pkt);
void fhci_flush_all_transmissions(struct fhci_usb *usb);
void fhci_schedule_transactions(struct fhci_usb *usb);
void fhci_device_connected_interrupt(struct fhci_hcd *fhci);
void fhci_device_disconnected_interrupt(struct fhci_hcd *fhci);
void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb);
u32 fhci_transfer_confirm_callback(struct fhci_hcd *fhci);
irqreturn_t fhci_irq(struct usb_hcd *hcd);
irqreturn_t fhci_frame_limit_timer_irq(int irq, void *_hcd);
/* fhci-q.h */
void fhci_urb_complete_free(struct fhci_hcd *fhci, struct urb *urb);
struct td *fhci_remove_td_from_ed(struct ed *ed);
struct td *fhci_remove_td_from_frame(struct fhci_time_frame *frame);
void fhci_move_td_from_ed_to_done_list(struct fhci_usb *usb, struct ed *ed);
struct td *fhci_peek_td_from_frame(struct fhci_time_frame *frame);
void fhci_add_td_to_frame(struct fhci_time_frame *frame, struct td *td);
struct td *fhci_remove_td_from_done_list(struct fhci_controller_list *p_list);
void fhci_done_td(struct urb *urb, struct td *td);
void fhci_del_ed_list(struct fhci_hcd *fhci, struct ed *ed);
#ifdef CONFIG_FHCI_DEBUG
void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er);
void fhci_dfs_destroy(struct fhci_hcd *fhci);
void fhci_dfs_create(struct fhci_hcd *fhci);
#else
static inline void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er) {}
static inline void fhci_dfs_destroy(struct fhci_hcd *fhci) {}
static inline void fhci_dfs_create(struct fhci_hcd *fhci) {}
#endif /* CONFIG_FHCI_DEBUG */
#endif /* __FHCI_H */

View File

@ -593,12 +593,10 @@ static int ohci_run (struct ohci_hcd *ohci)
* to be checked in case boot firmware (BIOS/SMM/...) has set up
* wakeup in a way the bus isn't aware of (e.g., legacy PCI PM).
* If the bus glue detected wakeup capability then it should
* already be enabled. Either way, if wakeup should be enabled
* but isn't, we'll enable it now.
* already be enabled; if so we'll just enable it again.
*/
if ((ohci->hc_control & OHCI_CTRL_RWC) != 0
&& !device_can_wakeup(hcd->self.controller))
device_init_wakeup(hcd->self.controller, 1);
if ((ohci->hc_control & OHCI_CTRL_RWC) != 0)
device_set_wakeup_capable(hcd->self.controller, 1);
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_OPER:

View File

@ -487,7 +487,6 @@ static struct pci_driver ohci_pci_driver = {
#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.suspend_late = usb_hcd_pci_suspend_late,
.resume_early = usb_hcd_pci_resume_early,
.resume = usb_hcd_pci_resume,
#endif

View File

@ -942,7 +942,6 @@ static struct pci_driver uhci_pci_driver = {
#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.suspend_late = usb_hcd_pci_suspend_late,
.resume_early = usb_hcd_pci_resume_early,
.resume = usb_hcd_pci_resume,
#endif /* PM */

View File

@ -57,7 +57,6 @@
#define USB_DEVICE_ID_LD_MACHINETEST 0x2040 /* USB Product ID of Machine Test System */
#define USB_VENDOR_ID_VERNIER 0x08f7
#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
@ -85,7 +84,6 @@ static struct usb_device_id ld_usb_table [] = {
{ 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_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
{ 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) },

View File

@ -37,6 +37,7 @@
#define MON_IOCX_GET _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get)
#define MON_IOCX_MFETCH _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch)
#define MON_IOCH_MFLUSH _IO(MON_IOC_MAGIC, 8)
#ifdef CONFIG_COMPAT
#define MON_IOCX_GET32 _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get32)
#define MON_IOCX_MFETCH32 _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch32)
@ -921,21 +922,6 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file,
}
break;
#ifdef CONFIG_COMPAT
case MON_IOCX_GET32: {
struct mon_bin_get32 getb;
if (copy_from_user(&getb, (void __user *)arg,
sizeof(struct mon_bin_get32)))
return -EFAULT;
ret = mon_bin_get_event(file, rp,
compat_ptr(getb.hdr32), compat_ptr(getb.data32),
getb.alloc32);
}
break;
#endif
case MON_IOCX_MFETCH:
{
struct mon_bin_mfetch mfetch;
@ -962,35 +948,6 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file,
}
break;
#ifdef CONFIG_COMPAT
case MON_IOCX_MFETCH32:
{
struct mon_bin_mfetch32 mfetch;
struct mon_bin_mfetch32 __user *uptr;
uptr = (struct mon_bin_mfetch32 __user *) compat_ptr(arg);
if (copy_from_user(&mfetch, uptr, sizeof(mfetch)))
return -EFAULT;
if (mfetch.nflush32) {
ret = mon_bin_flush(rp, mfetch.nflush32);
if (ret < 0)
return ret;
if (put_user(ret, &uptr->nflush32))
return -EFAULT;
}
ret = mon_bin_fetch(file, rp, compat_ptr(mfetch.offvec32),
mfetch.nfetch32);
if (ret < 0)
return ret;
if (put_user(ret, &uptr->nfetch32))
return -EFAULT;
ret = 0;
}
break;
#endif
case MON_IOCG_STATS: {
struct mon_bin_stats __user *sp;
unsigned int nevents;
@ -1018,6 +975,73 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file,
return ret;
}
#ifdef CONFIG_COMPAT
static long mon_bin_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct mon_reader_bin *rp = file->private_data;
int ret;
switch (cmd) {
case MON_IOCX_GET32: {
struct mon_bin_get32 getb;
if (copy_from_user(&getb, (void __user *)arg,
sizeof(struct mon_bin_get32)))
return -EFAULT;
ret = mon_bin_get_event(file, rp,
compat_ptr(getb.hdr32), compat_ptr(getb.data32),
getb.alloc32);
if (ret < 0)
return ret;
}
return 0;
case MON_IOCX_MFETCH32:
{
struct mon_bin_mfetch32 mfetch;
struct mon_bin_mfetch32 __user *uptr;
uptr = (struct mon_bin_mfetch32 __user *) compat_ptr(arg);
if (copy_from_user(&mfetch, uptr, sizeof(mfetch)))
return -EFAULT;
if (mfetch.nflush32) {
ret = mon_bin_flush(rp, mfetch.nflush32);
if (ret < 0)
return ret;
if (put_user(ret, &uptr->nflush32))
return -EFAULT;
}
ret = mon_bin_fetch(file, rp, compat_ptr(mfetch.offvec32),
mfetch.nfetch32);
if (ret < 0)
return ret;
if (put_user(ret, &uptr->nfetch32))
return -EFAULT;
}
return 0;
case MON_IOCG_STATS:
return mon_bin_ioctl(NULL, file, cmd,
(unsigned long) compat_ptr(arg));
case MON_IOCQ_URB_LEN:
case MON_IOCQ_RING_SIZE:
case MON_IOCT_RING_SIZE:
case MON_IOCH_MFLUSH:
return mon_bin_ioctl(NULL, file, cmd, arg);
default:
;
}
return -ENOTTY;
}
#endif /* CONFIG_COMPAT */
static unsigned int
mon_bin_poll(struct file *file, struct poll_table_struct *wait)
{
@ -1094,6 +1118,9 @@ static const struct file_operations mon_fops_binary = {
/* .write = mon_text_write, */
.poll = mon_bin_poll,
.ioctl = mon_bin_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = mon_bin_compat_ioctl,
#endif
.release = mon_bin_release,
.mmap = mon_bin_mmap,
};

View File

@ -11,6 +11,7 @@ config USB_MUSB_HDRC
depends on (USB || USB_GADGET) && HAVE_CLK
depends on !SUPERH
select TWL4030_USB if MACH_OMAP_3430SDP
select USB_OTG_UTILS
tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
help
Say Y here if your system has a dual role high speed USB
@ -49,7 +50,7 @@ comment "OMAP 343x high speed USB support"
depends on USB_MUSB_HDRC && ARCH_OMAP34XX
comment "Blackfin high speed USB Support"
depends on USB_MUSB_HDRC && (BF54x && !BF544) || (BF52x && !BF522 && !BF523)
depends on USB_MUSB_HDRC && ((BF54x && !BF544) || (BF52x && !BF522 && !BF523))
config USB_TUSB6010
boolean "TUSB 6010 support"

View File

@ -9,6 +9,7 @@
#include <linux/usb.h>
#include "musb_core.h"
#include "musb_debug.h"
#include "cppi_dma.h"
@ -423,6 +424,7 @@ cppi_rndis_update(struct cppi_channel *c, int is_rx,
}
}
#ifdef CONFIG_USB_MUSB_DEBUG
static void cppi_dump_rxbd(const char *tag, struct cppi_descriptor *bd)
{
pr_debug("RXBD/%s %08x: "
@ -431,10 +433,11 @@ static void cppi_dump_rxbd(const char *tag, struct cppi_descriptor *bd)
bd->hw_next, bd->hw_bufp, bd->hw_off_len,
bd->hw_options);
}
#endif
static void cppi_dump_rxq(int level, const char *tag, struct cppi_channel *rx)
{
#if MUSB_DEBUG > 0
#ifdef CONFIG_USB_MUSB_DEBUG
struct cppi_descriptor *bd;
if (!_dbg_level(level))
@ -881,12 +884,14 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket)
bd->hw_options |= CPPI_SOP_SET;
tail->hw_options |= CPPI_EOP_SET;
if (debug >= 5) {
#ifdef CONFIG_USB_MUSB_DEBUG
if (_dbg_level(5)) {
struct cppi_descriptor *d;
for (d = rx->head; d; d = d->next)
cppi_dump_rxbd("S", d);
}
#endif
/* in case the preceding transfer left some state... */
tail = rx->last_processed;
@ -990,6 +995,7 @@ static int cppi_channel_program(struct dma_channel *ch,
cppi_ch->offset = 0;
cppi_ch->maxpacket = maxpacket;
cppi_ch->buf_len = len;
cppi_ch->channel.actual_len = 0;
/* TX channel? or RX? */
if (cppi_ch->transmit)

View File

@ -32,9 +32,10 @@
#include <linux/io.h>
#include <linux/gpio.h>
#include <mach/arch/hardware.h>
#include <mach/arch/memory.h>
#include <mach/arch/gpio.h>
#include <mach/hardware.h>
#include <mach/memory.h>
#include <mach/gpio.h>
#include <asm/mach-types.h>
#include "musb_core.h"
@ -370,12 +371,6 @@ int musb_platform_set_mode(struct musb *musb, u8 mode)
return -EIO;
}
int musb_platform_set_mode(struct musb *musb, u8 mode)
{
/* EVM can't do this (right?) */
return -EIO;
}
int __init musb_platform_init(struct musb *musb)
{
void __iomem *tibase = musb->ctrl_base;

View File

@ -1824,8 +1824,9 @@ static void musb_free(struct musb *musb)
musb_gadget_cleanup(musb);
#endif
if (musb->nIrq >= 0 && musb->irq_wake) {
disable_irq_wake(musb->nIrq);
if (musb->nIrq >= 0) {
if (musb->irq_wake)
disable_irq_wake(musb->nIrq);
free_irq(musb->nIrq, musb);
}
if (is_dma_capable() && musb->dma_controller) {

View File

@ -874,10 +874,10 @@ static int musb_gadget_enable(struct usb_ep *ep,
status = -EBUSY;
goto fail;
}
musb_ep->type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
musb_ep->type = usb_endpoint_type(desc);
/* check direction and (later) maxpacket size against endpoint */
if ((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != epnum)
if (usb_endpoint_num(desc) != epnum)
goto fail;
/* REVISIT this rules out high bandwidth periodic transfers */
@ -890,7 +890,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
* packet size (or fail), set the mode, clear the fifo
*/
musb_ep_select(mbase, epnum);
if (desc->bEndpointAddress & USB_DIR_IN) {
if (usb_endpoint_dir_in(desc)) {
u16 int_txe = musb_readw(mbase, MUSB_INTRTXE);
if (hw_ep->is_shared_fifo)

View File

@ -1847,8 +1847,8 @@ static int musb_urb_enqueue(
goto done;
}
qh->epnum = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
qh->type = epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
qh->epnum = usb_endpoint_num(epd);
qh->type = usb_endpoint_type(epd);
/* NOTE: urb->dev->devnum is wrong during SET_ADDRESS */
qh->addr_reg = (u8) usb_pipedevice(urb->pipe);

View File

@ -15,8 +15,8 @@
#include <linux/usb.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <asm/arch/dma.h>
#include <asm/arch/mux.h>
#include <mach/dma.h>
#include <mach/mux.h>
#include "musb_core.h"

View File

@ -6,14 +6,14 @@
comment "OTG and related infrastructure"
if USB || USB_GADGET
config USB_OTG_UTILS
bool
help
Select this to make sure the build includes objects from
the OTG infrastructure directory.
if USB || USB_GADGET
#
# USB Transceiver Drivers
#

View File

@ -85,6 +85,8 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */
{ USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */
{ 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 demostration module */
{ USB_DEVICE(0x10c4, 0x8293) }, /* Telegesys ETRX2USB */
{ USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */
{ USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */

View File

@ -660,6 +660,8 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO4x4_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DGQG_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) },
{ USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) },
{ USB_DEVICE(FTDI_VID, DIEBOLD_BCS_SE923_PID) },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};

View File

@ -854,6 +854,10 @@
#define FTDI_DOMINTELL_DGQG_PID 0xEF50 /* Master */
#define FTDI_DOMINTELL_DUSB_PID 0xEF51 /* DUSB01 module */
/* Alti-2 products http://www.alti-2.com */
#define ALTI2_VID 0x1BC9
#define ALTI2_N3_PID 0x6001 /* Neptune 3 */
/* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
@ -880,6 +884,11 @@
#define RATOC_VENDOR_ID 0x0584
#define RATOC_PRODUCT_ID_USB60F 0xb020
/*
* DIEBOLD BCS SE923
*/
#define DIEBOLD_BCS_SE923_PID 0xfb99
/*
* BmRequestType: 1100 0000b
* bRequest: FTDI_E2_READ

View File

@ -158,6 +158,13 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po
#define HUAWEI_PRODUCT_E143E 0x143E
#define HUAWEI_PRODUCT_E143F 0x143F
#define QUANTA_VENDOR_ID 0x0408
#define QUANTA_PRODUCT_Q101 0xEA02
#define QUANTA_PRODUCT_Q111 0xEA03
#define QUANTA_PRODUCT_GLX 0xEA04
#define QUANTA_PRODUCT_GKE 0xEA05
#define QUANTA_PRODUCT_GLE 0xEA06
#define NOVATELWIRELESS_VENDOR_ID 0x1410
/* YISO PRODUCTS */
@ -224,7 +231,7 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po
#define ONDA_VENDOR_ID 0x19d2
#define ONDA_PRODUCT_MSA501HS 0x0001
#define ONDA_PRODUCT_ET502HS 0x0002
#define ONDA_PRODUCT_MT503HS 0x0200
#define ONDA_PRODUCT_MT503HS 0x2000
#define BANDRICH_VENDOR_ID 0x1A8D
#define BANDRICH_PRODUCT_C100_1 0x1002
@ -298,6 +305,11 @@ static struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_Q101) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_Q111) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLX) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) },

View File

@ -184,6 +184,7 @@ static struct usb_device_id ti_id_table_3410[7+TI_EXTRA_VID_PID_COUNT+1] = {
{ USB_DEVICE(MTS_VENDOR_ID, MTS_CDMA_PRODUCT_ID) },
{ USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_PRODUCT_ID) },
{ USB_DEVICE(MTS_VENDOR_ID, MTS_EDGE_PRODUCT_ID) },
{ USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) },
};
static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = {
@ -191,6 +192,7 @@ static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = {
{ USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) },
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) },
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) },
{ USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) },
};
static struct usb_device_id ti_id_table_combined[6+2*TI_EXTRA_VID_PID_COUNT+1] = {
@ -205,6 +207,7 @@ static struct usb_device_id ti_id_table_combined[6+2*TI_EXTRA_VID_PID_COUNT+1] =
{ USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) },
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) },
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) },
{ USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) },
{ }
};

View File

@ -27,7 +27,9 @@
/* Vendor and product ids */
#define TI_VENDOR_ID 0x0451
#define IBM_VENDOR_ID 0x04b3
#define TI_3410_PRODUCT_ID 0x3410
#define IBM_4543_PRODUCT_ID 0x4543
#define TI_3410_EZ430_ID 0xF430 /* TI ez430 development tool */
#define TI_5052_BOOT_PRODUCT_ID 0x5052 /* no EEPROM, no firmware */
#define TI_5152_BOOT_PRODUCT_ID 0x5152 /* no EEPROM, no firmware */

View File

@ -995,6 +995,16 @@ UNUSUAL_DEV( 0x071b, 0x3203, 0x0000, 0x0000,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_NO_WP_DETECT | US_FL_MAX_SECTORS_64),
/* Reported by Jean-Baptiste Onofre <jb@nanthrax.net>
* Support the following product :
* "Dane-Elec MediaTouch"
*/
UNUSUAL_DEV( 0x071b, 0x32bb, 0x0000, 0x0000,
"RockChip",
"MTP",
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_NO_WP_DETECT | US_FL_MAX_SECTORS_64),
/* Reported by Massimiliano Ghilardi <massimiliano.ghilardi@gmail.com>
* This USB MP3/AVI player device fails and disconnects if more than 128
* sectors (64kB) are read/written in a single command, and may be present
@ -1251,6 +1261,13 @@ UNUSUAL_DEV( 0x0840, 0x0084, 0x0001, 0x0001,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY),
/* Reported by Martijn Hijdra <martijn.hijdra@gmail.com> */
UNUSUAL_DEV( 0x0840, 0x0085, 0x0001, 0x0001,
"Argosy",
"Storage",
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY),
/* Entry and supporting patch by Theodore Kilgore <kilgota@auburn.edu>.
* Flag will support Bulk devices which use a standards-violating 32-byte
* Command Block Wrapper. Here, the "DC2MEGA" cameras (several brands) with
@ -1589,6 +1606,13 @@ UNUSUAL_DEV( 0x0fce, 0xd008, 0x0000, 0x0000,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_NO_WP_DETECT ),
/* Reported by The Solutor <thesolutor@gmail.com> */
UNUSUAL_DEV( 0x0fce, 0xd0e1, 0x0000, 0x0000,
"Sony Ericsson",
"MD400",
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_IGNORE_DEVICE),
/* Reported by Jan Mate <mate@fiit.stuba.sk>
* and by Soeren Sonnenburg <kernel@nn7.de> */
UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000,
@ -2031,15 +2055,11 @@ UNUSUAL_DEV( 0x1652, 0x6600, 0x0201, 0x0201,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE ),
/* Reported by Mauro Andreolini <andreoli@weblab.ing.unimo.it>
* This entry is needed to bypass the ZeroCD mechanism
* and to properly load as a modem device.
*/
UNUSUAL_DEV( 0x19d2, 0x2000, 0x0000, 0x0000,
"Onda ET502HS",
"USB MMC Storage",
UNUSUAL_DEV( 0x2116, 0x0320, 0x0001, 0x0001,
"ST",
"2A",
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_IGNORE_DEVICE),
US_FL_FIX_CAPACITY),
/* patch submitted by Davide Perini <perini.davide@dpsoftware.org>
* and Renato Perini <rperini@email.it>

View File

@ -418,6 +418,8 @@ struct usb_tt;
* @autosuspend_disabled: autosuspend disabled by the user
* @autoresume_disabled: autoresume disabled by the user
* @skip_sys_resume: skip the next system resume
* @wusb_dev: if this is a Wireless USB device, link to the WUSB
* specific data for the device.
*
* Notes:
* Usbcore drivers should not set usbdev->state directly. Instead use