OTG Transceiver --- ISP1301 的驱动代码分析(一)

原帖地址:http://blog.csdn.net/zkami/article/details/2747210

如需引用,请注明出处blog.csdn.net/zkami 作者ZhengKui

Isp1301是philips的外部收发器, 完全支持USB2.0和OTG1.0a规范。能以12Mbit/s(full-speed)和1.5Mbit/s(low-speed)的速度发送和接收串行数据。支持I2C总线接口。本文分析linux-2.6中isp1301 driver的code (isp1301_omap.c)

include/linux/usb/otg.h

/*
* the otg driver needs to interact with both device side and host side
* usb controllers. it decides which controller is active at a given
* moment, using the transceiver, ID signal, HNP and sometimes static
* configuration information (including "board isn't wired for otg").
*/
struct otg_transceiver {
struct device *dev;
const char *label;
u8 default_a;
enum usb_otg_state state;
struct usb_bus *host;
struct usb_gadget *gadget;
/* to pass extra port status to the root hub */
u16 port_status;
u16 port_change;
/* bind/unbind the host controller */
int (*set_host)(struct otg_transceiver *otg,
struct usb_bus *host);
/* bind/unbind the peripheral controller */
int (*set_peripheral)(struct otg_transceiver *otg,
struct usb_gadget *gadget);
/* effective for B devices, ignored for A-peripheral */
int (*set_power)(struct otg_transceiver *otg,
unsigned mA);
/* for non-OTG B devices: set transceiver into suspend mode */
int (*set_suspend)(struct otg_transceiver *otg,
int suspend);
/* for B devices only: start session with A-Host */
int (*start_srp)(struct otg_transceiver *otg);
/* start or continue HNP role switch */
int (*start_hnp)(struct otg_transceiver *otg);

};

drivers/i2c/chips/isp1301_omap.c

struct isp1301 {
struct otg_transceiver otg; //继承了strcuct otg_transceiver 的所有特性和接口
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 */
};

在booting的时侯首先调用isp1301_scan_bus ---> i2c_probe(bus, &addr_data, isp1301_probe)探测i2c总线设备,进而调用

/* no error returns, they'd just make bus scanning stop */
static int isp1301_probe(struct i2c_adapter *bus, int address, int kind)
{
//分配一个struct isp1301的对象,并初始化其中各项
struct isp1301 *isp = kzalloc(sizeof *isp, GFP_KERNEL);
...

INIT_WORK(&isp->work, isp1301_work, isp); //初始化isp1301的工作队列
init_timer(&isp->timer); //初始化isp1301的定时器
isp->timer.function = isp1301_timer;
isp->timer.data = (unsigned long) isp;
isp->irq = -1;

//探测是否是isp1301,如果是进行一些chip的初始化设置
...

status = otg_bind(isp); //注册omap_otg_driver
isp->otg.set_host = isp1301_set_host, //将这个设备的角色设为host
isp->otg.set_peripheral = isp1301_set_peripheral, //将这个设备的角色设为peripheral
isp->otg.set_power = isp1301_set_power, //设置transceiver的电流
isp->otg.start_srp = isp1301_start_srp, //发起srp
isp->otg.start_hnp = isp1301_start_hnp, //发起hnp
update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE));
update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS));

...
}

static int otg_bind(struct isp1301 *isp)
{
//注册一个platform_device,在omap_otg_init的时侯会注册一个platform_device
//platform_device_register(&otg_device),这样在device与driver间进行匹配
platform_driver_register(&omap_otg_driver);
...
//注册isp1301的中断处理程序
request_irq(otg_dev->resource[1].start, omap_otg_irq, SA_INTERRUPT, DRIVER_NAME, isp);
...
}

static int isp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host)
{//将这个设备的角色设为host
isp->otg.host = host; //用struct usb_bus来抽象这个host设备
host_suspend(isp); //挂起host
if (isp->otg.gadget) //如果这个设备角色还是peripheral
return isp1301_otg_enable(isp); //重新使能这个isp1301,因为一个设备不可能即是host又是peripheral
return 0;
}

static int isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget)
{//将这个设备的角色设为peripheral,同isp1301_set_host()
isp->otg.gadget = gadget;
/* gadget driver 可以挂起直到 vbus_connect () */
if (isp->otg.host)
return isp1301_otg_enable(isp);
return 0;
}

static int isp1301_set_power(struct otg_transceiver *dev, unsigned mA)
{//只有当设备角色是B PERIPHERAL时,才能设置transceiver的电流,大小是第二个参数
if (dev->state == OTG_STATE_B_PERIPHERAL)
enable_vbus_draw(the_transceiver, mA);
}

static int isp1301_start_srp(struct otg_transceiver *dev)
{
otg_ctrl = OTG_CTRL_REG;
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;
}

static int isp1301_start_hnp(struct otg_transceiver *dev)
{
跟距isp->otg.state判断:
(1) OTG_STATE_B_HOST: isp->otg.state=OTG_STATE_B_PERIPHERAL 接着挂起
(2) OTG_STATE_A_HOST: 调用者必须挂起,接着清A_BUSREQ标志
usb_gadget_vbus_connect(isp->otg.gadget)
OTG_CTRL_REQ |= OTG_A_SETB_HNPEN
(3) OTG_STATE_A_PERIPHERAL: 已经由B-HOST挂起初始化
}

void isp1301_work(struct work_struct *work)
{//由isp1301_timer定时调用
(1) WORK_UPDATE_ISP: 将state从otg engine传到isp1301
otg_update_isp(isp);
(2) WORK_UPDATE_ISP: 将state从isp1301传到otg engine
otg_update_OTG(isp);
(3) WORK_HOST_RESUME: 根距isp->otg.state判断
host_resume(isp)
}

发表评论