www.pudn.com > Linux2410_device.rar > superh.c
/* * linux/drivers/usbd/superh_bi/udc.c -- USB Device Controller driver. * * Copyright (c) 2000, 2001, 2002 Lineo * * By: * Stuart Lynne, * Tom Rushworth , * Bruce Balden * * 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. * */ /*****************************************************************************/ #include #include #include "../usbd-export.h" #include "../usbd-build.h" #include "../usbd-module.h" MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); MODULE_DESCRIPTION ("USB Device SuperH Bus Interface"); USBD_MODULE_INFO ("superh_bi 0.1-alpha"); #include #include #include #include #include #include #include #include #include #include #include "../usbd.h" #include "../usbd-func.h" #include "../usbd-bus.h" #include "../usbd-inline.h" #include "usbd-bi.h" #include "superh.h" #include "superh-hardware.h" #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) /* * Define what interrupts are high versus low priority */ #define F0_HIGH (EP1_FULL | EP2_TR | EP2_EMPTY ) #define F0_LOW (BRST | SETUP_TS | EP0o_TS | EP0i_TR | EP0i_TS) #define F1_HIGH (0) #define F1_LOW (EP3_TR | EP3_TS | VBUSF) static struct usb_device_instance *udc_device; // required for the interrupt handler /* * ep_endpoints - map physical endpoints to logical endpoints */ static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS]; static struct urb *ep0_urb; static unsigned char usb_address; extern unsigned int udc_interrupts; unsigned int udc_f0_interrupts; unsigned int udc_f1_interrupts; unsigned long udc_vbusf_time; /* ********************************************************************************************* */ /* * IO */ void and_b (unsigned short mask, unsigned long addr) { ctrl_outb (ctrl_inw (addr) & mask, addr); } void or_b (unsigned short mask, unsigned long addr) { ctrl_outb (ctrl_inw (addr) | mask, addr); } void and_w (unsigned short mask, unsigned long addr) { ctrl_outw (ctrl_inw (addr) & mask, addr); } void or_w (unsigned short mask, unsigned long addr) { ctrl_outw (ctrl_inw (addr) | mask, addr); } // IO addresses for each physical ports FIFO unsigned long ep_address_o[4] = { USBEPDR0O, USBEPDR1, 0L, 0L, }; unsigned long ep_address_i[4] = { USBEPDR0I, 0L, USBEPDR2, USBEPDR3, }; // IO addresses for each physical ports trigger unsigned char ep_trigger_o[4] = { EP0o_PKTE, EP1_PKTE, 0L, 0L, }; unsigned char ep_trigger_i[4] = { EP0i_PKTE, 0L, EP2_PKTE, EP3_PKTE, }; /** * superh_write_buffer - write a buffer to the superh fifo * @ep: endpoint * @b: pointer to buffer to write * @size: number of bytes to write */ static /*__inline__*/ void superh_write_buffer (unsigned char ep, unsigned char *b, unsigned char size) { if ((ep < UDC_MAX_ENDPOINTS) && ep_address_i[ep]) { while (size-- > 0) { ctrl_outb (*b++, ep_address_i[ep]); } ctrl_outb (ep_trigger_i[ep], USBTRG); } } /** * superh_read_buffer - fill a buffer from the superh fifo * @ep: endpoint * @b: pointer to buffer to fill * @size: number of bytes to read */ static /*__inline__*/ void superh_read_buffer (unsigned char ep, unsigned char *b, unsigned char size) { if ((ep < UDC_MAX_ENDPOINTS) && ep_address_o[ep]) { while (size-- > 0) { *b++ = ctrl_inb (ep_address_o[ep]); } ctrl_outb (ep_trigger_o[ep], USBTRG); } } /* ********************************************************************************************* */ /* Bulk OUT (recv) */ static void /*__inline__*/ superh_out_ep1 (struct usb_endpoint_instance *endpoint) { int size = ctrl_inb (USBEPSZ1); if (endpoint && endpoint->rcv_urb && size) { // read data superh_read_buffer (1, endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, size); usbd_rcv_complete_irq (endpoint, size, 0); } else { // reset fifo or_b (EP1_CLEAR, USBFCLR); usbd_rcv_complete_irq (endpoint, 0, EINVAL); } } /* ********************************************************************************************* */ /* Bulk IN (tx) */ /** * superh_in_epn - process tx interrupt * @ep: * @endpoint: * * Determine status of last data sent, queue new data. */ static /* __inline__ */ void superh_in_epn (int ep, int restart) { if (ctrl_inb (USBIFR0) & EP2_EMPTY) { if ((ep < UDC_MAX_ENDPOINTS) && ep_endpoints[ep]) { struct usb_endpoint_instance *endpoint = ep_endpoints[ep]; usbd_tx_complete_irq (endpoint, restart); if (endpoint->tx_urb) { struct urb *urb = endpoint->tx_urb; if ((urb->actual_length - endpoint->sent) > 0) { endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->tx_packetSize); superh_write_buffer (ep, urb->buffer + endpoint->sent, endpoint->last); } else { // XXX ZLP endpoint->last = 0; superh_write_buffer (ep, urb->buffer + endpoint->sent, 0); } // enable transmit done interrupt switch (ep) { case 2: or_b (EP2_TR | EP2_EMPTY, USBIER0); break; case 3: or_b (EP3_TR | EP3_TS, USBIER1); break; } } else { // disable transmit done interrupt switch (ep) { case 2: if (ctrl_inb (USBIER0) & (EP2_TR | EP2_EMPTY)) { and_b (~(EP2_TR | EP2_EMPTY), USBIER0); } break; case 3: and_b (~(EP3_TR | EP3_TS), USBIER1); break; } } } } } /* ********************************************************************************************* */ /* Control (endpoint zero) */ /** * superh_in_ep0 - start transmit * @ep: */ static void /*__inline__*/ superh_in_ep0 (struct usb_endpoint_instance *endpoint) { if (endpoint && endpoint->tx_urb) { struct urb *urb = endpoint->tx_urb; printk (KERN_DEBUG "superh_in_ep0: length: %d\n", endpoint->tx_urb->actual_length); // XXX if ((urb->actual_length - endpoint->sent) > 0) { endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->tx_packetSize); superh_write_buffer (0, urb->buffer + endpoint->sent, endpoint->last); } else { // XXX ZLP endpoint->last = 0; superh_write_buffer (0, urb->buffer + endpoint->sent, 0); } } } /** * superh_ep0_setup */ static void superh_ep0_setup (void) { if (ep_endpoints[0]) { int i; struct usb_endpoint_instance *endpoint = ep_endpoints[0]; unsigned char *cp = (unsigned char *) &ep0_urb->device_request; for (i = 0; i < 8; i++) { cp[i] = ctrl_inb (USBEPDR0S); } // process setup packet if (usbd_recv_setup (ep0_urb)) { printk (KERN_DEBUG "superh_ep0: setup failed\n"); return; } // check data direction if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { // should we setup to receive data if (le16_to_cpu (ep0_urb->device_request.wLength)) { printk (KERN_DEBUG "superh_ep0: setup to read data %d\n", le16_to_cpu (ep0_urb->device_request.wLength)); endpoint->rcv_urb = ep0_urb; endpoint->rcv_urb->actual_length = 0; //superh_out(0, endpoint); return; } printk (KERN_DEBUG "superh_ep0: send ack %d\n", le16_to_cpu (ep0_urb->device_request.wLength)); // XXX // should be finished, send ack ctrl_outb (EP0s_PKTE, USBTRG); return; } // we should be sending data back // verify that we have non-zero request length if (!le16_to_cpu (ep0_urb->device_request.wLength)) { udc_stall_ep (0); return; } // verify that we have non-zero length response if (!ep0_urb->actual_length) { udc_stall_ep (0); return; } // send ack prior to sending data ctrl_outb (EP0s_PKTE, USBTRG); // start sending endpoint->tx_urb = ep0_urb; endpoint->sent = 0; endpoint->last = 0; superh_in_ep0 (endpoint); } } /* ********************************************************************************************* */ /* Interrupt Handler(s) */ /** * superh_int_hndlr_f0 - high priority interrupt handler * */ static void superh_int_hndlr_f0 (int irq, void *dev_id, struct pt_regs *regs) { unsigned char f0_status; udc_interrupts++; udc_f0_interrupts++; f0_status = ctrl_inb (USBIFR0); if (f0_status & EP1_FULL) { superh_out_ep1 (ep_endpoints[1]); f0_status = ctrl_inb (USBIFR0); if (f0_status & EP1_FULL) { superh_out_ep1 (ep_endpoints[1]); } } else if (f0_status & (EP2_TR | EP2_EMPTY)) { superh_in_epn (2, 0); // XXX status? ctrl_outb (~(f0_status & EP2_TR), USBIFR0); } } /** * superh_int_hndlr_f1 - low priority interrupt handler * */ static void superh_int_hndlr_f1 (int irq, void *dev_id, struct pt_regs *regs) { unsigned char f0_status; unsigned char f1_status; udc_interrupts++; udc_f1_interrupts++; f0_status = ctrl_inb (USBIFR0); f1_status = ctrl_inb (USBIFR1); ctrl_outb (~(f0_status & F0_LOW), USBIFR0); ctrl_outb (~(f1_status & F1_LOW), USBIFR1); //printk(KERN_DEBUG"superh_int_hndlr_f1[%x] status %02x %02x\n", udc_interrupts, f0_status, f1_status); // XXX if (f1_status & VBUSF) { /* * XXX vbusf can bounce, probably should disarm vbusf interrupt, for now, just check * that we don't handle it more than once. */ if (!udc_vbusf_time || ((jiffies - udc_vbusf_time) > 1)) { if (udc_device) { if (f1_status & VBUSMN) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: VBUSF VBUSMN set\n", udc_interrupts); } else { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: VBUSF VBUSMN reset\n", udc_interrupts); } } } udc_vbusf_time = jiffies; } else { udc_vbusf_time = 0; } if (f0_status & BRST) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: BRST bus reset\n", udc_interrupts); // reset fifo's and stall's or_b (EP3_CLEAR | EP1_CLEAR | EP2_CLEAR | EP0o_CLEAR | EP0i_CLEAR, USBFCLR); or_b (0, USBEPSTL); if (udc_device->device_state != DEVICE_RESET) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: DEVICE RESET\n", udc_interrupts); usbd_device_event_irq (udc_device, DEVICE_RESET, 0); } } if (f0_status & SETUP_TS) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: SETUP TS\n", udc_interrupts); or_b (EP0o_CLEAR | EP0i_CLEAR, USBFCLR); superh_ep0_setup (); if (udc_device->device_state == STATE_DEFAULT) { /* * superh won't give us set address, configuration or interface, but at least * we know that at this point we know that a host is talking to us, close * enough. */ usbd_device_event (udc_device, DEVICE_ADDRESS_ASSIGNED, 0); udc_device->configuration = 0; usbd_device_event (udc_device, DEVICE_CONFIGURED, 0); udc_device->interface = 1; // no options udc_device->alternate = 0; // no options usbd_device_event (udc_device, DEVICE_SET_INTERFACE, 0); } } if (f0_status & EP0i_TR) { usbd_tx_complete_irq (ep_endpoints[0], 0); superh_in_ep0 (ep_endpoints[0]); } if (f0_status & EP0o_TS) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: ep0o TS\n", udc_interrupts); } if (f0_status & EP0i_TS) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: ep0iTS\n", udc_interrupts); } if (f1_status & EP3_TR) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: EP3 TR\n", udc_interrupts); superh_in_epn (3, 0); // XXX status? } if (f1_status & EP3_TS) { printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: EP3 TS\n", udc_interrupts); } superh_in_epn (3, 0); // XXX status? } /* ********************************************************************************************* */ /* ********************************************************************************************* */ /* Start of public functions. */ /** * udc_start_in_irq - start transmit * @endpoint: endpoint instance * * Called by bus interface driver to see if we need to start a data transmission. */ void udc_start_in_irq (struct usb_endpoint_instance *endpoint) { if (endpoint) { // XXX should verify logical address mapping to physical 2 superh_in_epn (2, 0); } } /** * udc_init - initialize * * Return non-zero if we cannot see device. **/ int udc_init (void) { // XXX move clock enable to udc_enable, add code to udc_disable to disable them // Reset and then Select Function USB1_pwr_en out (USB) c.f. Section 26, Table 26.1 PTE2 and_w (PN_PB2_MSK, PECR); or_w (PN_PB2_OF, PECR); // Reset and then Select Function UCLK c.f. Section 26, Table 26.1, PTD6 and_w (PN_PB6_MSK, PDCR); or_w (PN_PB6_OF, PDCR); // Stop USB module prior to setting clocks c.f. Section 9.2.3 and_b (~MSTP14, STBCR3); or_b (MSTP14, STBCR3); // Select external clock, 1/1 divisor c.f. Section 11.3.1 or_b (USBDIV_11 | USBCKS_EC, EXCPGCR); // Start USB c.f. Section 9.2.3 and_b (~MSTP14, STBCR3); // Disable pullup c.f. Section 23.5.19 printk (KERN_DEBUG "udc_init:\n"); printk (KERN_DEBUG "udc_init: Disable Pullup\n"); or_b (PULLUP_E, USBDMA); //and_b(~PULLUP_E, USBDMA); // Set port 1 to function, disabled c.f. Section 22.2.1 or_w (USB_TRANS_TRAN | USB_SEL_FUNC, EXPFC); // Enable pullup c.f. Section 23.5.19 printk (KERN_DEBUG "udc_init:\n"); printk (KERN_DEBUG "udc_init: Enable Pullup\n"); and_b (~PULLUP_E, USBDMA); //or_b(PULLUP_E, USBDMA); // reset fifo's and stall's or_b (EP3_CLEAR | EP1_CLEAR | EP2_CLEAR | EP0o_CLEAR | EP0i_CLEAR, USBFCLR); or_b (0, USBEPSTL); // setup interrupt priority by using the interrupt select registers ctrl_outb (F0_LOW, USBISR0); ctrl_outb (F1_LOW, USBISR1); printk (KERN_DEBUG "udc_init:\n"); return 0; } /** * udc_stall_ep - stall endpoint * @ep: physical endpoint * * Stall the endpoint. */ void udc_stall_ep (unsigned int ep) { if (ep < UDC_MAX_ENDPOINTS) { // stall } } /** * udc_reset_ep - reset endpoint * @ep: physical endpoint * reset the endpoint. * * returns : 0 if ok, -1 otherwise */ void udc_reset_ep (unsigned int ep) { if (ep < UDC_MAX_ENDPOINTS) { // reset } } /** * udc_endpoint_halted - is endpoint halted * @ep: * * Return non-zero if endpoint is halted */ int udc_endpoint_halted (unsigned int ep) { return 0; } /** * udc_set_address - set the USB address for this device * @address: * * Called from control endpoint function after it decodes a set address setup packet. */ void udc_set_address (unsigned char address) { // address cannot be setup until ack received usb_address = address; } /** * udc_serial_init - set a serial number if available */ int __init udc_serial_init (struct usb_bus_instance *bus) { return -EINVAL; } /* ********************************************************************************************* */ /** * udc_max_endpoints - max physical endpoints * * Return number of physical endpoints. */ int udc_max_endpoints (void) { return UDC_MAX_ENDPOINTS; } /** * udc_check_ep - check logical endpoint * @lep: * * Return physical endpoint number to use for this logical endpoint or zero if not valid. */ int udc_check_ep (int logical_endpoint, int packetsize) { return (((logical_endpoint & 0xf) >= UDC_MAX_ENDPOINTS) || (packetsize > 64)) ? 0 : (logical_endpoint & 0xf); } /** * udc_set_ep - setup endpoint * @ep: * @endpoint: * * Associate a physical endpoint with endpoint_instance */ void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, struct usb_endpoint_instance *endpoint) { printk (KERN_DEBUG "udc_setup_ep: ep: %d\n", ep); if (ep < UDC_MAX_ENDPOINTS) { ep_endpoints[ep] = endpoint; // ep0 if (ep == 0) { } // IN else if (endpoint->endpoint_address & 0x80) { } // OUT else if (endpoint->endpoint_address) { usbd_fill_rcv (device, endpoint, 5); endpoint->rcv_urb = first_urb_detached (&endpoint->rdy); } } } /** * udc_disable_ep - disable endpoint * @ep: * * Disable specified endpoint */ void udc_disable_ep (unsigned int ep) { if (ep < UDC_MAX_ENDPOINTS) { struct usb_endpoint_instance *endpoint; if ((endpoint = ep_endpoints[ep])) { ep_endpoints[ep] = NULL; usbd_flush_ep (endpoint); } } } /* ********************************************************************************************* */ /** * udc_connected - is the USB cable connected * * Return non-zero if cable is connected. */ int udc_connected () { return (ctrl_inb (USBIFR1) & VBUSMN) ? 1 : 0; } /** * udc_connect - enable pullup resistor * * Turn on the USB connection by enabling the pullup resistor. */ void udc_connect (void) { // enable pullup printk (KERN_DEBUG "udc_connect:\n"); and_b (~PULLUP_E, USBDMA); } /** * udc_disconnect - disable pullup resistor * * Turn off the USB connection by disabling the pullup resistor. */ void udc_disconnect (void) { // enable pullup printk (KERN_DEBUG "udc_disconnect:\n"); or_b (PULLUP_E, USBDMA); } /* ********************************************************************************************* */ /** * udc_enable_interrupts - enable interrupts * * Switch on UDC interrupts. * */ void udc_all_interrupts (struct usb_device_instance *device) { // set interrupt mask or_b (BRST | EP1_FULL | SETUP_TS | EP0o_TS | EP0i_TR | EP0i_TS, USBIER0); or_b (EP3_TR | EP3_TS | VBUSF, USBIER1); } /** * udc_suspended_interrupts - enable suspended interrupts * * Switch on only UDC resume interrupt. * */ void udc_suspended_interrupts (struct usb_device_instance *device) { } /** * udc_disable_interrupts - disable interrupts. * * switch off interrupts */ void udc_disable_interrupts (struct usb_device_instance *device) { // reset interrupt mask ctrl_outb (0x0, USBIER0); ctrl_outb (0x0, USBIER1); } /* ********************************************************************************************* */ /** * udc_ep0_packetsize - return ep0 packetsize */ int udc_ep0_packetsize (void) { return EP0_PACKETSIZE; } /** * udc_enable - enable the UDC * * Switch on the UDC */ void udc_enable (struct usb_device_instance *device) { // save the device structure pointer udc_device = device; // ep0 urb if (!ep0_urb) { if ((ep0_urb = usbd_alloc_urb (device, device->function_instance_array, 0, 512))) { printk (KERN_ERR "ep0_enable: usbd_alloc_urb failed\n"); } } else { printk (KERN_ERR "udc_enable: ep0_urb already allocated\n"); } // XXX enable UDC } /** * udc_disable - disable the UDC * * Switch off the UDC */ void udc_disable (void) { // XXX disable UDC // reset device pointer udc_device = NULL; // ep0 urb if (ep0_urb) { usbd_dealloc_urb (ep0_urb); ep0_urb = NULL; } } /** * udc_startup_events - allow udc code to do any additional startup */ void udc_startup_events (struct usb_device_instance *device) { printk (KERN_DEBUG "udc_startup_events:\n"); usbd_device_event (device, DEVICE_INIT, 0); usbd_device_event (device, DEVICE_CREATE, 0); usbd_device_event (device, DEVICE_HUB_CONFIGURED, 0); // XXX the following could be snuck into get descriptor LANGID #if 0 usbd_device_event (device, DEVICE_ADDRESS_ASSIGNED, 0); device->configuration = 0; usbd_device_event (device, DEVICE_CONFIGURED, 0); device->interface = 1; device->alternate = 0; usbd_device_event (device, DEVICE_SET_INTERFACE, 0); #endif } /* ********************************************************************************************* */ /** * udc_name - return name of USB Device Controller */ char *udc_name (void) { return UDC_NAME; } /** * udc_request_udc_irq - request UDC interrupt */ int udc_request_udc_irq () { if (request_irq (USBF0_IRQ, superh_int_hndlr_f0, SA_INTERRUPT | SA_SAMPLE_RANDOM, UDC_NAME " USBD Bus Interface (high priority)", NULL) != 0) { printk (KERN_DEBUG "usb_ctl: Couldn't request USB irq F0\n"); return -EINVAL; } return 0; } /** * udc_request_cable_irq - request Cable interrupt */ int udc_request_cable_irq () { if (request_irq (USBF1_IRQ, superh_int_hndlr_f1, SA_INTERRUPT | SA_SAMPLE_RANDOM, UDC_NAME " USBD Bus Interface (low priority)", NULL) != 0) { printk (KERN_DEBUG "usb_ctl: Couldn't request USB irq F1\n"); free_irq (USBF0_IRQ, NULL); return -EINVAL; } return 0; } /** * udc_request_udc_io - request UDC io region */ int udc_request_io () { return 0; } /** * udc_release_udc_irq - release UDC irq */ void udc_release_udc_irq () { free_irq (USBF0_IRQ, NULL); } /** * udc_release_cable_irq - release Cable irq */ void udc_release_cable_irq () { free_irq (USBF1_IRQ, NULL); } /** * udc_release_release_io - release UDC io region */ void udc_release_io () { } /** * udc_regs - dump registers */ void udc_regs (void) { printk (KERN_DEBUG "[%d:%d:%d] IFR[%02x:%02x] IER[%02x:%02x] ISR[%02x:%02x] DASTS[%02x] EPSTL[%02x] EPSZ1[%02x] DMA[%02x]\n", udc_interrupts, udc_f0_interrupts, udc_f1_interrupts, ctrl_inb (USBIFR0), ctrl_inb (USBIFR1), ctrl_inb (USBIER0), ctrl_inb (USBIER1), ctrl_inb (USBISR0), ctrl_inb (USBISR1), ctrl_inb (USBDASTS), ctrl_inb (USBEPSTL), ctrl_inb (USBEPSZ1), ctrl_inb (USBDMA) ); }