forked from Minki/linux
2390710647
This reverts the omap2430 changes of commitcf081d009c
("usb: musb: Set the DT node on the child device") Since v5.17-rc1, musb is broken on the gta04 and openpandora devices (omap3530/dm3730). BeagleBone Black (am335x) seems to work. Symptoms of this bug are a) main symptom [ 21.336517] using random host ethernet address [ 21.341430] using host ethernet address: 32:70:05:18:ff:78 [ 21.341461] using self ethernet address: 46:10:3a:b3:af:d9 [ 21.358184] usb0: HOST MAC 32:70:05:18:ff:78 [ 21.376678] usb0: MAC 46:10:3a:b3:af:d9 [ 21.388305] using random self ethernet address [ 21.393371] using random host ethernet address [ 21.398162] g_ether gadget: Ethernet Gadget, version: Memorial Day 2008 [ 21.421081] g_ether gadget: g_ether ready [ 21.492156] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 21.691345] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 21.803192] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 21.819427] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 22.124450] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 22.168518] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 22.179382] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 23.213592] musb-hdrc musb-hdrc.1.auto: pm runtime get failed in musb_gadget_queue [ 23.221832] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 23.227905] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 23.239440] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 23.401000] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 23.407073] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 23.426361] musb-hdrc musb-hdrc.1.auto: Could not enable: -22 [ 23.734466] musb-hdrc musb-hdrc.1.auto: pm runtime get failed in musb_gadget_queue [ 23.742462] musb-hdrc musb-hdrc.1.auto: pm runtime get failed in musb_gadget_queue [ 23.750396] musb-hdrc musb-hdrc.1.auto: pm runtime get failed in musb_gadget_queue ... (repeats with high frequency) This stops if the USB cable is unplugged and restarts if it is plugged in again. b) also found in the log [ 6.498107] ------------[ cut here ]------------ [ 6.502960] WARNING: CPU: 0 PID: 868 at arch/arm/mach-omap2/omap_hwmod.c:1885 _enable+0x50/0x234 [ 6.512207] omap_hwmod: usb_otg_hs: enabled state can only be entered from initialized, idle, or disabled state [ 6.522766] Modules linked in: omap2430(+) bmp280_i2c bmp280 itg3200 at24 tsc2007 leds_tca6507 bma180 hmc5843_i2c hmc5843_core industrialio_triggered_buffer lis3lv02d_i2c kfifo_buf lis3lv02d phy_twl4030_usb snd_soc_omap_mcbsp snd_soc_ti_sdma musb_hdrc snd_soc_twl4030 gnss_sirf twl4030_vibra twl4030_madc twl4030_charger twl4030_pwrbutton gnss industrialio ehci_omap omapdrm drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops drm drm_panel_orientation_quirks cec [ 6.566436] CPU: 0 PID: 868 Comm: udevd Not tainted 5.16.0-rc5-letux+ #8251 [ 6.573730] Hardware name: Generic OMAP36xx (Flattened Device Tree) [ 6.580322] [<c010ed30>] (unwind_backtrace) from [<c010a1d0>] (show_stack+0x10/0x14) [ 6.588470] [<c010a1d0>] (show_stack) from [<c0897c14>] (dump_stack_lvl+0x40/0x4c) [ 6.596405] [<c0897c14>] (dump_stack_lvl) from [<c0130cc4>] (__warn+0xb4/0xdc) [ 6.604003] [<c0130cc4>] (__warn) from [<c0130d5c>] (warn_slowpath_fmt+0x70/0x9c) [ 6.611846] [<c0130d5c>] (warn_slowpath_fmt) from [<c011f4d4>] (_enable+0x50/0x234) [ 6.619903] [<c011f4d4>] (_enable) from [<c012081c>] (omap_hwmod_enable+0x28/0x40) [ 6.627838] [<c012081c>] (omap_hwmod_enable) from [<c0120ff4>] (omap_device_enable+0x4c/0x78) [ 6.636779] [<c0120ff4>] (omap_device_enable) from [<c0121030>] (_od_runtime_resume+0x10/0x3c) [ 6.645812] [<c0121030>] (_od_runtime_resume) from [<c05c688c>] (__rpm_callback+0x3c/0xf4) [ 6.654510] [<c05c688c>] (__rpm_callback) from [<c05c6994>] (rpm_callback+0x50/0x54) [ 6.662628] [<c05c6994>] (rpm_callback) from [<c05c66b0>] (rpm_resume+0x448/0x4e4) [ 6.670593] [<c05c66b0>] (rpm_resume) from [<c05c6784>] (__pm_runtime_resume+0x38/0x50) [ 6.678985] [<c05c6784>] (__pm_runtime_resume) from [<bf14ab20>] (musb_init_controller+0x350/0xa5c [musb_hdrc]) [ 6.689727] [<bf14ab20>] (musb_init_controller [musb_hdrc]) from [<c05bccb8>] (platform_probe+0x58/0xa8) [ 6.699737] [<c05bccb8>] (platform_probe) from [<c05badf0>] (really_probe+0x170/0x2fc) [ 6.708068] [<c05badf0>] (really_probe) from [<c05bb040>] (__driver_probe_device+0xc4/0xd8) [ 6.716827] [<c05bb040>] (__driver_probe_device) from [<c05bb084>] (driver_probe_device+0x30/0xac) [ 6.726226] [<c05bb084>] (driver_probe_device) from [<c05bb3d0>] (__device_attach_driver+0x94/0xb4) [ 6.735717] [<c05bb3d0>] (__device_attach_driver) from [<c05b93f8>] (bus_for_each_drv+0xa0/0xb4) [ 6.744934] [<c05b93f8>] (bus_for_each_drv) from [<c05bb248>] (__device_attach+0xc0/0x134) [ 6.753631] [<c05bb248>] (__device_attach) from [<c05b9fcc>] (bus_probe_device+0x28/0x80) [ 6.762207] [<c05b9fcc>] (bus_probe_device) from [<c05b7e40>] (device_add+0x5fc/0x788) [ 6.770507] [<c05b7e40>] (device_add) from [<c05bd240>] (platform_device_add+0x70/0x1bc) [ 6.779022] [<c05bd240>] (platform_device_add) from [<bf177830>] (omap2430_probe+0x260/0x2d4 [omap2430]) [ 6.789001] [<bf177830>] (omap2430_probe [omap2430]) from [<c05bccb8>] (platform_probe+0x58/0xa8) [ 6.798309] [<c05bccb8>] (platform_probe) from [<c05badf0>] (really_probe+0x170/0x2fc) [ 6.806610] [<c05badf0>] (really_probe) from [<c05bb040>] (__driver_probe_device+0xc4/0xd8) [ 6.815399] [<c05bb040>] (__driver_probe_device) from [<c05bb084>] (driver_probe_device+0x30/0xac) [ 6.824798] [<c05bb084>] (driver_probe_device) from [<c05bb4b4>] (__driver_attach+0xc4/0xd8) [ 6.833648] [<c05bb4b4>] (__driver_attach) from [<c05b9308>] (bus_for_each_dev+0x64/0xa0) [ 6.842224] [<c05b9308>] (bus_for_each_dev) from [<c05ba248>] (bus_add_driver+0x148/0x1a4) [ 6.850891] [<c05ba248>] (bus_add_driver) from [<c05bbd1c>] (driver_register+0xb4/0xf8) [ 6.859313] [<c05bbd1c>] (driver_register) from [<c0101f54>] (do_one_initcall+0x90/0x1c8) [ 6.867889] [<c0101f54>] (do_one_initcall) from [<c0893968>] (do_init_module+0x4c/0x204) [ 6.876373] [<c0893968>] (do_init_module) from [<c01b4c30>] (load_module+0x13f0/0x1928) [ 6.884796] [<c01b4c30>] (load_module) from [<c01b53a0>] (sys_finit_module+0xa0/0xc0) [ 6.893005] [<c01b53a0>] (sys_finit_module) from [<c0100080>] (ret_fast_syscall+0x0/0x54) [ 6.901580] Exception stack(0xc2807fa8 to 0xc2807ff0) [ 6.906890] 7fa0: b6e517d4 00052068 00000006 b6e509f8 00000000 b6e5131c [ 6.915466] 7fc0: b6e517d4 00052068 cd718000 0000017b 00020000 00037f78 00050048 00063368 [ 6.924011] 7fe0: bed8fef0 bed8fee0 b6e4ac4b b6f55a42 [ 6.929321] ---[ end trace d715ff121b58763c ]--- c) git bisect result on testing for "musb-hdrc" in the console log:cf081d009c
is the first bad commit commitcf081d009c
Author: Rob Herring <robh@kernel.org> Date: Wed Dec 15 17:07:57 2021 -0600 usb: musb: Set the DT node on the child device The musb glue drivers just copy the glue resources to the musb child device. Instead, set the musb child device's DT node pointer to the parent device's node so that platform_get_irq_byname() can find the resources in the DT. This removes the need for statically populating the IRQ resources from the DT which has been deprecated for some time. Signed-off-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20211215230756.2009115-3-robh@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> drivers/usb/musb/am35x.c | 2 ++ drivers/usb/musb/da8xx.c | 2 ++ drivers/usb/musb/jz4740.c | 1 + drivers/usb/musb/mediatek.c | 2 ++ drivers/usb/musb/omap2430.c | 1 + drivers/usb/musb/ux500.c | 1 + 6 files changed, 9 insertions(+) Reverting this patch makes musb work again as before. Fixes:cf081d009c
("usb: musb: Set the DT node on the child device") Cc: Rob Herring <robh@kernel.org> Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com> Link: https://lore.kernel.org/r/f62f5fc11f9ecae7e57f3fd66939e051bd3b11fc.1646744166.git.hns@goldelico.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
569 lines
14 KiB
C
569 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2005-2007 by Texas Instruments
|
|
* Some code has been taken from tusb6010.c
|
|
* Copyrights for that are attributable to:
|
|
* Copyright (C) 2006 Nokia Corporation
|
|
* Tony Lindgren <tony@atomide.com>
|
|
*
|
|
* This file is part of the Inventra Controller Driver for Linux.
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/init.h>
|
|
#include <linux/list.h>
|
|
#include <linux/io.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/err.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/usb/musb.h>
|
|
#include <linux/phy/omap_control_phy.h>
|
|
#include <linux/of_platform.h>
|
|
|
|
#include "musb_core.h"
|
|
#include "omap2430.h"
|
|
|
|
struct omap2430_glue {
|
|
struct device *dev;
|
|
struct platform_device *musb;
|
|
enum musb_vbus_id_status status;
|
|
struct work_struct omap_musb_mailbox_work;
|
|
struct device *control_otghs;
|
|
unsigned int is_runtime_suspended:1;
|
|
unsigned int needs_resume:1;
|
|
unsigned int phy_suspended:1;
|
|
};
|
|
#define glue_to_musb(g) platform_get_drvdata(g->musb)
|
|
|
|
static struct omap2430_glue *_glue;
|
|
|
|
static inline void omap2430_low_level_exit(struct musb *musb)
|
|
{
|
|
u32 l;
|
|
|
|
/* in any role */
|
|
l = musb_readl(musb->mregs, OTG_FORCESTDBY);
|
|
l |= ENABLEFORCE; /* enable MSTANDBY */
|
|
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
|
|
}
|
|
|
|
static inline void omap2430_low_level_init(struct musb *musb)
|
|
{
|
|
u32 l;
|
|
|
|
l = musb_readl(musb->mregs, OTG_FORCESTDBY);
|
|
l &= ~ENABLEFORCE; /* disable MSTANDBY */
|
|
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
|
|
}
|
|
|
|
static int omap2430_musb_mailbox(enum musb_vbus_id_status status)
|
|
{
|
|
struct omap2430_glue *glue = _glue;
|
|
|
|
if (!glue) {
|
|
pr_err("%s: musb core is not yet initialized\n", __func__);
|
|
return -EPROBE_DEFER;
|
|
}
|
|
glue->status = status;
|
|
|
|
if (!glue_to_musb(glue)) {
|
|
pr_err("%s: musb core is not yet ready\n", __func__);
|
|
return -EPROBE_DEFER;
|
|
}
|
|
|
|
schedule_work(&glue->omap_musb_mailbox_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* HDRC controls CPEN, but beware current surges during device connect.
|
|
* They can trigger transient overcurrent conditions that must be ignored.
|
|
*
|
|
* Note that we're skipping A_WAIT_VFALL -> A_IDLE and jumping right to B_IDLE
|
|
* as set by musb_set_peripheral().
|
|
*/
|
|
static void omap_musb_set_mailbox(struct omap2430_glue *glue)
|
|
{
|
|
struct musb *musb = glue_to_musb(glue);
|
|
int error;
|
|
|
|
pm_runtime_get_sync(musb->controller);
|
|
|
|
dev_dbg(musb->controller, "VBUS %s, devctl %02x\n",
|
|
usb_otg_state_string(musb->xceiv->otg->state),
|
|
musb_readb(musb->mregs, MUSB_DEVCTL));
|
|
|
|
switch (glue->status) {
|
|
case MUSB_ID_GROUND:
|
|
dev_dbg(musb->controller, "ID GND\n");
|
|
switch (musb->xceiv->otg->state) {
|
|
case OTG_STATE_A_IDLE:
|
|
error = musb_set_host(musb);
|
|
if (error)
|
|
break;
|
|
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
|
|
fallthrough;
|
|
case OTG_STATE_A_WAIT_VRISE:
|
|
case OTG_STATE_A_WAIT_BCON:
|
|
case OTG_STATE_A_HOST:
|
|
/*
|
|
* On multiple ID ground interrupts just keep enabling
|
|
* VBUS. At least cpcap VBUS shuts down otherwise.
|
|
*/
|
|
otg_set_vbus(musb->xceiv->otg, 1);
|
|
break;
|
|
default:
|
|
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
|
|
musb->xceiv->last_event = USB_EVENT_ID;
|
|
if (musb->gadget_driver) {
|
|
omap_control_usb_set_mode(glue->control_otghs,
|
|
USB_MODE_HOST);
|
|
otg_set_vbus(musb->xceiv->otg, 1);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case MUSB_VBUS_VALID:
|
|
dev_dbg(musb->controller, "VBUS Connect\n");
|
|
|
|
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
|
|
musb->xceiv->last_event = USB_EVENT_VBUS;
|
|
omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE);
|
|
break;
|
|
|
|
case MUSB_ID_FLOAT:
|
|
case MUSB_VBUS_OFF:
|
|
dev_dbg(musb->controller, "VBUS Disconnect\n");
|
|
|
|
musb->xceiv->last_event = USB_EVENT_NONE;
|
|
musb_set_peripheral(musb);
|
|
otg_set_vbus(musb->xceiv->otg, 0);
|
|
omap_control_usb_set_mode(glue->control_otghs,
|
|
USB_MODE_DISCONNECT);
|
|
break;
|
|
default:
|
|
dev_dbg(musb->controller, "ID float\n");
|
|
}
|
|
pm_runtime_mark_last_busy(musb->controller);
|
|
pm_runtime_put_autosuspend(musb->controller);
|
|
atomic_notifier_call_chain(&musb->xceiv->notifier,
|
|
musb->xceiv->last_event, NULL);
|
|
}
|
|
|
|
|
|
static void omap_musb_mailbox_work(struct work_struct *mailbox_work)
|
|
{
|
|
struct omap2430_glue *glue = container_of(mailbox_work,
|
|
struct omap2430_glue, omap_musb_mailbox_work);
|
|
|
|
omap_musb_set_mailbox(glue);
|
|
}
|
|
|
|
static irqreturn_t omap2430_musb_interrupt(int irq, void *__hci)
|
|
{
|
|
unsigned long flags;
|
|
irqreturn_t retval = IRQ_NONE;
|
|
struct musb *musb = __hci;
|
|
|
|
spin_lock_irqsave(&musb->lock, flags);
|
|
|
|
musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
|
|
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
|
|
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
|
|
|
|
if (musb->int_usb || musb->int_tx || musb->int_rx)
|
|
retval = musb_interrupt(musb);
|
|
|
|
spin_unlock_irqrestore(&musb->lock, flags);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int omap2430_musb_init(struct musb *musb)
|
|
{
|
|
u32 l;
|
|
int status = 0;
|
|
struct device *dev = musb->controller;
|
|
struct musb_hdrc_platform_data *plat = dev_get_platdata(dev);
|
|
struct omap_musb_board_data *data = plat->board_data;
|
|
|
|
/* We require some kind of external transceiver, hooked
|
|
* up through ULPI. TWL4030-family PMICs include one,
|
|
* which needs a driver, drivers aren't always needed.
|
|
*/
|
|
musb->phy = devm_phy_get(dev->parent, "usb2-phy");
|
|
|
|
/* We can't totally remove musb->xceiv as of now because
|
|
* musb core uses xceiv.state and xceiv.otg. Once we have
|
|
* a separate state machine to handle otg, these can be moved
|
|
* out of xceiv and then we can start using the generic PHY
|
|
* framework
|
|
*/
|
|
musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0);
|
|
|
|
if (IS_ERR(musb->xceiv)) {
|
|
status = PTR_ERR(musb->xceiv);
|
|
|
|
if (status == -ENXIO)
|
|
return status;
|
|
|
|
dev_dbg(dev, "HS USB OTG: no transceiver configured\n");
|
|
return -EPROBE_DEFER;
|
|
}
|
|
|
|
if (IS_ERR(musb->phy)) {
|
|
dev_err(dev, "HS USB OTG: no PHY configured\n");
|
|
return PTR_ERR(musb->phy);
|
|
}
|
|
musb->isr = omap2430_musb_interrupt;
|
|
phy_init(musb->phy);
|
|
phy_power_on(musb->phy);
|
|
|
|
l = musb_readl(musb->mregs, OTG_INTERFSEL);
|
|
|
|
if (data->interface_type == MUSB_INTERFACE_UTMI) {
|
|
/* OMAP4 uses Internal PHY GS70 which uses UTMI interface */
|
|
l &= ~ULPI_12PIN; /* Disable ULPI */
|
|
l |= UTMI_8BIT; /* Enable UTMI */
|
|
} else {
|
|
l |= ULPI_12PIN;
|
|
}
|
|
|
|
musb_writel(musb->mregs, OTG_INTERFSEL, l);
|
|
|
|
dev_dbg(dev, "HS USB OTG: revision 0x%x, sysconfig 0x%02x, "
|
|
"sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x\n",
|
|
musb_readl(musb->mregs, OTG_REVISION),
|
|
musb_readl(musb->mregs, OTG_SYSCONFIG),
|
|
musb_readl(musb->mregs, OTG_SYSSTATUS),
|
|
musb_readl(musb->mregs, OTG_INTERFSEL),
|
|
musb_readl(musb->mregs, OTG_SIMENABLE));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void omap2430_musb_enable(struct musb *musb)
|
|
{
|
|
struct device *dev = musb->controller;
|
|
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
|
|
|
|
if (glue->status == MUSB_UNKNOWN)
|
|
glue->status = MUSB_VBUS_OFF;
|
|
omap_musb_set_mailbox(glue);
|
|
}
|
|
|
|
static void omap2430_musb_disable(struct musb *musb)
|
|
{
|
|
struct device *dev = musb->controller;
|
|
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
|
|
|
|
if (glue->status != MUSB_UNKNOWN)
|
|
omap_control_usb_set_mode(glue->control_otghs,
|
|
USB_MODE_DISCONNECT);
|
|
}
|
|
|
|
static int omap2430_musb_exit(struct musb *musb)
|
|
{
|
|
struct device *dev = musb->controller;
|
|
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
|
|
|
|
omap2430_low_level_exit(musb);
|
|
phy_power_off(musb->phy);
|
|
phy_exit(musb->phy);
|
|
musb->phy = NULL;
|
|
cancel_work_sync(&glue->omap_musb_mailbox_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct musb_platform_ops omap2430_ops = {
|
|
.quirks = MUSB_DMA_INVENTRA,
|
|
#ifdef CONFIG_USB_INVENTRA_DMA
|
|
.dma_init = musbhs_dma_controller_create,
|
|
.dma_exit = musbhs_dma_controller_destroy,
|
|
#endif
|
|
.init = omap2430_musb_init,
|
|
.exit = omap2430_musb_exit,
|
|
|
|
.enable = omap2430_musb_enable,
|
|
.disable = omap2430_musb_disable,
|
|
|
|
.phy_callback = omap2430_musb_mailbox,
|
|
};
|
|
|
|
static u64 omap2430_dmamask = DMA_BIT_MASK(32);
|
|
|
|
static int omap2430_probe(struct platform_device *pdev)
|
|
{
|
|
struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
|
struct omap_musb_board_data *data;
|
|
struct platform_device *musb;
|
|
struct omap2430_glue *glue;
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct musb_hdrc_config *config;
|
|
struct device_node *control_node;
|
|
struct platform_device *control_pdev;
|
|
int ret = -ENOMEM, val;
|
|
|
|
if (!np)
|
|
return -ENODEV;
|
|
|
|
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
|
|
if (!glue)
|
|
goto err0;
|
|
|
|
musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
|
|
if (!musb) {
|
|
dev_err(&pdev->dev, "failed to allocate musb device\n");
|
|
goto err0;
|
|
}
|
|
|
|
musb->dev.parent = &pdev->dev;
|
|
musb->dev.dma_mask = &omap2430_dmamask;
|
|
musb->dev.coherent_dma_mask = omap2430_dmamask;
|
|
|
|
glue->dev = &pdev->dev;
|
|
glue->musb = musb;
|
|
glue->status = MUSB_UNKNOWN;
|
|
glue->control_otghs = ERR_PTR(-ENODEV);
|
|
|
|
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
|
if (!pdata)
|
|
goto err2;
|
|
|
|
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
|
if (!data)
|
|
goto err2;
|
|
|
|
config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
|
|
if (!config)
|
|
goto err2;
|
|
|
|
of_property_read_u32(np, "mode", (u32 *)&pdata->mode);
|
|
of_property_read_u32(np, "interface-type",
|
|
(u32 *)&data->interface_type);
|
|
of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps);
|
|
of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits);
|
|
of_property_read_u32(np, "power", (u32 *)&pdata->power);
|
|
|
|
ret = of_property_read_u32(np, "multipoint", &val);
|
|
if (!ret && val)
|
|
config->multipoint = true;
|
|
|
|
pdata->board_data = data;
|
|
pdata->config = config;
|
|
|
|
control_node = of_parse_phandle(np, "ctrl-module", 0);
|
|
if (control_node) {
|
|
control_pdev = of_find_device_by_node(control_node);
|
|
if (!control_pdev) {
|
|
dev_err(&pdev->dev, "Failed to get control device\n");
|
|
ret = -EINVAL;
|
|
goto err2;
|
|
}
|
|
glue->control_otghs = &control_pdev->dev;
|
|
}
|
|
|
|
pdata->platform_ops = &omap2430_ops;
|
|
|
|
platform_set_drvdata(pdev, glue);
|
|
|
|
/*
|
|
* REVISIT if we ever have two instances of the wrapper, we will be
|
|
* in big trouble
|
|
*/
|
|
_glue = glue;
|
|
|
|
INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work);
|
|
|
|
ret = platform_device_add_resources(musb, pdev->resource, pdev->num_resources);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to add resources\n");
|
|
goto err2;
|
|
}
|
|
|
|
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to add platform_data\n");
|
|
goto err2;
|
|
}
|
|
|
|
pm_runtime_enable(glue->dev);
|
|
|
|
ret = platform_device_add(musb);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to register musb device\n");
|
|
goto err3;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err3:
|
|
pm_runtime_disable(glue->dev);
|
|
|
|
err2:
|
|
platform_device_put(musb);
|
|
|
|
err0:
|
|
return ret;
|
|
}
|
|
|
|
static int omap2430_remove(struct platform_device *pdev)
|
|
{
|
|
struct omap2430_glue *glue = platform_get_drvdata(pdev);
|
|
|
|
platform_device_unregister(glue->musb);
|
|
pm_runtime_disable(glue->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
static int omap2430_runtime_suspend(struct device *dev)
|
|
{
|
|
struct omap2430_glue *glue = dev_get_drvdata(dev);
|
|
struct musb *musb = glue_to_musb(glue);
|
|
|
|
if (!musb)
|
|
return 0;
|
|
|
|
musb->context.otg_interfsel = musb_readl(musb->mregs,
|
|
OTG_INTERFSEL);
|
|
|
|
omap2430_low_level_exit(musb);
|
|
|
|
if (!glue->phy_suspended) {
|
|
phy_power_off(musb->phy);
|
|
phy_exit(musb->phy);
|
|
}
|
|
|
|
glue->is_runtime_suspended = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int omap2430_runtime_resume(struct device *dev)
|
|
{
|
|
struct omap2430_glue *glue = dev_get_drvdata(dev);
|
|
struct musb *musb = glue_to_musb(glue);
|
|
|
|
if (!musb)
|
|
return 0;
|
|
|
|
if (!glue->phy_suspended) {
|
|
phy_init(musb->phy);
|
|
phy_power_on(musb->phy);
|
|
}
|
|
|
|
omap2430_low_level_init(musb);
|
|
musb_writel(musb->mregs, OTG_INTERFSEL,
|
|
musb->context.otg_interfsel);
|
|
|
|
/* Wait for musb to get oriented. Otherwise we can get babble */
|
|
usleep_range(200000, 250000);
|
|
|
|
glue->is_runtime_suspended = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* I2C and SPI PHYs need to be suspended before the glue layer */
|
|
static int omap2430_suspend(struct device *dev)
|
|
{
|
|
struct omap2430_glue *glue = dev_get_drvdata(dev);
|
|
struct musb *musb = glue_to_musb(glue);
|
|
|
|
phy_power_off(musb->phy);
|
|
phy_exit(musb->phy);
|
|
glue->phy_suspended = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Glue layer needs to be suspended after musb_suspend() */
|
|
static int omap2430_suspend_late(struct device *dev)
|
|
{
|
|
struct omap2430_glue *glue = dev_get_drvdata(dev);
|
|
|
|
if (glue->is_runtime_suspended)
|
|
return 0;
|
|
|
|
glue->needs_resume = 1;
|
|
|
|
return omap2430_runtime_suspend(dev);
|
|
}
|
|
|
|
static int omap2430_resume_early(struct device *dev)
|
|
{
|
|
struct omap2430_glue *glue = dev_get_drvdata(dev);
|
|
|
|
if (!glue->needs_resume)
|
|
return 0;
|
|
|
|
glue->needs_resume = 0;
|
|
|
|
return omap2430_runtime_resume(dev);
|
|
}
|
|
|
|
static int omap2430_resume(struct device *dev)
|
|
{
|
|
struct omap2430_glue *glue = dev_get_drvdata(dev);
|
|
struct musb *musb = glue_to_musb(glue);
|
|
|
|
phy_init(musb->phy);
|
|
phy_power_on(musb->phy);
|
|
glue->phy_suspended = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops omap2430_pm_ops = {
|
|
.runtime_suspend = omap2430_runtime_suspend,
|
|
.runtime_resume = omap2430_runtime_resume,
|
|
.suspend = omap2430_suspend,
|
|
.suspend_late = omap2430_suspend_late,
|
|
.resume_early = omap2430_resume_early,
|
|
.resume = omap2430_resume,
|
|
};
|
|
|
|
#define DEV_PM_OPS (&omap2430_pm_ops)
|
|
#else
|
|
#define DEV_PM_OPS NULL
|
|
#endif
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id omap2430_id_table[] = {
|
|
{
|
|
.compatible = "ti,omap4-musb"
|
|
},
|
|
{
|
|
.compatible = "ti,omap3-musb"
|
|
},
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, omap2430_id_table);
|
|
#endif
|
|
|
|
static struct platform_driver omap2430_driver = {
|
|
.probe = omap2430_probe,
|
|
.remove = omap2430_remove,
|
|
.driver = {
|
|
.name = "musb-omap2430",
|
|
.pm = DEV_PM_OPS,
|
|
.of_match_table = of_match_ptr(omap2430_id_table),
|
|
},
|
|
};
|
|
|
|
module_platform_driver(omap2430_driver);
|
|
|
|
MODULE_DESCRIPTION("OMAP2PLUS MUSB Glue Layer");
|
|
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
|
MODULE_LICENSE("GPL v2");
|