diff options
Diffstat (limited to 'drivers/usb/musb/omap2430.c')
| -rw-r--r-- | drivers/usb/musb/omap2430.c | 466 |
1 files changed, 237 insertions, 229 deletions
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 456f3e6ecf03..48bb9bfb2204 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2005-2007 by Texas Instruments * Some code has been taken from tusb6010.c @@ -6,23 +7,6 @@ * Tony Lindgren <tony@atomide.com> * * This file is part of the Inventra Controller Driver for Linux. - * - * The Inventra Controller Driver for Linux is free software; you - * can redistribute it and/or modify it under the terms of the GNU - * General Public License version 2 as published by the Free Software - * Foundation. - * - * The Inventra Controller Driver for Linux is distributed in - * the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - * License for more details. - * - * You should have received a copy of the GNU General Public License - * along with The Inventra Controller Driver for Linux ; if not, - * write to the Free Software Foundation, Inc., 59 Temple Place, - * Suite 330, Boston, MA 02111-1307 USA - * */ #include <linux/module.h> #include <linux/kernel.h> @@ -31,6 +15,7 @@ #include <linux/list.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/pm_runtime.h> @@ -49,76 +34,14 @@ struct omap2430_glue { 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 void omap2430_musb_set_vbus(struct musb *musb, int is_on) -{ - struct usb_otg *otg = musb->xceiv->otg; - u8 devctl; - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - /* HDRC controls CPEN, but beware current surges during device - * connect. They can trigger transient overcurrent conditions - * that must be ignored. - */ - - devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - - if (is_on) { - if (musb->xceiv->otg->state == OTG_STATE_A_IDLE) { - int loops = 100; - /* start the session */ - devctl |= MUSB_DEVCTL_SESSION; - musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - /* - * Wait for the musb to set as A device to enable the - * VBUS - */ - while (musb_readb(musb->mregs, MUSB_DEVCTL) & - MUSB_DEVCTL_BDEVICE) { - - mdelay(5); - cpu_relax(); - - if (time_after(jiffies, timeout) - || loops-- <= 0) { - dev_err(musb->controller, - "configured as A device timeout"); - break; - } - } - - otg_set_vbus(otg, 1); - } else { - musb->is_active = 1; - otg->default_a = 1; - musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; - devctl |= MUSB_DEVCTL_SESSION; - MUSB_HST_MODE(musb); - } - } else { - musb->is_active = 0; - - /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and - * jumping right to B_IDLE... - */ - - otg->default_a = 0; - musb->xceiv->otg->state = OTG_STATE_B_IDLE; - devctl &= ~MUSB_DEVCTL_SESSION; - - MUSB_DEV_MODE(musb); - } - musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - - dev_dbg(musb->controller, "VBUS %s, devctl %02x " - /* otg %3x conf %08x prcm %08x */ "\n", - usb_otg_state_string(musb->xceiv->otg->state), - musb_readb(musb->mregs, MUSB_DEVCTL)); -} - static inline void omap2430_low_level_exit(struct musb *musb) { u32 l; @@ -158,33 +81,58 @@ static int omap2430_musb_mailbox(enum musb_vbus_id_status status) 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); - struct musb_hdrc_platform_data *pdata = - dev_get_platdata(musb->controller); - struct omap_musb_board_data *data = pdata->board_data; - struct usb_otg *otg = musb->xceiv->otg; + 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"); - - otg->default_a = true; - 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); - omap2430_musb_set_vbus(musb, 1); + 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"); - otg->default_a = false; 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); @@ -195,19 +143,14 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) dev_dbg(musb->controller, "VBUS Disconnect\n"); musb->xceiv->last_event = USB_EVENT_NONE; - if (musb->gadget_driver) - omap2430_musb_set_vbus(musb, 0); - - if (data->interface_type == MUSB_INTERFACE_UTMI) - otg_set_vbus(musb->xceiv->otg, 0); - + 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); @@ -247,7 +190,6 @@ static int omap2430_musb_init(struct musb *musb) u32 l; int status = 0; struct device *dev = musb->controller; - struct omap2430_glue *glue = dev_get_drvdata(dev->parent); struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); struct omap_musb_board_data *data = plat->board_data; @@ -255,21 +197,15 @@ static int omap2430_musb_init(struct musb *musb) * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. */ - if (dev->parent->of_node) { - 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); - } else { - musb->xceiv = devm_usb_get_phy_dev(dev, 0); - musb->phy = devm_phy_get(dev, "usb"); - } + 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); @@ -309,50 +245,17 @@ static int omap2430_musb_init(struct musb *musb) musb_readl(musb->mregs, OTG_INTERFSEL), musb_readl(musb->mregs, OTG_SIMENABLE)); - if (glue->status != MUSB_UNKNOWN) - omap_musb_set_mailbox(glue); - return 0; } static void omap2430_musb_enable(struct musb *musb) { - u8 devctl; - unsigned long timeout = jiffies + msecs_to_jiffies(1000); struct device *dev = musb->controller; struct omap2430_glue *glue = dev_get_drvdata(dev->parent); - struct musb_hdrc_platform_data *pdata = dev_get_platdata(dev); - struct omap_musb_board_data *data = pdata->board_data; - - switch (glue->status) { - - case MUSB_ID_GROUND: - omap_control_usb_set_mode(glue->control_otghs, USB_MODE_HOST); - if (data->interface_type != MUSB_INTERFACE_UTMI) - break; - devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - /* start the session */ - devctl |= MUSB_DEVCTL_SESSION; - musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - while (musb_readb(musb->mregs, MUSB_DEVCTL) & - MUSB_DEVCTL_BDEVICE) { - cpu_relax(); - - if (time_after(jiffies, timeout)) { - dev_err(dev, "configured as A device timeout"); - break; - } - } - break; - - case MUSB_VBUS_VALID: - omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE); - break; - - default: - break; - } + if (glue->status == MUSB_UNKNOWN) + glue->status = MUSB_VBUS_OFF; + omap_musb_set_mailbox(glue); } static void omap2430_musb_disable(struct musb *musb) @@ -388,8 +291,6 @@ static const struct musb_platform_ops omap2430_ops = { .init = omap2430_musb_init, .exit = omap2430_musb_exit, - .set_vbus = omap2430_musb_set_vbus, - .enable = omap2430_musb_enable, .disable = omap2430_musb_disable, @@ -400,75 +301,87 @@ static u64 omap2430_dmamask = DMA_BIT_MASK(32); static int omap2430_probe(struct platform_device *pdev) { - struct resource musb_resources[3]; 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; + bool populate_irqs = false; + + if (!np) + return -ENODEV; glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) - goto err0; + return -ENOMEM; musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); - if (!musb) { - dev_err(&pdev->dev, "failed to allocate musb device\n"); - goto err0; - } + if (!musb) + return -ENOMEM; musb->dev.parent = &pdev->dev; musb->dev.dma_mask = &omap2430_dmamask; musb->dev.coherent_dma_mask = omap2430_dmamask; + /* + * Legacy SoCs using omap_device get confused if node is moved + * because of interconnect properties mixed into the node. + */ + if (of_property_present(np, "ti,hwmods")) { + dev_warn(&pdev->dev, "please update to probe with ti-sysc\n"); + populate_irqs = true; + } else { + device_set_of_node_from_dev(&musb->dev, &pdev->dev); + } + of_node_put(np); + glue->dev = &pdev->dev; glue->musb = musb; glue->status = MUSB_UNKNOWN; glue->control_otghs = ERR_PTR(-ENODEV); - if (np) { - struct device_node *control_node; - struct platform_device *control_pdev; - - 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 = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + goto err_put_musb; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + goto err_put_musb; + + config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); + if (!config) + goto err_put_musb; + + 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); + of_node_put(control_node); + if (!control_pdev) { + dev_err(&pdev->dev, "Failed to get control device\n"); + ret = -EINVAL; + goto err_put_musb; } + glue->control_otghs = &control_pdev->dev; } + pdata->platform_ops = &omap2430_ops; platform_set_drvdata(pdev, glue); @@ -481,35 +394,58 @@ static int omap2430_probe(struct platform_device *pdev) INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work); - memset(musb_resources, 0x00, sizeof(*musb_resources) * - ARRAY_SIZE(musb_resources)); + ret = platform_device_add_resources(musb, pdev->resource, pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "failed to add resources\n"); + goto err_put_control_otghs; + } - musb_resources[0].name = pdev->resource[0].name; - musb_resources[0].start = pdev->resource[0].start; - musb_resources[0].end = pdev->resource[0].end; - musb_resources[0].flags = pdev->resource[0].flags; + if (populate_irqs) { + struct resource musb_res[3]; + struct resource *res; + int i = 0; - musb_resources[1].name = pdev->resource[1].name; - musb_resources[1].start = pdev->resource[1].start; - musb_resources[1].end = pdev->resource[1].end; - musb_resources[1].flags = pdev->resource[1].flags; + memset(musb_res, 0, sizeof(*musb_res) * ARRAY_SIZE(musb_res)); - musb_resources[2].name = pdev->resource[2].name; - musb_resources[2].start = pdev->resource[2].start; - musb_resources[2].end = pdev->resource[2].end; - musb_resources[2].flags = pdev->resource[2].flags; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -EINVAL; + goto err_put_control_otghs; + } - ret = platform_device_add_resources(musb, musb_resources, - ARRAY_SIZE(musb_resources)); - if (ret) { - dev_err(&pdev->dev, "failed to add resources\n"); - goto err2; + musb_res[i].start = res->start; + musb_res[i].end = res->end; + musb_res[i].flags = res->flags; + musb_res[i].name = res->name; + i++; + + ret = of_irq_get_byname(np, "mc"); + if (ret > 0) { + musb_res[i].start = ret; + musb_res[i].flags = IORESOURCE_IRQ; + musb_res[i].name = "mc"; + i++; + } + + ret = of_irq_get_byname(np, "dma"); + if (ret > 0) { + musb_res[i].start = ret; + musb_res[i].flags = IORESOURCE_IRQ; + musb_res[i].name = "dma"; + i++; + } + + ret = platform_device_add_resources(musb, musb_res, i); + if (ret) { + dev_err(&pdev->dev, "failed to add IRQ resources\n"); + goto err_put_control_otghs; + } } ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); if (ret) { dev_err(&pdev->dev, "failed to add platform_data\n"); - goto err2; + goto err_put_control_otghs; } pm_runtime_enable(glue->dev); @@ -517,29 +453,30 @@ static int omap2430_probe(struct platform_device *pdev) ret = platform_device_add(musb); if (ret) { dev_err(&pdev->dev, "failed to register musb device\n"); - goto err3; + goto err_disable_rpm; } return 0; -err3: +err_disable_rpm: pm_runtime_disable(glue->dev); - -err2: +err_put_control_otghs: + if (!IS_ERR(glue->control_otghs)) + put_device(glue->control_otghs); +err_put_musb: platform_device_put(musb); -err0: return ret; } -static int omap2430_remove(struct platform_device *pdev) +static void 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; + if (!IS_ERR(glue->control_otghs)) + put_device(glue->control_otghs); } #ifdef CONFIG_PM @@ -557,6 +494,13 @@ static int omap2430_runtime_suspend(struct device *dev) 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; } @@ -568,16 +512,80 @@ static int omap2430_runtime_resume(struct device *dev) 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) |
