📄 isp1301_omap.c
字号:
/* implicit lock: we're the only task using this device */ isp->working = 1; do { stop = test_bit(WORK_STOP, &isp->todo);#ifdef CONFIG_USB_OTG /* transfer state from otg engine to isp1301 */ if (test_and_clear_bit(WORK_UPDATE_ISP, &isp->todo)) { otg_update_isp(isp); put_device(&isp->client.dev); }#endif /* transfer state from isp1301 to otg engine */ if (test_and_clear_bit(WORK_UPDATE_OTG, &isp->todo)) { u8 stat = isp1301_clear_latch(isp); isp_update_otg(isp, stat); put_device(&isp->client.dev); } if (test_and_clear_bit(WORK_HOST_RESUME, &isp->todo)) { u32 otg_ctrl; /* * skip A_WAIT_VRISE; hc transitions invisibly * skip A_WAIT_BCON; same. */ switch (isp->otg.state) { case OTG_STATE_A_WAIT_BCON: case OTG_STATE_A_WAIT_VRISE: isp->otg.state = OTG_STATE_A_HOST; pr_debug(" --> a_host\n"); otg_ctrl = OTG_CTRL_REG; otg_ctrl |= OTG_A_BUSREQ; otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) & OTG_CTRL_MASK; OTG_CTRL_REG = otg_ctrl; break; case OTG_STATE_B_WAIT_ACON: isp->otg.state = OTG_STATE_B_HOST; pr_debug(" --> b_host (acon)\n"); break; case OTG_STATE_B_HOST: case OTG_STATE_B_IDLE: case OTG_STATE_A_IDLE: break; default: pr_debug(" host resume in %s\n", state_name(isp)); } host_resume(isp); // mdelay(10); put_device(&isp->client.dev); } if (test_and_clear_bit(WORK_TIMER, &isp->todo)) {#ifdef VERBOSE dump_regs(isp, "timer"); if (!stop) mod_timer(&isp->timer, jiffies + TIMER_JIFFIES);#endif put_device(&isp->client.dev); } if (isp->todo) dev_vdbg(&isp->client.dev, "work done, todo = 0x%lx\n", isp->todo); if (stop) { dev_dbg(&isp->client.dev, "stop\n"); break; } } while (isp->todo); isp->working = 0;}static irqreturn_t isp1301_irq(int irq, void *isp, struct pt_regs *regs){ isp1301_defer_work(isp, WORK_UPDATE_OTG); return IRQ_HANDLED;}static void isp1301_timer(unsigned long _isp){ isp1301_defer_work((void *)_isp, WORK_TIMER);}/*-------------------------------------------------------------------------*/static void isp1301_release(struct device *dev){ struct isp1301 *isp; isp = container_of(dev, struct isp1301, client.dev); /* ugly -- i2c hijacks our memory hook to wait_for_completion() */ if (isp->i2c_release) isp->i2c_release(dev); kfree (isp);}static struct isp1301 *the_transceiver;static int isp1301_detach_client(struct i2c_client *i2c){ struct isp1301 *isp; isp = container_of(i2c, struct isp1301, client); isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); free_irq(isp->irq, isp);#ifdef CONFIG_USB_OTG otg_unbind(isp);#endif if (machine_is_omap_h2()) omap_free_gpio(2); isp->timer.data = 0; set_bit(WORK_STOP, &isp->todo); del_timer_sync(&isp->timer); flush_scheduled_work(); put_device(&i2c->dev); the_transceiver = 0; return i2c_detach_client(i2c);}/*-------------------------------------------------------------------------*//* NOTE: three modes are possible here, only one of which * will be standards-conformant on any given system: * * - OTG mode (dual-role), required if there's a Mini-AB connector * - HOST mode, for when there's one or more A (host) connectors * - DEVICE mode, for when there's a B/Mini-B (device) connector * * As a rule, you won't have an isp1301 chip unless it's there to * support the OTG mode. Other modes help testing USB controllers * in isolation from (full) OTG support, or maybe so later board * revisions can help to support those feature. */#ifdef CONFIG_USB_OTGstatic int isp1301_otg_enable(struct isp1301 *isp){ power_up(isp); otg_init(isp); /* NOTE: since we don't change this, this provides * a few more interrupts than are strictly needed. */ isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); dev_info(&isp->client.dev, "ready for dual-role USB ...\n"); return 0;}#endif/* add or disable the host device+driver */static intisp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host){ struct isp1301 *isp = container_of(otg, struct isp1301, otg); if (!otg || isp != the_transceiver) return -ENODEV; if (!host) { OTG_IRQ_EN_REG = 0; power_down(isp); isp->otg.host = 0; return 0; }#ifdef CONFIG_USB_OTG isp->otg.host = host; dev_dbg(&isp->client.dev, "registered host\n"); host_suspend(isp); if (isp->otg.gadget) return isp1301_otg_enable(isp); return 0;#elif !defined(CONFIG_USB_GADGET_OMAP) // FIXME update its refcount isp->otg.host = host; power_up(isp); if (machine_is_omap_h2()) isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); dev_info(&isp->client.dev, "A-Host sessions ok\n"); isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, INTR_ID_GND); isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, INTR_ID_GND); /* If this has a Mini-AB connector, this mode is highly * nonstandard ... but can be handy for testing, especially with * the Mini-A end of an OTG cable. (Or something nonstandard * like MiniB-to-StandardB, maybe built with a gender mender.) */ isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_VBUS_DRV); dump_regs(isp, __FUNCTION__); return 0;#else dev_dbg(&isp->client.dev, "host sessions not allowed\n"); return -EINVAL;#endif}static intisp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget){ struct isp1301 *isp = container_of(otg, struct isp1301, otg); if (!otg || isp != the_transceiver) return -ENODEV; if (!gadget) { OTG_IRQ_EN_REG = 0; if (!isp->otg.default_a) enable_vbus_draw(isp, 0); usb_gadget_vbus_disconnect(isp->otg.gadget); isp->otg.gadget = 0; power_down(isp); return 0; }#ifdef CONFIG_USB_OTG isp->otg.gadget = gadget; dev_dbg(&isp->client.dev, "registered gadget\n"); /* gadget driver may be suspended until vbus_connect () */ if (isp->otg.host) return isp1301_otg_enable(isp); return 0;#elif !defined(CONFIG_USB_OHCI_HCD) && !defined(CONFIG_USB_OHCI_HCD_MODULE) isp->otg.gadget = gadget; // FIXME update its refcount OTG_CTRL_REG = (OTG_CTRL_REG & OTG_CTRL_MASK & ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS)) | OTG_ID; power_up(isp); isp->otg.state = OTG_STATE_B_IDLE; if (machine_is_omap_h2()) isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, INTR_SESS_VLD); isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, INTR_VBUS_VLD); dev_info(&isp->client.dev, "B-Peripheral sessions ok\n"); dump_regs(isp, __FUNCTION__); /* If this has a Mini-AB connector, this mode is highly * nonstandard ... but can be handy for testing, so long * as you don't plug a Mini-A cable into the jack. */ if (isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE) & INTR_VBUS_VLD) b_peripheral(isp); return 0;#else dev_dbg(&isp->client.dev, "peripheral sessions not allowed\n"); return -EINVAL;#endif}/*-------------------------------------------------------------------------*/static intisp1301_set_power(struct otg_transceiver *dev, unsigned mA){ if (!the_transceiver) return -ENODEV; if (dev->state == OTG_STATE_B_PERIPHERAL) enable_vbus_draw(the_transceiver, mA); return 0;}static intisp1301_start_srp(struct otg_transceiver *dev){ struct isp1301 *isp = container_of(dev, struct isp1301, otg); u32 otg_ctrl; if (!dev || isp != the_transceiver || isp->otg.state != OTG_STATE_B_IDLE) return -ENODEV; otg_ctrl = OTG_CTRL_REG; if (!(otg_ctrl & OTG_BSESSEND)) return -EINVAL; otg_ctrl |= OTG_B_BUSREQ; otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK; OTG_CTRL_REG = otg_ctrl; isp->otg.state = OTG_STATE_B_SRP_INIT; pr_debug("otg: SRP, %s ... %06x\n", state_name(isp), OTG_CTRL_REG);#ifdef CONFIG_USB_OTG check_state(isp, __FUNCTION__);#endif return 0;}static intisp1301_start_hnp(struct otg_transceiver *dev){#ifdef CONFIG_USB_OTG struct isp1301 *isp = container_of(dev, struct isp1301, otg); if (!dev || isp != the_transceiver) return -ENODEV; if (isp->otg.default_a && (isp->otg.host == NULL || !isp->otg.host->b_hnp_enable)) return -ENOTCONN; if (!isp->otg.default_a && (isp->otg.gadget == NULL || !isp->otg.gadget->b_hnp_enable)) return -ENOTCONN; /* We want hardware to manage most HNP protocol timings. * So do this part as early as possible... */ switch (isp->otg.state) { case OTG_STATE_B_HOST: isp->otg.state = OTG_STATE_B_PERIPHERAL; /* caller will suspend next */ break; case OTG_STATE_A_HOST:#if 0 /* autoconnect mode avoids irq latency bugs */ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_BDIS_ACON_EN);#endif /* caller must suspend then clear A_BUSREQ */ usb_gadget_vbus_connect(isp->otg.gadget); OTG_CTRL_REG |= OTG_A_SETB_HNPEN; break; case OTG_STATE_A_PERIPHERAL: /* initiated by B-Host suspend */ break; default: return -EILSEQ; } pr_debug("otg: HNP %s, %06x ...\n", state_name(isp), OTG_CTRL_REG); check_state(isp, __FUNCTION__); return 0;#else /* srp-only */ return -EINVAL;#endif}/*-------------------------------------------------------------------------*//* no error returns, they'd just make bus scanning stop */static int isp1301_probe(struct i2c_adapter *bus, int address, int kind){ int status; struct isp1301 *isp; struct i2c_client *i2c; if (the_transceiver) return 0; isp = kzalloc(sizeof *isp, GFP_KERNEL); if (!isp) return 0; INIT_WORK(&isp->work, isp1301_work, isp); init_timer(&isp->timer); isp->timer.function = isp1301_timer; isp->timer.data = (unsigned long) isp; isp->irq = -1; isp->client.addr = address; i2c_set_clientdata(&isp->client, isp); isp->client.adapter = bus; isp->client.driver = &isp1301_driver; strlcpy(isp->client.name, DRIVER_NAME, I2C_NAME_SIZE); i2c = &isp->client; /* if this is a true probe, verify the chip ... */ if (kind < 0) { status = isp1301_get_u16(isp, ISP1301_VENDOR_ID); if (status != I2C_VENDOR_ID_PHILIPS) { dev_dbg(&bus->dev, "addr %d not philips id: %d\n", address, status); goto fail1; } status = isp1301_get_u16(isp, ISP1301_PRODUCT_ID); if (status != I2C_PRODUCT_ID_PHILIPS_1301) { dev_dbg(&bus->dev, "%d not isp1301, %d\n", address, status); goto fail1; } } status = i2c_attach_client(i2c); if (status < 0) { dev_dbg(&bus->dev, "can't attach %s to device %d, err %d\n", DRIVER_NAME, address, status);fail1: kfree(isp); return 0; } isp->i2c_release = i2c->dev.release; i2c->dev.release = isp1301_release; /* initial development used chiprev 2.00 */ status = i2c_smbus_read_word_data(i2c, ISP1301_BCD_DEVICE); dev_info(&i2c->dev, "chiprev %x.%02x, driver " DRIVER_VERSION "\n", status >> 8, status & 0xff); /* make like power-on reset */ isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_MASK); isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_BI_DI); isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, ~MC2_BI_DI); isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN); isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, ~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN)); isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, ~0); isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0);#ifdef CONFIG_USB_OTG status = otg_bind(isp); if (status < 0) { dev_dbg(&i2c->dev, "can't bind OTG\n"); goto fail2; }#endif if (machine_is_omap_h2()) { /* full speed signaling by default */ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SPEED_REG); isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_SPD_SUSP_CTRL); /* IRQ wired at M14 */ omap_cfg_reg(M14_1510_GPIO2); isp->irq = OMAP_GPIO_IRQ(2); omap_request_gpio(2); omap_set_gpio_direction(2, 1); omap_set_gpio_edge_ctrl(2, OMAP_GPIO_FALLING_EDGE); } status = request_irq(isp->irq, isp1301_irq, SA_SAMPLE_RANDOM, DRIVER_NAME, isp); if (status < 0) { dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n", isp->irq, status);#ifdef CONFIG_USB_OTGfail2:#endif i2c_detach_client(i2c); goto fail1; } isp->otg.dev = &isp->client.dev; isp->otg.label = DRIVER_NAME; isp->otg.set_host = isp1301_set_host, isp->otg.set_peripheral = isp1301_set_peripheral, isp->otg.set_power = isp1301_set_power, isp->otg.start_srp = isp1301_start_srp, isp->otg.start_hnp = isp1301_start_hnp, enable_vbus_draw(isp, 0); power_down(isp); the_transceiver = isp;#ifdef CONFIG_USB_OTG update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS));#endif dump_regs(isp, __FUNCTION__);#ifdef VERBOSE mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES);#endif status = otg_set_transceiver(&isp->otg); if (status < 0) dev_err(&i2c->dev, "can't register transceiver, %d\n", status); return 0;}static int isp1301_scan_bus(struct i2c_adapter *bus){ if (!i2c_check_functionality(bus, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_READ_WORD_DATA)) return -EINVAL; return i2c_probe(bus, &addr_data, isp1301_probe);}static struct i2c_driver isp1301_driver = { .owner = THIS_MODULE, .name = "isp1301_omap", .id = 1301, /* FIXME "official", i2c-ids.h */ .class = I2C_CLASS_HWMON, .flags = I2C_DF_NOTIFY, .attach_adapter = isp1301_scan_bus, .detach_client = isp1301_detach_client,};/*-------------------------------------------------------------------------*/static int __init isp_init(void){ return i2c_add_driver(&isp1301_driver);}module_init(isp_init);static void __exit isp_exit(void){ if (the_transceiver) otg_set_transceiver(0); i2c_del_driver(&isp1301_driver);}module_exit(isp_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -