📄 isp116x-hcd.c
字号:
}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 + -