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

📄 ohci-sl811.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * SL811HS OHCI HCD (Host Controller Driver) for USB. * * linux/drivers/usb/host/ohci-sl811.c * * Copyright (C) 2004 by Lothar Wassmann <LW at KARO-electronics.de> * * This file is subject to the terms and conditions of the GNU General Public * License.  See the file "COPYING" in the main directory of this archive * for more details. * * * SL811HS Bus Glue for PXA2xx * */#include <linux/device.h>#include <linux/timer.h>#include <asm/hardware.h>#include <asm/mach-types.h>#include <asm/arch/dma.h>#include <linux/kallsyms.h>#define CONFIG_ARCH_KARO	//add by hzh#ifndef CONFIG_ARCH_KARO#error "This file is HC_SL811 bus glue.  CONFIG_ARCH_KARO must be defined."#endif#include "ohci-sl811-emu.c"extern int usb_disabled(void);static int usb_hcd_sl811_remove(struct usb_hcd *hcd, struct hc_sl811_dev *dev);static inline void hc_sl811_set_drvdata(struct hc_sl811_dev *dev, struct usb_hcd *data){	dev_set_drvdata(&dev->dev, data);}static inline struct usb_hcd *hc_sl811_get_drvdata(struct hc_sl811_dev *dev){	struct usb_hcd *hcd;	hcd = dev_get_drvdata(&dev->dev);	return hcd;}static int __devinit sl811_irq_probe(struct hc_sl811_dev *dev){	int irq = NO_IRQ;	unsigned long mask;	hc_sl811_write_reg(dev, SL11H_INTENBLREG, 0);	mask = probe_irq_on();	hc_sl811_write_reg(dev, SL11H_CTLREG2, SL11H_CTL2MASK_HOSTMODE);	hc_sl811_write_reg(dev, SL11H_CTLREG1, SL11H_CTL1VAL_RESET);	hc_sl811_write_reg(dev, SL11H_INTENBLREG, SL11H_INTMASK_RESUME);	hc_sl811_read_reg(dev, SL11H_INTSTATREG);	hc_sl811_write_reg(dev, SL11H_CTLREG1, 0);	hc_sl811_write_reg(dev, SL11H_INTENBLREG, 0);	irq = probe_irq_off(mask);	return irq;}static int hc_sl811_chip_probe(struct hc_sl811_dev *dev){	int ret = 0;	int i;	u8 *tst_buf;	unsigned long flags;	sl811_set_hw_reset();	tst_buf = kmalloc(SL811_MAP_SIZE, GFP_KERNEL);	if (tst_buf == NULL) {		return -ENOMEM;	}	sl811_clr_hw_reset();	for (i = 0; i < SL811_BUF_SIZE; i++) {		tst_buf[i] = ~i;	}	local_irq_save(flags);	__hc_sl811_write_regs(dev, SL11H_DATA_START, tst_buf, SL811_BUF_SIZE);	for (i = 0; i < SL811_BUF_SIZE; i++) {		u8 val = __hc_sl811_read_reg(dev, SL11H_DATA_START + i);		u8 ref = ~i;		if (val != ref) {			printk(KERN_ERR "%s: SL811 register test failed @ %02x: wr: %02x, rd %02x\n",			       __FUNCTION__, i, ref, val);			ret = -ENODEV;			goto out;		}	}	memset(tst_buf, 0, SL811_MAP_SIZE);	//__hc_sl811_write_regs(dev, SL11H_DATA_START, tst_buf, SL811_BUF_SIZE);	__hc_sl811_write_regs(dev, 0, tst_buf, SL811_MAP_SIZE);	dev->hw_rev = __hc_sl811_read_reg(dev, SL11H_HWREVREG) >> 4;	if (dev->hw_rev == 0) {		printk("%s: Bad HW rev.\n", __FUNCTION__);		ret = -ENODEV;	} out:	local_irq_restore(flags);	kfree(tst_buf);	if (dev->irq == NO_IRQ) {		dev->irq = sl811_irq_probe(dev);		if (dev->irq == NO_IRQ) {			ret = -ENODEV;		}	}	sl811_set_hw_reset();	return ret;}static void sl811_start_hc(struct usb_hcd *hcd){	/*	 * Now, carefully enable the USB clock, and take	 * the USB host controller out of reset.	 */	struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd);	sl811_clr_hw_reset();		__hc_sl811_write_reg(dev, SL11H_INTENBLREG, 0);		__hc_sl811_write_reg(dev, SL11H_INTSTATREG, 0xff);		__hc_sl811_write_reg(dev, SL11H_CTLREG1, 0);		__hc_sl811_write_reg(dev, SL11H_CTLREG2, SL11H_CTL2MASK_HOSTMODE);//add by hzh		//__hc_sl811_write_reg(dev, SL11H_INTENBLREG, SL11H_INTMASK_INSRMV);	hc_sl811_detect_device(hcd);}static void sl811_stop_hc(struct hc_sl811_dev *dev){	dev->dev_state = DEV_IDLE;	del_timer(&dev->int_timer);	/*	 * Put the USB host controller into reset.	 */	hc_sl811_write_reg(dev, SL11H_INTENBLREG, 0);	sl811_set_hw_reset();	/*	 * Stop the USB clock.	 */	// No way to do it on KARO boards}/* * initialize the emulated OHCI registers */static void sl811_reset_hc(struct usb_hcd *hcd, int cold){	struct ohci_regs *regs = hcd->regs;	struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd);	const u8 nr_ports = 1;	const u8 pwr_on_delay = 100;	ohci_dbg(hcd_to_ohci(hcd), "%s: Performing %s reset of HC SL811\n", __FUNCTION__,		 cold ? "cold" : "warm");	if (cold) {		memset(regs, 0, sizeof(struct ohci_regs));		regs->revision = 0x10; // OHCI Rev.		regs->roothub.a = RH_A_PSM | RH_A_NOCP | nr_ports | (pwr_on_delay << 24);		regs->roothub.status = 0;		regs->roothub.portstatus[0] = RH_PS_PPS | RH_PS_PES;		regs->fminterval = DEFAULT_FMINTERVAL;		regs->periodicstart = ((9 * FI) / 10) & 0x3fff;		regs->lsthresh = LSTHRESH;	} else {		regs->ed_controlcurrent = 0;		regs->ed_controlhead = 0;		regs->ed_bulkcurrent = 0;		regs->ed_bulkhead = 0;	}	HcControl = OHCI_USB_RESET;	dev->intrdelay = 7;}static void hc_sl811_start_sof(struct usb_hcd *hcd, int connected){	struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd);	struct ohci_hcd *ohci = hcd_to_ohci(hcd);	struct ohci_regs *regs = ohci->regs;	u16 sof_ctr = (HcFmInterval & OHCI_FR) + 1;	u8 ctrl2 = SL11H_CTL2MASK_HOSTMODE | (sof_ctr >> 8);	u8 ctrl1 = SL11H_CTL1MASK_SOFENA;	u8 int_mask = SL11H_INTMASK_INSRMV | SL11H_INTMASK_SOFINTR | SL11H_INTMASK_XFERDONE | SL11H_INTMASK_XFERDONE_B;	unsigned long flags;	BUG_ON(!ohci);	local_irq_save(flags);	switch (connected) {	case 0:		ohci_dbg(ohci, "%s: Disabling SOF\n", __FUNCTION__);		__hc_sl811_write_reg(dev, SL11H_CTLREG1, 0);		dev->dev_state &= ~(DEV_ACTIVE | DEV_SOF);#if 0		__hc_sl811_write_reg(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SUSPEND);		dev->dev_state |= DEV_SLEEP;#endif		break;	case 1:		ctrl1 |= SL11H_CTL1MASK_NSPD;		ctrl2 |= SL11H_CTL2MASK_DSWAP;		// fall through	case 2:		ohci_dbg(ohci, "%s: Enabling SOF %d: %04x\n", __FUNCTION__, connected, sof_ctr);#if FLIP_BUFFERS > 1		int_mask |= SL11H_INTMASK_SOFINTR | SL11H_INTMASK_XFERDONE | SL11H_INTMASK_XFERDONE_B;#else		int_mask |= SL11H_INTMASK_SOFINTR | SL11H_INTMASK_XFERDONE;#endif		__hc_sl811_write_reg(dev, SL11H_BUFLNTHREG, 0);	//zero lenth		__hc_sl811_write_reg(dev, SL11H_PIDEPREG, 0x50);	//send SOF to EP0		__hc_sl811_write_reg(dev, SL11H_DEVADDRREG, 0x01);	//address0		__hc_sl811_write_reg(dev, SL11H_SOFLOWREG, (u8)sof_ctr);		__hc_sl811_write_reg(dev, SL11H_CTLREG2, ctrl2);		__hc_sl811_write_reg(dev, SL11H_CTLREG1, ctrl1); // Enable SOF		__hc_sl811_write_reg(dev, SL11H_HOSTCTLREG, SL11H_HCTLMASK_ARM);	}	__hc_sl811_write_reg(dev, SL11H_INTENBLREG, int_mask);	local_irq_restore(flags);	ohci_sl811_update_portstatus(ohci, 0, 0, RH_PS_PRS);}static int hc_sl811_detect_device(struct usb_hcd *hcd){	int ret = 0;	struct ohci_hcd *ohci = hcd_to_ohci(hcd);	struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd);	u8 int_stat;	u8 int_mask = SL11H_INTMASK_INSRMV;	unsigned long flags;	BUG_ON(!hcd);	BUG_ON(!ohci);	WARN_ON(irqs_disabled());	del_timer(&dev->int_timer);	if (dev->dev_state & DEV_SLEEP) {		ohci_info(ohci, "%s: WAKING up SL811 ctl1=%02x\n", __FUNCTION__,			  hc_sl811_read_reg(dev, SL11H_CTLREG1));		local_irq_save(flags);		HC_SL811_WRITE_ADDR(dev, SL11H_CTLREG1);		HC_SL811_WRITE_DATA(dev, SL11H_CTLREG1);		__hc_sl811_write_reg(dev, SL11H_CTLREG1, 0); // get chip out of suspend mode		local_irq_restore(flags);		ohci_info(ohci, "%s: WOKE up SL811 ctl1=%02x\n", __FUNCTION__,			  hc_sl811_read_reg(dev, SL11H_CTLREG1));	}	ohci_dbg(ohci, "%s: Enabling device detection; dev_state=%02x\n",		 __FUNCTION__, dev->dev_state);	local_irq_save(flags);	dev->dev_state &= ~(DEV_ACTIVE | DEV_CONNECTED | DEV_SOF);	__hc_sl811_write_reg(dev, SL11H_INTENBLREG, SL11H_INTMASK_RESUME);	local_irq_restore(flags);	mdelay(10);	local_irq_save(flags);	__hc_sl811_write_reg(dev, SL11H_INTSTATREG, SL11H_INTMASK_RESUME | SL11H_INTMASK_INSRMV);	int_stat = __hc_sl811_read_reg(dev, SL11H_INTSTATREG);	__hc_sl811_write_reg(dev, SL11H_INTENBLREG, 0);	ohci_dbg(ohci, "%s: Disabling device detection; dev_state=%02x, int_stat=%02x\n",		 __FUNCTION__, dev->dev_state, int_stat);	local_irq_restore(flags);	if (int_stat & SL11H_INTMASK_RESUME) {		ohci_dbg(ohci,  "%s: Device removed: %02x\n", __FUNCTION__, int_stat);		ohci_sl811_update_portstatus(ohci, 0, 0, RH_PS_CCS | RH_PS_PES);	} else {		if (int_stat & SL11H_INTMASK_DSTATE) {			ohci_dbg(ohci,  "%s: Full speed device attached: %02x\n", __FUNCTION__, int_stat);			ret = DEV_FULL_SPEED;			ohci_sl811_update_portstatus(ohci, 0, RH_PS_CCS | RH_PS_PES, RH_PS_LSDA);		} else {			ohci_dbg(ohci,  "%s: Low speed device attached: %02x\n", __FUNCTION__, int_stat);			ret = DEV_LOW_SPEED;			ohci_sl811_update_portstatus(ohci, 0, RH_PS_CCS | RH_PS_PES | RH_PS_LSDA, 0);		}	}	local_irq_save(flags);	__hc_sl811_write_reg(dev, SL11H_INTENBLREG, int_mask);	tasklet_disable(&dev->usb_reset_bh);	dev->dev_state = (dev->dev_state & ~DEV_CONNECTED) | DEV_ACTIVE | ret;	tasklet_enable(&dev->usb_reset_bh);	local_irq_restore(flags);		return ret;}static void hc_sl811_usb_reset(struct usb_hcd *hcd, int assert){	struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd);	WARN_ON(!irqs_disabled());	del_timer(&dev->int_timer);	__hc_sl811_write_reg(dev, SL11H_INTENBLREG, 0);	if (assert) {		__hc_sl811_write_reg(dev, SL11H_CTLREG1, SL11H_CTL1VAL_RESET);		//set_current_state(TASK_UNINTERRUPTIBLE);		//schedule_timeout(10);		//__hc_sl811_write_reg(dev, SL11H_CTLREG1, 0);		__hc_sl811_write_reg(dev, SL11H_INTSTATREG, 0xff);	}	if (assert < 0) {		if (in_interrupt()) {			mdelay(USB_RESET_WIDTH);		} else {			set_current_state(TASK_UNINTERRUPTIBLE);			schedule_timeout(msecs_to_jiffies(USB_RESET_WIDTH));		}	}	if (assert <= 0) {		__hc_sl811_write_reg(dev, SL11H_INTSTATREG, 0xff);		__hc_sl811_write_reg(dev, SL11H_CTLREG1, 0);		__hc_sl811_write_reg(dev, SL11H_CTLREG2, SL11H_CTL2MASK_HOSTMODE);		__hc_sl811_write_reg(dev, SL11H_INTENBLREG, SL11H_INTMASK_INSRMV);	}}static void ohci_sl811_bh(unsigned long data){	struct usb_hcd *hcd = (struct usb_hcd *)data;	struct ohci_hcd *ohci = hcd_to_ohci(hcd);	struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd);	int handled;	unsigned long flags;	local_irq_save(flags);	handled = dev->dev_state & DEV_TRIGGERED;	dev->dev_state &= ~handled;	local_irq_restore(flags);	if (!handled) {		ohci_warn(ohci, "%s: Spurious tasklet schedule: %02x:%02x!\n", __FUNCTION__,			  dev->dev_state, hc_sl811_read_reg(dev, SL11H_INTSTATREG));	}	if (handled & DEV_CONN_CHK) {		int connected;		local_irq_save(flags);		dev->dev_state |= DEV_CHECK;		local_irq_restore(flags);		ohci_dbg(ohci, "%s: Checking device connect status: %02x\n", __FUNCTION__,			 dev->dev_state);		dev->dev_state &= ~(DEV_ACTIVE | DEV_CONNECTED | DEV_SOF);		connected = hc_sl811_detect_device(hcd);		hc_sl811_start_sof(hcd, connected);		local_irq_save(flags);		dev->dev_state &= ~DEV_CHECK;		local_irq_restore(flags);		ohci_dbg(ohci,  "%s: Device detection completed: dev_state=%02x\n", __FUNCTION__,			 dev->dev_state);	}	if (handled & DEV_OHCI_IRQ) {		struct pt_regs pt_regs;		struct ohci_regs *regs = ohci->regs;		if (HcIntrStatus & HcIntrEnable) {			ohci_vdbg(ohci, "%s: Calling OHCI Interrupt: %08x:%08x #%04x\n",				  __FUNCTION__, HcIntrStatus, HcIntrEnable, HcFmNumber);			if (usb_hcd_irq(hcd->irq, hcd, &pt_regs) != IRQ_HANDLED) {				ohci_dbg(ohci, "%s: Spurious OHCI Interrupt: %08x:%08x #%04x\n",					 __FUNCTION__, HcIntrStatus, HcIntrEnable, HcFmNumber);			}		}	}}static void int_timer(unsigned long data){	unsigned int oscr = OSCR;	struct usb_hcd *hcd = (struct usb_hcd*)data;	struct ohci_regs *regs = hcd->regs;	struct ohci_hcd *ohci = hcd_to_ohci(hcd);	struct ohci_hcca *hcca = ohci->hcca;	struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd);	unsigned long flags;	local_irq_save(flags);	// windup the timer again in case we got here too early//	if ((int)((oscr - dev->last_sof) - USEC_TO_OSCR(1100)) < 0) {	if ((int)((oscr - dev->last_sof) - 4055) < 0) {	//1100*3.6864 = 4055		mod_timer(&dev->int_timer, jiffies + msecs_to_jiffies(1));		goto out;	}	// Nothing to do here, if a bottom half run is still pending	if (dev->dev_state & DEV_TRIGGERED) {		goto out;	}	WARN_ON(dev->current_td);	if (dev->current_td) {		ohci_warn(ohci, "%s: Killing current_td %08x\n", __FUNCTION__,			  (u32)dev->current_td);		kill_current_td(hcd, dev);	}	if ((HcIntrEnable & OHCI_INTR_SF) && !(HcIntrStatus & OHCI_INTR_SF)) {		ohci_dbg(ohci, "%s: Faking OHCI_INTR_SF: %08x->%08x #%04x\n", __FUNCTION__,			 HcIntrStatus, HcIntrStatus | OHCI_INTR_SF, (u16)(HcFmNumber + 1));		HcIntrStatus |= OHCI_INTR_SF;		HcFmNumber = (u16)(HcFmNumber + 1);		if (hcca) {			HCCAFrameNumber = HcFmNumber;			if (HcDoneHead && !(HcIntrStatus & OHCI_INTR_WDH)) {				int ohci_int = HcIntrStatus & HcIntrEnable;				HCCADoneHead = HcDoneHead | (ohci_int ? cpu_to_le32(1) : 0);				HcDoneHead = 0;				HcIntrStatus |= OHCI_INTR_WDH;				dev->intrdelay = 7;			}		}		sl811_trigger_bh(dev, DEV_OHCI_IRQ);	} else if (HcControl & (OHCI_CTRL_BLE | OHCI_CTRL_CLE | OHCI_CTRL_PLE)) {		mod_timer(&dev->int_timer, jiffies + msecs_to_jiffies(1));	} out:	local_irq_restore(flags);}/** * usb_hcd_sl811_probe - initialize HC-SL811-based HCDs * Context: !in_interrupt() * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. * * Store this function in the HCD's struct pci_driver as probe(). */static int usb_hcd_sl811_probe(struct hc_driver *driver, struct usb_hcd **hcd_out,			       struct hc_sl811_dev *dev){	int retval;	struct usb_hcd *hcd = NULL;	struct device *parent = dev->dev.parent;	const char *chip_names[] = {		"SL11H",		"SL811HS Rev 1.2",		"SL811HS Rev 1.5",		"Unknown"};	hcd = driver->hcd_alloc();	if (hcd == NULL) {		dev_err(hcd->self.controller, "hcd_alloc failed\n");		retval = -ENOMEM;		goto out;	}	hc_sl811_set_drvdata(dev, hcd);	hcd->regs = kmalloc(sizeof(struct ohci_regs), GFP_KERNEL);	if (hcd->regs == NULL) {		retval = -ENOMEM;		goto err1;	}	hcd->driver = driver;	hcd->description = driver->description;	hcd->self.bus_name = "sl811";	if (hcd->product_desc == NULL) {		hcd->product_desc = "HC-SL811 OHCI";	}	hcd->self.controller = parent;	init_timer(&dev->int_timer);	dev->int_timer.function = int_timer;	dev->int_timer.data = (unsigned long)hcd;	tasklet_init(&dev->usb_reset_bh, ohci_sl811_bh, (unsigned long)hcd);	retval = hcd_buffer_create(hcd);	if (retval != 0) {		dev_err(hcd->self.controller, "pool alloc failed\n");		goto err2;	}	if (driver->reset && (retval = driver->reset(hcd)) < 0) {		dev_err(hcd->self.controller, "can't reset\n");		goto err3;	}	hcd->state = USB_STATE_HALT;	retval = request_irq(dev->irq, ohci_sl811_interrupt, SA_INTERRUPT, hcd->description, hcd);	if (retval != 0) {		dev_err(hcd->self.controller, "requesting irq %d failed\n", dev->irq);		retval = -EBUSY;		goto err3;	}	hcd->irq = dev->irq;	set_irq_type(hcd->irq, IRQT_RISING);	usb_bus_init(&hcd->self);	hcd->self.op = &usb_hcd_operations;	hcd->self.hcpriv = hcd;	INIT_LIST_HEAD(&hcd->dev_list);	usb_register_bus(&hcd->self);	if ((retval = driver->start(hcd)) < 0) {		dev_err(hcd->self.controller, "%s: Start failed: %d; Removing driver\n",			__FUNCTION__, retval);		usb_hcd_sl811_remove(hcd, dev);		return retval;	}	info("%s (HC-%s) at 0x%p, irq %d\n", hcd->description,	     chip_names[dev->hw_rev & 0x0f], hcd->regs, hcd->irq);	*hcd_out = hcd;	return 0; err3:	hcd_buffer_destroy(hcd); err2:	kfree(hcd->regs);

⌨️ 快捷键说明

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