📄 omap.c
字号:
/* * linux/drivers/usb/device/bi/omap.c * TI OMAP1510/1610 USB bus interface driver * * Author: MontaVista Software, Inc. * source@mvista.com * * 2003 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/delay.h>#include <linux/timer.h>#include <asm/atomic.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/types.h>#include <asm/uaccess.h>#include <asm/io.h>#include "../usbd.h"#include "../usbd-export.h"#include "../usbd-build.h"#include "../usbd-module.h"#include "../usbd-func.h"#include "../usbd-bus.h"#include "../usbd-inline.h"#include "usbd-bi.h"#include "omap.h"#ifdef CONFIG_OMAP_H2#include <linux/i2c.h>#define ISP1301_I2C_ADDR 0x2D#define ISP1301_I2C_MODE_CONTROL_1 0x4#define ISP1301_I2C_MODE_CONTROL_2 0x12#define ISP1301_I2C_OTG_CONTROL_1 0x6#define ISP1301_I2C_OTG_CONTROL_2 0x10#define ISP1301_I2C_INTERRUPT_SOURCE 0x8#define ISP1301_I2C_INTERRUPT_LATCH 0xA#define ISP1301_I2C_INTERRUPT_FALLING 0xC#define ISP1301_I2C_INTERRUPT_RISING 0xE#define ISP1301_I2C_REG_CLEAR_ADDR 1#define OMAP1610_SET_FUNC_MUX_CTRL(mode,reg,bit) outl((inl(reg)&~(0x7<<bit))|(mode<<bit),reg)#define OMAP1610_CONFIRM_MUX_SETUP() outl(0xeaef,COMP_MODE_CTRL_0)#define open_drain(bit,reg) outl(inl(reg)|(1<<bit),reg)#endifMODULE_AUTHOR("source@mvista.com");MODULE_DESCRIPTION("OMAP1510/1610 Innovator USB Device Bus Interface");MODULE_LICENSE("GPL");USBD_MODULE_INFO("omap_bi 0.0.1");/* Some kind of debugging output... */#if 1#define UDCDBG(args,...)#define UDCREG(name)#define UDCREGL(name)#else /* The bugs still exists... */#define UDCDBG(args,...) printk("%d: " args "\n", __LINE__, ##__VA_ARGS__)#define UDCREG(name) UDCDBG("%s [16]: %.4x", (#name), inw(name)) /* For 16-bit regs */#define UDCREGL(name) UDCDBG("%s [32]: %.8x", (#name), inl(name)) /* For 32-bit regs */#endifstatic struct urb *ep0_urb;static unsigned char usb_address;static struct usb_device_instance *udc_device; /* Used in interrupt handler */extern unsigned int udc_interrupts; /* Irqs generated by UDC */static u16 udc_devstat = 0; /* UDC status (DEVSTAT) */#ifdef CONFIG_USBD_SELFPOWERED#define UDC_SYSCON1_INIT (UDC_Self_Pwr)#else#define UDC_SYSCON1_INIT 0#endif#ifdef CONFIG_OMAP_H2static int i2c_write(u8 buf, u8 subaddr);static struct file *i2c_file;static struct i2c_client *omap_i2c_client;static int initstate_i2c;static int initstate_region;static int initstate_thread;static DECLARE_MUTEX_LOCKED(usbd_connect_lock);static DECLARE_MUTEX(usbd_i2c_lock);static DECLARE_COMPLETION(connect_thread_exit);static int connect_thread_terminating = 0;static char usbd_do_connect = 0;static char usbd_connected = 0;static intusbd_connect_thread(void *data){ daemonize(); reparent_to_init(); strcpy(current->comm, "dpm_usbd_connect"); for (;;) { down(&usbd_connect_lock); if (connect_thread_terminating == 1) break; down(&usbd_i2c_lock); if ((usbd_do_connect) && (!usbd_connected)) { /*enable pull-up resistor on D+ */ i2c_write(0x9, ISP1301_I2C_OTG_CONTROL_1); i2c_write(~0x9, ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR); usbd_connected = 1; } usbd_do_connect = 0; up(&usbd_i2c_lock); } complete_and_exit(&connect_thread_exit, 0); return 0;}static inti2c_configure(void){ char filename[20]; int tmp; if (initstate_i2c) return 0; /*find the I2C driver we need */ for (tmp = 0; tmp < I2C_ADAP_MAX; tmp++) {#ifdef CONFIG_DEVFS_FS sprintf(filename, "/dev/i2c/%d", tmp);#else sprintf(filename, "/dev/i2c-%d", tmp);#endif if (!IS_ERR(i2c_file = filp_open(filename, O_RDWR, 0))) { /*found some driver */ omap_i2c_client = (struct i2c_client *) i2c_file->private_data; if (strlen(omap_i2c_client->adapter->name) >= 12) { if (!memcmp (omap_i2c_client->adapter->name, "OMAP1610 I2C", 12)) break; /*we found our driver! */ } filp_close(i2c_file, NULL); } } if (tmp == I2C_ADAP_MAX) { /*no matching I2C driver found */ printk(KERN_ERR UDC_NAME ": cannot find OMAP1610 I2C driver\n"); return -ENODEV; } initstate_i2c = 1; return 0;}static voidi2c_close(void){ if (initstate_i2c) filp_close(i2c_file, NULL); initstate_i2c = 0;}#if 0static inti2c_read(u8 subaddr){ u8 buf = 0; if (!i2c_configure()) { omap_i2c_client->addr = ISP1301_I2C_ADDR; i2c_master_send(omap_i2c_client, &subaddr, 1); i2c_master_recv(omap_i2c_client, &buf, 1); } return buf;}#endifstatic inti2c_write(u8 buf, u8 subaddr){ char tmpbuf[2]; if (!i2c_configure()) { omap_i2c_client->addr = ISP1301_I2C_ADDR; tmpbuf[0] = subaddr; /*register number */ tmpbuf[1] = buf; /*register data */ i2c_master_send(omap_i2c_client, &tmpbuf[0], 2); } return 0;}static voidisp1301_configure(void){ i2c_write(6, ISP1301_I2C_MODE_CONTROL_1); i2c_write(~6, ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR); i2c_write(4, ISP1301_I2C_MODE_CONTROL_2); i2c_write(~4, ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR); i2c_write(0xFF, ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR); i2c_write(0xFF, ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR); i2c_write(0xFF, ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR);}#endif#if 0static void__dump_ep(struct usb_endpoint_instance *endpoint){ int n; printk("ep [addr:%x st:%d sent:%d last:%d tx:%p]\n", endpoint->endpoint_address, endpoint->state, endpoint->sent, endpoint->last, endpoint->tx_urb); printk("TX urb contents:\n"); for (n = 0; n < endpoint->tx_urb->actual_length; n++) { printk(" %.2x", endpoint->tx_urb->buffer[n]); if (n && (n % 16 == 0)) printk("\n"); } printk("\n");}#endif/* ************************************************************************** *//* IO *//* * omap_prepare_endpoint_for_rx * * This function implements TRM Figure 14-11. * * The endpoint to prepare for transfer is specified as a physical endpoint * number. For OUT (rx) endpoints 1 through 15, the corresponding endpoint * configuration register is checked to see if the endpoint is ISO or not. * If the OUT endpoint is valid and is non-ISO then its FIFO is enabled. * No action is taken for endpoint 0 or for IN (tx) endpoints 16 through 30. */static voidomap_prepare_endpoint_for_rx(int ep){ int ep_addr = PHYS_EP_TO_EP_ADDR(ep); int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; unsigned long flags; if (((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) && (ep_num >= 1) && (ep_num <= 15)) { if ((inw(UDC_EP_RX(ep_num)) & (UDC_EPn_RX_Valid | UDC_EPn_RX_Iso)) == UDC_EPn_RX_Valid) { /* rx endpoint is valid, non-ISO, so enable its FIFO */ local_irq_save(flags); outw(ep_num, UDC_EP_NUM); outw(UDC_Set_FIFO_En, UDC_CTRL); local_irq_restore(flags); } }}/* omap_configure_endpoints * * This function implements TRM Figure 14-10. */static voidomap_configure_endpoints(struct usb_device_instance *device){ int ep; struct usb_bus_instance *bus; struct usb_endpoint_instance *endpoint; struct usb_bus_driver *driver; unsigned short ep_ptr; unsigned short ep_size; unsigned short ep_isoc; unsigned short ep_doublebuffer; int ep_addr; int packet_size; int buffer_size; int attributes; bus = device->bus; driver = bus->driver; /* There is a dedicated 2048 byte buffer for USB packets that may be * arbitrarily partitioned among the endpoints on 8-byte boundaries. * The first 8 bytes are reserved for receiving setup packets on * endpoint 0. */ ep_ptr = 8; /* reserve the first 8 bytes for the setup fifo */ for (ep = 0; ep < driver->max_endpoints; ep++) { endpoint = bus->endpoint_array + ep; ep_addr = PHYS_EP_TO_EP_ADDR(ep); if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { /* IN endpoint */ packet_size = endpoint->tx_packetSize; attributes = endpoint->tx_attributes; } else { /* OUT endpoint */ packet_size = endpoint->rcv_packetSize; attributes = endpoint->rcv_attributes; } switch (packet_size) { case 0: ep_size = 0; break; case 8: ep_size = 0; break; case 16: ep_size = 1; break; case 32: ep_size = 2; break; case 64: ep_size = 3; break; case 128: ep_size = 4; break; case 256: ep_size = 5; break; case 512: ep_size = 6; break; default: UDCDBG("ep 0x%02x has bad packet size %d", ep_addr, packet_size); packet_size = 0; ep_size = 0; break; } switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_CONTROL: case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: default: /* A non-isochronous endpoint may optionally be * double-buffered. For now we disable * double-buffering. */ ep_doublebuffer = 0; ep_isoc = 0; if (packet_size > 64) packet_size = 0; if (!ep || !ep_doublebuffer) buffer_size = packet_size; else buffer_size = packet_size * 2; break; case USB_ENDPOINT_XFER_ISOC: /* Isochronous endpoints are always double- * buffered, but the double-buffering bit * in the endpoint configuration register * becomes the msb of the endpoint size so we * set the double-buffering flag to zero. */ ep_doublebuffer = 0; ep_isoc = 1; buffer_size = packet_size * 2; break; } /* check to see if our packet buffer RAM is exhausted */ if ((ep_ptr + buffer_size) > 2048) { UDCDBG("out of packet RAM for ep 0x%02x buf size %d", ep_addr, buffer_size); buffer_size = packet_size = 0; } /* force a default configuration for endpoint 0 since it is * always enabled */ if (!ep && ((packet_size < 8) || (packet_size > 64))) { buffer_size = packet_size = 64; ep_size = 3; } if (!ep) { /* configure endpoint 0 */ outw((ep_size << 12) | (ep_ptr >> 3), UDC_EP0); UDCDBG("ep 0 buffer offset 0x%03x packet size 0x%03x", ep_ptr, packet_size); } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { /* IN endpoint */ if (packet_size) { outw((1 << 15) | (ep_doublebuffer << 14) | (ep_size << 12) | (ep_isoc << 11) | (ep_ptr >> 3), UDC_EP_TX(ep_addr & USB_ENDPOINT_NUMBER_MASK)); UDCDBG("IN ep %d buffer offset 0x%03x" " packet size 0x%03x", ep_addr & USB_ENDPOINT_NUMBER_MASK, ep_ptr, packet_size); } else { outw(0, UDC_EP_TX(ep_addr & USB_ENDPOINT_NUMBER_MASK)); } } else { /* OUT endpoint */ if (packet_size) { outw((1 << 15) | (ep_doublebuffer << 14) | (ep_size << 12) | (ep_isoc << 11) | (ep_ptr >> 3), UDC_EP_RX(ep_addr & USB_ENDPOINT_NUMBER_MASK)); UDCDBG("OUT ep %d buffer offset 0x%03x" " packet size 0x%03x", ep_addr & USB_ENDPOINT_NUMBER_MASK, ep_ptr, packet_size); } else { outw(0, UDC_EP_RX(ep_addr & USB_ENDPOINT_NUMBER_MASK)); } } ep_ptr += buffer_size; }}/* omap_deconfigure_device * * This function balances omap_configure_device. */static voidomap_deconfigure_device(void){ int epnum; UDCDBG("clear Cfg_Lock"); outw(inw(UDC_SYSCON1) & ~UDC_Cfg_Lock, UDC_SYSCON1); UDCREG(UDC_SYSCON1); /* deconfigure all endpoints */ for (epnum = 1; epnum <= 15; epnum++) { outw(0, UDC_EP_RX(epnum)); outw(0, UDC_EP_TX(epnum)); }}/* omap_configure_device * * This function implements TRM Figure 14-9. */static voidomap_configure_device(struct usb_device_instance *device){ omap_configure_endpoints(device); /* Figure 14-9 indicates we should enable interrupts here, but we have * other routines (udc_all_interrupts, udc_suspended_interrupts) to * do that. */ UDCDBG("set Cfg_Lock"); outw(inw(UDC_SYSCON1) | UDC_Cfg_Lock, UDC_SYSCON1); UDCREG(UDC_SYSCON1);}/* omap_write_noniso_tx_fifo * * This function implements TRM Figure 14-30. * * If the endpoint has an active tx_urb, then the next packet of data from the * URB is written to the tx FIFO. The total amount of data in the urb is given * by urb->actual_length. The maximum amount of data that can be sent in any * one packet is given by endpoint->tx_packetSize. The number of data bytes * from this URB that have already been transmitted is given by endpoint->sent. * endpoint->last is updated by this routine with the number of data bytes * transmitted in this packet. * * In accordance with Figure 14-30, the EP_NUM register must already have been * written with the value to select the appropriate tx FIFO before this routine * is called. */static voidomap_write_noniso_tx_fifo(struct usb_endpoint_instance *endpoint){ struct urb *urb = endpoint->tx_urb; if (urb) { int last; if ((last = MIN(urb->actual_length - endpoint->sent, endpoint->tx_packetSize))) { unsigned char *cp = urb->buffer + endpoint->sent; outsw(UDC_DATA, cp, last >> 1); if (last & 1) outb(*(cp + last - 1), UDC_DATA); } endpoint->last = last; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -