123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843 |
- /*
- * HCD driver for ADM5120 SoC
- *
- * Copyright (C) 2005 Jeroen Vreeken ([email protected])
- *
- * Based on the ADMtek 2.4 driver
- * (C) Copyright 2003 Junius Chen <[email protected]>
- * Which again was based on the ohci and uhci drivers.
- */
- #include <linux/module.h>
- #include <linux/delay.h>
- #include <linux/debugfs.h>
- #include <linux/seq_file.h>
- #include <linux/errno.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 ([email protected])");
- #define ADMHCD_REG_CONTROL 0x00
- #define ADMHCD_REG_INTSTATUS 0x04
- #define ADMHCD_REG_INTENABLE 0x08
- #define ADMHCD_REG_HOSTCONTROL 0x10
- #define ADMHCD_REG_FMINTERVAL 0x18
- #define ADMHCD_REG_FMNUMBER 0x1c
- #define ADMHCD_REG_LSTHRESH 0x70
- #define ADMHCD_REG_RHDESCR 0x74
- #define ADMHCD_REG_PORTSTATUS0 0x78
- #define ADMHCD_REG_PORTSTATUS1 0x7c
- #define ADMHCD_REG_HOSTHEAD 0x80
- #define ADMHCD_NUMPORTS 2
- #define ADMHCD_HOST_EN 0x00000001 /* Host enable */
- #define ADMHCD_SW_INTREQ 0x00000002 /* request software int */
- #define ADMHCD_SW_RESET 0x00000008 /* Reset */
- #define ADMHCD_INT_TD 0x00100000 /* TD completed */
- #define ADMHCD_INT_SW 0x20000000 /* software interrupt */
- #define ADMHCD_INT_FATAL 0x40000000 /* Fatal interrupt */
- #define ADMHCD_INT_ACT 0x80000000 /* Interrupt active */
- #define ADMHCD_STATE_RST 0x00000000 /* bus state reset */
- #define ADMHCD_STATE_RES 0x00000001 /* bus state resume */
- #define ADMHCD_STATE_OP 0x00000002 /* bus state operational */
- #define ADMHCD_STATE_SUS 0x00000003 /* bus state suspended */
- #define ADMHCD_DMA_EN 0x00000004 /* enable dma engine */
- #define ADMHCD_NPS 0x00000020 /* No Power Switch */
- #define ADMHCD_LPSC 0x04000000 /* Local power switch change */
- #define ADMHCD_CCS 0x00000001 /* current connect status */
- #define ADMHCD_PES 0x00000002 /* port enable status */
- #define ADMHCD_PSS 0x00000004 /* port suspend status */
- #define ADMHCD_POCI 0x00000008 /* port overcurrent indicator */
- #define ADMHCD_PRS 0x00000010 /* port reset status */
- #define ADMHCD_PPS 0x00000100 /* port power status */
- #define ADMHCD_LSDA 0x00000200 /* low speed device attached */
- #define ADMHCD_CSC 0x00010000 /* connect status change */
- #define ADMHCD_PESC 0x00020000 /* enable status change */
- #define ADMHCD_PSSC 0x00040000 /* suspend status change */
- #define ADMHCD_OCIC 0x00080000 /* overcurrent change*/
- #define ADMHCD_PRSC 0x00100000 /* reset status change */
- struct admhcd_ed {
- /* Don't change first four, they used for DMA */
- u32 control;
- struct admhcd_td *tail;
- struct admhcd_td *head;
- struct admhcd_ed *next;
- /* the rest is for the driver only: */
- struct admhcd_td *cur;
- struct usb_host_endpoint *ep;
- struct urb *urb;
- struct admhcd_ed *real;
- } __attribute__ ((packed));
- #define ADMHCD_ED_EPSHIFT 7 /* Shift for endpoint number */
- #define ADMHCD_ED_INT 0x00000800 /* Is this an int endpoint */
- #define ADMHCD_ED_SPEED 0x00002000 /* Is it a high speed dev? */
- #define ADMHCD_ED_SKIP 0x00004000 /* Skip this ED */
- #define ADMHCD_ED_FORMAT 0x00008000 /* Is this an isoc endpoint */
- #define ADMHCD_ED_MAXSHIFT 16 /* Shift for max packet size */
- struct admhcd_td {
- /* Don't change first four, they are used for DMA */
- u32 control;
- u32 buffer;
- u32 buflen;
- struct admhcd_td *next;
- /* the rest is for the driver only: */
- struct urb *urb;
- struct admhcd_td *real;
- } __attribute__ ((packed));
- #define ADMHCD_TD_OWN 0x80000000
- #define ADMHCD_TD_TOGGLE 0x00000000
- #define ADMHCD_TD_DATA0 0x01000000
- #define ADMHCD_TD_DATA1 0x01800000
- #define ADMHCD_TD_OUT 0x00200000
- #define ADMHCD_TD_IN 0x00400000
- #define ADMHCD_TD_SETUP 0x00000000
- #define ADMHCD_TD_ISO 0x00010000
- #define ADMHCD_TD_R 0x00040000
- #define ADMHCD_TD_INTEN 0x00010000
- static int admhcd_td_err[16] = {
- 0, /* No */
- -EREMOTEIO, /* CRC */
- -EREMOTEIO, /* bit stuff */
- -EREMOTEIO, /* data toggle */
- -EPIPE, /* stall */
- -ETIMEDOUT, /* timeout */
- -EPROTO, /* pid err */
- -EPROTO, /* unexpected pid */
- -EREMOTEIO, /* data overrun */
- -EREMOTEIO, /* data underrun */
- -ETIMEDOUT, /* 1010 */
- -ETIMEDOUT, /* 1011 */
- -EREMOTEIO, /* buffer overrun */
- -EREMOTEIO, /* buffer underrun */
- -ETIMEDOUT, /* 1110 */
- -ETIMEDOUT, /* 1111 */
- };
- #define ADMHCD_TD_ERRMASK 0x38000000
- #define ADMHCD_TD_ERRSHIFT 27
- #define TD(td) ((struct admhcd_td *)(((u32)(td)) & ~0xf))
- #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;
- unsigned long flags;
- };
- 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";
- static u32 admhcd_reg_get(struct admhcd *ahcd, int reg)
- {
- return *(volatile u32 *)KSEG1ADDR(ahcd->base+reg);
- }
- static void admhcd_reg_set(struct admhcd *ahcd, int reg, u32 val)
- {
- *(volatile u32 *)KSEG1ADDR(ahcd->base+reg) = val;
- }
- static void admhcd_lock(struct admhcd *ahcd)
- {
- spin_lock_irqsave(&ahcd->lock, ahcd->flags);
- ahcd->dma_en = admhcd_reg_get(ahcd, ADMHCD_REG_HOSTCONTROL) &
- ADMHCD_DMA_EN;
- admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, ADMHCD_STATE_OP);
- }
- static void admhcd_unlock(struct admhcd *ahcd)
- {
- admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL,
- ADMHCD_STATE_OP | ahcd->dma_en);
- spin_unlock_irqrestore(&ahcd->lock, ahcd->flags);
- }
- static struct admhcd_td *admhcd_td_alloc(struct admhcd_ed *ed, struct urb *urb)
- {
- struct admhcd_td *tdn, *td;
- tdn = kmalloc(sizeof(struct admhcd_td), GFP_ATOMIC);
- if (!tdn)
- return NULL;
- tdn->real = tdn;
- tdn = (struct admhcd_td *)KSEG1ADDR(tdn);
- memset(tdn, 0, sizeof(struct admhcd_td));
- if (ed->cur == NULL) {
- ed->cur = tdn;
- ed->head = tdn;
- ed->tail = tdn;
- td = tdn;
- } else {
- /* Supply back the old tail and link in new td as tail */
- td = TD(ed->tail);
- TD(ed->tail)->next = tdn;
- ed->tail = tdn;
- }
- td->urb = urb;
- return td;
- }
- static void admhcd_td_free(struct admhcd_ed *ed, struct urb *urb)
- {
- struct admhcd_td *td, **tdp;
- if (urb == NULL)
- ed->control |= ADMHCD_ED_SKIP;
- tdp = &ed->cur;
- td = ed->cur;
- do {
- if (td->urb == urb)
- break;
- tdp = &td->next;
- td = TD(td->next);
- } while (td);
- while (td && td->urb == urb) {
- *tdp = TD(td->next);
- kfree(td->real);
- td = *tdp;
- }
- }
- /* Find an endpoint's descriptor, if needed allocate a new one and link it
- in the DMA chain
- */
- static struct admhcd_ed *admhcd_get_ed(struct admhcd *ahcd,
- struct usb_host_endpoint *ep, struct urb *urb)
- {
- struct admhcd_ed *hosthead;
- struct admhcd_ed *found = NULL, *ed = NULL;
- unsigned int pipe = urb->pipe;
- admhcd_lock(ahcd);
- hosthead = (struct admhcd_ed *)admhcd_reg_get(ahcd, ADMHCD_REG_HOSTHEAD);
- if (hosthead) {
- for (ed = hosthead;; ed = ED(ed->next)) {
- if (ed->ep == ep) {
- found = ed;
- break;
- }
- if (ED(ed->next) == hosthead)
- break;
- }
- }
- if (!found) {
- found = kmalloc(sizeof(struct admhcd_ed), GFP_ATOMIC);
- if (!found)
- goto out;
- memset(found, 0, sizeof(struct admhcd_ed));
- found->real = found;
- found->ep = ep;
- found = (struct admhcd_ed *)KSEG1ADDR(found);
- found->control = usb_pipedevice(pipe) |
- (usb_pipeendpoint(pipe) << ADMHCD_ED_EPSHIFT) |
- (usb_pipeint(pipe) ? ADMHCD_ED_INT : 0) |
- (urb->dev->speed == USB_SPEED_FULL ? ADMHCD_ED_SPEED : 0) |
- (usb_pipeisoc(pipe) ? ADMHCD_ED_FORMAT : 0) |
- (usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe)) << ADMHCD_ED_MAXSHIFT);
- /* Alloc first dummy td */
- admhcd_td_alloc(found, NULL);
- if (hosthead) {
- found->next = hosthead;
- ed->next = found;
- } else {
- found->next = found;
- admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, (u32)found);
- }
- }
- out:
- admhcd_unlock(ahcd);
- return found;
- }
- static struct admhcd_td *admhcd_td_fill(u32 control, struct admhcd_td *td,
- dma_addr_t data, int len)
- {
- td->buffer = data;
- td->buflen = len;
- td->control = control;
- return TD(td->next);
- }
- static void admhcd_ed_start(struct admhcd *ahcd, struct admhcd_ed *ed)
- {
- struct admhcd_td *td = ed->cur;
- if (ed->urb)
- return;
- if (td->urb) {
- ed->urb = td->urb;
- while (1) {
- td->control |= ADMHCD_TD_OWN;
- if (TD(td->next)->urb != td->urb) {
- td->buflen |= ADMHCD_TD_INTEN;
- break;
- }
- td = TD(td->next);
- }
- }
- ed->head = TD(ed->head);
- ahcd->dma_en |= ADMHCD_DMA_EN;
- }
- static irqreturn_t adm5120hcd_irq(struct usb_hcd *hcd)
- {
- struct admhcd *ahcd = hcd_to_admhcd(hcd);
- u32 intstatus;
- intstatus = admhcd_reg_get(ahcd, ADMHCD_REG_INTSTATUS);
- if (intstatus & ADMHCD_INT_FATAL) {
- admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, ADMHCD_INT_FATAL);
- //
- }
- if (intstatus & ADMHCD_INT_SW) {
- admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, ADMHCD_INT_SW);
- //
- }
- 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);
- ed = head;
- if (ed) do {
- /* Is it a finished TD? */
- 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;
- ed->urb->status = admhcd_td_err[error];
- admhcd_td_free(ed, ed->urb);
- // Calculate real length!!!
- ed->urb->actual_length = ed->urb->transfer_buffer_length;
- ed->urb->hcpriv = NULL;
- usb_hcd_giveback_urb(hcd, ed->urb);
- ed->urb = NULL;
- }
- admhcd_ed_start(ahcd, ed);
- ed = ED(ed->next);
- } while (ed != head);
- }
- return IRQ_HANDLED;
- }
- static int admhcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep,
- struct urb *urb, gfp_t mem_flags)
- {
- struct admhcd *ahcd = hcd_to_admhcd(hcd);
- struct admhcd_ed *ed;
- struct admhcd_td *td;
- int size = 0, i, zero = 0, ret = 0;
- unsigned int pipe = urb->pipe, toggle = 0;
- dma_addr_t data = (dma_addr_t)urb->transfer_buffer;
- int data_len = urb->transfer_buffer_length;
- ed = admhcd_get_ed(ahcd, ep, urb);
- if (!ed)
- return -ENOMEM;
- switch(usb_pipetype(pipe)) {
- case PIPE_CONTROL:
- size = 2;
- case PIPE_INTERRUPT:
- case PIPE_BULK:
- default:
- size += urb->transfer_buffer_length / 4096;
- if (urb->transfer_buffer_length % 4096)
- size++;
- if (size == 0)
- size++;
- else if (urb->transfer_flags & URB_ZERO_PACKET &&
- !(urb->transfer_buffer_length %
- usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe)))) {
- size++;
- zero = 1;
- }
- break;
- case PIPE_ISOCHRONOUS:
- size = urb->number_of_packets;
- break;
- }
- admhcd_lock(ahcd);
- /* Remember the first td */
- td = admhcd_td_alloc(ed, urb);
- if (!td) {
- ret = -ENOMEM;
- goto out;
- }
- /* Allocate additionall tds first */
- for (i = 1; i < size; i++) {
- if (admhcd_td_alloc(ed, urb) == NULL) {
- admhcd_td_free(ed, urb);
- ret = -ENOMEM;
- goto out;
- }
- }
- if (usb_gettoggle(urb->dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)))
- toggle = ADMHCD_TD_TOGGLE;
- else {
- toggle = ADMHCD_TD_DATA0;
- usb_settoggle(urb->dev, usb_pipeendpoint(pipe),
- usb_pipeout(pipe), 1);
- }
- switch(usb_pipetype(pipe)) {
- case PIPE_CONTROL:
- 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
- | ADMHCD_TD_R |
- (usb_pipeout(pipe) ?
- ADMHCD_TD_OUT : ADMHCD_TD_IN), td,
- data, data_len % 4097);
- data_len -= 4096;
- }
- admhcd_td_fill(ADMHCD_TD_DATA1 | (usb_pipeout(pipe) ?
- ADMHCD_TD_IN : ADMHCD_TD_OUT), td,
- data, 0);
- break;
- case PIPE_INTERRUPT:
- case PIPE_BULK:
- //info ok for interrupt?
- i = 0;
- while(data_len > 4096) {
- td = admhcd_td_fill((usb_pipeout(pipe) ?
- ADMHCD_TD_OUT :
- ADMHCD_TD_IN | ADMHCD_TD_R) |
- (i ? ADMHCD_TD_TOGGLE : toggle), td,
- data, 4096);
- data += 4096;
- data_len -= 4096;
- i++;
- }
- td = admhcd_td_fill((usb_pipeout(pipe) ?
- ADMHCD_TD_OUT : ADMHCD_TD_IN) |
- (i ? ADMHCD_TD_TOGGLE : toggle), td, data, data_len);
- i++;
- if (zero)
- admhcd_td_fill((usb_pipeout(pipe) ?
- ADMHCD_TD_OUT : ADMHCD_TD_IN) |
- (i ? ADMHCD_TD_TOGGLE : toggle), td, 0, 0);
- break;
- case PIPE_ISOCHRONOUS:
- for (i = 0; i < urb->number_of_packets; i++) {
- td = admhcd_td_fill(ADMHCD_TD_ISO |
- ((urb->start_frame + i) & 0xffff), td,
- data + urb->iso_frame_desc[i].offset,
- urb->iso_frame_desc[i].length);
- }
- break;
- }
- urb->hcpriv = ed;
- admhcd_ed_start(ahcd, ed);
- out:
- admhcd_unlock(ahcd);
- return ret;
- }
- static int admhcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
- {
- struct admhcd *ahcd = hcd_to_admhcd(hcd);
- struct admhcd_ed *ed;
- admhcd_lock(ahcd);
- ed = urb->hcpriv;
- if (ed && ed->urb != urb)
- admhcd_td_free(ed, urb);
- admhcd_unlock(ahcd);
- return 0;
- }
- static void admhcd_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
- {
- struct admhcd *ahcd = hcd_to_admhcd(hcd);
- struct admhcd_ed *ed, *edt, *head;
- admhcd_lock(ahcd);
- head = (struct admhcd_ed *)admhcd_reg_get(ahcd, ADMHCD_REG_HOSTHEAD);
- if (!head)
- goto out;
- for (ed = head; ED(ed->next) != head; ed = ED(ed->next))
- if (ed->ep == ep)
- break;
- if (ed->ep != ep)
- goto out;
- while (ed->cur)
- admhcd_td_free(ed, ed->cur->urb);
- if (head == ed) {
- if (ED(ed->next) == ed) {
- admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, 0);
- ahcd->dma_en = 0;
- goto out_free;
- }
- head = ED(ed->next);
- for (edt = head; ED(edt->next) != head; edt = ED(edt->next));
- edt->next = ED(ed->next);
- admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, (u32)ed->next);
- goto out_free;
- }
- for (edt = head; edt->next != ed; edt = edt->next);
- edt->next = ed->next;
- out_free:
- kfree(ed->real);
- out:
- admhcd_unlock(ahcd);
- }
- static int admhcd_get_frame_number(struct usb_hcd *hcd)
- {
- struct admhcd *ahcd = hcd_to_admhcd(hcd);
- return admhcd_reg_get(ahcd, ADMHCD_REG_FMNUMBER) & 0x0000ffff;
- }
- static int admhcd_hub_status_data(struct usb_hcd *hcd, char *buf)
- {
- struct admhcd *ahcd = hcd_to_admhcd(hcd);
- int port;
- *buf = 0;
- for (port = 0; port < ADMHCD_NUMPORTS; port++) {
- if (admhcd_reg_get(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4) &
- (ADMHCD_CSC | ADMHCD_PESC | ADMHCD_PSSC | ADMHCD_OCIC |
- ADMHCD_PRSC))
- *buf |= (1 << (port + 1));
- }
- return !!*buf;
- }
- static __u8 root_hub_hub_des[] = {
- 0x09, /* __u8 bLength; */
- 0x29, /* __u8 bDescriptorType; Hub-descriptor */
- 0x02, /* __u8 bNbrPorts; */
- 0x0a, 0x00, /* __u16 wHubCharacteristics; */
- 0x01, /* __u8 bPwrOn2pwrGood; 2ms */
- 0x00, /* __u8 bHubContrCurrent; 0mA */
- 0x00, /* __u8 DeviceRemovable; */
- 0xff, /* __u8 PortPwrCtrlMask; */
- };
- static int admhcd_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
- u16 wIndex, char *buf, u16 wLength)
- {
- struct admhcd *ahcd = hcd_to_admhcd(hcd);
- int retval = 0, len;
- unsigned int port = wIndex -1;
- switch (typeReq) {
- case GetHubStatus:
- *(__le32 *)buf = cpu_to_le32(0);
- break;
- case GetPortStatus:
- if (port >= ADMHCD_NUMPORTS)
- goto err;
- *(__le32 *)buf = cpu_to_le32(
- admhcd_reg_get(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4));
- break;
- case SetHubFeature: /* We don't implement these */
- case ClearHubFeature:
- switch (wValue) {
- case C_HUB_OVER_CURRENT:
- case C_HUB_LOCAL_POWER:
- break;
- default:
- goto err;
- }
- case SetPortFeature:
- if (port >= ADMHCD_NUMPORTS)
- goto err;
- switch (wValue) {
- case USB_PORT_FEAT_SUSPEND:
- admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4,
- ADMHCD_PSS);
- break;
- case USB_PORT_FEAT_RESET:
- if (admhcd_reg_get(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4)
- & ADMHCD_CCS) {
- admhcd_reg_set(ahcd,
- ADMHCD_REG_PORTSTATUS0 + port*4,
- ADMHCD_PRS | ADMHCD_CSC);
- mdelay(50);
- admhcd_reg_set(ahcd,
- ADMHCD_REG_PORTSTATUS0 + port*4,
- ADMHCD_PES | ADMHCD_CSC);
- }
- break;
- case USB_PORT_FEAT_POWER:
- admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4,
- ADMHCD_PPS);
- break;
- default:
- goto err;
- }
- break;
- case ClearPortFeature:
- if (port >= ADMHCD_NUMPORTS)
- goto err;
- switch (wValue) {
- case USB_PORT_FEAT_ENABLE:
- admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4,
- ADMHCD_CCS);
- break;
- case USB_PORT_FEAT_C_ENABLE:
- admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4,
- ADMHCD_PESC);
- break;
- case USB_PORT_FEAT_SUSPEND:
- admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4,
- ADMHCD_POCI);
- break;
- case USB_PORT_FEAT_C_SUSPEND:
- admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4,
- ADMHCD_PSSC);
- case USB_PORT_FEAT_POWER:
- admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4,
- ADMHCD_LSDA);
- break;
- case USB_PORT_FEAT_C_CONNECTION:
- admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4,
- ADMHCD_CSC);
- break;
- case USB_PORT_FEAT_C_OVER_CURRENT:
- admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4,
- ADMHCD_OCIC);
- break;
- case USB_PORT_FEAT_C_RESET:
- admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4,
- ADMHCD_PRSC);
- break;
- default:
- goto err;
- }
- break;
- case GetHubDescriptor:
- len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
- memcpy(buf, root_hub_hub_des, len);
- break;
- default:
- err:
- retval = -EPIPE;
- }
- return retval;
- }
- static struct hc_driver adm5120_hc_driver = {
- .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,
- .endpoint_disable = admhcd_endpoint_disable,
- .get_frame_number = admhcd_get_frame_number,
- .hub_status_data = admhcd_hub_status_data,
- .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 resource *addr, *data;
- void __iomem *addr_reg;
- void __iomem *data_reg;
- 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);
- 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_CONTROL, ADMHCD_HOST_EN);
- admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, 0x00000000);
- admhcd_reg_set(ahcd, ADMHCD_REG_FMINTERVAL, 0x20002edf);
- admhcd_reg_set(ahcd, ADMHCD_REG_LSTHRESH, 0x628);
- admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE,
- ADMHCD_INT_ACT | ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD);
- admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS,
- ADMHCD_INT_ACT | ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD);
- admhcd_reg_set(ahcd, ADMHCD_REG_RHDESCR, ADMHCD_NPS | ADMHCD_LPSC);
- admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, ADMHCD_STATE_OP);
- err = usb_add_hcd(hcd, irq, IRQF_DISABLED);
- if (err)
- goto out_dev;
- return 0;
- out_dev:
- 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);
- out:
- return err;
- }
- static int __init_or_module adm5120hcd_remove(struct platform_device *pdev)
- {
- 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 = {
- .probe = adm5120hcd_probe,
- .remove = adm5120hcd_remove,
- .driver = {
- .name = "adm5120-hcd",
- .owner = THIS_MODULE,
- },
- };
- static int __init adm5120hcd_init(void)
- {
- if (usb_disabled())
- return -ENODEV;
- if (!adm5120_board.has_usb)
- return -ENODEV;
- return platform_driver_register(&adm5120hcd_driver);
- }
- static void __exit adm5120hcd_exit(void)
- {
- platform_driver_unregister(&adm5120hcd_driver);
- }
- module_init(adm5120hcd_init);
- module_exit(adm5120hcd_exit);
|