aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/cfu1
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/cfu1')
-rw-r--r--recipes/cfu1/cfu1-modules_2.6.bb30
-rw-r--r--recipes/cfu1/files/Makefile2
-rw-r--r--recipes/cfu1/files/hcd.h487
-rw-r--r--recipes/cfu1/files/hub.h227
-rw-r--r--recipes/cfu1/files/rex-cfu1.conf6
-rw-r--r--recipes/cfu1/files/sl811.h266
-rw-r--r--recipes/cfu1/files/sl811_cs.c518
-rw-r--r--recipes/cfu1/files/sl811_hcd.c1932
8 files changed, 3468 insertions, 0 deletions
diff --git a/recipes/cfu1/cfu1-modules_2.6.bb b/recipes/cfu1/cfu1-modules_2.6.bb
new file mode 100644
index 0000000000..b6446c689b
--- /dev/null
+++ b/recipes/cfu1/cfu1-modules_2.6.bb
@@ -0,0 +1,30 @@
+DESCRIPTION = "PCMCIA driver for the RATOC REX-CFU1 USB host controller CF card."
+SECTION = "kernel/modules"
+PRIORITY = "optional"
+DEPENDS = "virtual/kernel"
+LICENSE = "GPL"
+RDEPENDS = "kernel-module-usbcore"
+PR = "r2"
+
+SRC_URI = "file://rex-cfu1.conf \
+ file://Makefile \
+ file://sl811_cs.c \
+ file://sl811_hcd.c \
+ file://sl811.h \
+ file://hcd.h \
+ file://hub.h"
+
+S = "${WORKDIR}"
+
+inherit module
+
+EXTRA_OEMAKE = 'EXTRA_CFLAGS="-DCONFIG_USB_SL811_CS -DCONFIG_USB_DEBUG" -C ${STAGING_KERNEL_DIR} SUBDIRS=${WORKDIR}'
+
+do_install() {
+ install -d ${D}/lib/modules/${KERNEL_VERSION}/pcmcia/
+ install -m 0644 sl811_cs.ko ${D}/lib/modules/${KERNEL_VERSION}/pcmcia/
+ install -m 0644 sl811_hcd.ko ${D}/lib/modules/${KERNEL_VERSION}/pcmcia/
+
+ install -d ${D}/${sysconfdir}/pcmcia/
+ install -m 0644 ${WORKDIR}/rex-cfu1.conf ${D}/${sysconfdir}/pcmcia/
+}
diff --git a/recipes/cfu1/files/Makefile b/recipes/cfu1/files/Makefile
new file mode 100644
index 0000000000..dbfb40d18a
--- /dev/null
+++ b/recipes/cfu1/files/Makefile
@@ -0,0 +1,2 @@
+obj-m += sl811_cs.o
+obj-m += sl811_hcd.o
diff --git a/recipes/cfu1/files/hcd.h b/recipes/cfu1/files/hcd.h
new file mode 100644
index 0000000000..a9bb99053b
--- /dev/null
+++ b/recipes/cfu1/files/hcd.h
@@ -0,0 +1,487 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifdef __KERNEL__
+
+/* This file contains declarations of usbcore internals that are mostly
+ * used or exposed by Host Controller Drivers.
+ */
+
+/*
+ * USB Packet IDs (PIDs)
+ */
+#define USB_PID_UNDEF_0 0xf0
+#define USB_PID_OUT 0xe1
+#define USB_PID_ACK 0xd2
+#define USB_PID_DATA0 0xc3
+#define USB_PID_PING 0xb4 /* USB 2.0 */
+#define USB_PID_SOF 0xa5
+#define USB_PID_NYET 0x96 /* USB 2.0 */
+#define USB_PID_DATA2 0x87 /* USB 2.0 */
+#define USB_PID_SPLIT 0x78 /* USB 2.0 */
+#define USB_PID_IN 0x69
+#define USB_PID_NAK 0x5a
+#define USB_PID_DATA1 0x4b
+#define USB_PID_PREAMBLE 0x3c /* Token mode */
+#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */
+#define USB_PID_SETUP 0x2d
+#define USB_PID_STALL 0x1e
+#define USB_PID_MDATA 0x0f /* USB 2.0 */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * USB Host Controller Driver (usb_hcd) framework
+ *
+ * Since "struct usb_bus" is so thin, you can't share much code in it.
+ * This framework is a layer over that, and should be more sharable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_hcd { /* usb_bus.hcpriv points to this */
+
+ /*
+ * housekeeping
+ */
+ struct usb_bus self; /* hcd is-a bus */
+
+ const char *product_desc; /* product/vendor string */
+ char irq_descr[24]; /* driver + bus # */
+
+ struct timer_list rh_timer; /* drives root-hub polling */
+ struct urb *status_urb; /* the current status urb */
+
+ /*
+ * hardware info/state
+ */
+ const struct hc_driver *driver; /* hw-specific hooks */
+ unsigned saw_irq : 1;
+ unsigned can_wakeup:1; /* hw supports wakeup? */
+ unsigned remote_wakeup:1;/* sw should use wakeup? */
+ unsigned rh_registered:1;/* is root hub registered? */
+
+ /* The next flag is a stopgap, to be removed when all the HCDs
+ * support the new root-hub polling mechanism. */
+ unsigned uses_new_polling:1;
+ unsigned poll_rh:1; /* poll for rh status? */
+ unsigned poll_pending:1; /* status has changed? */
+
+ int irq; /* irq allocated */
+ void __iomem *regs; /* device memory/io */
+ u64 rsrc_start; /* memory/io resource start */
+ u64 rsrc_len; /* memory/io resource length */
+ unsigned power_budget; /* in mA, 0 = no limit */
+
+#define HCD_BUFFER_POOLS 4
+ struct dma_pool *pool [HCD_BUFFER_POOLS];
+
+ int state;
+# define __ACTIVE 0x01
+# define __SUSPEND 0x04
+# define __TRANSIENT 0x80
+
+# define HC_STATE_HALT 0
+# define HC_STATE_RUNNING (__ACTIVE)
+# define HC_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE)
+# define HC_STATE_RESUMING (__SUSPEND|__TRANSIENT)
+# define HC_STATE_SUSPENDED (__SUSPEND)
+
+#define HC_IS_RUNNING(state) ((state) & __ACTIVE)
+#define HC_IS_SUSPENDED(state) ((state) & __SUSPEND)
+
+ /* more shared queuing code would be good; it should support
+ * smarter scheduling, handle transaction translators, etc;
+ * input size of periodic table to an interrupt scheduler.
+ * (ohci 32, uhci 1024, ehci 256/512/1024).
+ */
+
+ /* The HC driver's private data is stored at the end of
+ * this structure.
+ */
+ unsigned long hcd_priv[0]
+ __attribute__ ((aligned (sizeof(unsigned long))));
+};
+
+/* 2.4 does this a bit differently ... */
+static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
+{
+ return &hcd->self;
+}
+
+
+// urb.hcpriv is really hardware-specific
+
+struct hcd_timeout { /* timeouts we allocate */
+ struct list_head timeout_list;
+ struct timer_list timer;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * FIXME usb_operations should vanish or become hc_driver,
+ * when usb_bus and usb_hcd become the same thing.
+ */
+
+struct usb_operations {
+ int (*get_frame_number) (struct usb_device *usb_dev);
+ int (*submit_urb) (struct urb *urb, int mem_flags);
+ int (*unlink_urb) (struct urb *urb, int status);
+
+ /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
+ void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
+ int mem_flags,
+ dma_addr_t *dma);
+ void (*buffer_free)(struct usb_bus *bus, size_t size,
+ void *addr, dma_addr_t dma);
+
+ void (*disable)(struct usb_device *udev,
+ struct usb_host_endpoint *ep);
+
+ /* global suspend/resume of bus */
+ int (*hub_suspend)(struct usb_bus *);
+ int (*hub_resume)(struct usb_bus *);
+};
+
+/* each driver provides one of these, and hardware init support */
+
+struct pt_regs;
+
+struct hc_driver {
+ const char *description; /* "ehci-hcd" etc */
+ const char *product_desc; /* product/vendor string */
+ size_t hcd_priv_size; /* size of private data */
+
+ /* irq handler */
+ irqreturn_t (*irq) (struct usb_hcd *hcd, struct pt_regs *regs);
+
+ int flags;
+#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
+#define HCD_USB11 0x0010 /* USB 1.1 */
+#define HCD_USB2 0x0020 /* USB 2.0 */
+
+ /* called to init HCD and root hub */
+ int (*reset) (struct usb_hcd *hcd);
+ int (*start) (struct usb_hcd *hcd);
+
+ /* NOTE: these suspend/resume calls relate to the HC as
+ * a whole, not just the root hub; they're for bus glue.
+ */
+ /* called after all devices were suspended */
+ int (*suspend) (struct usb_hcd *hcd, pm_message_t message);
+
+ /* called before any devices get resumed */
+ int (*resume) (struct usb_hcd *hcd);
+
+ /* cleanly make HCD stop writing memory and doing I/O */
+ void (*stop) (struct usb_hcd *hcd);
+
+ /* return current frame number */
+ int (*get_frame_number) (struct usb_hcd *hcd);
+
+ /* manage i/o requests, device state */
+ int (*urb_enqueue) (struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep,
+ struct urb *urb,
+ int mem_flags);
+ int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
+
+ /* hw synch, freeing endpoint resources that urb_dequeue can't */
+ void (*endpoint_disable)(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep);
+
+ /* root hub support */
+ int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
+ int (*hub_control) (struct usb_hcd *hcd,
+ u16 typeReq, u16 wValue, u16 wIndex,
+ char *buf, u16 wLength);
+ int (*hub_suspend)(struct usb_hcd *);
+ int (*hub_resume)(struct usb_hcd *);
+ int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
+ void (*hub_irq_enable)(struct usb_hcd *);
+ /* Needed only if port-change IRQs are level-triggered */
+};
+
+extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
+
+extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
+ struct device *dev, char *bus_name);
+extern void usb_put_hcd (struct usb_hcd *hcd);
+extern int usb_add_hcd(struct usb_hcd *hcd,
+ unsigned int irqnum, unsigned long irqflags);
+extern void usb_remove_hcd(struct usb_hcd *hcd);
+
+#ifdef CONFIG_PCI
+struct pci_dev;
+struct pci_device_id;
+extern int usb_hcd_pci_probe (struct pci_dev *dev,
+ const struct pci_device_id *id);
+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 state);
+extern int usb_hcd_pci_resume (struct pci_dev *dev);
+#endif /* CONFIG_PM */
+
+#endif /* CONFIG_PCI */
+
+/* pci-ish (pdev null is ok) buffer alloc/mapping support */
+int hcd_buffer_create (struct usb_hcd *hcd);
+void hcd_buffer_destroy (struct usb_hcd *hcd);
+
+void *hcd_buffer_alloc (struct usb_bus *bus, size_t size,
+ int mem_flags, dma_addr_t *dma);
+void hcd_buffer_free (struct usb_bus *bus, size_t size,
+ void *addr, dma_addr_t dma);
+
+/* generic bus glue, needed for host controllers that don't use PCI */
+extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r);
+
+extern void usb_hc_died (struct usb_hcd *hcd);
+extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd);
+
+/* -------------------------------------------------------------------------- */
+
+/* Enumeration is only for the hub driver, or HCD virtual root hubs */
+extern struct usb_device *usb_alloc_dev(struct usb_device *parent,
+ struct usb_bus *, unsigned port);
+extern int usb_new_device(struct usb_device *dev);
+extern void usb_disconnect(struct usb_device **);
+
+extern int usb_get_configuration(struct usb_device *dev);
+extern void usb_destroy_configuration(struct usb_device *dev);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * HCD Root Hub support
+ */
+
+#include "hub.h"
+
+/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
+#define DeviceRequest \
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define DeviceOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+
+#define InterfaceRequest \
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+
+#define EndpointRequest \
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define EndpointOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+
+/* class requests from the USB 2.0 hub spec, table 11-15 */
+/* GetBusState and SetHubDescriptor are optional, omitted */
+#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
+#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)
+#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)
+#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS)
+#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS)
+#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE)
+#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Generic bandwidth allocation constants/support
+ */
+#define FRAME_TIME_USECS 1000L
+#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */
+ /* Trying not to use worst-case bit-stuffing
+ of (7/6 * 8 * bytecount) = 9.33 * bytecount */
+ /* bytecount = data payload byte count */
+
+#define NS_TO_US(ns) ((ns + 500L) / 1000L)
+ /* convert & round nanoseconds to microseconds */
+
+extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb,
+ int bustime, int isoc);
+extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb,
+ int isoc);
+
+/*
+ * Full/low speed bandwidth allocation constants/support.
+ */
+#define BW_HOST_DELAY 1000L /* nanoseconds */
+#define BW_HUB_LS_SETUP 333L /* nanoseconds */
+ /* 4 full-speed bit times (est.) */
+
+#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */
+#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L)
+#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L)
+
+extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
+
+/*
+ * Ceiling microseconds (typical) for that many bytes at high speed
+ * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
+ * to preallocate bandwidth)
+ */
+#define USB2_HOST_DELAY 5 /* nsec, guess */
+#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \
+ + ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ + USB2_HOST_DELAY)
+#define HS_USECS_ISO(bytes) NS_TO_US ( ((38 * 8 * 2083)/1000) \
+ + ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ + USB2_HOST_DELAY)
+
+extern long usb_calc_bus_time (int speed, int is_input,
+ int isoc, int bytecount);
+
+/*-------------------------------------------------------------------------*/
+
+extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
+
+extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
+
+extern void usb_set_device_state(struct usb_device *udev,
+ enum usb_device_state new_state);
+
+/*-------------------------------------------------------------------------*/
+
+/* exported only within usbcore */
+
+extern struct list_head usb_bus_list;
+extern struct semaphore usb_bus_list_lock;
+extern wait_queue_head_t usb_kill_urb_queue;
+
+extern struct usb_bus *usb_bus_get (struct usb_bus *bus);
+extern void usb_bus_put (struct usb_bus *bus);
+
+extern void usb_enable_root_hub_irq (struct usb_bus *bus);
+
+extern int usb_find_interface_driver (struct usb_device *dev,
+ struct usb_interface *interface);
+
+#define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN))
+
+/*
+ * USB device fs stuff
+ */
+
+#ifdef CONFIG_USB_DEVICEFS
+
+/*
+ * these are expected to be called from the USB core/hub thread
+ * with the kernel lock held
+ */
+extern void usbfs_add_bus(struct usb_bus *bus);
+extern void usbfs_remove_bus(struct usb_bus *bus);
+extern void usbfs_add_device(struct usb_device *dev);
+extern void usbfs_remove_device(struct usb_device *dev);
+extern void usbfs_update_special (void);
+
+extern int usbfs_init(void);
+extern void usbfs_cleanup(void);
+
+#else /* CONFIG_USB_DEVICEFS */
+
+static inline void usbfs_add_bus(struct usb_bus *bus) {}
+static inline void usbfs_remove_bus(struct usb_bus *bus) {}
+static inline void usbfs_add_device(struct usb_device *dev) {}
+static inline void usbfs_remove_device(struct usb_device *dev) {}
+static inline void usbfs_update_special (void) {}
+
+static inline int usbfs_init(void) { return 0; }
+static inline void usbfs_cleanup(void) { }
+
+#endif /* CONFIG_USB_DEVICEFS */
+
+/*-------------------------------------------------------------------------*/
+
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+
+struct usb_mon_operations {
+ void (*urb_submit)(struct usb_bus *bus, struct urb *urb);
+ void (*urb_submit_error)(struct usb_bus *bus, struct urb *urb, int err);
+ void (*urb_complete)(struct usb_bus *bus, struct urb *urb);
+ /* void (*urb_unlink)(struct usb_bus *bus, struct urb *urb); */
+ void (*bus_add)(struct usb_bus *bus);
+ void (*bus_remove)(struct usb_bus *bus);
+};
+
+extern struct usb_mon_operations *mon_ops;
+
+static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb)
+{
+ if (bus->monitored)
+ (*mon_ops->urb_submit)(bus, urb);
+}
+
+static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
+ int error)
+{
+ if (bus->monitored)
+ (*mon_ops->urb_submit_error)(bus, urb, error);
+}
+
+static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb)
+{
+ if (bus->monitored)
+ (*mon_ops->urb_complete)(bus, urb);
+}
+
+static inline void usbmon_notify_bus_add(struct usb_bus *bus)
+{
+ if (mon_ops)
+ (*mon_ops->bus_add)(bus);
+}
+
+static inline void usbmon_notify_bus_remove(struct usb_bus *bus)
+{
+ if (mon_ops)
+ (*mon_ops->bus_remove)(bus);
+}
+
+int usb_mon_register(struct usb_mon_operations *ops);
+void usb_mon_deregister(void);
+
+#else
+
+static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) {}
+static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
+ int error) {}
+static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) {}
+static inline void usbmon_notify_bus_add(struct usb_bus *bus) {}
+static inline void usbmon_notify_bus_remove(struct usb_bus *bus) {}
+
+#endif /* CONFIG_USB_MON */
+
+/*-------------------------------------------------------------------------*/
+
+/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */
+// bleech -- resurfaced in 2.4.11 or 2.4.12
+#define bitmap DeviceRemovable
+
+
+/*-------------------------------------------------------------------------*/
+
+/* random stuff */
+
+#define RUN_CONTEXT (in_irq () ? "in_irq" \
+ : (in_interrupt () ? "in_interrupt" : "can sleep"))
+
+
+#endif /* __KERNEL__ */
+
diff --git a/recipes/cfu1/files/hub.h b/recipes/cfu1/files/hub.h
new file mode 100644
index 0000000000..53bf564962
--- /dev/null
+++ b/recipes/cfu1/files/hub.h
@@ -0,0 +1,227 @@
+#ifndef __LINUX_HUB_H
+#define __LINUX_HUB_H
+
+/*
+ * Hub protocol and driver data structures.
+ *
+ * Some of these are known to the "virtual root hub" code
+ * in host controller drivers.
+ */
+
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/compiler.h> /* likely()/unlikely() */
+
+/*
+ * Hub request types
+ */
+
+#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)
+#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
+
+/*
+ * Hub class requests
+ * See USB 2.0 spec Table 11-16
+ */
+#define HUB_CLEAR_TT_BUFFER 8
+#define HUB_RESET_TT 9
+#define HUB_GET_TT_STATE 10
+#define HUB_STOP_TT 11
+
+/*
+ * Hub Class feature numbers
+ * See USB 2.0 spec Table 11-17
+ */
+#define C_HUB_LOCAL_POWER 0
+#define C_HUB_OVER_CURRENT 1
+
+/*
+ * Port feature numbers
+ * See USB 2.0 spec Table 11-17
+ */
+#define USB_PORT_FEAT_CONNECTION 0
+#define USB_PORT_FEAT_ENABLE 1
+#define USB_PORT_FEAT_SUSPEND 2
+#define USB_PORT_FEAT_OVER_CURRENT 3
+#define USB_PORT_FEAT_RESET 4
+#define USB_PORT_FEAT_POWER 8
+#define USB_PORT_FEAT_LOWSPEED 9
+#define USB_PORT_FEAT_HIGHSPEED 10
+#define USB_PORT_FEAT_C_CONNECTION 16
+#define USB_PORT_FEAT_C_ENABLE 17
+#define USB_PORT_FEAT_C_SUSPEND 18
+#define USB_PORT_FEAT_C_OVER_CURRENT 19
+#define USB_PORT_FEAT_C_RESET 20
+#define USB_PORT_FEAT_TEST 21
+#define USB_PORT_FEAT_INDICATOR 22
+
+/*
+ * Hub Status and Hub Change results
+ * See USB 2.0 spec Table 11-19 and Table 11-20
+ */
+struct usb_port_status {
+ __le16 wPortStatus;
+ __le16 wPortChange;
+} __attribute__ ((packed));
+
+/*
+ * wPortStatus bit field
+ * See USB 2.0 spec Table 11-21
+ */
+#define USB_PORT_STAT_CONNECTION 0x0001
+#define USB_PORT_STAT_ENABLE 0x0002
+#define USB_PORT_STAT_SUSPEND 0x0004
+#define USB_PORT_STAT_OVERCURRENT 0x0008
+#define USB_PORT_STAT_RESET 0x0010
+/* bits 5 to 7 are reserved */
+#define USB_PORT_STAT_POWER 0x0100
+#define USB_PORT_STAT_LOW_SPEED 0x0200
+#define USB_PORT_STAT_HIGH_SPEED 0x0400
+#define USB_PORT_STAT_TEST 0x0800
+#define USB_PORT_STAT_INDICATOR 0x1000
+/* bits 13 to 15 are reserved */
+
+/*
+ * wPortChange bit field
+ * See USB 2.0 spec Table 11-22
+ * Bits 0 to 4 shown, bits 5 to 15 are reserved
+ */
+#define USB_PORT_STAT_C_CONNECTION 0x0001
+#define USB_PORT_STAT_C_ENABLE 0x0002
+#define USB_PORT_STAT_C_SUSPEND 0x0004
+#define USB_PORT_STAT_C_OVERCURRENT 0x0008
+#define USB_PORT_STAT_C_RESET 0x0010
+
+/*
+ * wHubCharacteristics (masks)
+ * See USB 2.0 spec Table 11-13, offset 3
+ */
+#define HUB_CHAR_LPSM 0x0003 /* D1 .. D0 */
+#define HUB_CHAR_COMPOUND 0x0004 /* D2 */
+#define HUB_CHAR_OCPM 0x0018 /* D4 .. D3 */
+#define HUB_CHAR_TTTT 0x0060 /* D6 .. D5 */
+#define HUB_CHAR_PORTIND 0x0080 /* D7 */
+
+struct usb_hub_status {
+ __le16 wHubStatus;
+ __le16 wHubChange;
+} __attribute__ ((packed));
+
+/*
+ * Hub Status & Hub Change bit masks
+ * See USB 2.0 spec Table 11-19 and Table 11-20
+ * Bits 0 and 1 for wHubStatus and wHubChange
+ * Bits 2 to 15 are reserved for both
+ */
+#define HUB_STATUS_LOCAL_POWER 0x0001
+#define HUB_STATUS_OVERCURRENT 0x0002
+#define HUB_CHANGE_LOCAL_POWER 0x0001
+#define HUB_CHANGE_OVERCURRENT 0x0002
+
+
+/*
+ * Hub descriptor
+ * See USB 2.0 spec Table 11-13
+ */
+
+#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
+#define USB_DT_HUB_NONVAR_SIZE 7
+
+struct usb_hub_descriptor {
+ __u8 bDescLength;
+ __u8 bDescriptorType;
+ __u8 bNbrPorts;
+ __u16 wHubCharacteristics;
+ __u8 bPwrOn2PwrGood;
+ __u8 bHubContrCurrent;
+ /* add 1 bit for hub status change; round to bytes */
+ __u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
+ __u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
+} __attribute__ ((packed));
+
+
+/* port indicator status selectors, tables 11-7 and 11-25 */
+#define HUB_LED_AUTO 0
+#define HUB_LED_AMBER 1
+#define HUB_LED_GREEN 2
+#define HUB_LED_OFF 3
+
+enum hub_led_mode {
+ INDICATOR_AUTO = 0,
+ INDICATOR_CYCLE,
+ /* software blinks for attention: software, hardware, reserved */
+ INDICATOR_GREEN_BLINK, INDICATOR_GREEN_BLINK_OFF,
+ INDICATOR_AMBER_BLINK, INDICATOR_AMBER_BLINK_OFF,
+ INDICATOR_ALT_BLINK, INDICATOR_ALT_BLINK_OFF
+} __attribute__ ((packed));
+
+struct usb_device;
+
+/*
+ * As of USB 2.0, full/low speed devices are segregated into trees.
+ * One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
+ * The other type grows from high speed hubs when they connect to
+ * full/low speed devices using "Transaction Translators" (TTs).
+ *
+ * TTs should only be known to the hub driver, and high speed bus
+ * drivers (only EHCI for now). They affect periodic scheduling and
+ * sometimes control/bulk error recovery.
+ */
+struct usb_tt {
+ struct usb_device *hub; /* upstream highspeed hub */
+ int multi; /* true means one TT per port */
+
+ /* for control/bulk error recovery (CLEAR_TT_BUFFER) */
+ spinlock_t lock;
+ struct list_head clear_list; /* of usb_tt_clear */
+ struct work_struct kevent;
+};
+
+struct usb_tt_clear {
+ struct list_head clear_list;
+ unsigned tt;
+ u16 devinfo;
+};
+
+extern void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe);
+
+struct usb_hub {
+ struct device *intfdev; /* the "interface" device */
+ struct usb_device *hdev;
+ struct urb *urb; /* for interrupt polling pipe */
+
+ /* buffer for urb ... with extra space in case of babble */
+ char (*buffer)[8];
+ dma_addr_t buffer_dma; /* DMA address for buffer */
+ union {
+ struct usb_hub_status hub;
+ struct usb_port_status port;
+ } *status; /* buffer for status reports */
+
+ int error; /* last reported error */
+ int nerrors; /* track consecutive errors */
+
+ struct list_head event_list; /* hubs w/data or errs ready */
+ unsigned long event_bits[1]; /* status change bitmask */
+ unsigned long change_bits[1]; /* ports with logical connect
+ status change */
+ unsigned long busy_bits[1]; /* ports being reset */
+#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
+#error event_bits[] is too short!
+#endif
+
+ struct usb_hub_descriptor *descriptor; /* class descriptor */
+ struct usb_tt tt; /* Transaction Translator */
+
+ u8 power_budget; /* in 2mA units; or zero */
+
+ unsigned quiescing:1;
+ unsigned activating:1;
+ unsigned resume_root_hub:1;
+
+ unsigned has_indicators:1;
+ enum hub_led_mode indicator[USB_MAXCHILDREN];
+ struct work_struct leds;
+};
+
+#endif /* __LINUX_HUB_H */
diff --git a/recipes/cfu1/files/rex-cfu1.conf b/recipes/cfu1/files/rex-cfu1.conf
new file mode 100644
index 0000000000..fd06c621df
--- /dev/null
+++ b/recipes/cfu1/files/rex-cfu1.conf
@@ -0,0 +1,6 @@
+device "sl811_cs"
+ class "usb" module "sl811_hcd", "sl811_cs"
+
+card "RATOC USB HOST CF+ Card"
+ manfid 0xc015, 0x0001
+ bind "sl811_cs"
diff --git a/recipes/cfu1/files/sl811.h b/recipes/cfu1/files/sl811.h
new file mode 100644
index 0000000000..7690d98e42
--- /dev/null
+++ b/recipes/cfu1/files/sl811.h
@@ -0,0 +1,266 @@
+/*
+ * SL811HS register declarations and HCD data structures
+ *
+ * Copyright (C) 2004 Psion Teklogix
+ * Copyright (C) 2004 David Brownell
+ * Copyright (C) 2001 Cypress Semiconductor Inc.
+ */
+
+/*
+ * SL811HS has transfer registers, and control registers. In host/master
+ * mode one set of registers is used; in peripheral/slave mode, another.
+ * - SL11H only has some "A" transfer registers from 0x00-0x04
+ * - SL811HS also has "B" registers from 0x08-0x0c
+ * - SL811S (or HS in slave mode) has four A+B sets, at 00, 10, 20, 30
+ */
+
+#define SL811_EP_A(base) ((base) + 0)
+#define SL811_EP_B(base) ((base) + 8)
+
+#define SL811_HOST_BUF 0x00
+#define SL811_PERIPH_EP0 0x00
+#define SL811_PERIPH_EP1 0x10
+#define SL811_PERIPH_EP2 0x20
+#define SL811_PERIPH_EP3 0x30
+
+
+/* TRANSFER REGISTERS: host and peripheral sides are similar
+ * except for the control models (master vs slave).
+ */
+#define SL11H_HOSTCTLREG 0
+# define SL11H_HCTLMASK_ARM 0x01
+# define SL11H_HCTLMASK_ENABLE 0x02
+# define SL11H_HCTLMASK_IN 0x00
+# define SL11H_HCTLMASK_OUT 0x04
+# define SL11H_HCTLMASK_ISOCH 0x10
+# define SL11H_HCTLMASK_AFTERSOF 0x20
+# define SL11H_HCTLMASK_TOGGLE 0x40
+# define SL11H_HCTLMASK_PREAMBLE 0x80
+#define SL11H_BUFADDRREG 1
+#define SL11H_BUFLNTHREG 2
+#define SL11H_PKTSTATREG 3 /* read */
+# define SL11H_STATMASK_ACK 0x01
+# define SL11H_STATMASK_ERROR 0x02
+# define SL11H_STATMASK_TMOUT 0x04
+# define SL11H_STATMASK_SEQ 0x08
+# define SL11H_STATMASK_SETUP 0x10
+# define SL11H_STATMASK_OVF 0x20
+# define SL11H_STATMASK_NAK 0x40
+# define SL11H_STATMASK_STALL 0x80
+#define SL11H_PIDEPREG 3 /* write */
+# define SL_SETUP 0xd0
+# define SL_IN 0x90
+# define SL_OUT 0x10
+# define SL_SOF 0x50
+# define SL_PREAMBLE 0xc0
+# define SL_NAK 0xa0
+# define SL_STALL 0xe0
+# define SL_DATA0 0x30
+# define SL_DATA1 0xb0
+#define SL11H_XFERCNTREG 4 /* read */
+#define SL11H_DEVADDRREG 4 /* write */
+
+
+/* CONTROL REGISTERS: host and peripheral are very different.
+ */
+#define SL11H_CTLREG1 5
+# define SL11H_CTL1MASK_SOF_ENA 0x01
+# define SL11H_CTL1MASK_FORCE 0x18
+# define SL11H_CTL1MASK_NORMAL 0x00
+# define SL11H_CTL1MASK_SE0 0x08 /* reset */
+# define SL11H_CTL1MASK_J 0x10
+# define SL11H_CTL1MASK_K 0x18 /* resume */
+# define SL11H_CTL1MASK_LSPD 0x20
+# define SL11H_CTL1MASK_SUSPEND 0x40
+#define SL11H_IRQ_ENABLE 6
+# define SL11H_INTMASK_DONE_A 0x01
+# define SL11H_INTMASK_DONE_B 0x02
+# define SL11H_INTMASK_SOFINTR 0x10
+# define SL11H_INTMASK_INSRMV 0x20 /* to/from SE0 */
+# define SL11H_INTMASK_RD 0x40
+# define SL11H_INTMASK_DP 0x80 /* only in INTSTATREG */
+#define SL11S_ADDRESS 7
+
+/* 0x08-0x0c are for the B buffer (not in SL11) */
+
+#define SL11H_IRQ_STATUS 0x0D /* write to ack */
+#define SL11H_HWREVREG 0x0E /* read */
+# define SL11H_HWRMASK_HWREV 0xF0
+#define SL11H_SOFLOWREG 0x0E /* write */
+#define SL11H_SOFTMRREG 0x0F /* read */
+
+/* a write to this register enables SL811HS features.
+ * HOST flag presumably overrides the chip input signal?
+ */
+#define SL811HS_CTLREG2 0x0F
+# define SL811HS_CTL2MASK_SOF_MASK 0x3F
+# define SL811HS_CTL2MASK_DSWAP 0x40
+# define SL811HS_CTL2MASK_HOST 0x80
+
+#define SL811HS_CTL2_INIT (SL811HS_CTL2MASK_HOST | 0x2e)
+
+
+/* DATA BUFFERS: registers from 0x10..0xff are for data buffers;
+ * that's 240 bytes, which we'll split evenly between A and B sides.
+ * Only ISO can use more than 64 bytes per packet.
+ * (The SL11S has 0x40..0xff for buffers.)
+ */
+#define H_MAXPACKET 120 /* bytes in A or B fifos */
+
+#define SL11H_DATA_START 0x10
+#define SL811HS_PACKET_BUF(is_a) ((is_a) \
+ ? SL11H_DATA_START \
+ : (SL11H_DATA_START + H_MAXPACKET))
+
+/*-------------------------------------------------------------------------*/
+
+#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
+#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
+
+struct sl811 {
+ spinlock_t lock;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+ struct sl811_platform_data *board;
+ struct proc_dir_entry *pde;
+
+ unsigned long stat_insrmv;
+ unsigned long stat_wake;
+ unsigned long stat_sof;
+ unsigned long stat_a;
+ unsigned long stat_b;
+ unsigned long stat_lost;
+ unsigned long stat_overrun;
+
+ /* sw model */
+ struct timer_list timer;
+ struct sl811h_ep *next_periodic;
+ struct sl811h_ep *next_async;
+
+ struct sl811h_ep *active_a;
+ unsigned long jiffies_a;
+ struct sl811h_ep *active_b;
+ unsigned long jiffies_b;
+
+ u32 port1;
+ u8 ctrl1, ctrl2, irq_enable;
+ u16 frame;
+
+ /* async schedule: control, bulk */
+ struct list_head async;
+
+ /* periodic schedule: interrupt, iso */
+ u16 load[PERIODIC_SIZE];
+ struct sl811h_ep *periodic[PERIODIC_SIZE];
+ unsigned periodic_count;
+};
+
+static inline struct sl811 *hcd_to_sl811(struct usb_hcd *hcd)
+{
+ return (struct sl811 *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *sl811_to_hcd(struct sl811 *sl811)
+{
+ return container_of((void *) sl811, struct usb_hcd, hcd_priv);
+}
+
+struct sl811h_ep {
+ struct usb_host_endpoint *hep;
+ struct usb_device *udev;
+
+ u8 defctrl;
+ u8 maxpacket;
+ u8 epnum;
+ u8 nextpid;
+
+ u16 error_count;
+ u16 nak_count;
+ u16 length; /* of current packet */
+
+ /* periodic schedule */
+ u16 period;
+ u16 branch;
+ u16 load;
+ struct sl811h_ep *next;
+
+ /* async schedule */
+ struct list_head schedule;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* These register utilities should work for the SL811S register API too
+ * NOTE: caller must hold sl811->lock.
+ */
+
+static inline u8 sl811_read(struct sl811 *sl811, int reg)
+{
+ writeb(reg, sl811->addr_reg);
+ return readb(sl811->data_reg);
+}
+
+static inline void sl811_write(struct sl811 *sl811, int reg, u8 val)
+{
+ writeb(reg, sl811->addr_reg);
+ writeb(val, sl811->data_reg);
+}
+
+static inline void
+sl811_write_buf(struct sl811 *sl811, int addr, const void *buf, size_t count)
+{
+ const u8 *data;
+ void __iomem *data_reg;
+
+ if (!count)
+ return;
+ writeb(addr, sl811->addr_reg);
+
+ data = buf;
+ data_reg = sl811->data_reg;
+ do {
+ writeb(*data++, data_reg);
+ } while (--count);
+}
+
+static inline void
+sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count)
+{
+ u8 *data;
+ void __iomem *data_reg;
+
+ if (!count)
+ return;
+ writeb(addr, sl811->addr_reg);
+
+ data = buf;
+ data_reg = sl811->data_reg;
+ do {
+ *data++ = readb(data_reg);
+ } while (--count);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG(stuff...) printk(KERN_DEBUG "sl811: " stuff)
+#else
+#define DBG(stuff...) do{}while(0)
+#endif
+
+#ifdef VERBOSE
+# define VDBG DBG
+#else
+# define VDBG(stuff...) do{}while(0)
+#endif
+
+#ifdef PACKET_TRACE
+# define PACKET VDBG
+#else
+# define PACKET(stuff...) do{}while(0)
+#endif
+
+#define ERR(stuff...) printk(KERN_ERR "sl811: " stuff)
+#define WARN(stuff...) printk(KERN_WARNING "sl811: " stuff)
+#define INFO(stuff...) printk(KERN_INFO "sl811: " stuff)
+
diff --git a/recipes/cfu1/files/sl811_cs.c b/recipes/cfu1/files/sl811_cs.c
new file mode 100644
index 0000000000..e223b647b3
--- /dev/null
+++ b/recipes/cfu1/files/sl811_cs.c
@@ -0,0 +1,518 @@
+/*
+ A SL811 CLIENT DRIVER for linux 2.4.x
+ Filename: sl811_cs.c
+ Version: 0.0.2
+ Author: Yukio Yamamoto
+
+ Port to sl811-hcd and 2.6.x by
+ Botond Botyanszki <boti()rocketmail.com>
+ Simon Pickering
+
+ Last update: 2005-05-05
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/version.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include <linux/usb_sl811.h>
+
+MODULE_AUTHOR("Botond Botyanszki");
+MODULE_DESCRIPTION("REX-CFU1 PCMCIA driver for 2.6");
+MODULE_LICENSE("GPL");
+
+
+/*====================================================================*/
+/* MACROS */
+/*====================================================================*/
+
+#if defined(DEBUG) || defined(CONFIG_USB_DEBUG)
+#define DBG(n, args...) printk(KERN_DEBUG "sl811_cs: " args)
+#else
+#define DBG(n, args...) do{}while(0)
+#endif
+
+#define INFO(args...) printk(KERN_INFO "sl811_cs: " args)
+
+/*static char *version = "sl811_cs.c 0.04 2005/04/27 00:00:00 (OpenZaurus Team)";*/
+
+#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
+
+#define CS_CHECK(fn, ret) \
+ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+/*====================================================================*/
+/* VARIABLES */
+/*====================================================================*/
+
+static dev_info_t dev_info = "sl811_cs";
+static dev_link_t *dev_list = NULL;
+
+static ioaddr_t base_addr = 0x00000000;
+static int irq = -1;
+
+static int irq_list[4] = { -1 };
+MODULE_PARM(irq_list, "1-4i");
+INT_MODULE_PARM(free_ports, 0);
+INT_MODULE_PARM(irq_mask, 0xdeb8);
+
+/*====================================================================*/
+/* PROTO TYPES */
+/*====================================================================*/
+
+static dev_link_t* sl811_cs_attach(void);
+static void sl811_cs_detach(dev_link_t *);
+static void sl811_cs_config(dev_link_t *link);
+static void sl811_cs_release(dev_link_t *arg);
+static int sl811_cs_event(event_t event, int priority,
+ event_callback_args_t *args);
+
+/*====================================================================*/
+/* PROTO TYPES */
+/*====================================================================*/
+
+typedef struct local_info_t {
+ dev_link_t link;
+ dev_node_t node;
+} local_info_t;
+
+static struct pcmcia_driver sl811_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "sl811_cs",
+ },
+ .attach = sl811_cs_attach,
+ .detach = sl811_cs_detach,
+};
+extern struct device_driver sl811h_driver;
+
+static struct sl811_platform_data platform_data;
+static struct res {
+ struct resource irq_res;
+ struct resource addr_res;
+ struct resource data_res;
+} resources;
+static struct platform_device platform_dev = {
+ .name = "sl811_cs",
+ .id = 0,
+ .num_resources = 0,
+ .dev.dma_mask = 0,
+ .dev.platform_data = &platform_data,
+ .dev.bus_id = "sl811-hcd",
+ .dev.driver = &sl811h_driver
+};
+
+/*====================================================================*/
+/* EXTERNAL FUNCTIONS */
+/*====================================================================*/
+int sl811h_probe(void *dev);
+
+/*====================================================================*/
+
+
+/*====================================================================*/
+static void release_platform_dev(struct device * dev) {
+ DBG(0, "sl811_cs platform_dev release\n");
+}
+
+/*====================================================================*/
+static dev_link_t *sl811_cs_attach(void)
+{
+ local_info_t *local;
+ dev_link_t *link;
+ client_reg_t client_reg;
+ int ret, i;
+
+ local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+ if (!local) return NULL;
+ memset(local, 0, sizeof(local_info_t));
+ link = &local->link; link->priv = local;
+
+ /* Initialize */
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+ if (irq_list[0] == -1)
+ link->irq.IRQInfo2 = irq_mask;
+ else
+ for (i = 0; i < 4; i++)
+ link->irq.IRQInfo2 |= 1 << irq_list[i];
+ link->irq.Handler = NULL;
+
+ link->conf.Attributes = 0;
+ link->conf.Vcc = 33;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+ client_reg.EventMask =
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.event_handler = &sl811_cs_event;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = pcmcia_register_client(&link->handle, &client_reg);
+ if (ret != CS_SUCCESS) {
+ cs_error(link->handle, RegisterClient, ret);
+ sl811_cs_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* sl811_cs_attach */
+
+/*====================================================================*/
+static void sl811_cs_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+
+ DBG(0, "sl811_cs_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+ if (*linkp == NULL)
+ return;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ if (link->state & DEV_CONFIG) {
+#ifdef PCMCIA_DEBUG
+ printk(KERN_DEBUG "sl811_cs: detach postponed, '%s' "
+ "still locked\n", link->dev->dev_name);
+#endif
+ link->state |= DEV_STALE_LINK;
+ return;
+ }
+#endif
+
+ /* Break the link with Card Services */
+ if (link->handle)
+ pcmcia_deregister_client(link->handle);
+
+ /* Unlink device structure, and free it */
+ *linkp = link->next;
+ /* This points to the parent local_info_t struct */
+ kfree(link->priv);
+
+} /* sl811_cs_detach */
+
+
+/*====================================================================*/
+static int sl811_hc_init(void)
+{
+ /* set up the device structure */
+ resources.irq_res.flags = IORESOURCE_IRQ;
+ resources.irq_res.start = irq;
+
+ resources.addr_res.flags = IORESOURCE_MEM;
+ resources.addr_res.start = base_addr;
+ resources.addr_res.end = base_addr + 1;
+
+ resources.data_res.flags = IORESOURCE_MEM;
+ resources.data_res.start = base_addr + 1;
+ resources.data_res.end = base_addr + 4;
+
+ platform_dev.dev.release = release_platform_dev;
+ platform_device_register(&platform_dev);
+ /* FIXME: we register the platform device with 0 resources
+ otherwise the unregister call won't work*/
+
+ platform_dev.num_resources = 3;
+ platform_dev.resource = (struct resource *) &resources;
+
+ /* try to initialize the host controller */
+ if (sl811h_probe(&platform_dev.dev) != 0) {
+ DBG(0, "sl811h_probe() didn't return 0\n");
+ return 0;
+ }
+ return 1;
+}
+
+
+/*====================================================================*/
+static void sl811_cs_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ local_info_t *dev = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int last_fn, last_ret;
+ u_char buf[64];
+ config_info_t conf;
+ cistpl_cftable_entry_t dflt = { 0 };
+
+ DBG(0, "sl811_cs_config(0x%p)\n", link);
+
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ /* Look up the current Vcc */
+ CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
+ link->conf.Vcc = conf.Vcc;
+
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ while (1) {
+ cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+ if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+ pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+ goto next_entry;
+
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT) {
+ dflt = *cfg;
+ }
+
+ if (cfg->index == 0) goto next_entry;
+
+ link->conf.ConfigIndex = cfg->index;
+
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status = CCSR_AUDIO_ENA;
+ }
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000) {
+ goto next_entry;
+ }
+ } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000) {
+ goto next_entry;
+ }
+ }
+
+ if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
+ link->conf.Vpp1 = link->conf.Vpp2 =
+ cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
+ else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
+ link->conf.Vpp1 = link->conf.Vpp2 =
+ dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+ link->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ link->io.NumPorts1 = link->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ link->io.BasePort1 = io->win[0].base;
+ link->io.NumPorts1 = io->win[0].len;
+
+ if (pcmcia_request_io(link->handle, &link->io) != 0)
+ goto next_entry;
+ }
+ break;
+
+ next_entry:
+ if (link->io.NumPorts1)
+ pcmcia_release_io(link->handle, &link->io);
+ last_ret = pcmcia_get_next_tuple(handle, &tuple);
+ }
+
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+
+ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+
+ if (free_ports) {
+ if (link->io.BasePort1)
+ release_region(link->io.BasePort1, link->io.NumPorts1);
+ }
+
+ sprintf(dev->node.dev_name, "cf_usb0");
+ dev->node.major = dev->node.minor = 0;
+ link->dev = &dev->node;
+
+ printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
+ dev->node.dev_name, link->conf.ConfigIndex,
+ link->conf.Vcc/10, link->conf.Vcc%10);
+ if (link->conf.Vpp1)
+ printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+ printk(", irq %d", link->irq.AssignedIRQ);
+ irq = link->irq.AssignedIRQ;
+ }
+
+ if (link->io.NumPorts1) {
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1+link->io.NumPorts1-1);
+ base_addr = link->io.BasePort1;
+ }
+
+ printk("\n");
+
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ /* Release resources claimed by PCMCIA for the sl811h driver */
+ release_region(link->io.BasePort1, link->io.NumPorts1);
+
+ if (sl811_hc_init() == 0) goto cs_failed;
+
+ return;
+
+ cs_failed:
+ printk("sl811_cs_config failed\n");
+ cs_error(link->handle, last_fn, last_ret);
+ sl811_cs_release(link);
+ link->state &= ~DEV_CONFIG_PENDING;
+
+} /* sl811_cs_config */
+
+/*====================================================================*/
+static void sl811_cs_release(dev_link_t * link)
+{
+
+ DBG(0, "sl811_cs_release(0x%p)\n", link);
+
+ if (link->open) {
+ DBG(1, "sl811_cs: release postponed, '%s' still open\n",
+ link->dev->dev_name);
+ link->state |= DEV_STALE_CONFIG;
+ return;
+ }
+
+ /* request IO, because PCMCIA thinks it has claimed it */
+ request_region(link->io.BasePort1, link->io.NumPorts1, "sl811_cs");
+
+ /* Unlink the device chain */
+ link->dev = NULL;
+
+ pcmcia_release_configuration(link->handle);
+ if (link->io.NumPorts1)
+ pcmcia_release_io(link->handle, &link->io);
+ if (link->irq.AssignedIRQ)
+ pcmcia_release_irq(link->handle, &link->irq);
+ link->state &= ~DEV_CONFIG;
+
+ if (link->state & DEV_STALE_LINK)
+ sl811_cs_detach(link);
+
+ /* FIXME: if the unregister call frees up the resources, it oopses
+ so we pretend to have 0 resources */
+ platform_dev.num_resources = 0;
+ platform_dev.resource = NULL;
+
+ platform_device_unregister(&platform_dev);
+
+} /* sl811_cs_release */
+
+/*====================================================================*/
+static int sl811_cs_event(event_t event, int priority, event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+
+ DBG(1, "sl811_cs_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG) {
+ sl811_cs_release(link);
+ }
+ break;
+
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ sl811_cs_config(link);
+ break;
+
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ pcmcia_release_configuration(link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (link->state & DEV_CONFIG)
+ pcmcia_request_configuration(link->handle, &link->conf);
+
+ INFO("FIXME: card reset\n");
+
+ break;
+ }
+ return 0;
+} /* sl811_cs_event */
+
+/*====================================================================*/
+static int __init init_sl811_cs(void)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ servinfo_t serv;
+ DBG(0, "%s\n", version);
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "sl811_cs: Card Services release "
+ "does not match!\n");
+ return -EINVAL;
+ }
+ register_pccard_driver(&dev_info, &sl811_cs_attach, &sl811_cs_detach);
+ return 0;
+#else
+ return pcmcia_register_driver(&sl811_driver);
+#endif
+
+}
+
+/*====================================================================*/
+static void __exit exit_sl811_cs(void)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ DBG(0, "sl811_cs: unloading\n");
+ unregister_pccard_driver(&dev_info);
+ while (dev_list != NULL) {
+ del_timer(&dev_list->release);
+ if (dev_list->state & DEV_CONFIG)
+ sl811_cs_release(dev_list);
+ sl811_cs_detach(dev_list);
+ }
+#else
+ pcmcia_unregister_driver(&sl811_driver);
+#endif
+
+}
+
+/*====================================================================*/
+
+module_init(init_sl811_cs);
+module_exit(exit_sl811_cs);
diff --git a/recipes/cfu1/files/sl811_hcd.c b/recipes/cfu1/files/sl811_hcd.c
new file mode 100644
index 0000000000..90e5f869b9
--- /dev/null
+++ b/recipes/cfu1/files/sl811_hcd.c
@@ -0,0 +1,1932 @@
+/*
+ * SL811HS HCD (Host Controller Driver) for USB.
+ *
+ * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Copyright (C) 2004 David Brownell
+ *
+ * Periodic scheduling is based on Roman's OHCI code
+ * Copyright (C) 1999 Roman Weissgaerber
+ *
+ * The SL811HS controller handles host side USB (like the SL11H, but with
+ * another register set and SOF generation) as well as peripheral side USB
+ * (like the SL811S). This driver version doesn't implement the Gadget API
+ * for the peripheral role; or OTG (that'd need much external circuitry).
+ *
+ * For documentation, see the SL811HS spec and the "SL811HS Embedded Host"
+ * document (providing significant pieces missing from that spec); plus
+ * the SL811S spec if you want peripheral side info.
+ */
+
+/*
+ * Status: Passed basic stress testing, works with hubs, mice, keyboards,
+ * and usb-storage.
+ *
+ * TODO:
+ * - usb suspend/resume triggered by sl811 (with USB_SUSPEND)
+ * - various issues noted in the code
+ * - performance work; use both register banks; ...
+ * - use urb->iso_frame_desc[] with ISO transfers
+ */
+
+#undef VERBOSE
+#undef PACKET_TRACE
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+# define DEBUG
+#else
+# undef DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb_sl811.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+
+#include "hcd.h"
+#include "sl811.h"
+
+
+MODULE_DESCRIPTION("SL811HS USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+
+#define DRIVER_VERSION "15 Dec 2004"
+
+
+#ifndef DEBUG
+# define STUB_DEBUG_FILE
+#endif
+
+/* for now, use only one transfer register bank */
+#undef USE_B
+
+/* this doesn't understand urb->iso_frame_desc[], but if you had a driver
+ * that just queued one ISO frame per URB then iso transfers "should" work
+ * using the normal urb status fields.
+ */
+#define DISABLE_ISO
+
+#define QUIRK2
+//#define QUIRK3
+
+static const char hcd_name[] = "sl811-hcd";
+
+/*-------------------------------------------------------------------------*/
+
+
+static void port_power(struct sl811 *sl811, int is_on)
+{
+ struct usb_hcd *hcd = sl811_to_hcd(sl811);
+
+ /* hub is inactive unless the port is powered */
+ if (is_on) {
+ if (sl811->port1 & (1 << USB_PORT_FEAT_POWER))
+ return;
+
+ sl811->port1 = (1 << USB_PORT_FEAT_POWER);
+ sl811->irq_enable = SL11H_INTMASK_INSRMV;
+ hcd->self.controller->power.power_state = PMSG_ON;
+ } else {
+ sl811->port1 = 0;
+ sl811->irq_enable = 0;
+ hcd->state = HC_STATE_HALT;
+ hcd->self.controller->power.power_state = PMSG_SUSPEND;
+ }
+ sl811->ctrl1 = 0;
+ sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
+ sl811_write(sl811, SL11H_IRQ_STATUS, ~0);
+
+ if (sl811->board && sl811->board->port_power) {
+ /* switch VBUS, at 500mA unless hub power budget gets set */
+ DBG("power %s\n", is_on ? "on" : "off");
+ sl811->board->port_power(hcd->self.controller, is_on);
+ }
+
+ /* reset as thoroughly as we can */
+ if (sl811->board && sl811->board->reset)
+ sl811->board->reset(hcd->self.controller);
+
+ sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ sl811_write(sl811, SL811HS_CTLREG2, SL811HS_CTL2_INIT);
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+ // if !is_on, put into lowpower mode now
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* This is a PIO-only HCD. Queueing appends URBs to the endpoint's queue,
+ * and may start I/O. Endpoint queues are scanned during completion irq
+ * handlers (one per packet: ACK, NAK, faults, etc) and urb cancelation.
+ *
+ * Using an external DMA engine to copy a packet at a time could work,
+ * though setup/teardown costs may be too big to make it worthwhile.
+ */
+
+/* SETUP starts a new control request. Devices are not allowed to
+ * STALL or NAK these; they must cancel any pending control requests.
+ */
+static void setup_packet(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ u8 bank,
+ u8 control
+)
+{
+ u8 addr;
+ u8 len;
+ void __iomem *data_reg;
+
+ addr = SL811HS_PACKET_BUF(bank == 0);
+ len = sizeof(struct usb_ctrlrequest);
+ data_reg = sl811->data_reg;
+ sl811_write_buf(sl811, addr, urb->setup_packet, len);
+
+ /* autoincrementing */
+ sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+ writeb(len, data_reg);
+ writeb(SL_SETUP /* | ep->epnum */, data_reg);
+ writeb(usb_pipedevice(urb->pipe), data_reg);
+
+ /* always OUT/data0 */ ;
+ sl811_write(sl811, bank + SL11H_HOSTCTLREG,
+ control | SL11H_HCTLMASK_OUT);
+ ep->length = 0;
+ PACKET("SETUP qh%p\n", ep);
+}
+
+/* STATUS finishes control requests, often after IN or OUT data packets */
+static void status_packet(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ u8 bank,
+ u8 control
+)
+{
+ int do_out;
+ void __iomem *data_reg;
+
+ do_out = urb->transfer_buffer_length && usb_pipein(urb->pipe);
+ data_reg = sl811->data_reg;
+
+ /* autoincrementing */
+ sl811_write(sl811, bank + SL11H_BUFADDRREG, 0);
+ writeb(0, data_reg);
+ writeb((do_out ? SL_OUT : SL_IN) /* | ep->epnum */, data_reg);
+ writeb(usb_pipedevice(urb->pipe), data_reg);
+
+ /* always data1; sometimes IN */
+ control |= SL11H_HCTLMASK_TOGGLE;
+ if (do_out)
+ control |= SL11H_HCTLMASK_OUT;
+ sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);
+ ep->length = 0;
+ PACKET("STATUS%s/%s qh%p\n", ep->nak_count ? "/retry" : "",
+ do_out ? "out" : "in", ep);
+}
+
+/* IN packets can be used with any type of endpoint. here we just
+ * start the transfer, data from the peripheral may arrive later.
+ * urb->iso_frame_desc is currently ignored here...
+ */
+static void in_packet(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ u8 bank,
+ u8 control
+)
+{
+ u8 addr;
+ u8 len;
+ void __iomem *data_reg;
+
+ /* avoid losing data on overflow */
+ len = ep->maxpacket;
+ addr = SL811HS_PACKET_BUF(bank == 0);
+ if (!(control & SL11H_HCTLMASK_ISOCH)
+ && usb_gettoggle(urb->dev, ep->epnum, 0))
+ control |= SL11H_HCTLMASK_TOGGLE;
+ data_reg = sl811->data_reg;
+
+ /* autoincrementing */
+ sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+ writeb(len, data_reg);
+ writeb(SL_IN | ep->epnum, data_reg);
+ writeb(usb_pipedevice(urb->pipe), data_reg);
+
+ sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);
+ ep->length = min((int)len,
+ urb->transfer_buffer_length - urb->actual_length);
+ PACKET("IN%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "",
+ !!usb_gettoggle(urb->dev, ep->epnum, 0), ep, len);
+}
+
+/* OUT packets can be used with any type of endpoint.
+ * urb->iso_frame_desc is currently ignored here...
+ */
+static void out_packet(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ u8 bank,
+ u8 control
+)
+{
+ void *buf;
+ u8 addr;
+ u8 len;
+ void __iomem *data_reg;
+
+ buf = urb->transfer_buffer + urb->actual_length;
+ prefetch(buf);
+
+ len = min((int)ep->maxpacket,
+ urb->transfer_buffer_length - urb->actual_length);
+
+ if (!(control & SL11H_HCTLMASK_ISOCH)
+ && usb_gettoggle(urb->dev, ep->epnum, 1))
+ control |= SL11H_HCTLMASK_TOGGLE;
+ addr = SL811HS_PACKET_BUF(bank == 0);
+ data_reg = sl811->data_reg;
+
+ sl811_write_buf(sl811, addr, buf, len);
+
+ /* autoincrementing */
+ sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+ writeb(len, data_reg);
+ writeb(SL_OUT | ep->epnum, data_reg);
+ writeb(usb_pipedevice(urb->pipe), data_reg);
+
+ sl811_write(sl811, bank + SL11H_HOSTCTLREG,
+ control | SL11H_HCTLMASK_OUT);
+ ep->length = len;
+ PACKET("OUT%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "",
+ !!usb_gettoggle(urb->dev, ep->epnum, 1), ep, len);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* caller updates on-chip enables later */
+
+static inline void sofirq_on(struct sl811 *sl811)
+{
+ if (sl811->irq_enable & SL11H_INTMASK_SOFINTR)
+ return;
+ VDBG("sof irq on\n");
+ sl811->irq_enable |= SL11H_INTMASK_SOFINTR;
+}
+
+static inline void sofirq_off(struct sl811 *sl811)
+{
+ if (!(sl811->irq_enable & SL11H_INTMASK_SOFINTR))
+ return;
+ VDBG("sof irq off\n");
+ sl811->irq_enable &= ~SL11H_INTMASK_SOFINTR;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* pick the next endpoint for a transaction, and issue it.
+ * frames start with periodic transfers (after whatever is pending
+ * from the previous frame), and the rest of the time is async
+ * transfers, scheduled round-robin.
+ */
+static struct sl811h_ep *start(struct sl811 *sl811, u8 bank)
+{
+ struct sl811h_ep *ep;
+ struct urb *urb;
+ int fclock;
+ u8 control;
+
+ /* use endpoint at schedule head */
+ if (sl811->next_periodic) {
+ ep = sl811->next_periodic;
+ sl811->next_periodic = ep->next;
+ } else {
+ if (sl811->next_async)
+ ep = sl811->next_async;
+ else if (!list_empty(&sl811->async))
+ ep = container_of(sl811->async.next,
+ struct sl811h_ep, schedule);
+ else {
+ /* could set up the first fullspeed periodic
+ * transfer for the next frame ...
+ */
+ return NULL;
+ }
+
+#ifdef USE_B
+ if ((bank && sl811->active_b == ep) || sl811->active_a == ep)
+ return NULL;
+#endif
+
+ if (ep->schedule.next == &sl811->async)
+ sl811->next_async = NULL;
+ else
+ sl811->next_async = container_of(ep->schedule.next,
+ struct sl811h_ep, schedule);
+ }
+
+ if (unlikely(list_empty(&ep->hep->urb_list))) {
+ DBG("empty %p queue?\n", ep);
+ return NULL;
+ }
+
+ urb = container_of(ep->hep->urb_list.next, struct urb, urb_list);
+ control = ep->defctrl;
+
+ /* if this frame doesn't have enough time left to transfer this
+ * packet, wait till the next frame. too-simple algorithm...
+ */
+ fclock = sl811_read(sl811, SL11H_SOFTMRREG) << 6;
+ fclock -= 100; /* setup takes not much time */
+ if (urb->dev->speed == USB_SPEED_LOW) {
+ if (control & SL11H_HCTLMASK_PREAMBLE) {
+ /* also note erratum 1: some hubs won't work */
+ fclock -= 800;
+ }
+ fclock -= ep->maxpacket << 8;
+
+ /* erratum 2: AFTERSOF only works for fullspeed */
+ if (fclock < 0) {
+ if (ep->period)
+ sl811->stat_overrun++;
+ sofirq_on(sl811);
+ return NULL;
+ }
+ } else {
+ fclock -= 12000 / 19; /* 19 64byte packets/msec */
+ if (fclock < 0) {
+ if (ep->period)
+ sl811->stat_overrun++;
+ control |= SL11H_HCTLMASK_AFTERSOF;
+
+ /* throttle bulk/control irq noise */
+ } else if (ep->nak_count)
+ control |= SL11H_HCTLMASK_AFTERSOF;
+ }
+
+
+ switch (ep->nextpid) {
+ case USB_PID_IN:
+ in_packet(sl811, ep, urb, bank, control);
+ break;
+ case USB_PID_OUT:
+ out_packet(sl811, ep, urb, bank, control);
+ break;
+ case USB_PID_SETUP:
+ setup_packet(sl811, ep, urb, bank, control);
+ break;
+ case USB_PID_ACK: /* for control status */
+ status_packet(sl811, ep, urb, bank, control);
+ break;
+ default:
+ DBG("bad ep%p pid %02x\n", ep, ep->nextpid);
+ ep = NULL;
+ }
+ return ep;
+}
+
+#define MIN_JIFFIES ((msecs_to_jiffies(2) > 1) ? msecs_to_jiffies(2) : 2)
+
+static inline void start_transfer(struct sl811 *sl811)
+{
+ if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))
+ return;
+ if (sl811->active_a == NULL) {
+ sl811->active_a = start(sl811, SL811_EP_A(SL811_HOST_BUF));
+ if (sl811->active_a != NULL)
+ sl811->jiffies_a = jiffies + MIN_JIFFIES;
+ }
+#ifdef USE_B
+ if (sl811->active_b == NULL) {
+ sl811->active_b = start(sl811, SL811_EP_B(SL811_HOST_BUF));
+ if (sl811->active_b != NULL)
+ sl811->jiffies_b = jiffies + MIN_JIFFIES;
+ }
+#endif
+}
+
+static void finish_request(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ struct pt_regs *regs,
+ int status
+) __releases(sl811->lock) __acquires(sl811->lock)
+{
+ unsigned i;
+
+ if (usb_pipecontrol(urb->pipe))
+ ep->nextpid = USB_PID_SETUP;
+
+ spin_lock(&urb->lock);
+ if (urb->status == -EINPROGRESS)
+ urb->status = status;
+ spin_unlock(&urb->lock);
+
+ spin_unlock(&sl811->lock);
+ usb_hcd_giveback_urb(sl811_to_hcd(sl811), urb, regs);
+ spin_lock(&sl811->lock);
+
+ /* leave active endpoints in the schedule */
+ if (!list_empty(&ep->hep->urb_list))
+ return;
+
+ /* async deschedule? */
+ if (!list_empty(&ep->schedule)) {
+ list_del_init(&ep->schedule);
+ if (ep == sl811->next_async)
+ sl811->next_async = NULL;
+ return;
+ }
+
+ /* periodic deschedule */
+ DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+ for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+ struct sl811h_ep *temp;
+ struct sl811h_ep **prev = &sl811->periodic[i];
+
+ while (*prev && ((temp = *prev) != ep))
+ prev = &temp->next;
+ if (*prev)
+ *prev = ep->next;
+ sl811->load[i] -= ep->load;
+ }
+ ep->branch = PERIODIC_SIZE;
+ sl811->periodic_count--;
+ sl811_to_hcd(sl811)->self.bandwidth_allocated
+ -= ep->load / ep->period;
+ if (ep == sl811->next_periodic)
+ sl811->next_periodic = ep->next;
+
+ /* we might turn SOFs back on again for the async schedule */
+ if (sl811->periodic_count == 0)
+ sofirq_off(sl811);
+}
+
+static void
+done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank, struct pt_regs *regs)
+{
+ u8 status;
+ struct urb *urb;
+ int urbstat = -EINPROGRESS;
+
+ if (unlikely(!ep))
+ return;
+
+ status = sl811_read(sl811, bank + SL11H_PKTSTATREG);
+
+ urb = container_of(ep->hep->urb_list.next, struct urb, urb_list);
+
+ /* we can safely ignore NAKs */
+ if (status & SL11H_STATMASK_NAK) {
+ // PACKET("...NAK_%02x qh%p\n", bank, ep);
+ if (!ep->period)
+ ep->nak_count++;
+ ep->error_count = 0;
+
+ /* ACK advances transfer, toggle, and maybe queue */
+ } else if (status & SL11H_STATMASK_ACK) {
+ struct usb_device *udev = urb->dev;
+ int len;
+ unsigned char *buf;
+
+ /* urb->iso_frame_desc is currently ignored here... */
+
+ ep->nak_count = ep->error_count = 0;
+ switch (ep->nextpid) {
+ case USB_PID_OUT:
+ // PACKET("...ACK/out_%02x qh%p\n", bank, ep);
+ urb->actual_length += ep->length;
+ usb_dotoggle(udev, ep->epnum, 1);
+ if (urb->actual_length
+ == urb->transfer_buffer_length) {
+ if (usb_pipecontrol(urb->pipe))
+ ep->nextpid = USB_PID_ACK;
+
+ /* some bulk protocols terminate OUT transfers
+ * by a short packet, using ZLPs not padding.
+ */
+ else if (ep->length < ep->maxpacket
+ || !(urb->transfer_flags
+ & URB_ZERO_PACKET))
+ urbstat = 0;
+ }
+ break;
+ case USB_PID_IN:
+ // PACKET("...ACK/in_%02x qh%p\n", bank, ep);
+ buf = urb->transfer_buffer + urb->actual_length;
+ prefetchw(buf);
+ len = ep->maxpacket - sl811_read(sl811,
+ bank + SL11H_XFERCNTREG);
+ if (len > ep->length) {
+ len = ep->length;
+ urb->status = -EOVERFLOW;
+ }
+ urb->actual_length += len;
+ sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0),
+ buf, len);
+ usb_dotoggle(udev, ep->epnum, 0);
+ if (urb->actual_length == urb->transfer_buffer_length)
+ urbstat = 0;
+ else if (len < ep->maxpacket) {
+ if (urb->transfer_flags & URB_SHORT_NOT_OK)
+ urbstat = -EREMOTEIO;
+ else
+ urbstat = 0;
+ }
+ if (usb_pipecontrol(urb->pipe)
+ && (urbstat == -EREMOTEIO
+ || urbstat == 0)) {
+
+ /* NOTE if the status stage STALLs (why?),
+ * this reports the wrong urb status.
+ */
+ spin_lock(&urb->lock);
+ if (urb->status == -EINPROGRESS)
+ urb->status = urbstat;
+ spin_unlock(&urb->lock);
+
+ urb = NULL;
+ ep->nextpid = USB_PID_ACK;
+ }
+ break;
+ case USB_PID_SETUP:
+ // PACKET("...ACK/setup_%02x qh%p\n", bank, ep);
+ if (urb->transfer_buffer_length == urb->actual_length)
+ ep->nextpid = USB_PID_ACK;
+ else if (usb_pipeout(urb->pipe)) {
+ usb_settoggle(udev, 0, 1, 1);
+ ep->nextpid = USB_PID_OUT;
+ } else {
+ usb_settoggle(udev, 0, 0, 1);
+ ep->nextpid = USB_PID_IN;
+ }
+ break;
+ case USB_PID_ACK:
+ // PACKET("...ACK/status_%02x qh%p\n", bank, ep);
+ urbstat = 0;
+ break;
+ }
+
+ /* STALL stops all transfers */
+ } else if (status & SL11H_STATMASK_STALL) {
+ PACKET("...STALL_%02x qh%p\n", bank, ep);
+ ep->nak_count = ep->error_count = 0;
+ urbstat = -EPIPE;
+
+ /* error? retry, until "3 strikes" */
+ } else if (++ep->error_count >= 3) {
+ if (status & SL11H_STATMASK_TMOUT)
+ urbstat = -ETIMEDOUT;
+ else if (status & SL11H_STATMASK_OVF)
+ urbstat = -EOVERFLOW;
+ else
+ urbstat = -EPROTO;
+ ep->error_count = 0;
+ PACKET("...3STRIKES_%02x %02x qh%p stat %d\n",
+ bank, status, ep, urbstat);
+ }
+
+ if (urb && (urbstat != -EINPROGRESS || urb->status != -EINPROGRESS))
+ finish_request(sl811, ep, urb, regs, urbstat);
+}
+
+static inline u8 checkdone(struct sl811 *sl811)
+{
+ u8 ctl;
+ u8 irqstat = 0;
+
+ if (sl811->active_a && time_before_eq(sl811->jiffies_a, jiffies)) {
+ ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG));
+ if (ctl & SL11H_HCTLMASK_ARM)
+ sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);
+ DBG("%s DONE_A: ctrl %02x sts %02x\n",
+ (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",
+ ctl,
+ sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG)));
+ irqstat |= SL11H_INTMASK_DONE_A;
+ }
+#ifdef USE_B
+ if (sl811->active_b && time_before_eq(sl811->jiffies_b, jiffies)) {
+ ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG));
+ if (ctl & SL11H_HCTLMASK_ARM)
+ sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);
+ DBG("%s DONE_B: ctrl %02x sts %02x\n",
+ (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",
+ ctl,
+ sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG)));
+ irqstat |= SL11H_INTMASK_DONE_A;
+ }
+#endif
+ return irqstat;
+}
+
+static irqreturn_t sl811h_irq(struct usb_hcd *hcd, struct pt_regs *regs)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ u8 irqstat;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned retries = 5;
+
+ spin_lock(&sl811->lock);
+
+retry:
+ irqstat = sl811_read(sl811, SL11H_IRQ_STATUS) & ~SL11H_INTMASK_DP;
+ if (irqstat) {
+ sl811_write(sl811, SL11H_IRQ_STATUS, irqstat);
+ irqstat &= sl811->irq_enable;
+ }
+
+#ifdef QUIRK2
+ /* this may no longer be necessary ... */
+ if (irqstat == 0 && ret == IRQ_NONE) {
+ irqstat = checkdone(sl811);
+ if (irqstat /* && irq != ~0 */ )
+ sl811->stat_lost++;
+ }
+#endif
+
+ /* USB packets, not necessarily handled in the order they're
+ * issued ... that's fine if they're different endpoints.
+ */
+ if (irqstat & SL11H_INTMASK_DONE_A) {
+ done(sl811, sl811->active_a, SL811_EP_A(SL811_HOST_BUF), regs);
+ sl811->active_a = NULL;
+ sl811->stat_a++;
+ }
+#ifdef USE_B
+ if (irqstat & SL11H_INTMASK_DONE_B) {
+ done(sl811, sl811->active_b, SL811_EP_B(SL811_HOST_BUF), regs);
+ sl811->active_b = NULL;
+ sl811->stat_b++;
+ }
+#endif
+ if (irqstat & SL11H_INTMASK_SOFINTR) {
+ unsigned index;
+
+ index = sl811->frame++ % (PERIODIC_SIZE - 1);
+ sl811->stat_sof++;
+
+ /* be graceful about almost-inevitable periodic schedule
+ * overruns: continue the previous frame's transfers iff
+ * this one has nothing scheduled.
+ */
+ if (sl811->next_periodic) {
+ // ERR("overrun to slot %d\n", index);
+ sl811->stat_overrun++;
+ }
+ if (sl811->periodic[index])
+ sl811->next_periodic = sl811->periodic[index];
+ }
+
+ /* khubd manages debouncing and wakeup */
+ if (irqstat & SL11H_INTMASK_INSRMV) {
+ sl811->stat_insrmv++;
+
+ /* most stats are reset for each VBUS session */
+ sl811->stat_wake = 0;
+ sl811->stat_sof = 0;
+ sl811->stat_a = 0;
+ sl811->stat_b = 0;
+ sl811->stat_lost = 0;
+
+ sl811->ctrl1 = 0;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+ sl811->irq_enable = SL11H_INTMASK_INSRMV;
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+ /* usbcore nukes other pending transactions on disconnect */
+ if (sl811->active_a) {
+ sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);
+ finish_request(sl811, sl811->active_a,
+ container_of(sl811->active_a->hep->urb_list.next,
+ struct urb, urb_list),
+ NULL, -ESHUTDOWN);
+ sl811->active_a = NULL;
+ }
+#ifdef USE_B
+ if (sl811->active_b) {
+ sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);
+ finish_request(sl811, sl811->active_b,
+ container_of(sl811->active_b->hep->urb_list.next,
+ struct urb, urb_list),
+ NULL, -ESHUTDOWN);
+ sl811->active_b = NULL;
+ }
+#endif
+
+ /* port status seems wierd until after reset, so
+ * force the reset and make khubd clean up later.
+ */
+ sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION)
+ | (1 << USB_PORT_FEAT_CONNECTION);
+
+ } else if (irqstat & SL11H_INTMASK_RD) {
+ if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) {
+ DBG("wakeup\n");
+ sl811->port1 |= 1 << USB_PORT_FEAT_C_SUSPEND;
+ sl811->stat_wake++;
+ } else
+ irqstat &= ~SL11H_INTMASK_RD;
+ }
+
+ if (irqstat) {
+ if (sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))
+ start_transfer(sl811);
+ ret = IRQ_HANDLED;
+ if (retries--)
+ goto retry;
+ }
+
+ if (sl811->periodic_count == 0 && list_empty(&sl811->async))
+ sofirq_off(sl811);
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+ spin_unlock(&sl811->lock);
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* usb 1.1 says max 90% of a frame is available for periodic transfers.
+ * this driver doesn't promise that much since it's got to handle an
+ * IRQ per packet; irq handling latencies also use up that time.
+ */
+#define MAX_PERIODIC_LOAD 500 /* out of 1000 usec */
+
+static int balance(struct sl811 *sl811, u16 period, u16 load)
+{
+ int i, branch = -ENOSPC;
+
+ /* search for the least loaded schedule branch of that period
+ * which has enough bandwidth left unreserved.
+ */
+ for (i = 0; i < period ; i++) {
+ if (branch < 0 || sl811->load[branch] > sl811->load[i]) {
+ int j;
+
+ for (j = i; j < PERIODIC_SIZE; j += period) {
+ if ((sl811->load[j] + load)
+ > MAX_PERIODIC_LOAD)
+ break;
+ }
+ if (j < PERIODIC_SIZE)
+ continue;
+ branch = i;
+ }
+ }
+ return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int sl811h_urb_enqueue(
+ struct usb_hcd *hcd,
+ struct usb_host_endpoint *hep,
+ struct urb *urb,
+ int mem_flags
+) {
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ struct usb_device *udev = urb->dev;
+ unsigned int pipe = urb->pipe;
+ int is_out = !usb_pipein(pipe);
+ int type = usb_pipetype(pipe);
+ int epnum = usb_pipeendpoint(pipe);
+ struct sl811h_ep *ep = NULL;
+ unsigned long flags;
+ int i;
+ int retval = 0;
+
+#ifdef DISABLE_ISO
+ if (type == PIPE_ISOCHRONOUS)
+ return -ENOSPC;
+#endif
+
+ /* avoid all allocations within spinlocks */
+ if (!hep->hcpriv) {
+ ep = kcalloc(1, sizeof *ep, mem_flags);
+ /* set hep, otherwise sl811h_ep *start(struct sl811 *sl811, u8 bank) crashes */
+ ep->hep = hep;
+ }
+
+ spin_lock_irqsave(&sl811->lock, flags);
+
+ /* don't submit to a dead or disabled port */
+ if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))
+ || !HC_IS_RUNNING(hcd->state)) {
+ retval = -ENODEV;
+ goto fail;
+ }
+
+ if (hep->hcpriv) {
+ kfree(ep);
+ ep = hep->hcpriv;
+ } else if (!ep) {
+ retval = -ENOMEM;
+ goto fail;
+
+ } else {
+ INIT_LIST_HEAD(&ep->schedule);
+ ep->udev = usb_get_dev(udev);
+ ep->epnum = epnum;
+ ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
+ ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE;
+ usb_settoggle(udev, epnum, is_out, 0);
+
+ if (type == PIPE_CONTROL)
+ ep->nextpid = USB_PID_SETUP;
+ else if (is_out)
+ ep->nextpid = USB_PID_OUT;
+ else
+ ep->nextpid = USB_PID_IN;
+
+ if (ep->maxpacket > H_MAXPACKET) {
+ /* iso packets up to 240 bytes could work... */
+ DBG("dev %d ep%d maxpacket %d\n",
+ udev->devnum, epnum, ep->maxpacket);
+ retval = -EINVAL;
+ goto fail;
+ }
+
+ if (udev->speed == USB_SPEED_LOW) {
+ /* send preamble for external hub? */
+ if (!(sl811->ctrl1 & SL11H_CTL1MASK_LSPD))
+ ep->defctrl |= SL11H_HCTLMASK_PREAMBLE;
+ }
+ switch (type) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ if (urb->interval > PERIODIC_SIZE)
+ urb->interval = PERIODIC_SIZE;
+ ep->period = urb->interval;
+ ep->branch = PERIODIC_SIZE;
+ if (type == PIPE_ISOCHRONOUS)
+ ep->defctrl |= SL11H_HCTLMASK_ISOCH;
+ ep->load = usb_calc_bus_time(udev->speed, !is_out,
+ (type == PIPE_ISOCHRONOUS),
+ usb_maxpacket(udev, pipe, is_out))
+ / 1000;
+ break;
+ }
+
+ hep->hcpriv = ep;
+ }
+
+ /* maybe put endpoint into schedule */
+ switch (type) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ if (list_empty(&ep->schedule))
+ list_add_tail(&ep->schedule, &sl811->async);
+ break;
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ urb->interval = ep->period;
+ if (ep->branch < PERIODIC_SIZE)
+ break;
+
+ retval = balance(sl811, ep->period, ep->load);
+ if (retval < 0)
+ goto fail;
+ ep->branch = retval;
+ retval = 0;
+ urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1))
+ + ep->branch;
+
+ /* sort each schedule branch by period (slow before fast)
+ * to share the faster parts of the tree without needing
+ * dummy/placeholder nodes
+ */
+ DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+ for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+ struct sl811h_ep **prev = &sl811->periodic[i];
+ struct sl811h_ep *here = *prev;
+
+ while (here && ep != here) {
+ if (ep->period > here->period)
+ break;
+ prev = &here->next;
+ here = *prev;
+ }
+ if (ep != here) {
+ ep->next = here;
+ *prev = ep;
+ }
+ sl811->load[i] += ep->load;
+ }
+ sl811->periodic_count++;
+ hcd->self.bandwidth_allocated += ep->load / ep->period;
+ sofirq_on(sl811);
+ }
+
+ /* in case of unlink-during-submit */
+ spin_lock(&urb->lock);
+ if (urb->status != -EINPROGRESS) {
+ spin_unlock(&urb->lock);
+ finish_request(sl811, ep, urb, NULL, 0);
+ retval = 0;
+ goto fail;
+ }
+
+ /* when uncommented, causes this error on mouse open:
+ drivers/usb/input/usbmouse.c: can't resubmit intr, sl811-hcd-1/input0, status -22
+ urb->hcpriv = hep; */
+
+ spin_unlock(&urb->lock);
+
+ start_transfer(sl811);
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+fail:
+ spin_unlock_irqrestore(&sl811->lock, flags);
+ return retval;
+}
+
+static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ struct usb_host_endpoint *hep = urb->hcpriv;
+ unsigned long flags;
+ struct sl811h_ep *ep;
+ int retval = 0;
+
+ if (!hep)
+ return -EINVAL;
+
+ spin_lock_irqsave(&sl811->lock, flags);
+ ep = hep->hcpriv;
+ if (ep) {
+ /* finish right away if this urb can't be active ...
+ * note that some drivers wrongly expect delays
+ */
+ if (ep->hep->urb_list.next != &urb->urb_list) {
+ /* not front of queue? never active */
+
+ /* for active transfers, we expect an IRQ */
+ } else if (sl811->active_a == ep) {
+ if (time_before_eq(sl811->jiffies_a, jiffies)) {
+ /* happens a lot with lowspeed?? */
+ DBG("giveup on DONE_A: ctrl %02x sts %02x\n",
+ sl811_read(sl811,
+ SL811_EP_A(SL11H_HOSTCTLREG)),
+ sl811_read(sl811,
+ SL811_EP_A(SL11H_PKTSTATREG)));
+ sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG),
+ 0);
+ sl811->active_a = NULL;
+ } else
+ urb = NULL;
+#ifdef USE_B
+ } else if (sl811->active_b == ep) {
+ if (time_before_eq(sl811->jiffies_a, jiffies)) {
+ /* happens a lot with lowspeed?? */
+ DBG("giveup on DONE_B: ctrl %02x sts %02x\n",
+ sl811_read(sl811,
+ SL811_EP_B(SL11H_HOSTCTLREG)),
+ sl811_read(sl811,
+ SL811_EP_B(SL11H_PKTSTATREG)));
+ sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG),
+ 0);
+ sl811->active_b = NULL;
+ } else
+ urb = NULL;
+#endif
+ } else {
+ /* front of queue for inactive endpoint */
+ }
+
+ if (urb)
+ finish_request(sl811, ep, urb, NULL, 0);
+ else
+ VDBG("dequeue, urb %p active %s; wait4irq\n", urb,
+ (sl811->active_a == ep) ? "A" : "B");
+ } else
+ retval = -EINVAL;
+ spin_unlock_irqrestore(&sl811->lock, flags);
+ return retval;
+}
+
+static void
+sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
+{
+ struct sl811h_ep *ep = hep->hcpriv;
+
+ if (!ep)
+ return;
+
+ /* assume we'd just wait for the irq */
+ if (!list_empty(&hep->urb_list))
+ msleep(3);
+ if (!list_empty(&hep->urb_list))
+ WARN("ep %p not empty?\n", ep);
+
+ usb_put_dev(ep->udev);
+ kfree(ep);
+ hep->hcpriv = NULL;
+}
+
+static int
+sl811h_get_frame(struct usb_hcd *hcd)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+
+ /* wrong except while periodic transfers are scheduled;
+ * never matches the on-the-wire frame;
+ * subject to overruns.
+ */
+ return sl811->frame;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* the virtual root hub timer IRQ checks for hub status */
+static int
+sl811h_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+#ifdef QUIRK3
+ unsigned long flags;
+
+ /* non-SMP HACK: use root hub timer as i/o watchdog
+ * this seems essential when SOF IRQs aren't in use...
+ */
+ local_irq_save(flags);
+ if (!timer_pending(&sl811->timer)) {
+ if (sl811h_irq( /* ~0, */ hcd, NULL) != IRQ_NONE)
+ sl811->stat_lost++;
+ }
+ local_irq_restore(flags);
+#endif
+
+ if (!(sl811->port1 & (0xffff << 16)))
+ return 0;
+
+ /* tell khubd port 1 changed */
+ *buf = (1 << 1);
+ return 1;
+}
+
+static void
+sl811h_hub_descriptor (
+ struct sl811 *sl811,
+ struct usb_hub_descriptor *desc
+) {
+ u16 temp = 0;
+
+ desc->bDescriptorType = 0x29;
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = 1;
+ desc->bDescLength = 9;
+
+ /* per-port power switching (gang of one!), or none */
+ desc->bPwrOn2PwrGood = 0;
+ if (sl811->board && sl811->board->port_power) {
+ desc->bPwrOn2PwrGood = sl811->board->potpg;
+ if (!desc->bPwrOn2PwrGood)
+ desc->bPwrOn2PwrGood = 10;
+ temp = 0x0001;
+ } else
+ temp = 0x0002;
+
+ /* no overcurrent errors detection/handling */
+ temp |= 0x0010;
+
+ desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp);
+
+ /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */
+ desc->bitmap[0] = 1 << 1;
+ desc->bitmap[1] = ~0;
+}
+
+static void
+sl811h_timer(unsigned long _sl811)
+{
+ struct sl811 *sl811 = (void *) _sl811;
+ unsigned long flags;
+ u8 irqstat;
+ u8 signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE;
+ const u32 mask = (1 << USB_PORT_FEAT_CONNECTION)
+ | (1 << USB_PORT_FEAT_ENABLE)
+ | (1 << USB_PORT_FEAT_LOWSPEED);
+
+ spin_lock_irqsave(&sl811->lock, flags);
+
+ /* stop special signaling */
+ sl811->ctrl1 &= ~SL11H_CTL1MASK_FORCE;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ udelay(3);
+
+ irqstat = sl811_read(sl811, SL11H_IRQ_STATUS);
+
+ switch (signaling) {
+ case SL11H_CTL1MASK_SE0:
+ DBG("end reset\n");
+ sl811->port1 = (1 << USB_PORT_FEAT_C_RESET)
+ | (1 << USB_PORT_FEAT_POWER);
+ sl811->ctrl1 = 0;
+ /* don't wrongly ack RD */
+ if (irqstat & SL11H_INTMASK_INSRMV)
+ irqstat &= ~SL11H_INTMASK_RD;
+ break;
+ case SL11H_CTL1MASK_K:
+ DBG("end resume\n");
+ sl811->port1 &= ~(1 << USB_PORT_FEAT_SUSPEND);
+ break;
+ default:
+ DBG("odd timer signaling: %02x\n", signaling);
+ break;
+ }
+ sl811_write(sl811, SL11H_IRQ_STATUS, irqstat);
+
+ if (irqstat & SL11H_INTMASK_RD) {
+ /* usbcore nukes all pending transactions on disconnect */
+ if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION))
+ sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION)
+ | (1 << USB_PORT_FEAT_C_ENABLE);
+ sl811->port1 &= ~mask;
+ sl811->irq_enable = SL11H_INTMASK_INSRMV;
+ } else {
+ sl811->port1 |= mask;
+ if (irqstat & SL11H_INTMASK_DP)
+ sl811->port1 &= ~(1 << USB_PORT_FEAT_LOWSPEED);
+ sl811->irq_enable = SL11H_INTMASK_INSRMV | SL11H_INTMASK_RD;
+ }
+
+ if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) {
+ u8 ctrl2 = SL811HS_CTL2_INIT;
+
+ sl811->irq_enable |= SL11H_INTMASK_DONE_A;
+#ifdef USE_B
+ sl811->irq_enable |= SL11H_INTMASK_DONE_B;
+#endif
+ if (sl811->port1 & (1 << USB_PORT_FEAT_LOWSPEED)) {
+ sl811->ctrl1 |= SL11H_CTL1MASK_LSPD;
+ ctrl2 |= SL811HS_CTL2MASK_DSWAP;
+ }
+
+ /* start SOFs flowing, kickstarting with A registers */
+ sl811->ctrl1 |= SL11H_CTL1MASK_SOF_ENA;
+ sl811_write(sl811, SL11H_SOFLOWREG, 0xe0);
+ sl811_write(sl811, SL811HS_CTLREG2, ctrl2);
+
+ /* autoincrementing */
+ sl811_write(sl811, SL811_EP_A(SL11H_BUFLNTHREG), 0);
+ writeb(SL_SOF, sl811->data_reg);
+ writeb(0, sl811->data_reg);
+ sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG),
+ SL11H_HCTLMASK_ARM);
+
+ /* khubd provides debounce delay */
+ } else {
+ sl811->ctrl1 = 0;
+ }
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+ /* reenable irqs */
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+ spin_unlock_irqrestore(&sl811->lock, flags);
+}
+
+static int
+sl811h_hub_control(
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ int retval = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sl811->lock, flags);
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (wIndex != 1 || wLength != 0)
+ goto error;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ sl811->port1 &= (1 << USB_PORT_FEAT_POWER);
+ sl811->ctrl1 = 0;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ sl811->irq_enable = SL11H_INTMASK_INSRMV;
+ sl811_write(sl811, SL11H_IRQ_ENABLE,
+ sl811->irq_enable);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ if (!(sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)))
+ break;
+
+ /* 20 msec of resume/K signaling, other irqs blocked */
+ DBG("start resume...\n");
+ sl811->irq_enable = 0;
+ sl811_write(sl811, SL11H_IRQ_ENABLE,
+ sl811->irq_enable);
+ sl811->ctrl1 |= SL11H_CTL1MASK_K;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+ mod_timer(&sl811->timer, jiffies
+ + msecs_to_jiffies(20));
+ break;
+ case USB_PORT_FEAT_POWER:
+ port_power(sl811, 0);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_SUSPEND:
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ case USB_PORT_FEAT_C_RESET:
+ break;
+ default:
+ goto error;
+ }
+ sl811->port1 &= ~(1 << wValue);
+ break;
+ case GetHubDescriptor:
+ sl811h_hub_descriptor(sl811, (struct usb_hub_descriptor *) buf);
+ break;
+ case GetHubStatus:
+ *(__le32 *) buf = cpu_to_le32(0);
+ break;
+ case GetPortStatus:
+ if (wIndex != 1)
+ goto error;
+ *(__le32 *) buf = cpu_to_le32(sl811->port1);
+
+#ifndef VERBOSE
+ if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
+#endif
+ DBG("GetPortStatus %08x\n", sl811->port1);
+ break;
+ case SetPortFeature:
+ if (wIndex != 1 || wLength != 0)
+ goto error;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ if (sl811->port1 & (1 << USB_PORT_FEAT_RESET))
+ goto error;
+ if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)))
+ goto error;
+
+ DBG("suspend...\n");
+ sl811->ctrl1 &= ~SL11H_CTL1MASK_SOF_ENA;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ break;
+ case USB_PORT_FEAT_POWER:
+ port_power(sl811, 1);
+ break;
+ case USB_PORT_FEAT_RESET:
+ if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))
+ goto error;
+ if (!(sl811->port1 & (1 << USB_PORT_FEAT_POWER)))
+ break;
+
+ /* 50 msec of reset/SE0 signaling, irqs blocked */
+ sl811->irq_enable = 0;
+ sl811_write(sl811, SL11H_IRQ_ENABLE,
+ sl811->irq_enable);
+ sl811->ctrl1 = SL11H_CTL1MASK_SE0;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ sl811->port1 |= (1 << USB_PORT_FEAT_RESET);
+ mod_timer(&sl811->timer, jiffies
+ + msecs_to_jiffies(50));
+ break;
+ default:
+ goto error;
+ }
+ sl811->port1 |= 1 << wValue;
+ break;
+
+ default:
+error:
+ /* "protocol stall" on error */
+ retval = -EPIPE;
+ }
+
+ spin_unlock_irqrestore(&sl811->lock, flags);
+ return retval;
+}
+
+#ifdef CONFIG_PM
+
+static int
+sl811h_hub_suspend(struct usb_hcd *hcd)
+{
+ // SOFs off
+ DBG("%s\n", __FUNCTION__);
+ return 0;
+}
+
+static int
+sl811h_hub_resume(struct usb_hcd *hcd)
+{
+ // SOFs on
+ DBG("%s\n", __FUNCTION__);
+ return 0;
+}
+
+#else
+
+#define sl811h_hub_suspend NULL
+#define sl811h_hub_resume NULL
+
+#endif
+
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef STUB_DEBUG_FILE
+
+static inline void create_debug_file(struct sl811 *sl811) { }
+static inline void remove_debug_file(struct sl811 *sl811) { }
+
+#else
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+static void dump_irq(struct seq_file *s, char *label, u8 mask)
+{
+ seq_printf(s, "%s %02x%s%s%s%s%s%s\n", label, mask,
+ (mask & SL11H_INTMASK_DONE_A) ? " done_a" : "",
+ (mask & SL11H_INTMASK_DONE_B) ? " done_b" : "",
+ (mask & SL11H_INTMASK_SOFINTR) ? " sof" : "",
+ (mask & SL11H_INTMASK_INSRMV) ? " ins/rmv" : "",
+ (mask & SL11H_INTMASK_RD) ? " rd" : "",
+ (mask & SL11H_INTMASK_DP) ? " dp" : "");
+}
+
+static int proc_sl811h_show(struct seq_file *s, void *unused)
+{
+ struct sl811 *sl811 = s->private;
+ struct sl811h_ep *ep;
+ unsigned i;
+
+ seq_printf(s, "%s\n%s version %s\nportstatus[1] = %08x\n",
+ sl811_to_hcd(sl811)->product_desc,
+ hcd_name, DRIVER_VERSION,
+ sl811->port1);
+
+ seq_printf(s, "insert/remove: %ld\n", sl811->stat_insrmv);
+ seq_printf(s, "current session: done_a %ld done_b %ld "
+ "wake %ld sof %ld overrun %ld lost %ld\n\n",
+ sl811->stat_a, sl811->stat_b,
+ sl811->stat_wake, sl811->stat_sof,
+ sl811->stat_overrun, sl811->stat_lost);
+
+ spin_lock_irq(&sl811->lock);
+
+ if (sl811->ctrl1 & SL11H_CTL1MASK_SUSPEND)
+ seq_printf(s, "(suspended)\n\n");
+ else {
+ u8 t = sl811_read(sl811, SL11H_CTLREG1);
+
+ seq_printf(s, "ctrl1 %02x%s%s%s%s\n", t,
+ (t & SL11H_CTL1MASK_SOF_ENA) ? " sofgen" : "",
+ ({char *s; switch (t & SL11H_CTL1MASK_FORCE) {
+ case SL11H_CTL1MASK_NORMAL: s = ""; break;
+ case SL11H_CTL1MASK_SE0: s = " se0/reset"; break;
+ case SL11H_CTL1MASK_K: s = " k/resume"; break;
+ default: s = "j"; break;
+ }; s; }),
+ (t & SL11H_CTL1MASK_LSPD) ? " lowspeed" : "",
+ (t & SL11H_CTL1MASK_SUSPEND) ? " suspend" : "");
+
+ dump_irq(s, "irq_enable",
+ sl811_read(sl811, SL11H_IRQ_ENABLE));
+ dump_irq(s, "irq_status",
+ sl811_read(sl811, SL11H_IRQ_STATUS));
+ seq_printf(s, "frame clocks remaining: %d\n",
+ sl811_read(sl811, SL11H_SOFTMRREG) << 6);
+ }
+
+ seq_printf(s, "A: qh%p ctl %02x sts %02x\n", sl811->active_a,
+ sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)),
+ sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG)));
+ seq_printf(s, "B: qh%p ctl %02x sts %02x\n", sl811->active_b,
+ sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)),
+ sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG)));
+ seq_printf(s, "\n");
+ list_for_each_entry (ep, &sl811->async, schedule) {
+ struct urb *urb;
+
+ seq_printf(s, "%s%sqh%p, ep%d%s, maxpacket %d"
+ " nak %d err %d\n",
+ (ep == sl811->active_a) ? "(A) " : "",
+ (ep == sl811->active_b) ? "(B) " : "",
+ ep, ep->epnum,
+ ({ char *s; switch (ep->nextpid) {
+ case USB_PID_IN: s = "in"; break;
+ case USB_PID_OUT: s = "out"; break;
+ case USB_PID_SETUP: s = "setup"; break;
+ case USB_PID_ACK: s = "status"; break;
+ default: s = "?"; break;
+ }; s;}),
+ ep->maxpacket,
+ ep->nak_count, ep->error_count);
+ list_for_each_entry (urb, &ep->hep->urb_list, urb_list) {
+ seq_printf(s, " urb%p, %d/%d\n", urb,
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ }
+ }
+ if (!list_empty(&sl811->async))
+ seq_printf(s, "\n");
+
+ seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
+
+ for (i = 0; i < PERIODIC_SIZE; i++) {
+ ep = sl811->periodic[i];
+ if (!ep)
+ continue;
+ seq_printf(s, "%2d [%3d]:\n", i, sl811->load[i]);
+
+ /* DUMB: prints shared entries multiple times */
+ do {
+ seq_printf(s,
+ " %s%sqh%d/%p (%sdev%d ep%d%s max %d) "
+ "err %d\n",
+ (ep == sl811->active_a) ? "(A) " : "",
+ (ep == sl811->active_b) ? "(B) " : "",
+ ep->period, ep,
+ (ep->udev->speed == USB_SPEED_FULL)
+ ? "" : "ls ",
+ ep->udev->devnum, ep->epnum,
+ (ep->epnum == 0) ? ""
+ : ((ep->nextpid == USB_PID_IN)
+ ? "in"
+ : "out"),
+ ep->maxpacket, ep->error_count);
+ ep = ep->next;
+ } while (ep);
+ }
+
+ spin_unlock_irq(&sl811->lock);
+ seq_printf(s, "\n");
+
+ return 0;
+}
+
+static int proc_sl811h_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, proc_sl811h_show, PDE(inode)->data);
+}
+
+static struct file_operations proc_ops = {
+ .open = proc_sl811h_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* expect just one sl811 per system */
+static const char proc_filename[] = "driver/sl811h";
+
+static void create_debug_file(struct sl811 *sl811)
+{
+ struct proc_dir_entry *pde;
+
+ pde = create_proc_entry(proc_filename, 0, NULL);
+ if (pde == NULL)
+ return;
+
+ pde->proc_fops = &proc_ops;
+ pde->data = sl811;
+ sl811->pde = pde;
+}
+
+static void remove_debug_file(struct sl811 *sl811)
+{
+ if (sl811->pde)
+ remove_proc_entry(proc_filename, NULL);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static void
+sl811h_stop(struct usb_hcd *hcd)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ unsigned long flags;
+
+ del_timer_sync(&hcd->rh_timer);
+
+ spin_lock_irqsave(&sl811->lock, flags);
+ port_power(sl811, 0);
+ spin_unlock_irqrestore(&sl811->lock, flags);
+}
+
+static int
+sl811h_start(struct usb_hcd *hcd)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+
+ /* chip has been reset, VBUS power is off */
+ hcd->state = HC_STATE_RUNNING;
+
+ if (sl811->board) {
+ hcd->can_wakeup = sl811->board->can_wakeup;
+ hcd->power_budget = sl811->board->power * 2;
+ }
+
+ // enable power and interupts
+ port_power(sl811, 1);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct hc_driver sl811h_hc_driver = {
+ .description = hcd_name,
+ .hcd_priv_size = sizeof(struct sl811),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = sl811h_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /* Basic lifecycle operations */
+ .start = sl811h_start,
+ .stop = sl811h_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = sl811h_urb_enqueue,
+ .urb_dequeue = sl811h_urb_dequeue,
+ .endpoint_disable = sl811h_endpoint_disable,
+
+ /*
+ * periodic schedule support
+ */
+ .get_frame_number = sl811h_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = sl811h_hub_status_data,
+ .hub_control = sl811h_hub_control,
+ .hub_suspend = sl811h_hub_suspend,
+ .hub_resume = sl811h_hub_resume,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init_or_module
+sl811h_remove(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ struct platform_device *pdev;
+ struct resource *res;
+
+ DBG("sl811h_remove\n");
+ pdev = container_of(dev, struct platform_device, dev);
+
+ remove_debug_file(sl811);
+ usb_remove_hcd(hcd);
+
+#ifndef CONFIG_USB_SL811_CS
+ iounmap(sl811->data_reg);
+#endif
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res != NULL) release_mem_region(res->start, 1);
+ else DBG("res: %d\n", res);
+#ifndef CONFIG_USB_SL811_CS
+ iounmap(sl811->addr_reg);
+#endif
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res != NULL) release_mem_region(res->start, 1);
+ else DBG("res: %d\n", res);
+
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+
+/***************************************************************************
+ * This routine tests the Read/Write functionality of SL811HS registers
+ *
+ * 1) Store original register value into a buffer
+ * 2) Write to registers with a RAMP pattern. (10, 11, 12, ..., 255)
+ * 3) Read from register
+ * 4) Compare the written value with the read value and make sure they are
+ * equivalent
+ * 5) Restore the original register value
+ *
+ * Input: hci = data structure for the host controller
+ *
+ * Return: 1 = passed; 0 = failed
+ **************************************************************************/
+static int test_registers(struct sl811 * sl811)
+{
+ int i, result = 1;
+ u8 buf[256], data;
+
+ for (i = 0x10; i < 256; i++) {
+ /* save the original buffer */
+ buf[i] = sl811_read(sl811, i);
+
+ /* Write the new data to the buffer */
+ sl811_write(sl811, i, i);
+ }
+
+ /* compare the written data */
+ for (i = 0x10; i < 256; i++) {
+ data = sl811_read(sl811, i);
+ if (data != i) {
+ DBG("Pattern test failed!! value = 0x%x, s/b 0x%x\n",
+ data, i);
+ result = 0;
+ break;
+ }
+ }
+
+ /* restore the data */
+ for (i = 0x10; i < 256; i++) {
+ sl811_write(sl811, i, buf[i]);
+ }
+
+ return result;
+}
+
+
+int sl811h_probe(struct device *dev)
+{
+ struct usb_hcd *hcd;
+ struct sl811 *sl811;
+ struct platform_device *pdev;
+ struct resource *addr, *data;
+ int irq;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+ int retval;
+ u8 tmp;
+
+ /* basic sanity checks first. board-specific init logic should
+ * have initialized these three resources and probably board
+ * specific platform_data. we don't probe for IRQs, and do only
+ * minimal sanity checking.
+ */
+ pdev = container_of(dev, struct platform_device, dev);
+ if (pdev->num_resources < 3)
+ return -ENODEV;
+
+ addr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ irq = platform_get_irq(pdev, 0);
+ if (!addr || !data || irq < 0)
+ return -ENODEV;
+
+ /* refuse to confuse usbcore */
+ if (dev->dma_mask) {
+ DBG("no we won't dma\n");
+ return -EINVAL;
+ }
+
+ if (!request_mem_region(addr->start, 1, hcd_name)) {
+ retval = -EBUSY;
+ goto err1;
+ }
+#ifdef CONFIG_USB_SL811_CS
+ addr_reg = (void *) addr->start;
+#else
+ addr_reg = ioremap(addr->start, resource_len(addr));
+#endif
+ if (addr_reg == NULL) {
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ if (!request_mem_region(data->start, 1, hcd_name)) {
+ retval = -EBUSY;
+ goto err3;
+ }
+
+#ifdef CONFIG_USB_SL811_CS
+ data_reg = (void *) data->start;
+#else
+ data_reg = ioremap(data->start, resource_len(data));
+#endif
+ if (data_reg == NULL) {
+ retval = -ENOMEM;
+ goto err4;
+ }
+
+ DBG("SL811_HCD: addr_reg: 0x%04x, data_reg: 0x%04x\n",
+ (int) addr_reg, (int) data_reg);
+
+ /* allocate and initialize hcd */
+ hcd = usb_create_hcd(&sl811h_hc_driver, dev, dev->bus_id);
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err5;
+ }
+ hcd->rsrc_start = addr->start;
+
+ sl811 = hcd_to_sl811(hcd);
+
+ spin_lock_init(&sl811->lock);
+ INIT_LIST_HEAD(&sl811->async);
+ sl811->board = dev->platform_data;
+ init_timer(&sl811->timer);
+ sl811->timer.function = sl811h_timer;
+ sl811->timer.data = (unsigned long) sl811;
+ sl811->addr_reg = addr_reg;
+ sl811->data_reg = data_reg;
+
+ if (test_registers(sl811) == 0) {
+ DBG("ERROR: register test failed!\n");
+ goto err5;
+ }
+ else DBG("sl811 register test passed.\n");
+
+
+ spin_lock_irq(&sl811->lock);
+ port_power(sl811, 0);
+ spin_unlock_irq(&sl811->lock);
+ msleep(200);
+
+ tmp = sl811_read(sl811, SL11H_HWREVREG);
+ switch (tmp >> 4) {
+ case 1:
+ hcd->product_desc = "SL811HS v1.2";
+ break;
+ case 2:
+ hcd->product_desc = "SL811HS v1.5";
+ break;
+ default:
+ /* reject case 0, SL11S is less functional */
+ DBG("chiprev %02x\n", tmp);
+ retval = -ENXIO;
+ goto err6;
+ }
+ /* sl811s would need a different handler for this irq */
+
+#if !defined(CONFIG_USB_SL811_CS) && defined(CONFIG_ARM)
+ /* Cypress docs say the IRQ is IRQT_HIGH ... */
+ set_irq_type(irq, IRQT_RISING);
+
+#endif
+ retval = usb_add_hcd(hcd, irq, SA_INTERRUPT);
+ if (retval != 0)
+ goto err6;
+
+ INFO("%s, irq %d\n", hcd->product_desc, irq);
+
+ create_debug_file(sl811);
+ DBG("sl811_hcd initialized\n");
+ return retval;
+
+ err6:
+ usb_put_hcd(hcd);
+ err5:
+
+#ifndef CONFIG_USB_SL811_CS
+ iounmap(data_reg);
+#endif
+ err4:
+ release_mem_region(data->start, 1);
+ err3:
+#ifndef CONFIG_USB_SL811_CS
+ iounmap(addr_reg);
+#endif
+ err2:
+ release_mem_region(addr->start, 1);
+ err1:
+
+ DBG("init error, %d\n", retval);
+ return retval;
+}
+EXPORT_SYMBOL (sl811h_probe);
+
+#ifdef CONFIG_PM
+
+/* for this device there's no useful distinction between the controller
+ * and its root hub, except that the root hub only gets direct PM calls
+ * when CONFIG_USB_SUSPEND is enabled.
+ */
+
+static int
+sl811h_suspend(struct device *dev, pm_message_t state, u32 phase)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ int retval = 0;
+
+ if (phase != SUSPEND_POWER_DOWN)
+ return retval;
+
+ if (state <= PM_SUSPEND_MEM)
+ retval = sl811h_hub_suspend(hcd);
+ else
+ port_power(sl811, 0);
+ if (retval == 0)
+ dev->power.power_state = state;
+ return retval;
+}
+
+static int
+sl811h_resume(struct device *dev, u32 phase)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+
+ if (phase != RESUME_POWER_ON)
+ return 0;
+
+ /* with no "check to see if VBUS is still powered" board hook,
+ * let's assume it'd only be powered to enable remote wakeup.
+ */
+ if (dev->power.power_state > PM_SUSPEND_MEM
+ || !hcd->can_wakeup) {
+ sl811->port1 = 0;
+ port_power(sl811, 1);
+ return 0;
+ }
+
+ dev->power.power_state = PMSG_ON;
+ return sl811h_hub_resume(hcd);
+}
+
+#else
+
+#define sl811h_suspend NULL
+#define sl811h_resume NULL
+
+#endif
+
+
+struct device_driver sl811h_driver = {
+ .name = (char *) hcd_name,
+ .bus = &platform_bus_type,
+
+ .probe = sl811h_probe,
+ .remove = sl811h_remove,
+
+ .suspend = sl811h_suspend,
+ .resume = sl811h_resume,
+};
+EXPORT_SYMBOL(sl811h_driver);
+
+/*-------------------------------------------------------------------------*/
+
+static int __init sl811h_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION);
+ return driver_register(&sl811h_driver);
+}
+module_init(sl811h_init);
+
+static void __exit sl811h_cleanup(void)
+{
+ driver_unregister(&sl811h_driver);
+}
+module_exit(sl811h_cleanup);