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

📄 isp1301_omap.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * isp1301_omap - ISP 1301 USB transceiver, talking to OMAP OTG controller * * Copyright (C) 2004 Texas Instruments * Copyright (C) 2004 David Brownell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#undef	DEBUG#undef	VERBOSE#include <linux/config.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/platform_device.h>#include <linux/usb_ch9.h>#include <linux/usb_gadget.h>#include <linux/usb.h>#include <linux/usb_otg.h>#include <linux/i2c.h>#include <linux/workqueue.h>#include <asm/irq.h>#include <asm/arch/usb.h>#ifndef	DEBUG#undef	VERBOSE#endif#define	DRIVER_VERSION	"24 August 2004"#define	DRIVER_NAME	(isp1301_driver.name)MODULE_DESCRIPTION("ISP1301 USB OTG Transceiver Driver");MODULE_LICENSE("GPL");struct isp1301 {	struct otg_transceiver	otg;	struct i2c_client	client;	void			(*i2c_release)(struct device *dev);	int			irq;	u32			last_otg_ctrl;	unsigned		working:1;	struct timer_list	timer;	/* use keventd context to change the state for us */	struct work_struct	work;		unsigned long		todo;#		define WORK_UPDATE_ISP	0	/* update ISP from OTG */#		define WORK_UPDATE_OTG	1	/* update OTG from ISP */#		define WORK_HOST_RESUME	4	/* resume host */#		define WORK_TIMER	6	/* timer fired */#		define WORK_STOP	7	/* don't resubmit */};/* bits in OTG_CTRL_REG */#define	OTG_XCEIV_OUTPUTS \	(OTG_ASESSVLD|OTG_BSESSEND|OTG_BSESSVLD|OTG_VBUSVLD|OTG_ID)#define	OTG_XCEIV_INPUTS \	(OTG_PULLDOWN|OTG_PULLUP|OTG_DRV_VBUS|OTG_PD_VBUS|OTG_PU_VBUS|OTG_PU_ID)#define	OTG_CTRL_BITS \	(OTG_A_BUSREQ|OTG_A_SETB_HNPEN|OTG_B_BUSREQ|OTG_B_HNPEN|OTG_BUSDROP)	/* and OTG_PULLUP is sometimes written */#define	OTG_CTRL_MASK	(OTG_DRIVER_SEL| \	OTG_XCEIV_OUTPUTS|OTG_XCEIV_INPUTS| \	OTG_CTRL_BITS)/*-------------------------------------------------------------------------*/#ifdef	CONFIG_MACH_OMAP_H2/* board-specific PM hooks */#include <asm/arch/gpio.h>#include <asm/arch/mux.h>#include <asm/mach-types.h>#if	defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE)#include <asm/arch/tps65010.h>#elsestatic inline int tps65010_set_vbus_draw(unsigned mA){	pr_debug("tps65010: draw %d mA (STUB)\n", mA);	return 0;}#endifstatic void enable_vbus_draw(struct isp1301 *isp, unsigned mA){	int status = tps65010_set_vbus_draw(mA);	if (status < 0)		pr_debug("  VBUS %d mA error %d\n", mA, status);}static void enable_vbus_source(struct isp1301 *isp){	/* this board won't supply more than 8mA vbus power.	 * some boards can switch a 100ma "unit load" (or more).	 */}/* products will deliver OTG messages with LEDs, GUI, etc */static inline void notresponding(struct isp1301 *isp){	printk(KERN_NOTICE "OTG device not responding.\n");}#endif/*-------------------------------------------------------------------------*//* only two addresses possible */#define	ISP_BASE		0x2cstatic unsigned short normal_i2c[] = {	ISP_BASE, ISP_BASE + 1,	I2C_CLIENT_END };I2C_CLIENT_INSMOD;static struct i2c_driver isp1301_driver;/* smbus apis are used for portability */static inline u8isp1301_get_u8(struct isp1301 *isp, u8 reg){	return i2c_smbus_read_byte_data(&isp->client, reg + 0);}static inline intisp1301_get_u16(struct isp1301 *isp, u8 reg){	return i2c_smbus_read_word_data(&isp->client, reg);}static inline intisp1301_set_bits(struct isp1301 *isp, u8 reg, u8 bits){	return i2c_smbus_write_byte_data(&isp->client, reg + 0, bits);}static inline intisp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits){	return i2c_smbus_write_byte_data(&isp->client, reg + 1, bits);}/*-------------------------------------------------------------------------*//* identification */#define	ISP1301_VENDOR_ID		0x00	/* u16 read */#define	ISP1301_PRODUCT_ID		0x02	/* u16 read */#define	ISP1301_BCD_DEVICE		0x14	/* u16 read */#define	I2C_VENDOR_ID_PHILIPS		0x04cc#define	I2C_PRODUCT_ID_PHILIPS_1301	0x1301/* operational registers */#define	ISP1301_MODE_CONTROL_1		0x04	/* u8 read, set, +1 clear */#	define	MC1_SPEED_REG		(1 << 0)#	define	MC1_SUSPEND_REG		(1 << 1)#	define	MC1_DAT_SE0		(1 << 2)#	define	MC1_TRANSPARENT		(1 << 3)#	define	MC1_BDIS_ACON_EN	(1 << 4)#	define	MC1_OE_INT_EN		(1 << 5)#	define	MC1_UART_EN		(1 << 6)#	define	MC1_MASK		0x7f#define	ISP1301_MODE_CONTROL_2		0x12	/* u8 read, set, +1 clear */#	define	MC2_GLOBAL_PWR_DN	(1 << 0)#	define	MC2_SPD_SUSP_CTRL	(1 << 1)#	define	MC2_BI_DI		(1 << 2)#	define	MC2_TRANSP_BDIR0	(1 << 3)#	define	MC2_TRANSP_BDIR1	(1 << 4)#	define	MC2_AUDIO_EN		(1 << 5)#	define	MC2_PSW_EN		(1 << 6)#	define	MC2_EN2V7		(1 << 7)#define	ISP1301_OTG_CONTROL_1		0x06	/* u8 read, set, +1 clear */#	define	OTG1_DP_PULLUP		(1 << 0)#	define	OTG1_DM_PULLUP		(1 << 1)#	define	OTG1_DP_PULLDOWN	(1 << 2)#	define	OTG1_DM_PULLDOWN	(1 << 3)#	define	OTG1_ID_PULLDOWN	(1 << 4)#	define	OTG1_VBUS_DRV		(1 << 5)#	define	OTG1_VBUS_DISCHRG	(1 << 6)#	define	OTG1_VBUS_CHRG		(1 << 7)#define	ISP1301_OTG_STATUS		0x10	/* u8 readonly */#	define	OTG_B_SESS_END		(1 << 6)#	define	OTG_B_SESS_VLD		(1 << 7)#define	ISP1301_INTERRUPT_SOURCE	0x08	/* u8 read */#define	ISP1301_INTERRUPT_LATCH		0x0A	/* u8 read, set, +1 clear */#define	ISP1301_INTERRUPT_FALLING	0x0C	/* u8 read, set, +1 clear */#define	ISP1301_INTERRUPT_RISING	0x0E	/* u8 read, set, +1 clear *//* same bitfields in all interrupt registers */#	define	INTR_VBUS_VLD		(1 << 0)#	define	INTR_SESS_VLD		(1 << 1)#	define	INTR_DP_HI		(1 << 2)#	define	INTR_ID_GND		(1 << 3)#	define	INTR_DM_HI		(1 << 4)#	define	INTR_ID_FLOAT		(1 << 5)#	define	INTR_BDIS_ACON		(1 << 6)#	define	INTR_CR_INT		(1 << 7)/*-------------------------------------------------------------------------*/static const char *state_string(enum usb_otg_state state){	switch (state) {	case OTG_STATE_A_IDLE:		return "a_idle";	case OTG_STATE_A_WAIT_VRISE:	return "a_wait_vrise";	case OTG_STATE_A_WAIT_BCON:	return "a_wait_bcon";	case OTG_STATE_A_HOST:		return "a_host";	case OTG_STATE_A_SUSPEND:	return "a_suspend";	case OTG_STATE_A_PERIPHERAL:	return "a_peripheral";	case OTG_STATE_A_WAIT_VFALL:	return "a_wait_vfall";	case OTG_STATE_A_VBUS_ERR:	return "a_vbus_err";	case OTG_STATE_B_IDLE:		return "b_idle";	case OTG_STATE_B_SRP_INIT:	return "b_srp_init";	case OTG_STATE_B_PERIPHERAL:	return "b_peripheral";	case OTG_STATE_B_WAIT_ACON:	return "b_wait_acon";	case OTG_STATE_B_HOST:		return "b_host";	default:			return "UNDEFINED";	}}static inline const char *state_name(struct isp1301 *isp){	return state_string(isp->otg.state);}#ifdef	VERBOSE#define	dev_vdbg			dev_dbg#else#define	dev_vdbg(dev, fmt, arg...)	do{}while(0)#endif/*-------------------------------------------------------------------------*//* NOTE:  some of this ISP1301 setup is specific to H2 boards; * not everything is guarded by board-specific checks, or even using * omap_usb_config data to deduce MC1_DAT_SE0 and MC2_BI_DI. * * ALSO:  this currently doesn't use ISP1301 low-power modes * while OTG is running. */static void power_down(struct isp1301 *isp){	isp->otg.state = OTG_STATE_UNDEFINED;	// isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN);	isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND_REG);	isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_ID_PULLDOWN);	isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);}static void power_up(struct isp1301 *isp){	// isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN);	isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND_REG);		/* do this only when cpu is driving transceiver,	 * so host won't see a low speed device...	 */	isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);}#define	NO_HOST_SUSPENDstatic int host_suspend(struct isp1301 *isp){#ifdef	NO_HOST_SUSPEND	return 0;#else	struct device	*dev;	if (!isp->otg.host)		return -ENODEV;	/* Currently ASSUMES only the OTG port matters;	 * other ports could be active...	 */	dev = isp->otg.host->controller;	return dev->driver->suspend(dev, 3, 0);#endif}static int host_resume(struct isp1301 *isp){#ifdef	NO_HOST_SUSPEND	return 0;#else	struct device	*dev;	if (!isp->otg.host)		return -ENODEV;	dev = isp->otg.host->controller;	return dev->driver->resume(dev, 0);#endif}static int gadget_suspend(struct isp1301 *isp){	isp->otg.gadget->b_hnp_enable = 0;	isp->otg.gadget->a_hnp_support = 0;	isp->otg.gadget->a_alt_hnp_support = 0;	return usb_gadget_vbus_disconnect(isp->otg.gadget);}/*-------------------------------------------------------------------------*/#define	TIMER_MINUTES	10#define	TIMER_JIFFIES	(TIMER_MINUTES * 60 * HZ)/* Almost all our I2C messaging comes from a work queue's task context. * NOTE: guaranteeing certain response times might mean we shouldn't * share keventd's work queue; a realtime task might be safest. */voidisp1301_defer_work(struct isp1301 *isp, int work){	int status;	if (isp && !test_and_set_bit(work, &isp->todo)) {		(void) get_device(&isp->client.dev);		status = schedule_work(&isp->work);		if (!status && !isp->working)			dev_vdbg(&isp->client.dev,				"work item %d may be lost\n", work);	}}/* called from irq handlers */static void a_idle(struct isp1301 *isp, const char *tag){	if (isp->otg.state == OTG_STATE_A_IDLE)		return;	isp->otg.default_a = 1;	if (isp->otg.host) {		isp->otg.host->is_b_host = 0;		host_suspend(isp);	}	if (isp->otg.gadget) {		isp->otg.gadget->is_a_peripheral = 1;		gadget_suspend(isp);	}	isp->otg.state = OTG_STATE_A_IDLE;	isp->last_otg_ctrl = OTG_CTRL_REG = OTG_CTRL_REG & OTG_XCEIV_OUTPUTS;	pr_debug("  --> %s/%s\n", state_name(isp), tag);}/* called from irq handlers */static void b_idle(struct isp1301 *isp, const char *tag){	if (isp->otg.state == OTG_STATE_B_IDLE)		return;	isp->otg.default_a = 0;	if (isp->otg.host) {		isp->otg.host->is_b_host = 1;		host_suspend(isp);	}	if (isp->otg.gadget) {		isp->otg.gadget->is_a_peripheral = 0;		gadget_suspend(isp);	}	isp->otg.state = OTG_STATE_B_IDLE;	isp->last_otg_ctrl = OTG_CTRL_REG = OTG_CTRL_REG & OTG_XCEIV_OUTPUTS;	pr_debug("  --> %s/%s\n", state_name(isp), tag);}static voiddump_regs(struct isp1301 *isp, const char *label){#ifdef	DEBUG	u8	ctrl = isp1301_get_u8(isp, ISP1301_OTG_CONTROL_1);	u8	status = isp1301_get_u8(isp, ISP1301_OTG_STATUS);	u8	src = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE);	pr_debug("otg: %06x, %s %s, otg/%02x stat/%02x.%02x\n",		OTG_CTRL_REG, label, state_name(isp),		ctrl, status, src);	/* mode control and irq enables don't change much */#endif}/*-------------------------------------------------------------------------*/#ifdef	CONFIG_USB_OTG/* * The OMAP OTG controller handles most of the OTG state transitions. * * We translate isp1301 outputs (mostly voltage comparator status) into * OTG inputs; OTG outputs (mostly pullup/pulldown controls) and HNP state * flags into isp1301 inputs ... and infer state transitions. */#ifdef	VERBOSEstatic void check_state(struct isp1301 *isp, const char *tag){	enum usb_otg_state	state = OTG_STATE_UNDEFINED;	u8			fsm = OTG_TEST_REG & 0x0ff;	unsigned		extra = 0;	switch (fsm) {	/* default-b */	case 0x0:		state = OTG_STATE_B_IDLE;		break;	case 0x3:	case 0x7:		extra = 1;	case 0x1:		state = OTG_STATE_B_PERIPHERAL;		break;	case 0x11:		state = OTG_STATE_B_SRP_INIT;		break;	/* extra dual-role default-b states */	case 0x12:	case 0x13:	case 0x16:		extra = 1;	case 0x17:		state = OTG_STATE_B_WAIT_ACON;		break;	case 0x34:		state = OTG_STATE_B_HOST;		break;	/* default-a */	case 0x36:		state = OTG_STATE_A_IDLE;		break;	case 0x3c:		state = OTG_STATE_A_WAIT_VFALL;		break;	case 0x7d:		state = OTG_STATE_A_VBUS_ERR;		break;	case 0x9e:	case 0x9f:		extra = 1;	case 0x89:		state = OTG_STATE_A_PERIPHERAL;		break;	case 0xb7:		state = OTG_STATE_A_WAIT_VRISE;		break;	case 0xb8:		state = OTG_STATE_A_WAIT_BCON;		break;	case 0xb9:		state = OTG_STATE_A_HOST;		break;	case 0xba:		state = OTG_STATE_A_SUSPEND;		break;	default:		break;	}	if (isp->otg.state == state && !extra)		return;	pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag,		state_string(state), fsm, state_name(isp), OTG_CTRL_REG);}#elsestatic inline void check_state(struct isp1301 *isp, const char *tag) { }#endif/* outputs from ISP1301_INTERRUPT_SOURCE */static void update_otg1(struct isp1301 *isp, u8 int_src){	u32	otg_ctrl;	otg_ctrl = OTG_CTRL_REG			& OTG_CTRL_MASK			& ~OTG_XCEIV_INPUTS			& ~(OTG_ID|OTG_ASESSVLD|OTG_VBUSVLD);	if (int_src & INTR_SESS_VLD)		otg_ctrl |= OTG_ASESSVLD;	else if (isp->otg.state == OTG_STATE_A_WAIT_VFALL) {		a_idle(isp, "vfall");		otg_ctrl &= ~OTG_CTRL_BITS;	}	if (int_src & INTR_VBUS_VLD)		otg_ctrl |= OTG_VBUSVLD;	if (int_src & INTR_ID_GND) {		/* default-A */		if (isp->otg.state == OTG_STATE_B_IDLE				|| isp->otg.state == OTG_STATE_UNDEFINED) {			a_idle(isp, "init");			return;		}	} else {				/* default-B */		otg_ctrl |= OTG_ID;		if (isp->otg.state == OTG_STATE_A_IDLE				|| isp->otg.state == OTG_STATE_UNDEFINED) {			b_idle(isp, "init");			return;		}	}	OTG_CTRL_REG = otg_ctrl;}/* outputs from ISP1301_OTG_STATUS */static void update_otg2(struct isp1301 *isp, u8 otg_status){	u32	otg_ctrl;	otg_ctrl = OTG_CTRL_REG			& OTG_CTRL_MASK

⌨️ 快捷键说明

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