s3c2410 usb主机驱动解析ohci-s3c2410.c

以下是平台设备源码

#include <linux/gfp.h>

#include <linux/kernel.h>

#include <linux/string.h>

#include <linux/platform_device.h>

#include <mach/irqs.h>

#include <mach/map.h>

#include <plat/devs.h>

#include <plat/usb-control.h>

/*usb地址和中断资源

static struct resource s3c_usb_resource[] = {

[0] = {

.start = S3C_PA_USBHOST,  //基址开始地址

.end   = S3C_PA_USBHOST + 0x100 - 1,//基址结束地址

.flags = IORESOURCE_MEM,//资源内存

},

[1] = {

.start = IRQ_USBH,

.end   = IRQ_USBH,//中断地址

.flags = IORESOURCE_IRQ,

}

};

static u64 s3c_device_usb_dmamask = 0xffffffffUL;//指明掩码

/*填充ohci平台设备*/

struct platform_device s3c_device_ohci = {

.name   = "s3c2410-ohci",

.id   = -1,

.num_resources   = ARRAY_SIZE(s3c_usb_resource),

.resource   = s3c_usb_resource,

.dev              = {

.dma_mask = &s3c_device_usb_dmamask,

.coherent_dma_mask = 0xffffffffUL

}

};

EXPORT_SYMBOL(s3c_device_ohci);

/*建立平台数据,在后面的主机驱动程序中会被调用*/

void __init s3c_ohci_set_platdata(struct s3c2410_hcd_info *info)

{

struct s3c2410_hcd_info *npd;

npd = kmemdup(info, sizeof(struct s3c2410_hcd_info), GFP_KERNEL);

if (!npd)

printk(KERN_ERR "%s: no memory for platform data/n", __func__);

s3c_device_ohci.dev.platform_data = npd//刚申请的内存保存到平台设备数据中

}

#include <linux/platform_device.h>

#include <linux/clk.h>

#include <plat/usb-control.h>

/*指定2个端口*/

#define valid_port(idx) ((idx) == 1 || (idx) == 2)

/* 时钟与hcd发生联系 */

static struct clk *clk;

static struct clk *usb_clk;

static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc);

static struct s3c2410_hcd_info *to_s3c2410_info(struct usb_hcd *hcd)

{

return hcd->self.controller->platform_data;

}

/*传输函数*/

static void s3c2410_start_hc(struct platform_device *dev, struct usb_hcd *hcd)

{

struct s3c2410_hcd_info *info = dev->dev.platform_data;//得到平台数据

dev_dbg(&dev->dev, "s3c2410_start_hc:/n");//打印消息

clk_enable(usb_clk);//使能总线时钟

mdelay(2); /* let the bus clock stabilise */

clk_enable(clk);//使能时钟

if (info != NULL) {

info->hcd = hcd;

info->report_oc = s3c2410_hcd_oc;填充s3c2410_hcd_info

if (info->enable_oc != NULL) {

(info->enable_oc)(info, 1);

}

}

}

static void s3c2410_stop_hc(struct platform_device *dev)

{

struct s3c2410_hcd_info *info = dev->dev.platform_data;

dev_dbg(&dev->dev, "s3c2410_stop_hc:/n");

if (info != NULL) {

info->report_oc = NULL;

info->hcd = NULL;

if (info->enable_oc != NULL) {

(info->enable_oc)(info, 0);

}

}

clk_disable(clk);

clk_disable(usb_clk);

}

/* ohci_s3c2410_hub_status_data

 *

更新hub状态

*/

static int

ohci_s3c2410_hub_status_data (struct usb_hcd *hcd, char *buf)

{

struct s3c2410_hcd_info *info = to_s3c2410_info(hcd);

struct s3c2410_hcd_port *port;

int orig;

int portno;

orig  = ohci_hub_status_data (hcd, buf);

if (info == NULL)

return orig;

port = &info->port[0];

/* mark any changed port as changed */

for (portno = 0; portno < 2; port++, portno++) {

if (port->oc_changed == 1 &&

    port->flags & S3C_HCDFLG_USED) {

dev_dbg(hcd->self.controller,

"oc change on port %d/n", portno);

if (orig < 1)

orig = 1;

buf[0] |= 1<<(portno+1);

}

}

return orig;

}

/* s3c2410_usb_set_power

 *

 *配置端口电源

*/

static void s3c2410_usb_set_power(struct s3c2410_hcd_info *info,

  int port, int to)

{

if (info == NULL)

return;

if (info->power_control != NULL) {

info->port[port-1].power = to;

(info->power_control)(port-1, to);

}

}

/* ohci_s3c2410_hub_control

 *

 * look at control requests to the hub, and see if we need

 * to take any action or over-ride the results from the

 * request.

*/

static int ohci_s3c2410_hub_control (

struct usb_hcd *hcd,

u16 typeReq,

u16 wValue,

u16 wIndex,

char *buf,

u16 wLength)

{

struct s3c2410_hcd_info *info = to_s3c2410_info(hcd);

struct usb_hub_descriptor *desc;

int ret = -EINVAL;

u32 *data = (u32 *)buf;

dev_dbg(hcd->self.controller,

"s3c2410_hub_control(%p,0x%04x,0x%04x,0x%04x,%p,%04x)/n",

hcd, typeReq, wValue, wIndex, buf, wLength);

/* if we are only an humble host without any special capabilities

 * process the request straight away and exit */

if (info == NULL) {

ret = ohci_hub_control(hcd, typeReq, wValue,

       wIndex, buf, wLength);

goto out;

}

/* check the request to see if it needs handling */

switch (typeReq) {

case SetPortFeature:

if (wValue == USB_PORT_FEAT_POWER) {

dev_dbg(hcd->self.controller, "SetPortFeat: POWER/n");

s3c2410_usb_set_power(info, wIndex, 1);

goto out;

}

break;

case ClearPortFeature:

switch (wValue) {

case USB_PORT_FEAT_C_OVER_CURRENT:

dev_dbg(hcd->self.controller,

"ClearPortFeature: C_OVER_CURRENT/n");

if (valid_port(wIndex)) {

info->port[wIndex-1].oc_changed = 0;

info->port[wIndex-1].oc_status = 0;

}

goto out;

case USB_PORT_FEAT_OVER_CURRENT:

dev_dbg(hcd->self.controller,

"ClearPortFeature: OVER_CURRENT/n");

if (valid_port(wIndex)) {

info->port[wIndex-1].oc_status = 0;

}

goto out;

case USB_PORT_FEAT_POWER:

dev_dbg(hcd->self.controller,

"ClearPortFeature: POWER/n");

if (valid_port(wIndex)) {

s3c2410_usb_set_power(info, wIndex, 0);

return 0;

}

}

break;

}

ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);

if (ret)

goto out;

switch (typeReq) {

case GetHubDescriptor:

/* update the hub's descriptor */

desc = (struct usb_hub_descriptor *)buf;

if (info->power_control == NULL)

return ret;

dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x/n",

desc->wHubCharacteristics);

/* remove the old configurations for power-switching, and

 * over-current protection, and insert our new configuration

 */

desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM);

desc->wHubCharacteristics |= cpu_to_le16(0x0001);

if (info->enable_oc) {

desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM);

desc->wHubCharacteristics |=  cpu_to_le16(0x0008|0x0001);

}

dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x/n",

desc->wHubCharacteristics);

return ret;

case GetPortStatus:

/* check port status */

dev_dbg(hcd->self.controller, "GetPortStatus(%d)/n", wIndex);

if (valid_port(wIndex)) {

if (info->port[wIndex-1].oc_changed) {

*data |= cpu_to_le32(RH_PS_OCIC);

}

if (info->port[wIndex-1].oc_status) {

*data |= cpu_to_le32(RH_PS_POCI);

}

}

}

 out:

return ret;

}

/* s3c2410_hcd_oc

 *

 * handle an over-current report

*/

static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc)

{

struct s3c2410_hcd_port *port;

struct usb_hcd *hcd;

unsigned long flags;

int portno;

if (info == NULL)

return;

port = &info->port[0];

hcd = info->hcd;

local_irq_save(flags);

for (portno = 0; portno < 2; port++, portno++) {

if (port_oc & (1<<portno) &&

    port->flags & S3C_HCDFLG_USED) {

port->oc_status = 1;

port->oc_changed = 1;

/* ok, once over-current is detected,

   the port needs to be powered down */

s3c2410_usb_set_power(info, portno+1, 0);

}

}

local_irq_restore(flags);

}

/* may be called without controller electrically present */

/* may be called with controller, bus, and devices active */

/*

 * usb_hcd_s3c2410_remove - shutdown processing for HCD

 * @dev: USB Host Controller being removed

 * Context: !in_interrupt()

 *

 * Reverses the effect of usb_hcd_3c2410_probe(), first invoking

 * the HCD's stop() method.  It is always called from a thread

 * context, normally "rmmod", "apmd", or something similar.

 *

*/

static void

usb_hcd_s3c2410_remove (struct usb_hcd *hcd, struct platform_device *dev)

{

usb_remove_hcd(hcd);

s3c2410_stop_hc(dev);

iounmap(hcd->regs);

release_mem_region(hcd->rsrc_start, hcd->rsrc_len);

usb_put_hcd(hcd);

}

/*使能时钟,寄存器映射

 */

static int usb_hcd_s3c2410_probe (const struct hc_driver *driver,

  struct platform_device *dev)

{

struct usb_hcd *hcd = NULL;

int retval;

s3c2410_usb_set_power(dev->dev.platform_data, 1, 1);

s3c2410_usb_set_power(dev->dev.platform_data, 2, 1);

hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx");//申请建立hcd

if (hcd == NULL)

return -ENOMEM;

hcd->rsrc_start = dev->resource[0].start;//hcd基址内存地址的开始

hcd->rsrc_len   = dev->resource[0].end - dev->resource[0].start + 1;基址内存地址长度

if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {

dev_err(&dev->dev, "request_mem_region failed/n");

retval = -EBUSY;

goto err_put;

}//如果刚才指定的I/O内存申请失败则dev_err

clk = clk_get(&dev->dev, "usb-host");//使能时钟

if (IS_ERR(clk)) {

dev_err(&dev->dev, "cannot get usb-host clock/n");

retval = -ENOENT;

goto err_mem;

}

usb_clk = clk_get(&dev->dev, "usb-bus-host");//得到时钟

if (IS_ERR(usb_clk)) {

dev_err(&dev->dev, "cannot get usb-bus-host clock/n");

retval = -ENOENT;

goto err_clk;

}

s3c2410_start_hc(dev, hcd);//调用start传输开始

hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);//刚才申请的内存地址利用ioremap转化为虚拟地址

if (!hcd->regs) {

dev_err(&dev->dev, "ioremap failed/n");

retval = -ENOMEM;

goto err_ioremap;

}

ohci_hcd_init(hcd_to_ohci(hcd));//初始化hcd

retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);//成功添加hcd,指定中断方式

if (retval != 0)

goto err_ioremap;

return 0;

 err_ioremap:

s3c2410_stop_hc(dev);

iounmap(hcd->regs);

clk_put(usb_clk);

 err_clk:

clk_put(clk);

 err_mem:

release_mem_region(hcd->rsrc_start, hcd->rsrc_len);

 err_put:

usb_put_hcd(hcd);

return retval;

}

/*-------------------------------------------------------------------------*/

static int

ohci_s3c2410_start (struct usb_hcd *hcd)

{

struct ohci_hcd *ohci = hcd_to_ohci (hcd);

int ret;

if ((ret = ohci_init(ohci)) < 0)

return ret;

if ((ret = ohci_run (ohci)) < 0) {

err ("can't start %s", hcd->self.bus_name);

ohci_stop (hcd);

return ret;

}

return 0;

}

static const struct hc_driver ohci_s3c2410_hc_driver = {

.description = hcd_name,

.product_desc = "S3C24XX OHCI",

.hcd_priv_size = sizeof(struct ohci_hcd),

/*

 * generic hardware linkage

 */

.irq = ohci_irq,

.flags = HCD_USB11 | HCD_MEMORY,

/*

 * basic lifecycle operations

 */

.start = ohci_s3c2410_start,

.stop = ohci_stop,

.shutdown = ohci_shutdown,

/*

 * managing i/o requests and associated device resources

 */

.urb_enqueue = ohci_urb_enqueue,

.urb_dequeue = ohci_urb_dequeue,

.endpoint_disable = ohci_endpoint_disable,

/*

 * scheduling support

 */

.get_frame_number = ohci_get_frame,

/*

 * root hub support

 */

.hub_status_data = ohci_s3c2410_hub_status_data,

.hub_control = ohci_s3c2410_hub_control,

#ifdef CONFIG_PM

.bus_suspend = ohci_bus_suspend,

.bus_resume = ohci_bus_resume,

#endif

.start_port_reset = ohci_start_port_reset,

};

/* device driver */

static int ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev)

{

return usb_hcd_s3c2410_probe(&ohci_s3c2410_hc_driver, pdev);

}

static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev)

{

struct usb_hcd *hcd = platform_get_drvdata(pdev);

usb_hcd_s3c2410_remove(hcd, pdev);

return 0;

}

/*平台驱动文件操作*/

static struct platform_driver ohci_hcd_s3c2410_driver = {

.probe = ohci_hcd_s3c2410_drv_probe,

.remove = ohci_hcd_s3c2410_drv_remove,

.shutdown = usb_hcd_platform_shutdown,

/*.suspend = ohci_hcd_s3c2410_drv_suspend, */

/*.resume = ohci_hcd_s3c2410_drv_resume, */

.driver = {

.owner = THIS_MODULE,

.name = "s3c2410-ohci",

},

};

MODULE_ALIAS("platform:s3c2410-ohci");

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值