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:
Коммит
e4a7ca2903
|
@ -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;
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(®s->usb_mod), in_8(®s->usb_addr),
|
||||
in_8(®s->usb_comm), in_be16(®s->usb_ep[0]),
|
||||
in_be16(®s->usb_event), in_be16(®s->usb_mask),
|
||||
in_8(®s->usb_status), in_be16(®s->usb_sof_tmr),
|
||||
in_be16(®s->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);
|
||||
}
|
|
@ -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");
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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++;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) },
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) },
|
||||
|
|
|
@ -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) },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче