上回介绍了OTG功能的 OTG部分驱动,本片分析OTG功能的从设备部分驱动。从设备的注册过程和OTG的一样,首先注册设备。
流程是:
1.
定义platform_device结构。
2.
定义platform_device下的struct
resource设备资源结构
3.
定义
platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)
4.
调用platform_device_register将platform_device结构
注
册上面4个过程调用结束后,设备的信息就被注册到系统中,等待驱动的使用 下面是按照上面顺序贴出代码
定义platform_device结构
static
struct
platform_device
__maybe_unused dr_udc_device =
{
.
name =
"fsl-usb2-udc"
,
.
id =
-
1,
.
dev
=
{
.
release =
dr_udc_release,
.
dma_mask =
&
dr_udc_dmamask,
.
coherent_dma_mask =
0xffffffff,
}
,
.
resource
=
otg_resources,
.
num_resources =
ARRAY_SIZE(
otg_resources)
,
}
;
我们可以看到resource和OTG的resource一样,使用的都是OTG那
部分资源
定义platform_device下的struct resource设备资源结构
static
struct
resource otg_resources[
]
=
{
[
0]
=
{
.
start
=
(
u32)
(
USB_OTGREGS_BASE)
,
.
end
=
(
u32)
(
USB_OTGREGS_BASE +
0x1ff)
,
.
flags =
IORESOURCE_MEM,
}
,
[
1]
=
{
.
start =
MXC_INT_USB_OTG,
.
flags =
IORESOURCE_IRQ,
}
,
}
;
定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)
static
struct
fsl_usb2_platform_data
__maybe_unused dr_utmi_config =
{
.
name =
"DR"
,
.
platform_init =
usbotg_init,
.
platform_uninit =
usbotg_uninit,
.
phy_mode
=
FSL_USB2_PHY_UTMI_WIDE,
.
power_budget =
500,
/* via RT9706 */
.
gpio_usb_active =
gpio_usbotg_utmi_active,
.
gpio_usb_inactive =
gpio_usbotg_utmi_inactive,
.
transceiver
=
"utmi"
,
.
wake_up_enable =
_wake_up_enable,
}
;
/*将设备注册
进系统*/
static
inline
void
dr_register_udc(
void
)
{
PDATA-
>
operating_mode =
DR_UDC_MODE; //在OTG功能设备注册的时候这里的模式是“FSL_USB2_DR_OTG
”,
时还不知道为什么不开始就直接把模式写成这个而是后该,现在知道了 因为主从和OTG都在用一个资源,所以谁用就的把模式该了。
/*#define PDATA
(&dr_utmi_config)PDATA指的就是上面的结构
*/
dr_udc_device.
dev.
platform_data =
PDATA;
if
(
platform_device_register(
&
dr_udc_device)
)
printk(
KERN_ERR "usb: can't register DR gadget/n"
)
;
else
printk(
KERN_INFO "usb: DR gadget (%s) registered/n"
,
PDATA-
>
transceiver)
;
}
上面就完成了设备的注册。
下面看看驱动和设备的链接。
static struct
platform_driver udc_driver =
{
.
remove =
__exit_p(
fsl_udc_remove)
,
/*
these suspend and
resume are not
usb suspend and
resume *
/
.
suspend =
fsl_udc_suspend, //切换到主设备后的处理过程
.
resume =
fsl_udc_resume, //切换到从设备的处理过程
.
probe =
fsl_udc_probe,
.
driver =
{
.
name
=
driver_name,
//就是fsl-usb2-udc,用于匹配设备的
.
owner =
THIS_MODULE,
},
}
将驱动注册进系统,如果发现了设备就调用Probe函数,因为我们上面注册了设备所以必然调用Probe
static
int
__init udc_init(
void
)
{
printk(
KERN_INFO "%s (%s)/n"
,
driver_desc,
DRIVER_VERSION)
;
return
platform_driver_register(
&
udc_driver)
;//驱动注册进系统
}
发现设备后调用PROBE函数
static
int
__init fsl_udc_probe(
struct
platform_device *
pdev)
{
struct
resource *
res;
struct
fsl_usb2_platform_data *
pdata =
pdev-
>
dev.
platform_data;
int
ret =
-
ENODEV;
unsigned
int
i;
u32 dccparams,
portsc;
if
(
strcmp
(
pdev-
>
name,
driver_name)
)
{
VDBG(
"Wrong device/n"
)
;
return
-
ENODEV;
}
/********************************************************/
static struct fsl_udc *udc_controller;
全局变量定义
/*******************************************************/
udc_controller
=
kzalloc(
sizeof
(
struct
fsl_udc)
,
GFP_KERNEL)
;
if
(
udc_controller
=
=
NULL
)
{
ERR(
"malloc udc failed/n"
)
;
return
-
ENOMEM;
}
udc_controller-
>
pdata =
pdata; //私有变量赋值
#
ifdef
CONFIG_USB_OTG
/* Memory and interrupt resources will be passed from OTG */
udc_controller-
>
transceiver =
otg_get_transceiver(
)
;//在OTG功能中已经通过otg_set_transceiver
设置了transceiver结构,这里面就可以GET
if
(
!
udc_controller-
>
transceiver)
{
printk(
KERN_ERR "Can't find OTG driver!/n"
)
;
ret
=
-
ENODEV;
goto
err1a;
}
res
=
otg_get_resources(
)
; //获得资源
if
(
!
res)
{
DBG(
"resource not registered!/n"
)
;
return
-
ENODEV;
}
#
else
if
(
(
pdev-
>
dev.
parent)
&
&
(
to_platform_device(
pdev-
>
dev.
parent)
-
>
resource)
)
{
pdev-
>
resource =
to_platform_device(
pdev-
>
dev.
parent)
-
>
resource;
pdev-
>
num_resources
=
to_platform_device(
pdev-
>
dev.
parent)
-
>
num_resources;
}
res =
platform_get_resource(
pdev,
IORESOURCE_MEM,
0)
;
if
(
!
res)
{
ret =
-
ENXIO;
goto
err1a;
}
if
(
!
request_mem_region(
res-
>
start,
resource_size(
res)
,
driver_name)
)
{
ERR(
"request mem region for %s failed /n"
,
pdev-
>
name)
;
ret
=
-
EBUSY;
goto
err1a;
}
#
endif
/*将物理地址映射为驱动可以访问的虚拟地址*/
dr_regs =
ioremap(
res-
>
start,
resource_size(
res)
)
;
if
(
!
dr_regs)
{
ret =
-
ENOMEM;
goto
err1;
}
pdata-
>
regs =
(
void
*
)
dr_regs; //私有数据接收映射地址
/*
* do platform specific init: check the clock,
grab/config pins, etc.
*/
/*调用私有数据的初始化函数,关于初始化函数下面分析*/
if
(
pdata-
>
platform_init &
&
pdata-
>
platform_init(
pdev)
)
{
ret =
-
ENODEV;
goto
err2a;
}
if
(
pdata-
>
have_sysif_regs)
usb_sys_regs =
(
struct
usb_sys_interface *
)
(
(
u32)
dr_regs +
USB_DR_SYS_OFFSET)
;
/*
Read Device Controller Capability Parameters register */
dccparams
=
fsl_readl(
&
dr_regs-
>
dccparams)
;
if
(
!
(
dccparams &
DCCPARAMS_DC)
)
{
ERR(
"This SOC doesn't support device role/n"
)
;
ret =
-
ENODEV;
goto
err2;
}
/* Get max device endpoints */
/* DEN is bidirectional ep number,
max_ep doubles the number */
udc_controller-
>
max_ep =
(
dccparams &
DCCPARAMS_DEN_MASK)
*
2;
#
ifdef
CONFIG_USB_OTG
res+
+
;
udc_controller-
>
irq =
res-
>
start;
#
else
udc_controller-
>
irq =
platform_get_irq(
pdev,
0)
;
#
endif
if
(
!
udc_controller-
>
irq)
{
ret
=
-
ENODEV;
goto
err2;
}
/*注册中断,该中断和OTG的中断共享一个*/
ret =
request_irq(
udc_controller-
>
irq,
fsl_udc_irq,
IRQF_SHARED,
driver_name,
udc_controller)
;
if
(
ret !
=
0)
{
ERR(
"cannot request irq %d err %d /n"
,
udc_controller-
>
irq,
ret)
;
goto
err2;
}
/* Initialize the udc structure including QH member
and other member */
/*对一些资源进行空间开辟等初始化操作*/
if
(
struct_udc_setup(
udc_controller,
pdev)
)
{
ERR(
"Can't initialize udc data structure/n"
)
;
ret
=
-
ENOMEM;
goto
err3;
}
if
(
!
udc_controller-
>
transceiver)
{
/* initialize usb hw reg except for
regs for EP,
* leave usbintr reg untouched */
dr_controller_setup(
udc_controller)
;
}
/* Setup gadget structure */
udc_controller-
>
gadget.
ops
=
&
fsl_gadget_ops;
udc_controller-
>
gadget.
is_dualspeed =
1;
udc_controller-
>
gadget.
ep0
=
&
udc_controller-
>
eps[
0]
.
ep;
INIT_LIST_HEAD(
&
udc_controller-
>
gadget.
ep_list)
;
udc_controller-
>
gadget.
speed
=
USB_SPEED_UNKNOWN;
udc_controller-
>
gadget.
name
=
driver_name;
/* Setup gadget.dev and register with kernel */
dev_set_name(
&
udc_controller-
>
gadget.
dev,
"gadget"
)
;
udc_controller-
>
gadget.
dev.
release =
fsl_udc_release;
udc_controller-
>
gadget.
dev.
parent =
&
pdev-
>
dev;
ret
=
device_register(
&
udc_controller-
>
gadget.
dev)
;
if
(
ret
<
0)
goto
err3;
if
(
udc_controller-
>
transceiver)
{
udc_controller-
>
gadget.
is_otg =
1;
/* now didn't support lpm in OTG
mode*/
device_set_wakeup_capable(
&
pdev-
>
dev,
0)
;
}
/* setup QH and epctrl for ep0 */
ep0_setup(
udc_controller)
;
/*
setup udc->eps[] for ep0 */
struct_ep_setup(
udc_controller,
0,
"ep0"
,
0)
;
/* for ep0: the desc defined here;
* for
other eps, gadget layer called ep_enable with defined desc
*/
udc_controller-
>
eps[
0]
.
desc =
&
fsl_ep0_desc;
udc_controller-
>
eps[
0]
.
ep.
maxpacket =
USB_MAX_CTRL_PAYLOAD;
/* setup the udc->eps[] for non-control endpoints
and link
* to gadget.ep_list */
for
(
i =
1;
i <
(
int
)
(
udc_controller-
>
max_ep /
2)
;
i+
+
)
{
char
name[
14]
;
sprintf
(
name,
"ep%dout"
,
i)
;
struct_ep_setup(
udc_controller,
i *
2,
name,
1)
;
sprintf
(
name,
"ep%din"
,
i)
;
struct_ep_setup(
udc_controller,
i *
2 +
1,
name,
1)
;
}
/* use dma_pool for TD management */
udc_controller-
>
td_pool =
dma_pool_create(
"udc_td"
,
&
pdev-
>
dev,
sizeof
(
struct
ep_td_struct)
,
DTD_ALIGNMENT,
UDC_DMA_BOUNDARY)
;
if
(
udc_controller-
>
td_pool =
=
NULL
)
{
ret =
-
ENOMEM;
goto
err4;
}
if
(
g_iram_size)
{
for
(
i
=
0;
i <
IRAM_PPH_NTD;
i+
+
)
{
udc_controller-
>
iram_buffer[
i]
=
USB_IRAM_BASE_ADDR +
i *
g_iram_size;
udc_controller-
>
iram_buffer_v[
i]
=
IO_ADDRESS(
udc_controller-
>
iram_buffer[
i]
)
;
}
}
#
ifdef
POSTPONE_FREE_LAST_DTD
last_free_td =
NULL
;
#
endif
/*
disable all INTR */
fsl_writel(
0,
&
dr_regs-
>
usbintr)
;
dr_wake_up_enable(
udc_controller,
false
)
;
udc_controller-
>
stopped =
1;
portsc =
fsl_readl(
&
dr_regs-
>
portsc1)
;
portsc |
=
PORTSCX_PHY_LOW_POWER_SPD;
fsl_writel(
portsc,
&
dr_regs-
>
portsc1)
;
if
(
udc_controller-
>
pdata-
>
usb_clock_for_pm)
udc_controller-
>
pdata-
>
usb_clock_for_pm(
false
)
;
create_proc_file(
)
;
return
0;
err4:
device_unregister(
&
udc_controller-
>
gadget.
dev)
;
err3:
free_irq(
udc_controller-
>
irq,
udc_controller)
;
err2:
if
(
pdata-
>
platform_uninit)
pdata-
>
platform_uninit(
pdata)
;
err2a:
iounmap(
(
u8 __iomem *
)
dr_regs)
;
err1:
if
(
!
udc_controller-
>
transceiver)
release_mem_region(
res-
>
start,
resource_size(
res)
)
;
err1a:
kfree(
udc_controller)
;
udc_controller =
NULL
;
return
ret;
}
当设备使用主设备时 DEVICE的处理
static
int
udc_suspend(
struct
fsl_udc *
udc)
{
u32 mode,
usbcmd;
/* open clock for register access */
if
(
udc_controller-
>
pdata-
>
usb_clock_for_pm)
udc_controller-
>
pdata-
>
usb_clock_for_pm(
true
)
;
mode =
fsl_readl(
&
dr_regs-
>
usbmode)
&
USB_MODE_CTRL_MODE_MASK;
usbcmd =
fsl_readl(
&
dr_regs-
>
usbcmd)
;
pr_debug(
"%s(): mode 0x%x stopped %d/n"
,
__func__
,
mode,
udc-
>
stopped)
;
/*
* If the controller is
already stopped, then this must be a
* PM suspend. Remember this
fact, so that we will leave the
* controller stopped at PM
resume time.
*/
if
(
udc-
>
stopped)
{
pr_debug(
"gadget already stopped, leaving early/n"
)
;
udc-
>
already_stopped =
1;
goto
out;
}
if
(
mode
!
=
USB_MODE_CTRL_MODE_DEVICE)
{
pr_debug(
"gadget not in device mode, leaving early/n"
)
;
goto
out;
}
udc-
>
stopped =
1;
/* if the suspend is not for switch to host in otg
mode */
if
(
(
!
(
udc-
>
gadget.
is_otg)
)
|
|
(
fsl_readl(
&
dr_regs-
>
otgsc)
&
OTGSC_STS_USB_ID)
)
{
dr_wake_up_enable(
udc,
true
)
;
dr_phy_low_power_mode(
udc,
true
)
;
}
/* stop the controller */
usbcmd
=
fsl_readl(
&
dr_regs-
>
usbcmd)
&
~
USB_CMD_RUN_STOP;
fsl_writel(
usbcmd,
&
dr_regs-
>
usbcmd)
;
printk(
KERN_INFO "USB Gadget suspended/n"
)
;
out:
if
(
udc_controller-
>
pdata-
>
usb_clock_for_pm)
udc_controller-
>
pdata-
>
usb_clock_for_pm(
false
)
;
return
0;
}
当切换到从设备时调用
static
int
fsl_udc_resume(
struct
platform_device *
pdev)
{
pr_debug(
"%s(): stopped %d already_stopped %d/n"
,
__func__
,
udc_controller-
>
stopped,
udc_controller-
>
already_stopped)
;
/*
* If the controller was stopped at suspend time, then
* don't
resume it now.
*/
if
(
udc_controller-
>
already_stopped)
{
udc_controller-
>
already_stopped =
0;
pr_debug(
"gadget was already stopped, leaving early/n"
)
;
return
0;
}
/* Enable DR irq reg and set controller Run */
if
(
udc_controller-
>
stopped)
{
dr_wake_up_enable(
udc_controller,
false
)
;
dr_phy_low_power_mode(
udc_controller,
false
)
;
mdelay(
1)
;
dr_controller_setup(
udc_controller)
;
dr_controller_run(
udc_controller)
;
}
udc_controller-
>
usb_state
=
USB_STATE_ATTACHED;
udc_controller-
>
ep0_dir =
0;
printk(
KERN_INFO "USB Gadget resumed/n"
)
;
return
0;
}
上面的两个函数就是在上一篇OTG 一中介绍的
gadget_pdrv-
>
resume
(
gadget_pdev
)
;
gadget_pdrv
-
>
suspend
(
gadget_pdev
,
otg_suspend_state
)
;
这两处就是只想的这个函数。而把
前面的函数和上面这两个指针链接的地方就是在OTG中的
fsl_otg_start_gadget
函数。
从
上面我们可以看到以下几点:
1.OTG
功能的从设备使用的资源和私有数据与OTG设备的一致,(主设备也是一致)
2.
从设备主要为OTG功能提供fsl_udc_resume和udc_suspend两个函数。