musb_core.c 是usb_general.c 調用進musb子產品的核心函數,
裡面有usb,musb,dma,platform子產品等内容。
1. musb_init():
/* make us init after usbcore and i2c (transceivers, regulators, etc)
* and before usb gadget and host-side drivers start to register
*/
fs_initcall(musb_init);
static int __init musb_init(void)
{
#ifdef CONFIG_USB_MUSB_HDRC_HCD
if (usb_disabled())
return 0;
#endif
pr_info("%s: version " MUSB_VERSION ", "
#ifdef CONFIG_MUSB_PIO_ONLY //這些宏由核心配置選項中來
"pio"
#elif defined(CONFIG_USB_TI_CPPI_DMA)
"cppi-dma"
#elif defined(CONFIG_USB_INVENTRA_DMA)
"musb-dma"
#elif defined(CONFIG_USB_TUSB_OMAP_DMA)
"tusb-omap-dma"
#elif defined(CONFIG_USB_CARTESIO_DMA)
"pl080-dma"
#else
"?dma?"
#endif
", "
#ifdef CONFIG_USB_MUSB_OTG
"otg (peripheral+host)"
#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
"peripheral"
#elif defined(CONFIG_USB_MUSB_HDRC_HCD)
"host"
#endif
", debug=%d\n",
musb_driver_name, musb_debug);
return platform_driver_probe(&musb_driver, musb_probe); //這裡調用了platform 平台函數,注冊進platform類型隊列中。
}
musb_driver:
static struct platform_driver musb_driver = {
.driver = {
.name = (char *)musb_driver_name,
.bus = &platform_bus_type, //platform bus類型
.owner = THIS_MODULE,
.pm = MUSB_DEV_PM_OPS, //PM OPS, suspend+resume
},
.remove = __exit_p(musb_remove),
.shutdown = musb_shutdown,
};
1.1 musb_driver_name 定義:
#define MUSB_DRIVER_NAME "musb_hdrc"
const char musb_driver_name[] = MUSB_DRIVER_NAME;
1.2 MUSB_DEV_PM_OPS 定義:
#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops)
#else
#define MUSB_DEV_PM_OPS NULL
#endif
static const struct dev_pm_ops musb_dev_pm_ops = {
.suspend = musb_suspend,
.resume = musb_resume_noirq,
};
static int musb_suspend(struct device *dev)
static int musb_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
unsigned long flags;
struct musb *musb = dev_to_musb(&pdev->dev);
if (!musb->clock)
return 0;
spin_lock_irqsave(&musb->lock, flags);
if (is_peripheral_active(musb)) {
/* FIXME force disconnect unless we know USB will wake
* the system up quickly enough to respond ...
*/
} else if (is_host_active(musb)) {
/* we know all the children are suspended; sometimes
* they will even be wakeup-enabled.
*/
}
musb_save_context(musb);
if (musb->set_clock)
musb->set_clock(musb->clock, 0);
else
clk_disable(musb->clock);
spin_unlock_irqrestore(&musb->lock, flags);
return 0;
}
static int musb_resume_noirq(struct device *dev):
static int musb_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct musb *musb = dev_to_musb(&pdev->dev);
#ifdef MUSB_RESTORE_REG
int status;
#endif
if (!musb->clock)
return 0;
if (musb->set_clock)
musb->set_clock(musb->clock, 1);
else
clk_enable(musb->clock);
musb_restore_context(musb);
#ifdef MUSB_RESTORE_REG
status = musb_core_init(musb->is_multipoint
? MUSB_CONTROLLER_MHDRC
: MUSB_CONTROLLER_HDRC, musb);
if (status < 0)
return status;
/* we need to apply the DEV_CTRL SESSION bit to initialize the VBUS.
* This should be generally called through root hub POWERON feature,
* but it is needed also here if autosuspend is disabled.
*/
musb_start(musb);
/* wait for the connect interrupt */
mdelay(200);
#endif
return 0;
}
1.4 static int __exit musb_remove(struct platform_device *pdev)
static int __exit musb_remove(struct platform_device *pdev)
{
struct musb *musb = dev_to_musb(&pdev->dev);
void __iomem *ctrl_base = musb->ctrl_base;
/* this gets called on rmmod.
* - Host mode: host may still be active
* - Peripheral mode: peripheral is deactivated (or never-activated)
* - OTG mode: both roles are deactivated (or never-activated)
*/
musb_shutdown(pdev);
#ifdef CONFIG_USB_MUSB_HDRC_HCD
if (musb->board_mode == MUSB_HOST)
usb_remove_hcd(musb_to_hcd(musb));
#endif
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
musb_platform_exit(musb);
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
musb_free(musb);
iounmap(ctrl_base);
device_init_wakeup(&pdev->dev, 0);
#ifndef CONFIG_MUSB_PIO_ONLY
pdev->dev.dma_mask = orig_dma_mask;
#endif
return 0;
}
1.5 static void musb_shutdown(struct platform_device *pdev)
static void musb_shutdown(struct platform_device *pdev)
{
struct musb *musb = dev_to_musb(&pdev->dev);
unsigned long flags;
spin_lock_irqsave(&musb->lock, flags);
musb_platform_disable(musb);
musb_generic_disable(musb);
if (musb->clock)
clk_put(musb->clock);
spin_unlock_irqrestore(&musb->lock, flags);
/* FIXME power down */
}
2. musb_cleanup():
static void __exit musb_cleanup(void)
{
platform_driver_unregister(&musb_driver);
}
module_exit(musb_cleanup);
3. static int __init musb_probe(struct platform_device *pdev)
static int __init musb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int irq = platform_get_irq(pdev, 0);
int status;
struct resource *iomem;
void __iomem *base;
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); //擷取 platform 資源
if (!iomem || irq == 0)
return -ENODEV;
base = ioremap(iomem->start, resource_size(iomem)); //映射記憶體
if (!base) {
dev_err(dev, "ioremap failed\n");
return -ENOMEM;
}
#ifndef CONFIG_MUSB_PIO_ONLY
/* clobbered by use_dma=n */
orig_dma_mask = dev->dma_mask; //如果核心配置了CONFIG_MUSB_PIO_ONLY,則屏蔽DMA
#endif
status = musb_init_controller(dev, irq, base); //這個才是主要的函數
if (status < 0)
iounmap(base);
return status;
}
4. static int __init
musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
/*
* Perform generic per-controller initialization.
*
* @pDevice: the controller (already clocked, etc)
* @nIrq: irq
* @mregs: virtual address of controller registers,
* not yet corrected for platform-specific offsets
*/
static int __init
musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
{
int status;
struct musb *musb;
struct musb_hdrc_platform_data *plat = dev->platform_data;
/* The driver might handle more features than the board; OK.
* Fail when the board needs a feature that's not enabled.
*/
if (!plat) {
dev_dbg(dev, "no platform_data?\n");
status = -ENODEV;
goto fail0;
}
switch (plat->mode) {
case MUSB_HOST:
#ifdef CONFIG_USB_MUSB_HDRC_HCD
break;
#else
goto bad_config;
#endif
case MUSB_PERIPHERAL:
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
break;
#else
goto bad_config;
#endif
case MUSB_OTG:
#ifdef CONFIG_USB_MUSB_OTG
break;
#else
bad_config:
#endif
default:
dev_err(dev, "incompatible Kconfig role setting\n");
status = -EINVAL;
goto fail0;
}
/* allocate */
musb = allocate_instance(dev, plat->config, ctrl); //配置設定執行個體
if (!musb) {
status = -ENOMEM;
goto fail0;
}
spin_lock_init(&musb->lock);
musb->board_mode = plat->mode;
musb->board_set_power = plat->set_power;
musb->set_clock = plat->set_clock;
musb->min_power = plat->min_power;
/* Clock usage is chip-specific ... functional clock (DaVinci,
* OMAP2430), or PHY ref (some TUSB6010 boards). All this core
* code does is make sure a clock handle is available; platform
* code manages it during start/stop and suspend/resume.
*/
if (plat->clock) {
musb->clock = clk_get(dev, plat->clock);
if (IS_ERR(musb->clock)) {
status = PTR_ERR(musb->clock);
musb->clock = NULL;
goto fail1;
}
}
/* The musb_platform_init() call:
* - adjusts musb->mregs and musb->isr if needed,
* - may initialize an integrated tranceiver
* - initializes musb->xceiv, usually by otg_get_transceiver()
* - activates clocks.
* - stops powering VBUS
* - assigns musb->board_set_vbus if host mode is enabled
*
* There are various transciever configurations. Blackfin,
* DaVinci, TUSB60x0, and others integrate them. OMAP3 uses
* external/discrete ones in various flavors (twl4030 family,
* isp1504, non-OTG, etc) mostly hooking up through ULPI.
*/
musb->isr = generic_interrupt; //指派 musb->irq
status = musb_platform_init(musb); //初始化 musb
if (status < 0)
goto fail2;
if (!musb->isr) {
status = -ENODEV;
goto fail3;
}
#ifndef CONFIG_MUSB_PIO_ONLY
if (use_dma && dev->dma_mask) { //屏蔽DMA
struct dma_controller *c;
c = dma_controller_create(musb, musb->mregs);
musb->dma_controller = c;
if (c)
(void) c->start(c);
}
#endif
/* ideally this would be abstracted in platform setup */
if (!is_dma_capable() || !musb->dma_controller)
dev->dma_mask = NULL;
/* be sure interrupts are disabled before connecting ISR */ //先關閉中斷
musb_platform_disable(musb);
musb_generic_disable(musb);
/* setup musb parts of the core (especially endpoints) */
status = musb_core_init(plat->config->multipoint //這個是主要函數
? MUSB_CONTROLLER_MHDRC
: MUSB_CONTROLLER_HDRC, musb);
if (status < 0)
goto fail3;
#ifdef CONFIG_USB_MUSB_OTG
setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb);
#endif
/* Init IRQ workqueue before request_irq */
INIT_WORK(&musb->irq_work, musb_irq_work); //初始化工作隊列
/* attach to the IRQ */
if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) { //申請中斷号
dev_err(dev, "request_irq %d failed!\n", nIrq);
status = -ENODEV;
goto fail3;
}
musb->nIrq = nIrq;
/* FIXME this handles wakeup irqs wrong */
if (enable_irq_wake(nIrq) == 0) {
musb->irq_wake = 1;
device_init_wakeup(dev, 1);
} else {
musb->irq_wake = 0;
}
/* host side needs more setup */ //使能host模式
if (is_host_enabled(musb)) {
struct usb_hcd *hcd = musb_to_hcd(musb);
otg_set_host(musb->xceiv, &hcd->self);
if (is_otg_enabled(musb))
hcd->self.otg_port = 1;
musb->xceiv->host = &hcd->self;
hcd->power_budget = 2 * (plat->power ? : 250);
/* program PHY to use external vBus if required */
if (plat->extvbus) {
u8 busctl = musb_read_ulpi_buscontrol(musb->mregs);
busctl |= MUSB_ULPI_USE_EXTVBUS;
musb_write_ulpi_buscontrol(musb->mregs, busctl);
}
}
/* For the host-only role, we can activate right away.
* (We expect the ID pin to be forcibly grounded!!)
* Otherwise, wait till the gadget driver hooks up.
*/
if (!is_otg_enabled(musb) && is_host_enabled(musb)) { //如果使能了 OTG 模式或 host 模式
MUSB_HST_MODE(musb);
musb->xceiv->default_a = 1;
musb->xceiv->state = OTG_STATE_A_IDLE;
status = usb_add_hcd(musb_to_hcd(musb), -1, 0); //把 musb 添加到 hcd(host controller driver)中
DBG(1, "%s mode, status %d, devctl %02x %c\n",
"HOST", status,
musb_readb(musb->mregs, MUSB_DEVCTL),
(musb_readb(musb->mregs, MUSB_DEVCTL)
& MUSB_DEVCTL_BDEVICE
? 'B' : 'A'));
} else /* peripheral is enabled */ { //如果使能了 Peripheral 模式
MUSB_DEV_MODE(musb);
musb->xceiv->default_a = 0;
musb->xceiv->state = OTG_STATE_B_IDLE;
status = musb_gadget_setup(musb); //則建立 gadget
DBG(1, "%s mode, status %d, dev%02x\n",
is_otg_enabled(musb) ? "OTG" : "PERIPHERAL",
status,
musb_readb(musb->mregs, MUSB_DEVCTL));
}
if (status < 0)
goto fail3;
#ifdef CONFIG_SYSFS
status = sysfs_create_group(&musb->controller->kobj, &musb_attr_group); //在 /sys 下建立目錄
if (status)
goto fail4;
#endif
dev_info(dev, "USB %s mode controller at %p using %s, IRQ %d\n", //dmesg中列印消息
({char *s;
switch (musb->board_mode) {
case MUSB_HOST: s = "Host"; break;
case MUSB_PERIPHERAL: s = "Peripheral"; break;
default: s = "OTG"; break;
}; s; }),
ctrl,
(is_dma_capable() && musb->dma_controller)
? "DMA" : "PIO",
musb->nIrq);
return 0;
fail4:
if (!is_otg_enabled(musb) && is_host_enabled(musb))
usb_remove_hcd(musb_to_hcd(musb));
else
musb_gadget_cleanup(musb);
fail3:
if (musb->irq_wake)
device_init_wakeup(dev, 0);
musb_platform_exit(musb);
fail2:
if (musb->clock)
clk_put(musb->clock);
fail1:
dev_err(musb->controller,
"musb_init_controller failed with status %d\n", status);
musb_free(musb);
fail0:
return status;
}
static struct musb *__init
allocate_instance(struct device *dev,
struct musb_hdrc_config *config, void __iomem *mbase)
/* --------------------------------------------------------------------------
* Init support
*/
static struct musb *__init
allocate_instance(struct device *dev,
struct musb_hdrc_config *config, void __iomem *mbase)
{
struct musb *musb;
struct musb_hw_ep *ep;
int epnum;
#ifdef CONFIG_USB_MUSB_HDRC_HCD
struct usb_hcd *hcd;
hcd = usb_create_hcd(&musb_hc_driver, dev, dev_name(dev)); //建立 host controller 端的驅動,這個會在musb_host.c中介紹
if (!hcd)
return NULL;
/* usbcore sets dev->driver_data to hcd, and sometimes uses that... */
musb = hcd_to_musb(hcd);
INIT_LIST_HEAD(&musb->control);
INIT_LIST_HEAD(&musb->in_bulk);
INIT_LIST_HEAD(&musb->out_bulk);
hcd->uses_new_polling = 1;
hcd->has_tt = 1;
musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
musb->a_wait_bcon = OTG_TIME_A_WAIT_BCON;
#else
musb = kzalloc(sizeof *musb, GFP_KERNEL);
if (!musb)
return NULL;
dev_set_drvdata(dev, musb);
#endif
musb->mregs = mbase;
musb->ctrl_base = mbase;
musb->nIrq = -ENODEV;
musb->config = config;
BUG_ON(musb->config->num_eps > MUSB_C_NUM_EPS);
for (epnum = 0, ep = musb->endpoints;
epnum < musb->config->num_eps;
epnum++, ep++) {
ep->musb = musb;
ep->epnum = epnum;
}
musb->controller = dev;
return musb;
}
int __init musb_platform_init(struct musb *musb) --- 這個函數定義于 /driver/usb/musb/cartesio.c 中,是ULPI的實作初始化了。
int __init musb_platform_init(struct musb *musb)
{
int ret;
if (!musb->clock)
return 0;
if (musb->set_clock)
musb->set_clock(musb->clock, 1);
else
clk_enable(musb->clock);
/*
* This is a custom implementation of nop-usb-xceiv, introduced
* because the original one does not support multiple instances.
* This will be replaced by ulpi trainsceiver after moving to
* kernel 2.6.33
*/
musb->xceiv = cartesio_nop_xceiv_register();
if (!musb->xceiv) {
dev_err(musb->controller, "No transceiver configured\n");
return -ENODEV;
}
if (is_host_enabled(musb))
musb->board_set_vbus = cartesio_set_vbus;
setup_timer(&musb->idle_timer, musb_do_idle, (unsigned long)musb);
if (musb_controller_is(musb, "musb_sta2062_ulpi") ||
musb_controller_is(musb, "musb_sta2065_ulpi")) {
ret = musb_cartesio_ulpi_init(musb);
if (ret)
return ret;
}
if (musb_controller_is(musb, "musb_sta2065_ephyfs"))
musb_sta2065_ephyfs_init(musb);
if (musb_controller_is(musb, "musb_sta2062_ephy"))
musb_sta2062_ephy_enable(musb);
return 0;
}
static void musb_generic_disable(struct musb *musb) --- 關閉中斷
static void musb_generic_disable(struct musb *musb)
{
void __iomem *mbase = musb->mregs;
u16 temp;
/* disable interrupts */
musb_writeb(mbase, MUSB_INTRUSBE, 0);
musb_writew(mbase, MUSB_INTRTXE, 0);
musb_writew(mbase, MUSB_INTRRXE, 0);
/* off */
musb_writeb(mbase, MUSB_DEVCTL, 0);
/* flush pending interrupts */
temp = musb_readb(mbase, MUSB_INTRUSB);
temp = musb_readw(mbase, MUSB_INTRTX);
temp = musb_readw(mbase, MUSB_INTRRX);
}
void musb_otg_timer_func(unsigned long data)
/*
* Handles OTG hnp timeouts, such as b_ase0_brst
*/
void musb_otg_timer_func(unsigned long data)
{
struct musb *musb = (struct musb *)data;
unsigned long flags;
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv->state) {
case OTG_STATE_B_WAIT_ACON:
DBG(1, "HNP: b_wait_acon timeout; back to b_peripheral\n");
musb_g_disconnect(musb);
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
musb->is_active = 0;
break;
case OTG_STATE_A_SUSPEND:
case OTG_STATE_A_WAIT_BCON:
DBG(1, "HNP: %s timeout\n", otg_state_string(musb));
musb_set_vbus(musb, 0);
musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
break;
default:
DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb));
}
musb->ignore_disconnect = 0;
spin_unlock_irqrestore(&musb->lock, flags);
}
5. static int MUSB_INIT musb_core_init(u16 musb_type, struct musb *musb)
/* Initialize MUSB (M)HDRC part of the USB hardware subsystem;
* configure endpoints, or take their config from silicon
*/
static int MUSB_INIT musb_core_init(u16 musb_type, struct musb *musb)
{
u8 reg;
char *type;
char aInfo[90], aRevision[32], aDate[12];
void __iomem *mbase = musb->mregs;
int status = 0;
int i;
/* log core options (read using indexed model) */
reg = musb_read_configdata(mbase);
strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8"); //在aInfo中添加一堆字尾
if (reg & MUSB_CONFIGDATA_DYNFIFO) {
strcat(aInfo, ", dyn FIFOs");
musb->dyn_fifo = true;
}
if (reg & MUSB_CONFIGDATA_MPRXE) {
strcat(aInfo, ", bulk combine");
musb->bulk_combine = true;
}
if (reg & MUSB_CONFIGDATA_MPTXE) {
strcat(aInfo, ", bulk split");
musb->bulk_split = true;
}
if (reg & MUSB_CONFIGDATA_HBRXE) {
strcat(aInfo, ", HB-ISO Rx");
musb->hb_iso_rx = true;
}
if (reg & MUSB_CONFIGDATA_HBTXE) {
strcat(aInfo, ", HB-ISO Tx");
musb->hb_iso_tx = true;
}
if (reg & MUSB_CONFIGDATA_SOFTCONE)
strcat(aInfo, ", SoftConn");
printk(KERN_DEBUG "%s: ConfigData=0x%02x (%s)\n",
musb_driver_name, reg, aInfo);
aDate[0] = 0;
if (MUSB_CONTROLLER_MHDRC == musb_type) {
musb->is_multipoint = 1;
type = "M";
} else {
musb->is_multipoint = 0;
type = "";
#ifdef CONFIG_USB_MUSB_HDRC_HCD
#ifndef CONFIG_USB_OTG_BLACKLIST_HUB
printk(KERN_ERR
"%s: kernel must blacklist external hubs\n",
musb_driver_name);
#endif
#endif
}
/* log release info */
musb->hwvers = musb_read_hwvers(mbase);
snprintf(aRevision, 32, "%d.%d%s", MUSB_HWVERS_MAJOR(musb->hwvers),
MUSB_HWVERS_MINOR(musb->hwvers),
(musb->hwvers & MUSB_HWVERS_RC) ? "RC" : "");
printk(KERN_DEBUG "%s: %sHDRC RTL version %s %s\n", //列印出 hdrc 内容
musb_driver_name, type, aRevision, aDate);
/* configure ep0 */
musb_configure_ep0(musb); //configure musb 的 ep0
/* discover endpoint configuration */
musb->nr_endpoints = 1;
musb->epmask = 1;
if (musb->dyn_fifo)
status = ep_config_from_table(musb); //from table
else
status = ep_config_from_hw(musb); // hardware config
if (status < 0)
return status;
/* finish init, and print endpoint config */
for (i = 0; i < musb->nr_endpoints; i++) {
struct musb_hw_ep *hw_ep = musb->endpoints + i;
hw_ep->fifo = MUSB_FIFO_OFFSET(i) + mbase;
#ifdef CONFIG_USB_TUSB6010
hw_ep->fifo_async = musb->async + 0x400 + MUSB_FIFO_OFFSET(i);
hw_ep->fifo_sync = musb->sync + 0x400 + MUSB_FIFO_OFFSET(i);
hw_ep->fifo_sync_va =
musb->sync_va + 0x400 + MUSB_FIFO_OFFSET(i);
if (i == 0)
hw_ep->conf = mbase - 0x400 + TUSB_EP0_CONF;
else
hw_ep->conf = mbase + 0x400 + (((i - 1) & 0xf) << 2);
#endif
hw_ep->regs = MUSB_EP_OFFSET(i, 0) + mbase;
#ifdef CONFIG_USB_MUSB_HDRC_HCD
hw_ep->target_regs = musb_read_target_reg_base(i, mbase);
hw_ep->rx_reinit = 1;
hw_ep->tx_reinit = 1;
#endif
if (hw_ep->max_packet_sz_tx) {
DBG(1,
"%s: hw_ep %d%s, %smax %d\n",
musb_driver_name, i,
hw_ep->is_shared_fifo ? "shared" : "tx",
hw_ep->tx_double_buffered
? "doublebuffer, " : "",
hw_ep->max_packet_sz_tx);
}
if (hw_ep->max_packet_sz_rx && !hw_ep->is_shared_fifo) {
DBG(1,
"%s: hw_ep %d%s, %smax %d\n",
musb_driver_name, i,
"rx",
hw_ep->rx_double_buffered
? "doublebuffer, " : "",
hw_ep->max_packet_sz_rx);
}
if (!(hw_ep->max_packet_sz_tx || hw_ep->max_packet_sz_rx))
DBG(1, "hw_ep %d not configured\n", i);
}
return 0;
}
static inline void musb_configure_ep0(struct musb *musb)
static inline void musb_configure_ep0(struct musb *musb)
{
musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE;
musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
musb->endpoints[0].is_shared_fifo = true;
}
6. static int MUSB_INIT ep_config_from_table(struct musb *musb) --- 配置endpoint
static int MUSB_INIT ep_config_from_table(struct musb *musb)
{
const struct fifo_cfg *cfg;
unsigned i, n;
int offset;
struct musb_hw_ep *hw_ep = musb->endpoints;
switch (fifo_mode) {
default:
fifo_mode = 0;
/* FALLTHROUGH */
case 0:
cfg = mode_0_cfg;
n = ARRAY_SIZE(mode_0_cfg);
break;
case 1:
cfg = mode_1_cfg;
n = ARRAY_SIZE(mode_1_cfg);
break;
case 2:
cfg = mode_2_cfg;
n = ARRAY_SIZE(mode_2_cfg);
break;
case 3:
cfg = mode_3_cfg;
n = ARRAY_SIZE(mode_3_cfg);
break;
case 4:
cfg = mode_4_cfg;
n = ARRAY_SIZE(mode_4_cfg);
break;
case 5:
cfg = mode_5_cfg;
n = ARRAY_SIZE(mode_5_cfg);
break;
}
printk(KERN_DEBUG "%s: setup fifo_mode %d\n",
musb_driver_name, fifo_mode);
offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0);
/* assert(offset > 0) */
/* NOTE: for RTL versions >= 1.400 EPINFO and RAMINFO would
* be better than static musb->config->num_eps and DYN_FIFO_SIZE...
*/
for (i = 0; i < n; i++) {
u8 epn = cfg->hw_ep_num;
if (epn >= musb->config->num_eps) {
pr_debug("%s: invalid ep %d\n",
musb_driver_name, epn);
return -EINVAL;
}
offset = fifo_setup(musb, hw_ep + epn, cfg++, offset);
if (offset < 0) {
pr_debug("%s: mem overrun, ep %d\n",
musb_driver_name, epn);
return -EINVAL;
}
epn++;
musb->nr_endpoints = max(epn, musb->nr_endpoints);
}
printk(KERN_DEBUG "%s: %d/%d max ep, %d/%d memory\n",
musb_driver_name,
n + 1, musb->config->num_eps * 2 - 1,
offset, (1 << (musb->config->ram_bits + 2)));
#ifdef CONFIG_USB_MUSB_HDRC_HCD
if (!musb->bulk_ep) {
pr_debug("%s: missing bulk\n", musb_driver_name);
return -EINVAL;
}
#endif
return 0;
}
fifo_mode 定義:
#if defined(CONFIG_USB_TUSB6010) || \
defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3)
static ushort MUSB_INITDATA fifo_mode = 4;
#elif defined(CONFIG_ARCH_CARTESIO)
static ushort MUSB_INITDATA fifo_mode = 5;
#else
static ushort MUSB_INITDATA fifo_mode = 2;
#endif
static int MUSB_INIT
fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep,
const struct fifo_cfg *cfg, u16 offset) ------------- 給endpoint 配置性能
/*
* configure a fifo; for non-shared endpoints, this may be called
* once for a tx fifo and once for an rx fifo.
*
* returns negative errno or offset for next fifo.
*/
static int MUSB_INIT
fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep,
const struct fifo_cfg *cfg, u16 offset)
{
void __iomem *mbase = musb->mregs;
int size = 0;
u16 maxpacket = cfg->maxpacket;
u16 c_off = offset >> 3;
u8 c_size;
/* expect hw_ep has already been zero-initialized */
size = ffs(max(maxpacket, (u16) 8)) - 1;
maxpacket = 1 << size;
c_size = size - 3;
if (cfg->mode == BUF_DOUBLE) {
if ((offset + (maxpacket << 1)) >
(1 << (musb->config->ram_bits + 2)))
return -EMSGSIZE;
c_size |= MUSB_FIFOSZ_DPB;
} else {
if ((offset + maxpacket) > (1 << (musb->config->ram_bits + 2)))
return -EMSGSIZE;
}
/* configure the FIFO */
musb_writeb(mbase, MUSB_INDEX, hw_ep->epnum);
#ifdef CONFIG_USB_MUSB_HDRC_HCD
/* EP0 reserved endpoint for control, bidirectional;
* EP1 reserved for bulk, two unidirection halves.
*/
if (hw_ep->epnum == 1)
musb->bulk_ep = hw_ep;
/* REVISIT error check: be sure ep0 can both rx and tx ... */
#endif
switch (cfg->style) {
case FIFO_TX:
musb_write_txfifosz(mbase, c_size);
musb_write_txfifoadd(mbase, c_off);
hw_ep->tx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
hw_ep->max_packet_sz_tx = maxpacket;
break;
case FIFO_RX:
musb_write_rxfifosz(mbase, c_size);
musb_write_rxfifoadd(mbase, c_off);
hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
hw_ep->max_packet_sz_rx = maxpacket;
break;
case FIFO_RXTX:
musb_write_txfifosz(mbase, c_size);
musb_write_txfifoadd(mbase, c_off);
hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
hw_ep->max_packet_sz_rx = maxpacket;
musb_write_rxfifosz(mbase, c_size);
musb_write_rxfifoadd(mbase, c_off);
hw_ep->tx_double_buffered = hw_ep->rx_double_buffered;
hw_ep->max_packet_sz_tx = maxpacket;
hw_ep->is_shared_fifo = true;
break;
}
/* NOTE rx and tx endpoint irqs aren't managed separately,
* which happens to be ok
*/
musb->epmask |= (1 << hw_ep->epnum);
return offset + (maxpacket << ((c_size & MUSB_FIFOSZ_DPB) ? 1 : 0));
}
各種 fifo_cfg mode_*cfg[] :
/*
* tables defining fifo_mode values. define more if you like.
* for host side, make sure both halves of ep1 are set up.
*/
/* mode 0 - fits in 2KB */
static struct fifo_cfg MUSB_INITDATA mode_0_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, },
{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, },
};
/* mode 1 - fits in 4KB */
static struct fifo_cfg MUSB_INITDATA mode_1_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, },
{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, },
};
/* mode 2 - fits in 4KB */
static struct fifo_cfg MUSB_INITDATA mode_2_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, },
{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, },
};
/* mode 3 - fits in 4KB */
static struct fifo_cfg MUSB_INITDATA mode_3_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, },
{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, },
};
/* mode 4 - fits in 16KB */
static struct fifo_cfg MUSB_INITDATA mode_4_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 8, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 256, },
{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 64, },
{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 256, },
{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 64, },
{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 256, },
{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 64, },
{ .hw_ep_num = 13, .style = FIFO_RXTX, .maxpacket = 4096, },
{ .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, },
{ .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, },
};
/* mode 5 - fits in 12KB */
static struct fifo_cfg MUSB_INITDATA mode_5_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 1024, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 1024, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 1024, },
{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 1024, },
{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 64, },
{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 64, },
{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 256, },
{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 256, },
{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 256, },
};
#if 0
/* mode 5 - fits in 8KB */
static struct fifo_cfg __initdata mode_5_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 32, },
{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 32, },
{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 32, },
{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 32, },
{ .hw_ep_num = 8, .style = FIFO_TX, .maxpacket = 32, },
{ .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 32, },
{ .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 32, },
{ .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 32, },
{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 32, },
{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 32, },
{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 32, },
{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 32, },
{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 32, },
{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 32, },
{ .hw_ep_num = 13, .style = FIFO_RXTX, .maxpacket = 512, },
{ .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, },
{ .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, },
};
#endif
/*
* configure a fifo; for non-shared endpoints, this may be called
* once for a tx fifo and once for an rx fifo.
*
* returns negative errno or offset for next fifo.
*/
static int MUSB_INIT
fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep,
const struct fifo_cfg *cfg, u16 offset)
{
void __iomem *mbase = musb->mregs;
int size = 0;
u16 maxpacket = cfg->maxpacket;
u16 c_off = offset >> 3;
u8 c_size;
/* expect hw_ep has already been zero-initialized */
size = ffs(max(maxpacket, (u16) 8)) - 1;
maxpacket = 1 << size;
c_size = size - 3;
if (cfg->mode == BUF_DOUBLE) {
if ((offset + (maxpacket << 1)) >
(1 << (musb->config->ram_bits + 2)))
return -EMSGSIZE;
c_size |= MUSB_FIFOSZ_DPB;