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

📄 isp116x-hcd.c

📁 usb driver for 2.6.17
💻 C
📖 第 1 页 / 共 3 页
字号:
}static void dump_int(struct seq_file *s, char *label, u32 mask){	seq_printf(s, "%s %08x%s%s%s%s%s%s%s\n", label, mask,		   mask & HCINT_MIE ? " MIE" : "",		   mask & HCINT_RHSC ? " rhsc" : "",		   mask & HCINT_FNO ? " fno" : "",		   mask & HCINT_UE ? " ue" : "",		   mask & HCINT_RD ? " rd" : "",		   mask & HCINT_SF ? " sof" : "", mask & HCINT_SO ? " so" : "");}static int isp116x_show_dbg(struct seq_file *s, void *unused){	struct isp116x *isp116x = s->private;	seq_printf(s, "%s\n%s version %s\n",		   isp116x_to_hcd(isp116x)->product_desc, hcd_name,		   DRIVER_VERSION);	if (HC_IS_SUSPENDED(isp116x_to_hcd(isp116x)->state)) {		seq_printf(s, "HCD is suspended\n");		return 0;	}	if (!HC_IS_RUNNING(isp116x_to_hcd(isp116x)->state)) {		seq_printf(s, "HCD not running\n");		return 0;	}	spin_lock_irq(&isp116x->lock);	dump_irq(s, "hc_irq_enable", isp116x_read_reg16(isp116x, HCuPINTENB));	dump_irq(s, "hc_irq_status", isp116x_read_reg16(isp116x, HCuPINT));	dump_int(s, "hc_int_enable", isp116x_read_reg32(isp116x, HCINTENB));	dump_int(s, "hc_int_status", isp116x_read_reg32(isp116x, HCINTSTAT));	isp116x_show_regs_seq(isp116x, s);	spin_unlock_irq(&isp116x->lock);	seq_printf(s, "\n");	return 0;}static int isp116x_open_seq(struct inode *inode, struct file *file){	return single_open(file, isp116x_show_dbg, inode->u.generic_ip);}static struct file_operations isp116x_debug_fops = {	.open = isp116x_open_seq,	.read = seq_read,	.llseek = seq_lseek,	.release = single_release,};static int create_debug_file(struct isp116x *isp116x){	isp116x->dentry = debugfs_create_file(hcd_name,					      S_IRUGO, NULL, isp116x,					      &isp116x_debug_fops);	if (!isp116x->dentry)		return -ENOMEM;	return 0;}static void remove_debug_file(struct isp116x *isp116x){	debugfs_remove(isp116x->dentry);}#else#define	create_debug_file(d)	0#define	remove_debug_file(d)	do{}while(0)#endif				/* CONFIG_DEBUG_FS *//*-----------------------------------------------------------------*//*  Software reset - can be called from any contect.*/static int isp116x_sw_reset(struct isp116x *isp116x){	int retries = 15;	unsigned long flags;	int ret = 0;	spin_lock_irqsave(&isp116x->lock, flags);	isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC);	isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR);	while (--retries) {		/* It usually resets within 1 ms */		mdelay(1);		if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR))			break;	}	if (!retries) {		ERR("Software reset timeout\n");		ret = -ETIME;	}	spin_unlock_irqrestore(&isp116x->lock, flags);	return ret;}static int isp116x_reset(struct usb_hcd *hcd){	struct isp116x *isp116x = hcd_to_isp116x(hcd);	unsigned long t;	u16 clkrdy = 0;	int ret, timeout = 15 /* ms */ ;	ret = isp116x_sw_reset(isp116x);	if (ret)		return ret;	t = jiffies + msecs_to_jiffies(timeout);	while (time_before_eq(jiffies, t)) {		msleep(4);		spin_lock_irq(&isp116x->lock);		clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY;		spin_unlock_irq(&isp116x->lock);		if (clkrdy)			break;	}	if (!clkrdy) {		ERR("Clock not ready after %dms\n", timeout);		/* After sw_reset the clock won't report to be ready, if		   H_WAKEUP pin is high. */		ERR("Please make sure that the H_WAKEUP pin is pulled low!\n");		ret = -ENODEV;	}	return ret;}static void isp116x_stop(struct usb_hcd *hcd){	struct isp116x *isp116x = hcd_to_isp116x(hcd);	unsigned long flags;	u32 val;	spin_lock_irqsave(&isp116x->lock, flags);	isp116x_write_reg16(isp116x, HCuPINTENB, 0);	/* Switch off ports' power, some devices don't come up	   after next 'insmod' without this */	val = isp116x_read_reg32(isp116x, HCRHDESCA);	val &= ~(RH_A_NPS | RH_A_PSM);	isp116x_write_reg32(isp116x, HCRHDESCA, val);	isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS);	spin_unlock_irqrestore(&isp116x->lock, flags);	isp116x_sw_reset(isp116x);}/*  Configure the chip. The chip must be successfully reset by now.*/static int isp116x_start(struct usb_hcd *hcd){	struct isp116x *isp116x = hcd_to_isp116x(hcd);	struct isp116x_platform_data *board = isp116x->board;	u32 val;	unsigned long flags;	spin_lock_irqsave(&isp116x->lock, flags);	/* clear interrupt status and disable all interrupt sources */	isp116x_write_reg16(isp116x, HCuPINT, 0xff);	isp116x_write_reg16(isp116x, HCuPINTENB, 0);	val = isp116x_read_reg16(isp116x, HCCHIPID);	if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) {		ERR("Invalid chip ID %04x\n", val);		spin_unlock_irqrestore(&isp116x->lock, flags);		return -ENODEV;	}	/* To be removed in future */	hcd->uses_new_polling = 1;	isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE);	isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE);	/* ----- HW conf */	val = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1);	if (board->sel15Kres)		val |= HCHWCFG_15KRSEL;	/* Remote wakeup won't work without working clock */	if (board->remote_wakeup_enable)		val |= HCHWCFG_CLKNOTSTOP;	if (board->oc_enable)		val |= HCHWCFG_ANALOG_OC;	if (board->int_act_high)		val |= HCHWCFG_INT_POL;	if (board->int_edge_triggered)		val |= HCHWCFG_INT_TRIGGER;	isp116x_write_reg16(isp116x, HCHWCFG, val);	/* ----- Root hub conf */	val = (25 << 24) & RH_A_POTPGT;	/* AN10003_1.pdf recommends RH_A_NPS (no power switching) to	   be always set. Yet, instead, we request individual port	   power switching. */	val |= RH_A_PSM;	/* Report overcurrent per port */	val |= RH_A_OCPM;	isp116x_write_reg32(isp116x, HCRHDESCA, val);	isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA);	val = RH_B_PPCM;	isp116x_write_reg32(isp116x, HCRHDESCB, val);	isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB);	val = 0;	if (board->remote_wakeup_enable) {		if (!device_can_wakeup(hcd->self.controller))			device_init_wakeup(hcd->self.controller, 1);		val |= RH_HS_DRWE;	}	isp116x_write_reg32(isp116x, HCRHSTATUS, val);	isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS);	isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf);	hcd->state = HC_STATE_RUNNING;	/* Set up interrupts */	isp116x->intenb = HCINT_MIE | HCINT_RHSC | HCINT_UE;	if (board->remote_wakeup_enable)		isp116x->intenb |= HCINT_RD;	isp116x->irqenb = HCuPINT_ATL | HCuPINT_OPR;	/* | HCuPINT_SUSP; */	isp116x_write_reg32(isp116x, HCINTENB, isp116x->intenb);	isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb);	/* Go operational */	val = HCCONTROL_USB_OPER;	if (board->remote_wakeup_enable)		val |= HCCONTROL_RWE;	isp116x_write_reg32(isp116x, HCCONTROL, val);	/* Disable ports to avoid race in device enumeration */	isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS);	isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS);	isp116x_show_regs_log(isp116x);	spin_unlock_irqrestore(&isp116x->lock, flags);	return 0;}#ifdef	CONFIG_PMstatic int isp116x_bus_suspend(struct usb_hcd *hcd){	struct isp116x *isp116x = hcd_to_isp116x(hcd);	unsigned long flags;	u32 val;	int ret = 0;	spin_lock_irqsave(&isp116x->lock, flags);	val = isp116x_read_reg32(isp116x, HCCONTROL);	switch (val & HCCONTROL_HCFS) {	case HCCONTROL_USB_OPER:		spin_unlock_irqrestore(&isp116x->lock, flags);		val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE);		val |= HCCONTROL_USB_SUSPEND;		if (device_may_wakeup(&hcd->self.root_hub->dev))			val |= HCCONTROL_RWE;		/* Wait for usb transfers to finish */		msleep(2);		spin_lock_irqsave(&isp116x->lock, flags);		isp116x_write_reg32(isp116x, HCCONTROL, val);		spin_unlock_irqrestore(&isp116x->lock, flags);		/* Wait for devices to suspend */		msleep(5);		break;	case HCCONTROL_USB_RESUME:		isp116x_write_reg32(isp116x, HCCONTROL,				    (val & ~HCCONTROL_HCFS) |				    HCCONTROL_USB_RESET);	case HCCONTROL_USB_RESET:		ret = -EBUSY;	default:		/* HCCONTROL_USB_SUSPEND */		spin_unlock_irqrestore(&isp116x->lock, flags);		break;	}	return ret;}static int isp116x_bus_resume(struct usb_hcd *hcd){	struct isp116x *isp116x = hcd_to_isp116x(hcd);	u32 val;	msleep(5);	spin_lock_irq(&isp116x->lock);	val = isp116x_read_reg32(isp116x, HCCONTROL);	switch (val & HCCONTROL_HCFS) {	case HCCONTROL_USB_SUSPEND:		val &= ~HCCONTROL_HCFS;		val |= HCCONTROL_USB_RESUME;		isp116x_write_reg32(isp116x, HCCONTROL, val);	case HCCONTROL_USB_RESUME:		break;	case HCCONTROL_USB_OPER:		spin_unlock_irq(&isp116x->lock);		/* Without setting power_state here the		   SUSPENDED state won't be removed from		   sysfs/usbN/power.state as a response to remote		   wakeup. Maybe in the future. */		hcd->self.root_hub->dev.power.power_state = PMSG_ON;		return 0;	default:		/* HCCONTROL_USB_RESET: this may happen, when during		   suspension the HC lost power. Reinitialize completely */		spin_unlock_irq(&isp116x->lock);		DBG("Chip has been reset while suspended. Reinit from scratch.\n");		isp116x_reset(hcd);		isp116x_start(hcd);		isp116x_hub_control(hcd, SetPortFeature,				    USB_PORT_FEAT_POWER, 1, NULL, 0);		if ((isp116x->rhdesca & RH_A_NDP) == 2)			isp116x_hub_control(hcd, SetPortFeature,					    USB_PORT_FEAT_POWER, 2, NULL, 0);		hcd->self.root_hub->dev.power.power_state = PMSG_ON;		return 0;	}	val = isp116x->rhdesca & RH_A_NDP;	while (val--) {		u32 stat =		    isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1);		/* force global, not selective, resume */		if (!(stat & RH_PS_PSS))			continue;		DBG("%s: Resuming port %d\n", __func__, val);		isp116x_write_reg32(isp116x, RH_PS_POCI, val				    ? HCRHPORT2 : HCRHPORT1);	}	spin_unlock_irq(&isp116x->lock);	hcd->state = HC_STATE_RESUMING;	msleep(20);	/* Go operational */	spin_lock_irq(&isp116x->lock);	val = isp116x_read_reg32(isp116x, HCCONTROL);	isp116x_write_reg32(isp116x, HCCONTROL,			    (val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER);	spin_unlock_irq(&isp116x->lock);	/* see analogous comment above */	hcd->self.root_hub->dev.power.power_state = PMSG_ON;	hcd->state = HC_STATE_RUNNING;	return 0;}#else#define	isp116x_bus_suspend	NULL#define	isp116x_bus_resume	NULL#endifstatic struct hc_driver isp116x_hc_driver = {	.description = hcd_name,	.product_desc = "ISP116x Host Controller",	.hcd_priv_size = sizeof(struct isp116x),	.irq = isp116x_irq,	.flags = HCD_USB11,	.reset = isp116x_reset,	.start = isp116x_start,	.stop = isp116x_stop,	.urb_enqueue = isp116x_urb_enqueue,	.urb_dequeue = isp116x_urb_dequeue,	.endpoint_disable = isp116x_endpoint_disable,	.get_frame_number = isp116x_get_frame,	.hub_status_data = isp116x_hub_status_data,	.hub_control = isp116x_hub_control,	.bus_suspend = isp116x_bus_suspend,	.bus_resume = isp116x_bus_resume,};/*----------------------------------------------------------------*/static int isp116x_remove(struct platform_device *pdev){	struct usb_hcd *hcd = platform_get_drvdata(pdev);	struct isp116x *isp116x;	struct resource *res;	if (!hcd)		return 0;	isp116x = hcd_to_isp116x(hcd);	remove_debug_file(isp116x);	usb_remove_hcd(hcd);	iounmap(isp116x->data_reg);	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);	release_mem_region(res->start, 2);	iounmap(isp116x->addr_reg);	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);	release_mem_region(res->start, 2);	usb_put_hcd(hcd);	return 0;}#define resource_len(r) (((r)->end - (r)->start) + 1)static int __init isp116x_probe(struct platform_device *pdev){	struct usb_hcd *hcd;	struct isp116x *isp116x;	struct resource *addr, *data;	void __iomem *addr_reg;	void __iomem *data_reg;	int irq;	int ret = 0;	if (pdev->num_resources < 3) {		ret = -ENODEV;		goto err1;	}	data = platform_get_resource(pdev, IORESOURCE_MEM, 0);	addr = platform_get_resource(pdev, IORESOURCE_MEM, 1);	irq = platform_get_irq(pdev, 0);	if (!addr || !data || irq < 0) {		ret = -ENODEV;		goto err1;	}	if (pdev->dev.dma_mask) {		DBG("DMA not supported\n");		ret = -EINVAL;		goto err1;	}	if (!request_mem_region(addr->start, 2, hcd_name)) {		ret = -EBUSY;		goto err1;	}	addr_reg = ioremap(addr->start, resource_len(addr));	if (addr_reg == NULL) {		ret = -ENOMEM;		goto err2;	}	if (!request_mem_region(data->start, 2, hcd_name)) {		ret = -EBUSY;		goto err3;	}	data_reg = ioremap(data->start, resource_len(data));	if (data_reg == NULL) {		ret = -ENOMEM;		goto err4;	}	/* allocate and initialize hcd */	hcd = usb_create_hcd(&isp116x_hc_driver, &pdev->dev, pdev->dev.bus_id);	if (!hcd) {		ret = -ENOMEM;		goto err5;	}	/* this rsrc_start is bogus */	hcd->rsrc_start = addr->start;	isp116x = hcd_to_isp116x(hcd);	isp116x->data_reg = data_reg;	isp116x->addr_reg = addr_reg;	spin_lock_init(&isp116x->lock);	INIT_LIST_HEAD(&isp116x->async);	isp116x->board = pdev->dev.platform_data;	if (!isp116x->board) {		ERR("Platform data structure not initialized\n");		ret = -ENODEV;		goto err6;	}	if (isp116x_check_platform_delay(isp116x)) {		ERR("USE_PLATFORM_DELAY defined, but delay function not "		    "implemented.\n");		ERR("See comments in drivers/usb/host/isp116x-hcd.c\n");		ret = -ENODEV;		goto err6;	}	ret = usb_add_hcd(hcd, irq, SA_INTERRUPT);	if (ret)		goto err6;	ret = create_debug_file(isp116x);	if (ret) {		ERR("Couldn't create debugfs entry\n");		goto err7;	}	return 0;      err7:	usb_remove_hcd(hcd);      err6:	usb_put_hcd(hcd);      err5:	iounmap(data_reg);      err4:	release_mem_region(data->start, 2);      err3:	iounmap(addr_reg);      err2:	release_mem_region(addr->start, 2);      err1:	ERR("init error, %d\n", ret);	return ret;}#ifdef	CONFIG_PM/*  Suspend of platform device*/static int isp116x_suspend(struct platform_device *dev, pm_message_t state){	VDBG("%s: state %x\n", __func__, state.event);	dev->dev.power.power_state = state;	return 0;}/*  Resume platform device*/static int isp116x_resume(struct platform_device *dev){	VDBG("%s:  state %x\n", __func__, dev->power.power_state.event);	dev->dev.power.power_state = PMSG_ON;	return 0;}#else#define	isp116x_suspend    NULL#define	isp116x_resume     NULL#endifstatic struct platform_driver isp116x_driver = {	.probe = isp116x_probe,	.remove = isp116x_remove,	.suspend = isp116x_suspend,	.resume = isp116x_resume,	.driver = {		   .name = (char *)hcd_name,		   },};/*-----------------------------------------------------------------*/static int __init isp116x_init(void){	if (usb_disabled())		return -ENODEV;	INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION);	return platform_driver_register(&isp116x_driver);}module_init(isp116x_init);static void __exit isp116x_cleanup(void){	platform_driver_unregister(&isp116x_driver);}module_exit(isp116x_cleanup);

⌨️ 快捷键说明

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