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

📄 isp1301_omap.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 3 页
字号:
	/* 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 + -