aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/Kconfig11
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-hcd.c65
-rw-r--r--drivers/usb/host/ehci-hub.c8
-rw-r--r--drivers/usb/host/ehci.h19
-rw-r--r--drivers/usb/host/hc_crisv10.c2
-rw-r--r--drivers/usb/host/sl811-hcd.c150
-rw-r--r--drivers/usb/host/sl811_cs.c442
8 files changed, 599 insertions, 99 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 3196c3265ff5..19e598c9641f 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -124,3 +124,14 @@ config USB_SL811_HCD
To compile this driver as a module, choose M here: the
module will be called sl811-hcd.
+config USB_SL811_CS
+ tristate "CF/PCMCIA support for SL811HS HCD"
+ depends on USB_SL811_HCD && PCMCIA
+ default N
+ help
+ Wraps a PCMCIA driver around the SL811HS HCD, supporting the RATOC
+ REX-CFU1U CF card (often used with PDAs). If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called "sl811_cs".
+
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index a574ca06cf6b..5dbd3e7a27c7 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -7,4 +7,5 @@ obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
+obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 84d2b93aca37..bc69bd7acebe 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -346,6 +346,22 @@ ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
return 0;
}
+static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
+{
+ unsigned port;
+
+ if (!HCS_PPC (ehci->hcs_params))
+ return;
+
+ ehci_dbg (ehci, "...power%s ports...\n", is_on ? "up" : "down");
+ for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; )
+ (void) ehci_hub_control(ehci_to_hcd(ehci),
+ is_on ? SetPortFeature : ClearPortFeature,
+ USB_PORT_FEAT_POWER,
+ port--, NULL, 0);
+ msleep(20);
+}
+
/* called by khubd or root hub init threads */
@@ -362,8 +378,10 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
dbg_hcs_params (ehci, "reset");
dbg_hcc_params (ehci, "reset");
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = readl (&ehci->caps->hcs_params);
+
#ifdef CONFIG_PCI
- /* EHCI 0.96 and later may have "extended capabilities" */
if (hcd->self.controller->bus == &pci_bus_type) {
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
@@ -383,9 +401,30 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
break;
}
+ /* optional debug port, normally in the first BAR */
+ temp = pci_find_capability (pdev, 0x0a);
+ if (temp) {
+ pci_read_config_dword(pdev, temp, &temp);
+ temp >>= 16;
+ if ((temp & (3 << 13)) == (1 << 13)) {
+ temp &= 0x1fff;
+ ehci->debug = hcd->regs + temp;
+ temp = readl (&ehci->debug->control);
+ ehci_info (ehci, "debug port %d%s\n",
+ HCS_DEBUG_PORT(ehci->hcs_params),
+ (temp & DBGP_ENABLED)
+ ? " IN USE"
+ : "");
+ if (!(temp & DBGP_ENABLED))
+ ehci->debug = NULL;
+ }
+ }
+
temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params));
} else
temp = 0;
+
+ /* EHCI 0.96 and later may have "extended capabilities" */
while (temp && count--) {
u32 cap;
@@ -414,8 +453,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
ehci_reset (ehci);
#endif
- /* cache this readonly data; minimize PCI reads */
- ehci->hcs_params = readl (&ehci->caps->hcs_params);
+ ehci_port_power (ehci, 0);
/* at least the Genesys GL880S needs fixup here */
temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
@@ -657,16 +695,11 @@ done2:
static void ehci_stop (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
- u8 rh_ports, port;
ehci_dbg (ehci, "stop\n");
/* Turn off port power on all root hub ports. */
- rh_ports = HCS_N_PORTS (ehci->hcs_params);
- for (port = 1; port <= rh_ports; port++)
- (void) ehci_hub_control(hcd,
- ClearPortFeature, USB_PORT_FEAT_POWER,
- port, NULL, 0);
+ ehci_port_power (ehci, 0);
/* no more interrupts ... */
del_timer_sync (&ehci->watchdog);
@@ -748,7 +781,6 @@ static int ehci_resume (struct usb_hcd *hcd)
unsigned port;
struct usb_device *root = hcd->self.root_hub;
int retval = -EINVAL;
- int powerup = 0;
// maybe restore (PCI) FLADJ
@@ -766,8 +798,6 @@ static int ehci_resume (struct usb_hcd *hcd)
up (&hcd->self.root_hub->serialize);
break;
}
- if ((status & PORT_POWER) == 0)
- powerup = 1;
if (!root->children [port])
continue;
dbg_port (ehci, __FUNCTION__, port + 1, status);
@@ -794,16 +824,9 @@ static int ehci_resume (struct usb_hcd *hcd)
retval = ehci_start (hcd);
/* here we "know" root ports should always stay powered;
- * but some controllers may lost all power.
+ * but some controllers may lose all power.
*/
- if (powerup) {
- ehci_dbg (ehci, "...powerup ports...\n");
- for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; )
- (void) ehci_hub_control(hcd,
- SetPortFeature, USB_PORT_FEAT_POWER,
- port--, NULL, 0);
- msleep(20);
- }
+ ehci_port_power (ehci, 1);
}
return retval;
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 2373537fabed..d7b4f7939ded 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -72,6 +72,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd)
}
/* turn off now-idle HC */
+ del_timer_sync (&ehci->watchdog);
ehci_halt (ehci);
hcd->state = HC_STATE_SUSPENDED;
@@ -281,6 +282,8 @@ ehci_hub_descriptor (
temp = 0x0008; /* per-port overcurrent reporting */
if (HCS_PPC (ehci->hcs_params))
temp |= 0x0001; /* per-port power control */
+ else
+ temp |= 0x0002; /* no power switching */
#if 0
// re-enable when we support USB_PORT_FEAT_INDICATOR below.
if (HCS_INDICATOR (ehci->hcs_params))
@@ -436,9 +439,12 @@ static int ehci_hub_control (
/* force reset to complete */
writel (temp & ~PORT_RESET,
&ehci->regs->port_status [wIndex]);
+ /* REVISIT: some hardware needs 550+ usec to clear
+ * this bit; seems too long to spin routinely...
+ */
retval = handshake (
&ehci->regs->port_status [wIndex],
- PORT_RESET, 0, 500);
+ PORT_RESET, 0, 750);
if (retval != 0) {
ehci_err (ehci, "port %d reset error %d\n",
wIndex + 1, retval);
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index e763a8399a75..4df498231752 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -47,6 +47,12 @@ struct ehci_stats {
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
struct ehci_hcd { /* one per controller */
+ /* glue to PCI and HCD framework */
+ struct ehci_caps __iomem *caps;
+ struct ehci_regs __iomem *regs;
+ struct ehci_dbg_port __iomem *debug;
+
+ __u32 hcs_params; /* cached register copy */
spinlock_t lock;
/* async schedule support */
@@ -84,11 +90,6 @@ struct ehci_hcd { /* one per controller */
unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */
- /* glue to PCI and HCD framework */
- struct ehci_caps __iomem *caps;
- struct ehci_regs __iomem *regs;
- __u32 hcs_params; /* cached register copy */
-
/* irq statistics */
#ifdef EHCI_STATS
struct ehci_stats stats;
@@ -165,7 +166,7 @@ struct ehci_caps {
/* these fields are specified as 8 and 16 bit registers,
* but some hosts can't perform 8 or 16 bit PCI accesses.
*/
- u32 hc_capbase;
+ u32 hc_capbase;
#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
u32 hcs_params; /* HCSPARAMS - offset 0x4 */
@@ -273,7 +274,7 @@ struct ehci_dbg_port {
#define DBGP_ENABLED (1<<28)
#define DBGP_DONE (1<<16)
#define DBGP_INUSE (1<<10)
-#define DBGP_ERRCODE(x) (((x)>>7)&0x0f)
+#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
# define DBGP_ERR_BAD 1
# define DBGP_ERR_SIGNAL 2
#define DBGP_ERROR (1<<6)
@@ -282,11 +283,11 @@ struct ehci_dbg_port {
#define DBGP_LEN(x) (((x)>>0)&0x0f)
u32 pids;
#define DBGP_PID_GET(x) (((x)>>16)&0xff)
-#define DBGP_PID_SET(data,tok) (((data)<<8)|(tok));
+#define DBGP_PID_SET(data,tok) (((data)<<8)|(tok))
u32 data03;
u32 data47;
u32 address;
-#define DBGP_EPADDR(dev,ep) (((dev)<<8)|(ep));
+#define DBGP_EPADDR(dev,ep) (((dev)<<8)|(ep))
} __attribute__ ((packed));
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c
index 376f8a034f65..d9883d774d3a 100644
--- a/drivers/usb/host/hc_crisv10.c
+++ b/drivers/usb/host/hc_crisv10.c
@@ -4329,7 +4329,7 @@ static int __init etrax_usb_hc_init(void)
bus->bus_name="ETRAX 100LX";
bus->hcpriv = hc;
- /* Initalize RH to the default address.
+ /* Initialize RH to the default address.
And make sure that we have no status change indication */
hc->rh.numports = 2; /* The RH has two ports */
hc->rh.devnum = 1;
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index d309e292198e..99d43f758ad0 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -2,8 +2,8 @@
* SL811HS HCD (Host Controller Driver) for USB.
*
* Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
- * Copyright (C) 2004 David Brownell
- *
+ * Copyright (C) 2004-2005 David Brownell
+ *
* Periodic scheduling is based on Roman's OHCI code
* Copyright (C) 1999 Roman Weissgaerber
*
@@ -15,7 +15,7 @@
* 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,
@@ -67,7 +67,7 @@
MODULE_DESCRIPTION("SL811HS USB Host Controller Driver");
MODULE_LICENSE("GPL");
-#define DRIVER_VERSION "15 Dec 2004"
+#define DRIVER_VERSION "19 May 2005"
#ifndef DEBUG
@@ -121,6 +121,10 @@ static void port_power(struct sl811 *sl811, int is_on)
/* reset as thoroughly as we can */
if (sl811->board && sl811->board->reset)
sl811->board->reset(hcd->self.controller);
+ else {
+ sl811_write(sl811, SL11H_CTLREG1, SL11H_CTL1MASK_SE0);
+ mdelay(20);
+ }
sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
@@ -134,7 +138,7 @@ static void port_power(struct sl811 *sl811, int is_on)
/* 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.
+ * handlers (one per packet: ACK, NAK, faults, etc) and urb cancellation.
*
* 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.
@@ -443,6 +447,7 @@ static void finish_request(
spin_lock(&urb->lock);
if (urb->status == -EINPROGRESS)
urb->status = status;
+ urb->hcpriv = NULL;
spin_unlock(&urb->lock);
spin_unlock(&sl811->lock);
@@ -472,7 +477,7 @@ static void finish_request(
if (*prev)
*prev = ep->next;
sl811->load[i] -= ep->load;
- }
+ }
ep->branch = PERIODIC_SIZE;
sl811->periodic_count--;
sl811_to_hcd(sl811)->self.bandwidth_allocated
@@ -661,9 +666,9 @@ retry:
#ifdef QUIRK2
/* this may no longer be necessary ... */
- if (irqstat == 0 && ret == IRQ_NONE) {
+ if (irqstat == 0) {
irqstat = checkdone(sl811);
- if (irqstat /* && irq != ~0 */ )
+ if (irqstat)
sl811->stat_lost++;
}
#endif
@@ -722,7 +727,8 @@ retry:
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,
+ container_of(sl811->active_a
+ ->hep->urb_list.next,
struct urb, urb_list),
NULL, -ESHUTDOWN);
sl811->active_a = NULL;
@@ -731,14 +737,15 @@ retry:
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,
+ 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
+ /* port status seems weird until after reset, so
* force the reset and make khubd clean up later.
*/
sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION)
@@ -761,7 +768,7 @@ retry:
goto retry;
}
- if (sl811->periodic_count == 0 && list_empty(&sl811->async))
+ if (sl811->periodic_count == 0 && list_empty(&sl811->async))
sofirq_off(sl811);
sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
@@ -796,7 +803,7 @@ static int balance(struct sl811 *sl811, u16 period, u16 load)
}
if (j < PERIODIC_SIZE)
continue;
- branch = i;
+ branch = i;
}
}
return branch;
@@ -890,6 +897,7 @@ static int sl811h_urb_enqueue(
break;
}
+ ep->hep = hep;
hep->hcpriv = ep;
}
@@ -961,15 +969,16 @@ fail:
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;
+ struct usb_host_endpoint *hep;
unsigned long flags;
struct sl811h_ep *ep;
int retval = 0;
+ spin_lock_irqsave(&sl811->lock, flags);
+ hep = urb->hcpriv;
if (!hep)
- return -EINVAL;
+ goto fail;
- spin_lock_irqsave(&sl811->lock, flags);
ep = hep->hcpriv;
if (ep) {
/* finish right away if this urb can't be active ...
@@ -1017,6 +1026,7 @@ static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
VDBG("dequeue, urb %p active %s; wait4irq\n", urb,
(sl811->active_a == ep) ? "A" : "B");
} else
+fail:
retval = -EINVAL;
spin_unlock_irqrestore(&sl811->lock, flags);
return retval;
@@ -1576,6 +1586,9 @@ sl811h_start(struct usb_hcd *hcd)
if (sl811->board && sl811->board->power)
hub_set_power_budget(udev, sl811->board->power * 2);
+ /* enable power and interupts */
+ port_power(sl811, 1);
+
return 0;
}
@@ -1618,7 +1631,7 @@ static struct hc_driver sl811h_hc_driver = {
/*-------------------------------------------------------------------------*/
-static int __init_or_module
+static int __devexit
sl811h_remove(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
@@ -1631,21 +1644,20 @@ sl811h_remove(struct device *dev)
remove_debug_file(sl811);
usb_remove_hcd(hcd);
- iounmap(sl811->data_reg);
+ /* some platforms may use IORESOURCE_IO */
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- release_mem_region(res->start, 1);
+ if (res)
+ iounmap(sl811->data_reg);
- iounmap(sl811->addr_reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, 1);
+ if (res)
+ iounmap(sl811->addr_reg);
usb_put_hcd(hcd);
return 0;
}
-#define resource_len(r) (((r)->end - (r)->start) + 1)
-
-static int __init
+static int __devinit
sl811h_probe(struct device *dev)
{
struct usb_hcd *hcd;
@@ -1656,7 +1668,7 @@ sl811h_probe(struct device *dev)
void __iomem *addr_reg;
void __iomem *data_reg;
int retval;
- u8 tmp;
+ u8 tmp, ioaddr = 0;
/* basic sanity checks first. board-specific init logic should
* have initialized these three resources and probably board
@@ -1664,13 +1676,8 @@ sl811h_probe(struct device *dev)
* 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)
+ if (pdev->num_resources < 3 || irq < 0)
return -ENODEV;
/* refuse to confuse usbcore */
@@ -1679,24 +1686,31 @@ sl811h_probe(struct device *dev)
return -EINVAL;
}
- if (!request_mem_region(addr->start, 1, hcd_name)) {
- retval = -EBUSY;
- goto err1;
- }
- addr_reg = ioremap(addr->start, resource_len(addr));
- if (addr_reg == NULL) {
- retval = -ENOMEM;
- goto err2;
- }
+ /* the chip may be wired for either kind of addressing */
+ addr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ retval = -EBUSY;
+ if (!addr || !data) {
+ addr = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ data = platform_get_resource(pdev, IORESOURCE_IO, 1);
+ if (!addr || !data)
+ return -ENODEV;
+ ioaddr = 1;
+
+ addr_reg = (void __iomem *) addr->start;
+ data_reg = (void __iomem *) data->start;
+ } else {
+ addr_reg = ioremap(addr->start, 1);
+ if (addr_reg == NULL) {
+ retval = -ENOMEM;
+ goto err2;
+ }
- if (!request_mem_region(data->start, 1, hcd_name)) {
- retval = -EBUSY;
- goto err3;
- }
- data_reg = ioremap(data->start, resource_len(addr));
- if (data_reg == NULL) {
- retval = -ENOMEM;
- goto err4;
+ data_reg = ioremap(data->start, 1);
+ if (data_reg == NULL) {
+ retval = -ENOMEM;
+ goto err4;
+ }
}
/* allocate and initialize hcd */
@@ -1737,12 +1751,14 @@ sl811h_probe(struct device *dev)
goto err6;
}
- /* sl811s would need a different handler for this irq */
-#ifdef 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);
+ /* The chip's IRQ is level triggered, active high. A requirement
+ * for platform device setup is to cope with things like signal
+ * inverters (e.g. CF is active low) or working only with edge
+ * triggers (e.g. most ARM CPUs). Initial driver stress testing
+ * was on a system with single edge triggering, so most sorts of
+ * triggering arrangement should work.
+ */
+ retval = usb_add_hcd(hcd, irq, SA_INTERRUPT | SA_SHIRQ);
if (retval != 0)
goto err6;
@@ -1752,14 +1768,12 @@ sl811h_probe(struct device *dev)
err6:
usb_put_hcd(hcd);
err5:
- iounmap(data_reg);
+ if (!ioaddr)
+ iounmap(data_reg);
err4:
- release_mem_region(data->start, 1);
- err3:
- iounmap(addr_reg);
+ if (!ioaddr)
+ iounmap(addr_reg);
err2:
- release_mem_region(addr->start, 1);
- err1:
DBG("init error, %d\n", retval);
return retval;
}
@@ -1767,7 +1781,7 @@ sl811h_probe(struct device *dev)
#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
+ * and its root hub, except that the root hub only gets direct PM calls
* when CONFIG_USB_SUSPEND is enabled.
*/
@@ -1821,20 +1835,22 @@ sl811h_resume(struct device *dev, u32 phase)
#endif
-static struct device_driver sl811h_driver = {
+/* this driver is exported so sl811_cs can depend on it */
+struct device_driver sl811h_driver = {
.name = (char *) hcd_name,
.bus = &platform_bus_type,
.probe = sl811h_probe,
- .remove = sl811h_remove,
+ .remove = __devexit_p(sl811h_remove),
.suspend = sl811h_suspend,
.resume = sl811h_resume,
};
+EXPORT_SYMBOL(sl811h_driver);
/*-------------------------------------------------------------------------*/
-
-static int __init sl811h_init(void)
+
+static int __init sl811h_init(void)
{
if (usb_disabled())
return -ENODEV;
@@ -1844,8 +1860,8 @@ static int __init sl811h_init(void)
}
module_init(sl811h_init);
-static void __exit sl811h_cleanup(void)
-{
+static void __exit sl811h_cleanup(void)
+{
driver_unregister(&sl811h_driver);
}
module_exit(sl811h_cleanup);
diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c
new file mode 100644
index 000000000000..6e173265095c
--- /dev/null
+++ b/drivers/usb/host/sl811_cs.c
@@ -0,0 +1,442 @@
+/*
+ * PCMCIA driver for SL811HS (as found in REX-CFU1U)
+ * Filename: sl811_cs.c
+ * Author: Yukio Yamamoto
+ *
+ * Port to sl811-hcd and 2.6.x by
+ * Botond Botyanszki <boti@rocketmail.com>
+ * Simon Pickering
+ *
+ * Last update: 2005-05-12
+ */
+
+#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 <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-CFU1U PCMCIA driver for 2.6");
+MODULE_LICENSE("GPL");
+
+
+/*====================================================================*/
+/* MACROS */
+/*====================================================================*/
+
+#if defined(DEBUG) || defined(CONFIG_USB_DEBUG) || defined(PCMCIA_DEBUG)
+
+static int pc_debug = 0;
+module_param(pc_debug, int, 0644);
+
+#define DBG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG "sl811_cs: " args)
+
+#else
+#define DBG(n, args...) do{}while(0)
+#endif /* no debugging */
+
+#define INFO(args...) printk(KERN_INFO "sl811_cs: " args)
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
+
+#define CS_CHECK(fn, ret) \
+ do { \
+ last_fn = (fn); \
+ if ((last_ret = (ret)) != 0) \
+ goto cs_failed; \
+ } while (0)
+
+/*====================================================================*/
+/* VARIABLES */
+/*====================================================================*/
+
+static const char driver_name[DEV_NAME_LEN] = "sl811_cs";
+
+static dev_link_t *dev_list = NULL;
+
+static int irq_list[4] = { -1 };
+static int irq_list_count;
+
+module_param_array(irq_list, int, &irq_list_count, 0444);
+
+INT_MODULE_PARM(irq_mask, 0xdeb8);
+
+typedef struct local_info_t {
+ dev_link_t link;
+ dev_node_t node;
+} local_info_t;
+
+/*====================================================================*/
+
+static void release_platform_dev(struct device * dev)
+{
+ DBG(0, "sl811_cs platform_dev release\n");
+ dev->parent = NULL;
+}
+
+static struct sl811_platform_data platform_data = {
+ .potpg = 100,
+ .power = 50, /* == 100mA */
+ // .reset = ... FIXME: invoke CF reset on the card
+};
+
+static struct resource resources[] = {
+ [0] = {
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ // .name = "address",
+ .flags = IORESOURCE_IO,
+ },
+ [2] = {
+ // .name = "data",
+ .flags = IORESOURCE_IO,
+ },
+};
+
+extern struct device_driver sl811h_driver;
+
+static struct platform_device platform_dev = {
+ .id = -1,
+ .dev = {
+ .platform_data = &platform_data,
+ .release = release_platform_dev,
+ },
+ .resource = resources,
+ .num_resources = ARRAY_SIZE(resources),
+};
+
+static int sl811_hc_init(struct device *parent, ioaddr_t base_addr, int irq)
+{
+ if (platform_dev.dev.parent)
+ return -EBUSY;
+ platform_dev.dev.parent = parent;
+
+ /* finish seting up the platform device */
+ resources[0].start = irq;
+
+ resources[1].start = base_addr;
+ resources[1].end = base_addr;
+
+ resources[2].start = base_addr + 1;
+ resources[2].end = base_addr + 1;
+
+ /* The driver core will probe for us. We know sl811-hcd has been
+ * initialized already because of the link order dependency.
+ */
+ platform_dev.name = sl811h_driver.name;
+ return platform_device_register(&platform_dev);
+}
+
+/*====================================================================*/
+
+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;
+
+ /* 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);
+}
+
+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;
+ }
+
+ /* Unlink the device chain */
+ link->dev = NULL;
+
+ platform_device_unregister(&platform_dev);
+ 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);
+}
+
+static void sl811_cs_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ struct device *parent = &handle_to_dev(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;
+
+ /* 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 (cfg->vcc.param[CISTPL_POWER_VNOM]/10000
+ != conf.Vcc)
+ goto next_entry;
+ } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (dflt.vcc.param[CISTPL_POWER_VNOM]/10000
+ != conf.Vcc)
+ 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;
+
+ /* we need 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);
+ }
+
+ /* require an IRQ and two registers */
+ if (!link->io.NumPorts1 || link->io.NumPorts1 < 2)
+ goto cs_failed;
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ CS_CHECK(RequestIRQ,
+ pcmcia_request_irq(link->handle, &link->irq));
+ else
+ goto cs_failed;
+
+ CS_CHECK(RequestConfiguration,
+ pcmcia_request_configuration(link->handle, &link->conf));
+
+ sprintf(dev->node.dev_name, driver_name);
+ 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);
+ printk(", irq %d", link->irq.AssignedIRQ);
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1+link->io.NumPorts1-1);
+ printk("\n");
+
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ if (sl811_hc_init(parent, link->io.BasePort1, link->irq.AssignedIRQ)
+ < 0) {
+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;
+ }
+}
+
+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);
+ DBG(0, "reset sl811-hcd here?\n");
+ break;
+ }
+ return 0;
+}
+
+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 < irq_list_count; 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_t *) &driver_name;
+ 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;
+}
+
+static struct pcmcia_driver sl811_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = (char *)driver_name,
+ },
+ .attach = sl811_cs_attach,
+ .detach = sl811_cs_detach,
+};
+
+/*====================================================================*/
+
+static int __init init_sl811_cs(void)
+{
+ return pcmcia_register_driver(&sl811_cs_driver);
+}
+module_init(init_sl811_cs);
+
+static void __exit exit_sl811_cs(void)
+{
+ pcmcia_unregister_driver(&sl811_cs_driver);
+}
+module_exit(exit_sl811_cs);