[uClinux-dev] The problem of OHCI driver for Winbond W90N740.
Darwin Chen
pstudio at 163.com
Mon Oct 1 10:53:43 EDT 2007
Hi, All,
The following is the OHCI driver programmed by myself. There are some problems:
1. the kernel will hanged up when the usb device connected to the host with message:
w90n740 start USB host controller.
w90n740-ohci w90n740-ohci: W90N74X OHCI
w90n740-ohci w90n740-ohci: new USB bus registered, assigned bus number 1
w90n740-ohci w90n740-ohci: irq 9, io mem 0xfff05000
usb usb1: Product: W90N74X OHCI
usb usb1: Manufacturer: Linux 2.6.22-uc0 ohci_hcd
usb usb1: SerialNumber: W90N74x
usb usb1: configuration #1 chosen from 1 choice
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 2 ports detected
Initializing USB Mass Storage driver...
usb 1-1: new full speed USB device using w90n740-ohci and address 2
2. The kernel can be booted normally if the usb device isn't connected to the host, the device can be mounted after the device is connected to host sometimes.
3. If the OHCI_VERBOSE_DEBUG is undefined, the kernel always be hanged up when the device is connected to the host.
Could you help me to fix these problems? Or tell me how to debug step by step?
Darwin Chen <PStudio at 163.com>
/*
* linux/drivers/usb/host/ohci-w90n740.c
* OHCI HCD (Host Controller Driver) for Winbond w90n740.
*
* (C) Copyright 2006-2008 Chen Weiwen <PStudio at 163.com>
*
* USB Bus Glue for Winbond W90N740
*
* Written by Chen Weiwen <PStudio at 163.com>
* Based on fragments of Samsung S3c24xx Christopher Hoover <ch at hpl.hp.com>.
*
* Modified for W90N740 from ohci-s3c2410.c
* by Chen Weiwen, <PStudio at 163.com>
* Copyright (C) 2006-2008 Chen Weiwen
*
* This file is licenced under the GPL.
*/
#include <linux/platform_device.h>
#include <asm/hardware.h>
#include <asm/arch/usb-control.h>
#define valid_port(idx) ((idx) == 1 || (idx) == 2)
#define HCRootHubStatus 0xFFF05050
void enable_usb_clock()
{
unsigned long nClockSel;
nClockSel = CSR_READ(CLKSEL);
nClockSel |= 0x00000080;
CSR_WRITE(CLKSEL, nClockSel);
}
void disable_usb_clock()
{
unsigned long nClockSel;
nClockSel = CSR_READ(CLKSEL);
nClockSel &= 0xFFFFFF7F;
CSR_WRITE(CLKSEL, nClockSel);
}
static void w90n740_usb_power_control(int port, int to)
{
int ps = CSR_READ(HCRootHubStatus);
if(to)
CSR_WRITE(HCRootHubStatus, ps|0x10000);
else
CSR_WRITE(HCRootHubStatus, ps|0x01);
}
static void w90n740_usb_enable_oc(struct w90n740_hcd_info * info, int on)
{
}
static struct w90n740_hcd_info *to_w90n740_info(struct usb_hcd *hcd)
{
return hcd->self.controller->platform_data;
}
static void w90n740_start_hc(struct platform_device *dev, struct usb_hcd *hcd)
{
struct w90n740_hcd_info *info = dev->dev.platform_data;
printk("w90n740 start USB host controller.\n");
enable_usb_clock();
mdelay(2); //let the bus clock stabilise
}
static void w90n740_stop_hc(struct platform_device *dev)
{
struct w90n740_hcd_info *info = dev->dev.platform_data;
if (info != NULL) {
info->report_oc = NULL;
info->hcd = NULL;
if (info->enable_oc != NULL) {
(info->enable_oc)(info, 0);
}
}
disable_usb_clock();
}
/* w90n740_usb_set_power
*
* configure the power on a port, by calling the platform device
* routine registered with the platform device
*/
static void w90n740_usb_set_power(struct w90n740_hcd_info *info,
int port, int to)
{
int ps = CSR_READ(0xfff05048);
if (info == NULL)
return;
printk(KERN_DEBUG "w90n740_usb_set_power(%08x, %d, %d)\n", info, port, to);
CSR_WRITE(0xfff05048,ps|0x200);
ps = CSR_READ(HCRootHubStatus);
if(to)
CSR_WRITE(HCRootHubStatus, ps|0x10000);
else
CSR_WRITE(HCRootHubStatus, ps|0x01);
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/*
* usb_hcd_w90n740_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_w90n740_remove (struct usb_hcd *hcd, struct platform_device *dev)
{
usb_remove_hcd(hcd);
w90n740_stop_hc(dev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
/**
* usb_hcd_w90n740_probe - initialize W90N740-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
*/
static int usb_hcd_w90n740_probe (const struct hc_driver *driver,
struct platform_device *dev)
{
struct usb_hcd *hcd = NULL;
int retval;
struct w90n740_hcd_info * info = (struct w90n740_hcd_info *)dev->dev.platform_data;
w90n740_usb_set_power(dev->dev.platform_data, 1, 1);
w90n740_usb_set_power(dev->dev.platform_data, 2, 1);
hcd = usb_create_hcd(driver, &dev->dev, "W90N74x");
if (hcd == NULL)
return -ENOMEM;
hcd->rsrc_start = dev->resource[0].start;
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");
retval = -EBUSY;
goto err_put;
}
w90n740_start_hc(dev, hcd);
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
dev_err(&dev->dev, "ioremap failed\n");
retval = -ENOMEM;
goto err_ioremap;
}
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd,INT_USBINT0 , IRQF_DISABLED);
if (retval != 0)
goto err_ioremap;
return 0;
err_ioremap:
w90n740_stop_hc(dev);
iounmap(hcd->regs);
err_mem:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_put:
usb_put_hcd(hcd);
return retval;
}
static int ohci_w90n740_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_w90n740_hc_driver = {
.description = hcd_name,
.product_desc = "W90N74X OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_w90n740_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_hub_status_data,
.hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#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_w90n740_drv_probe(struct platform_device *pdev)
{
return usb_hcd_w90n740_probe(&ohci_w90n740_hc_driver, pdev);
}
static int ohci_hcd_w90n740_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_hcd_w90n740_remove(hcd, pdev);
return 0;
}
static struct platform_driver ohci_hcd_w90n740_driver = {
.probe = ohci_hcd_w90n740_drv_probe,
.remove = ohci_hcd_w90n740_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.owner = THIS_MODULE,
.name = "w90n740-ohci",
},
};
More information about the uClinux-dev
mailing list