📄 sl811_usb.c
字号:
/* * (C) Copyright 2004 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * This code is based on linux driver for sl811hs chip, source at * drivers/usb/host/sl811.c: * * SL811 Host Controller Interface driver for USB. * * Copyright (c) 2003/06, Courage Co., Ltd. * * Based on: * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, * Adam Richter, Gregory P. Smith; * 2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com> * 3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn> * * See file CREDITS for list of people who contributed to this * project. * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */#include <common.h>#ifdef CONFIG_USB_SL811HS#include <mpc8xx.h>#include <usb.h>#include "sl811.h"#include "../board/kup/common/kup.h"#ifdef __PPC__# define EIEIO __asm__ volatile ("eieio")#else# define EIEIO /* nothing */#endif#define SL811_ADR (0x50000000)#define SL811_DAT (0x50000001)#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);})#ifdef SL811_DEBUGstatic int debug = 9;#endifstatic int root_hub_devnum = 0;static struct usb_port_status rh_status = { 0 };/* root hub port status */static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe, void *data, int buf_len, struct devrequest *cmd);static void sl811_write (__u8 index, __u8 data){ *(volatile unsigned char *) (SL811_ADR) = index; EIEIO; *(volatile unsigned char *) (SL811_DAT) = data; EIEIO;}static __u8 sl811_read (__u8 index){ __u8 data; *(volatile unsigned char *) (SL811_ADR) = index; EIEIO; data = *(volatile unsigned char *) (SL811_DAT); EIEIO; return (data);}/* * Read consecutive bytes of data from the SL811H/SL11H buffer */static void inline sl811_read_buf(__u8 offset, __u8 *buf, __u8 size){ *(volatile unsigned char *) (SL811_ADR) = offset; EIEIO; while (size--) { *buf++ = *(volatile unsigned char *) (SL811_DAT); EIEIO; }}/* * Write consecutive bytes of data to the SL811H/SL11H buffer */static void inline sl811_write_buf(__u8 offset, __u8 *buf, __u8 size){ *(volatile unsigned char *) (SL811_ADR) = offset; EIEIO; while (size--) { *(volatile unsigned char *) (SL811_DAT) = *buf++; EIEIO; }}int usb_init_kup4x (void){ volatile immap_t *immap = (immap_t *) CFG_IMMR; volatile memctl8xx_t *memctl = &immap->im_memctl; int i; unsigned char tmp; memctl = &immap->im_memctl; memctl->memc_or7 = 0xFFFF8726; memctl->memc_br7 = 0x50000401; /* start at 0x50000000 */ /* BP 14 low = USB ON */ immap->im_cpm.cp_pbdat &= ~(BP_USB_VCC); /* PB 14 nomal port */ immap->im_cpm.cp_pbpar &= ~(BP_USB_VCC); /* output */ immap->im_cpm.cp_pbdir |= (BP_USB_VCC); puts ("USB: "); for (i = 0x10; i < 0xff; i++) { sl811_write(i, i); tmp = (sl811_read(i)); if (tmp != i) { printf ("SL811 compare error index=0x%02x read=0x%02x\n", i, tmp); return (-1); } } printf ("SL811 ready\n"); return (0);}/* * This function resets SL811HS controller and detects the speed of * the connecting device * * Return: 0 = no device attached; 1 = USB device attached */static int sl811_hc_reset(void){ int status ; sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); mdelay(20); /* Disable hardware SOF generation, clear all irq status. */ sl811_write(SL811_CTRL1, 0); mdelay(2); sl811_write(SL811_INTRSTS, 0xff); status = sl811_read(SL811_INTRSTS); if (status & SL811_INTR_NOTPRESENT) { /* Device is not present */ PDEBUG(0, "Device not present\n"); rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE); rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; sl811_write(SL811_INTR, SL811_INTR_INSRMV); return 0; } /* Send SOF to address 0, endpoint 0. */ sl811_write(SL811_LEN_B, 0); sl811_write(SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0)); sl811_write(SL811_DEV_B, 0x00); sl811_write(SL811_SOFLOW, SL811_12M_LOW); if (status & SL811_INTR_SPEED_FULL) { /* full speed device connect directly to root hub */ PDEBUG (0, "Full speed Device attached\n"); sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); mdelay(20); sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); sl811_write(SL811_CTRL1, SL811_CTRL1_SOF); /* start the SOF or EOP */ sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION; rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; mdelay(2); sl811_write(SL811_INTRSTS, 0xff); } else { /* slow speed device connect directly to root-hub */ PDEBUG(0, "Low speed Device attached\n"); sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); mdelay(20); sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI); sl811_write(SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF); /* start the SOF or EOP */ sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED; mdelay(2); sl811_write(SL811_INTRSTS, 0xff); } rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; sl811_write(SL811_INTR, /*SL811_INTR_INSRMV*/SL811_INTR_DONE_A); return 1;}int usb_lowlevel_init(void){ root_hub_devnum = 0; sl811_hc_reset(); return 0;}int usb_lowlevel_stop(void){ sl811_hc_reset(); return 0;}static int calc_needed_buswidth(int bytes, int need_preamble){ return !need_preamble ? bytes * 8 + 256 : 8 * 8 * bytes + 2048;}static int sl811_send_packet(struct usb_device *dev, unsigned long pipe, __u8 *buffer, int len){ __u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; __u16 status = 0; int err = 0, time_start = get_timer(0); int need_preamble = !(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && usb_pipeslow(pipe); if (len > 239) return -1; if (usb_pipeout(pipe)) ctrl |= SL811_USB_CTRL_DIR_OUT; if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) ctrl |= SL811_USB_CTRL_TOGGLE_1; if (need_preamble) ctrl |= SL811_USB_CTRL_PREAMBLE; sl811_write(SL811_INTRSTS, 0xff); while (err < 3) { sl811_write(SL811_ADDR_A, 0x10); sl811_write(SL811_LEN_A, len); if (usb_pipeout(pipe) && len) sl811_write_buf(0x10, buffer, len); if (!(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && sl811_read(SL811_SOFCNTDIV)*64 < calc_needed_buswidth(len, need_preamble)) ctrl |= SL811_USB_CTRL_SOF; else ctrl &= ~SL811_USB_CTRL_SOF; sl811_write(SL811_CTRL_A, ctrl); while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) { if (5*CFG_HZ < get_timer(time_start)) { printf("USB transmit timed out\n"); return -USB_ST_CRC_ERR; } } sl811_write(SL811_INTRSTS, 0xff); status = sl811_read(SL811_STS_A); if (status & SL811_USB_STS_ACK) { int remainder = sl811_read(SL811_CNT_A); if (remainder) { PDEBUG(0, "usb transfer remainder = %d\n", remainder); len -= remainder; } if (usb_pipein(pipe) && len) sl811_read_buf(0x10, buffer, len); return len; } if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK) continue; PDEBUG(0, "usb transfer error %#x\n", (int)status); err++; } err = 0; if (status & SL811_USB_STS_ERROR) err |= USB_ST_BUF_ERR; if (status & SL811_USB_STS_TIMEOUT) err |= USB_ST_CRC_ERR; if (status & SL811_USB_STS_STALL) err |= USB_ST_STALLED; return -err;}int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len){ int dir_out = usb_pipeout(pipe); int ep = usb_pipeendpoint(pipe); int max = usb_maxpacket(dev, pipe); int done = 0; PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n", usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out); dev->status = 0; sl811_write(SL811_DEV_A, usb_pipedevice(pipe)); sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep)); while (done < len) { int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done, max > len - done ? len - done : max); if (res < 0) { dev->status = -res; return res; } if (!dir_out && res < max) /* short packet */ break; done += res; usb_dotoggle(dev, ep, dir_out); } dev->act_len = done; return 0;}int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len,struct devrequest *setup){ int done = 0; int devnum = usb_pipedevice(pipe); int ep = usb_pipeendpoint(pipe); dev->status = 0; if (devnum == root_hub_devnum) return sl811_rh_submit_urb(dev, pipe, buffer, len, setup); PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x bus = %i\n", devnum, ep, buffer, len, (int)setup->requesttype, (int)setup->request, sl811_read(SL811_SOFCNTDIV)*64); sl811_write(SL811_DEV_A, devnum); sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, ep)); /* setup phase */ usb_settoggle(dev, ep, 1, 0); if (sl811_send_packet(dev, usb_sndctrlpipe(dev, ep), (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) { int dir_in = usb_pipein(pipe); int max = usb_maxpacket(dev, pipe); /* data phase */ sl811_write(SL811_PIDEP_A, PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, ep));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -