⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usb_ohci.c

📁 适合KS8695X
💻 C
📖 第 1 页 / 共 4 页
字号:
		stat = hc_interrupt();

		if (stat < 0) {
			stat = USB_ST_CRC_ERR;
			break;
		}

		/* NOTE: since we are not interrupt driven in U-Boot and always
		 * handle only one URB at a time, we cannot assume the
		 * transaction finished on the first successful return from
		 * hc_interrupt().. unless the flag for current URB is set,
		 * meaning that all TD's to/from device got actually
		 * transferred and processed. If the current URB is not
		 * finished we need to re-iterate this loop so as
		 * hc_interrupt() gets called again as there needs to be some
		 * more TD's to process still */
		if ((stat >= 0) && (stat != 0xff) && (urb_finished)) {
			/* 0xff is returned for an SF-interrupt */
			break;
		}

		if (--timeout) {
			wait_ms(1);
			if (!urb_finished)
				dbg("\%");

		} else {
			err("CTL:TIMEOUT ");
			dbg("submit_common_msg: TO status %x\n", stat);
			stat = USB_ST_CRC_ERR;
			urb_finished = 1;
			break;
		}
	}

#if 0
	/* we got an Root Hub Status Change interrupt */
	if (got_rhsc) {
#ifdef DEBUG
		ohci_dump_roothub (&gohci, 1);
#endif
		got_rhsc = 0;
		/* abuse timeout */
		timeout = rh_check_port_status(&gohci);
		if (timeout >= 0) {
#if 0 /* this does nothing useful, but leave it here in case that changes */
			/* the called routine adds 1 to the passed value */
			usb_hub_port_connect_change(gohci.rh.dev, timeout - 1);
#endif
			/*
			 * XXX
			 * This is potentially dangerous because it assumes
			 * that only one device is ever plugged in!
			 */
			devgone = dev;
		}
	}
#endif

	dev->status = stat;
	dev->act_len = transfer_len;

#ifdef DEBUG
	pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe));
#else
	wait_ms(1);
#endif

	/* free TDs in urb_priv */
	urb_free_priv (&urb_priv);
	return 0;
}

/* submit routines called from usb.c */
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
		int transfer_len)
{
	info("submit_bulk_msg");
	return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0);
}

int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
		int transfer_len, struct devrequest *setup)
{
	int maxsize = usb_maxpacket(dev, pipe);

	info("submit_control_msg");
#ifdef DEBUG
	urb_priv.actual_length = 0;
	pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe));
#else
	wait_ms(1);
#endif
	if (!maxsize) {
		err("submit_control_message: pipesize for pipe %lx is zero",
			pipe);
		return -1;
	}
	if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) {
		gohci.rh.dev = dev;
		/* root hub - redirect */
		return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len,
			setup);
	}

	return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0);
}

int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
		int transfer_len, int interval)
{
	info("submit_int_msg");
	return -1;
}

/*-------------------------------------------------------------------------*
 * HC functions
 *-------------------------------------------------------------------------*/

/* reset the HC and BUS */

static int hc_reset (ohci_t *ohci)
{
	int timeout = 30;
	int smm_timeout = 50; /* 0,5 sec */

	if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */
		writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */
		info("USB HC TakeOver from SMM");
		while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
			wait_ms (10);
			if (--smm_timeout == 0) {
				err("USB HC TakeOver failed!");
				return -1;
			}
		}
	}

	/* Disable HC interrupts */
	writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);

	dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;",
		ohci->slot_name,
		readl (&ohci->regs->control));

	/* Reset USB (needed by some controllers) */
	writel (0, &ohci->regs->control);

	/* HC Reset requires max 10 us delay */
	writel (OHCI_HCR,  &ohci->regs->cmdstatus);
	while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
		if (--timeout == 0) {
			err("USB HC reset timed out!");
			return -1;
		}
		udelay (1);
	}
	return 0;
}

/*-------------------------------------------------------------------------*/

/* Start an OHCI controller, set the BUS operational
 * enable interrupts
 * connect the virtual root hub */

static int hc_start (ohci_t * ohci)
{
	__u32 mask;
	unsigned int fminterval;

	ohci->disabled = 1;

	/* Tell the controller where the control and bulk lists are
	 * The lists are empty now. */

	writel (0, &ohci->regs->ed_controlhead);
	writel (0, &ohci->regs->ed_bulkhead);

	writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */

	fminterval = 0x2edf;
	writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
	fminterval |= ((((fminterval - 210) * 6) / 7) << 16);
	writel (fminterval, &ohci->regs->fminterval);
	writel (0x628, &ohci->regs->lsthresh);

	/* start controller operations */
	ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
	ohci->disabled = 0;
	writel (ohci->hc_control, &ohci->regs->control);

	/* disable all interrupts */
	mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD |
			OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC |
			OHCI_INTR_OC | OHCI_INTR_MIE);
	writel (mask, &ohci->regs->intrdisable);
	/* clear all interrupts */
	mask &= ~OHCI_INTR_MIE;
	writel (mask, &ohci->regs->intrstatus);
	/* Choose the interrupts we care about now  - but w/o MIE */
	mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
	writel (mask, &ohci->regs->intrenable);

#ifdef	OHCI_USE_NPS
	/* required for AMD-756 and some Mac platforms */
	writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM,
		&ohci->regs->roothub.a);
	writel (RH_HS_LPSC, &ohci->regs->roothub.status);
#endif	/* OHCI_USE_NPS */

#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);})
	/* POTPGT delay is bits 24-31, in 2 ms units. */
	mdelay ((roothub_a (ohci) >> 23) & 0x1fe);

	/* connect the virtual root hub */
	ohci->rh.devnum = 0;

	return 0;
}

/*-------------------------------------------------------------------------*/

/* an interrupt happens */

static int
hc_interrupt (void)
{
	ohci_t *ohci = &gohci;
	struct ohci_regs *regs = ohci->regs;
	int ints;
	int stat = -1;

	if ((ohci->hcca->done_head != 0) &&
	     !(m32_swap (ohci->hcca->done_head) & 0x01)) {

		ints =  OHCI_INTR_WDH;

	} else if ((ints = readl (&regs->intrstatus)) == ~(u32)0) {
		ohci->disabled++;
		err ("%s device removed!", ohci->slot_name);
		return -1;

	} else if ((ints &= readl (&regs->intrenable)) == 0) {
		dbg("hc_interrupt: returning..\n");
		return 0xff;
	}

	/* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */

	if (ints & OHCI_INTR_RHSC) {
		got_rhsc = 1;
		stat = 0xff;
	}

	if (ints & OHCI_INTR_UE) {
		ohci->disabled++;
		err ("OHCI Unrecoverable Error, controller usb-%s disabled",
			ohci->slot_name);
		/* e.g. due to PCI Master/Target Abort */

#ifdef	DEBUG
		ohci_dump (ohci, 1);
#else
	wait_ms(1);
#endif
		/* FIXME: be optimistic, hope that bug won't repeat often. */
		/* Make some non-interrupt context restart the controller. */
		/* Count and limit the retries though; either hardware or */
		/* software errors can go forever... */
		hc_reset (ohci);
		return -1;
	}

	if (ints & OHCI_INTR_WDH) {
		wait_ms(1);

		writel (OHCI_INTR_WDH, &regs->intrdisable);
		stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci));
		writel (OHCI_INTR_WDH, &regs->intrenable);
	}

	if (ints & OHCI_INTR_SO) {
		dbg("USB Schedule overrun\n");
		writel (OHCI_INTR_SO, &regs->intrenable);
		stat = -1;
	}

	/* FIXME:  this assumes SOF (1/ms) interrupts don't get lost... */
	if (ints & OHCI_INTR_SF) {
		unsigned int frame = m16_swap (ohci->hcca->frame_no) & 1;
		wait_ms(1);
		writel (OHCI_INTR_SF, &regs->intrdisable);
		if (ohci->ed_rm_list[frame] != NULL)
			writel (OHCI_INTR_SF, &regs->intrenable);
		stat = 0xff;
	}

	writel (ints, &regs->intrstatus);
	return stat;
}

/*-------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------*/

/* De-allocate all resources.. */

static void hc_release_ohci (ohci_t *ohci)
{
	dbg ("USB HC release ohci usb-%s", ohci->slot_name);

	if (!ohci->disabled)
		hc_reset (ohci);
}

/*-------------------------------------------------------------------------*/

/*
 * low level initalisation routine, called from usb.c
 */
static char ohci_inited = 0;

int usb_lowlevel_init(void)
{
	S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
	S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();

	/*
	 * Set the 48 MHz UPLL clocking. Values are taken from
	 * "PLL value selection guide", 6-23, s3c2400_UM.pdf.
	 */
	clk_power->UPLLCON = ((40 << 12) + (1 << 4) + 2);
	gpio->MISCCR |= 0x8; /* 1 = use pads related USB for USB host */

	/*
	 * Enable USB host clock.
	 */
	clk_power->CLKCON |= (1 << 4);

	memset (&gohci, 0, sizeof (ohci_t));
	memset (&urb_priv, 0, sizeof (urb_priv_t));

	/* align the storage */
	if ((__u32)&ghcca[0] & 0xff) {
		err("HCCA not aligned!!");
		return -1;
	}
	phcca = &ghcca[0];
	info("aligned ghcca %p", phcca);
	memset(&ohci_dev, 0, sizeof(struct ohci_device));
	if ((__u32)&ohci_dev.ed[0] & 0x7) {
		err("EDs not aligned!!");
		return -1;
	}
	memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1));
	if ((__u32)gtd & 0x7) {
		err("TDs not aligned!!");
		return -1;
	}
	ptd = gtd;
	gohci.hcca = phcca;
	memset (phcca, 0, sizeof (struct ohci_hcca));

	gohci.disabled = 1;
	gohci.sleeping = 0;
	gohci.irq = -1;
	gohci.regs = (struct ohci_regs *)S3C24X0_USB_HOST_BASE;

	gohci.flags = 0;
	gohci.slot_name = "s3c2400";

	if (hc_reset (&gohci) < 0) {
		hc_release_ohci (&gohci);
		/* Initialization failed */
		clk_power->CLKCON &= ~(1 << 4);
		return -1;
	}

	/* FIXME this is a second HC reset; why?? */
	writel (gohci.hc_control = OHCI_USB_RESET, &gohci.regs->control);
	wait_ms (10);

	if (hc_start (&gohci) < 0) {
		err ("can't start usb-%s", gohci.slot_name);
		hc_release_ohci (&gohci);
		/* Initialization failed */
		clk_power->CLKCON &= ~(1 << 4);
		return -1;
	}

#ifdef	DEBUG
	ohci_dump (&gohci, 1);
#else
	wait_ms(1);
#endif
	ohci_inited = 1;
	urb_finished = 1;

	return 0;
}

int usb_lowlevel_stop(void)
{
	S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();

	/* this gets called really early - before the controller has */
	/* even been initialized! */
	if (!ohci_inited)
		return 0;
	/* TODO release any interrupts, etc. */
	/* call hc_release_ohci() here ? */
	hc_reset (&gohci);
	/* may not want to do this */
	clk_power->CLKCON &= ~(1 << 4);
	return 0;
}

#endif /* CONFIG_USB_OHCI */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -