📄 isp116x-hcd.c
字号:
if (!list_empty(&isp116x->async)) seq_printf(s, "\n"); seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE); for (i = 0; i < PERIODIC_SIZE; i++) { ep = isp116x->periodic[i]; if (!ep) continue; seq_printf(s, "%2d [%3d]:\n", i, isp116x->load[i]); /* DUMB: prints shared entries multiple times */ do { seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n", ep->period, ep, (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ", ep->udev->devnum, ep->epnum, (ep->epnum == 0) ? "" : ((ep->nextpid == USB_PID_IN) ? "in" : "out"), ep->maxpacket); ep = ep->next; } while (ep); } spin_unlock_irq(&isp116x->lock); seq_printf(s, "\n"); return 0;}static int proc_isp116x_open(struct inode *inode, struct file *file){ return single_open(file, proc_isp116x_show, PDE(inode)->data);}static struct file_operations proc_ops = { .open = proc_isp116x_open, .read = seq_read, .llseek = seq_lseek, .release = single_release,};/* expect just one isp116x per system */static const char proc_filename[] = "driver/isp116x";static void create_debug_file(struct isp116x *isp116x){ struct proc_dir_entry *pde; pde = create_proc_entry(proc_filename, 0, NULL); if (pde == NULL) return; pde->proc_fops = &proc_ops; pde->data = isp116x; isp116x->pde = pde;}static void remove_debug_file(struct isp116x *isp116x){ if (isp116x->pde) remove_proc_entry(proc_filename, NULL);}#endif/*-----------------------------------------------------------------*//* 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 = 0, 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 20ms\n"); /* 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) { hcd->can_wakeup = 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(isp116x); spin_unlock_irqrestore(&isp116x->lock, flags); return 0;}/*-----------------------------------------------------------------*/static 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 __init_or_module 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 != 0) goto err6; create_debug_file(isp116x); return 0; 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){ int ret = 0; VDBG("%s: state %x\n", __func__, state); dev->dev.power.power_state = state; return ret;}/* Resume platform device*/static int isp116x_resume(struct platform_device *dev){ int ret = 0; VDBG("%s: state %x\n", __func__, dev->dev.power.power_state); dev->dev.power.power_state = PMSG_ON; return ret;}#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 + -