*
* Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org)
*
- * Based on the ADMtek 2.4 driver
+ * Based on the ADMtek 2.4 driver
* (C) Copyright 2003 Junius Chen <juniusc@admtek.com.tw>
* Which again was based on the ohci and uhci drivers.
*/
-#include <linux/autoconf.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <linux/errno.h>
-#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/list.h>
#include <linux/usb.h>
#include <linux/platform_device.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/mach-adm5120/adm5120_info.h>
+
#include "../core/hcd.h"
MODULE_DESCRIPTION("ADM5120 USB Host Controller Driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jeroen Vreeken (pe1rxq@amsat.org)");
+#define PFX "adm5120-hcd: "
+
#define ADMHCD_REG_CONTROL 0x00
#define ADMHCD_REG_INTSTATUS 0x04
#define ADMHCD_REG_INTENABLE 0x08
#define ED(ed) ((struct admhcd_ed *)(((u32)(ed)) & ~0xf))
struct admhcd {
+ spinlock_t lock;
+
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+ /* Root hub registers */
+ u32 rhdesca;
+ u32 rhdescb;
+ u32 rhstatus;
+ u32 rhport[2];
+
+ /* async schedule: control, bulk */
+ struct list_head async;
u32 base;
u32 dma_en;
- spinlock_t lock;
unsigned long flags;
+
};
-#define hcd_to_admhcd(hcd) ((struct admhcd *)(hcd)->hcd_priv)
+static inline struct admhcd *hcd_to_admhcd(struct usb_hcd *hcd)
+{
+ return (struct admhcd *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *admhcd_to_hcd(struct admhcd *admhcd)
+{
+ return container_of((void *)admhcd, struct usb_hcd, hcd_priv);
+}
static char hcd_name[] = "adm5120-hcd";
if (ed->cur == NULL) {
ed->cur = tdn;
ed->head = tdn;
- ed->tail = tdn;
+ ed->tail = tdn;
td = tdn;
} else {
/* Supply back the old tail and link in new td as tail */
ahcd->dma_en |= ADMHCD_DMA_EN;
}
-static irqreturn_t adm5120hcd_irq(int irq, void *ptr, struct pt_regs *regs)
+static irqreturn_t adm5120hcd_irq(struct usb_hcd *hcd)
{
- struct usb_hcd *hcd = (struct usb_hcd *)ptr;
struct admhcd *ahcd = hcd_to_admhcd(hcd);
u32 intstatus;
}
if (intstatus & ADMHCD_INT_TD) {
struct admhcd_ed *ed, *head;
-
+
admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, ADMHCD_INT_TD);
head = (struct admhcd_ed *)admhcd_reg_get(ahcd, ADMHCD_REG_HOSTHEAD);
if (ed->urb && !(ed->cur->control & ADMHCD_TD_OWN)) {
struct admhcd_td *td;
int error;
-
+
td = ed->cur;
error = (td->control & ADMHCD_TD_ERRMASK) >>
ADMHCD_TD_ERRSHIFT;
}
static int admhcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep,
- struct urb *urb, int mem_flags)
+ struct urb *urb, gfp_t mem_flags)
{
struct admhcd *ahcd = hcd_to_admhcd(hcd);
struct admhcd_ed *ed;
td = admhcd_td_fill(ADMHCD_TD_SETUP | ADMHCD_TD_DATA0,
td, (dma_addr_t)urb->setup_packet, 8);
while (data_len > 0) {
- td = admhcd_td_fill(ADMHCD_TD_DATA1
+ td = admhcd_td_fill(ADMHCD_TD_DATA1
| ADMHCD_TD_R |
(usb_pipeout(pipe) ?
ADMHCD_TD_OUT : ADMHCD_TD_IN), td,
i = 0;
while(data_len > 4096) {
td = admhcd_td_fill((usb_pipeout(pipe) ?
- ADMHCD_TD_OUT :
+ ADMHCD_TD_OUT :
ADMHCD_TD_IN | ADMHCD_TD_R) |
(i ? ADMHCD_TD_TOGGLE : toggle), td,
data, 4096);
data_len -= 4096;
i++;
}
- td = admhcd_td_fill((usb_pipeout(pipe) ?
+ td = admhcd_td_fill((usb_pipeout(pipe) ?
ADMHCD_TD_OUT : ADMHCD_TD_IN) |
(i ? ADMHCD_TD_TOGGLE : toggle), td, data, data_len);
i++;
case USB_PORT_FEAT_RESET:
if (admhcd_reg_get(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4)
& ADMHCD_CCS) {
- admhcd_reg_set(ahcd,
+ admhcd_reg_set(ahcd,
ADMHCD_REG_PORTSTATUS0 + port*4,
ADMHCD_PRS | ADMHCD_CSC);
mdelay(50);
.description = hcd_name,
.product_desc = "ADM5120 HCD",
.hcd_priv_size = sizeof(struct admhcd),
+ .irq = adm5120hcd_irq,
.flags = HCD_USB11,
.urb_enqueue = admhcd_urb_enqueue,
.urb_dequeue = admhcd_urb_dequeue,
.hub_control = admhcd_hub_control,
};
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+
static int __init adm5120hcd_probe(struct platform_device *pdev)
{
- struct usb_hcd *hcd;
- struct admhcd *ahcd;
- struct usb_device *udev;
- int err = 0;
+ struct usb_hcd *hcd;
+ struct admhcd *ahcd;
+ struct resource *addr, *data;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
- if (!request_mem_region(pdev->resource[0].start,
- pdev->resource[0].end - pdev->resource[0].start, hcd_name)) {
- pr_debug("couldn't request mem\n");
- err = -EBUSY;
- goto out;
- }
-
- //hcd = usb_create_hcd(&adm5120_hc_driver, pdev, pdev->bus_id);
- if (!hcd)
- goto out_mem;
+ int err = 0, irq;
+ if (pdev->num_resources < 3) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ if (pdev->dev.dma_mask) {
+ printk(KERN_DEBUG "no we won't dma\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ data = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ addr = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ if (!addr || !data || irq < 0) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ if (!request_mem_region(addr->start, 2, hcd_name)) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ addr_reg = ioremap(addr->start, resource_len(addr));
+ if (addr_reg == NULL) {
+ err = -ENOMEM;
+ goto out_mem;
+ }
+ if (!request_mem_region(data->start, 2, hcd_name)) {
+ err = -EBUSY;
+ goto out_unmap;
+ }
+
+ data_reg = ioremap(data->start, resource_len(data));
+ if (data_reg == NULL) {
+ err = -ENOMEM;
+ goto out_mem;
+ }
+
+ hcd = usb_create_hcd(&adm5120_hc_driver, &pdev->dev, pdev->dev.bus_id);
+ if (!hcd)
+ goto out_mem;
+
+ hcd->rsrc_start = addr->start;
ahcd = hcd_to_admhcd(hcd);
- dev_set_drvdata(pdev, ahcd);
- hcd->self.controller = pdev;
- //hcd->self.bus_name = pdev->bus_id;
- hcd->irq = pdev->resource[1].start;
- hcd->regs = (void *)pdev->resource[0].start;
- hcd->product_desc = hcd_name;
- ahcd->base = pdev->resource[0].start;
-
- if (request_irq(pdev->resource[1].start, adm5120hcd_irq, 0, hcd_name,
- hcd)) {
- pr_debug("couldn't request irq\n");
- err = -EBUSY;
- goto out_hcd;
- }
-
- //err = usb_register_bus(&hcd->self);
- //if (err < 0)
- // goto out_irq;
spin_lock_init(&ahcd->lock);
+ INIT_LIST_HEAD(&ahcd->async);
+
+ ahcd->data_reg = data_reg;
+ ahcd->addr_reg = addr_reg;
+ hcd->product_desc = "ADM5120 HCD";
+
+ /* Initialise the HCD registers */
admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0);
mdelay(10);
+
admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_SW_RESET);
+
while (admhcd_reg_get(ahcd, ADMHCD_REG_CONTROL) & ADMHCD_SW_RESET)
mdelay(1);
admhcd_reg_set(ahcd, ADMHCD_REG_RHDESCR, ADMHCD_NPS | ADMHCD_LPSC);
admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, ADMHCD_STATE_OP);
- udev = usb_alloc_dev(NULL, &hcd->self, 0);
- if (!udev) {
- err = -ENOMEM;
- goto out_bus;
- }
-
- udev->speed = USB_SPEED_FULL;
- hcd->state = HC_STATE_RUNNING;
-
- //err = hcd_register_root(udev, hcd);
- //if (err != 0) {
- // usb_put_dev(udev);
- // goto out_dev;
- //}
+ err = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ if (err)
+ goto out_dev;
return 0;
out_dev:
- usb_put_dev(udev);
-out_bus:
- //usb_deregister_bus(&hcd->self);
-out_irq:
- free_irq(pdev->resource[1].start, hcd);
-out_hcd:
usb_put_hcd(hcd);
+out_unmap:
+ iounmap(addr_reg);
out_mem:
- release_mem_region(pdev->resource[0].start,
- pdev->resource[0].end - pdev->resource[0].start);
+ release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start);
out:
return err;
}
static int __init_or_module adm5120hcd_remove(struct platform_device *pdev)
{
- device_init_wakeup(&pdev->dev, 0);
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct admhcd *ahcd;
+
+ if (!hcd)
+ return 0;
+ ahcd = hcd_to_admhcd(hcd);
+ usb_remove_hcd(hcd);
+
+ usb_put_hcd(hcd);
+ return 0;
}
static struct platform_driver adm5120hcd_driver = {
static int __init adm5120hcd_init(void)
{
- if (usb_disabled())
+ if (usb_disabled())
return -ENODEV;
+ if (!adm5120_board.has_usb) {
+ printk(KERN_DEBUG PFX "this board does not have USB\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO PFX "registered\n");
return platform_driver_register(&adm5120hcd_driver);
}