#include <linux/usb/msm_hsusb.h>
#include <linux/usb/msm_hsusb_hw.h>
#include <linux/regulator/consumer.h>
+#include <linux/msm-bus.h>
#define MSM_USB_BASE (motg->regs)
#define DRIVER_NAME "msm_otg"
{
int ret = 0;
+ if (IS_ERR(motg->vddcx))
+ return 0;
+
if (init) {
ret = regulator_set_voltage(motg->vddcx,
motg->vdd_levels[VDD_LEVEL_MIN],
* Kick the state machine work, if peripheral is not supported
* or peripheral is already registered with us.
*/
- if (motg->pdata->mode == USB_DR_MODE_HOST || otg->gadget) {
+ if (motg->pdata->mode == USB_DR_MODE_HOST ||
+ motg->pdata->mode == USB_DR_MODE_OTG || otg->gadget) {
pm_runtime_get_sync(otg->usb_phy->dev);
schedule_work(&motg->sm_work);
}
* Kick the state machine work, if host is not supported
* or host is already registered with us.
*/
- if (motg->pdata->mode == USB_DR_MODE_PERIPHERAL || otg->host) {
+ if (motg->pdata->mode == USB_DR_MODE_PERIPHERAL ||
+ motg->pdata->mode == USB_DR_MODE_OTG || otg->host) {
pm_runtime_get_sync(otg->usb_phy->dev);
schedule_work(&motg->sm_work);
}
{
struct msm_otg_platform_data *pdata;
struct extcon_dev *ext_id, *ext_vbus;
- const struct of_device_id *id;
struct device_node *node = pdev->dev.of_node;
struct property *prop;
int len, ret, words;
motg->pdata = pdata;
- id = of_match_device(msm_otg_dt_match, &pdev->dev);
- pdata->phy_type = (enum msm_usb_phy_type) id->data;
+ pdata->phy_type = (enum msm_usb_phy_type)of_device_get_match_data(&pdev->dev);
+ if (!pdata->phy_type)
+ return 1;
motg->link_rst = devm_reset_control_get(&pdev->dev, "link");
if (IS_ERR(motg->link_rst))
&motg->id.nb);
if (ret < 0) {
dev_err(&pdev->dev, "register ID notifier failed\n");
+ extcon_unregister_notifier(motg->vbus.extcon,
+ EXTCON_USB, &motg->vbus.nb);
return ret;
}
return NOTIFY_DONE;
}
+static void msm_otg_bus_vote(struct msm_otg *motg, enum usb_bus_vote vote)
+{
+ int ret;
+
+ if (motg->bus_perf_client) {
+ ret = msm_bus_scale_client_update_request(
+ motg->bus_perf_client, vote);
+ if (ret)
+ dev_err(motg->phy.dev, "%s: Failed to vote (%d)\n"
+ "for bus bw %d\n", __func__, vote, ret);
+ }
+}
+
static int msm_otg_probe(struct platform_device *pdev)
{
- struct regulator_bulk_data regs[3];
+ struct regulator_bulk_data regs[2];
int ret = 0;
struct device_node *np = pdev->dev.of_node;
struct msm_otg_platform_data *pdata;
if (!motg)
return -ENOMEM;
- pdata = dev_get_platdata(&pdev->dev);
- if (!pdata) {
- if (!np)
- return -ENXIO;
- ret = msm_otg_read_dt(pdev, motg);
- if (ret)
- return ret;
- }
-
motg->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
GFP_KERNEL);
if (!motg->phy.otg)
phy = &motg->phy;
phy->dev = &pdev->dev;
+ INIT_WORK(&motg->sm_work, msm_otg_sm_work);
+ INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work);
motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk");
if (IS_ERR(motg->clk)) {
np ? "alt_core" : "usb_hs_core_clk");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -EINVAL;
- motg->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!motg->regs)
- return -ENOMEM;
+ motg->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(motg->regs))
+ return PTR_ERR(motg->regs);
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ if (!np)
+ return -ENXIO;
+ ret = msm_otg_read_dt(pdev, motg);
+ if (ret)
+ return ret;
+ }
/*
* NOTE: The PHYs can be multiplexed between the chipidea controller
*/
if (motg->phy_number) {
phy_select = devm_ioremap_nocache(&pdev->dev, USB2_PHY_SEL, 4);
- if (!phy_select)
- return -ENOMEM;
+ if (!phy_select) {
+ ret = -ENOMEM;
+ goto unregister_extcon;
+ }
/* Enable second PHY with the OTG port */
writel(0x1, phy_select);
}
motg->irq = platform_get_irq(pdev, 0);
if (motg->irq < 0) {
dev_err(&pdev->dev, "platform_get_irq failed\n");
- return motg->irq;
+ ret = motg->irq;
+ goto unregister_extcon;
}
- regs[0].supply = "vddcx";
- regs[1].supply = "v3p3";
- regs[2].supply = "v1p8";
+ regs[0].supply = "v3p3";
+ regs[1].supply = "v1p8";
ret = devm_regulator_bulk_get(motg->phy.dev, ARRAY_SIZE(regs), regs);
- if (ret)
- return ret;
+ if (ret) {
+ dev_err(&pdev->dev, "no v3p3 or v1p8\n");
+ goto unregister_extcon;
+ }
+
+ motg->v3p3 = regs[0].consumer;
+ motg->v1p8 = regs[1].consumer;
- motg->vddcx = regs[0].consumer;
- motg->v3p3 = regs[1].consumer;
- motg->v1p8 = regs[2].consumer;
+ motg->vddcx = devm_regulator_get_optional(motg->phy.dev, "vddcx");
+ if (IS_ERR(motg->vddcx))
+ dev_info(&pdev->dev, "no vddcx\n");
clk_set_rate(motg->clk, 60000000);
writel(0, USB_USBINTR);
writel(0, USB_OTGSC);
- INIT_WORK(&motg->sm_work, msm_otg_sm_work);
- INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work);
ret = devm_request_irq(&pdev->dev, motg->irq, msm_otg_irq, IRQF_SHARED,
"msm_otg", motg);
if (ret) {
goto disable_ldo;
}
+ motg->pdata->bus_scale_table = msm_bus_cl_get_pdata(pdev);
+ if (!motg->pdata->bus_scale_table)
+ dev_dbg(&pdev->dev, "bus scaling is disabled\n");
+ else {
+ motg->bus_perf_client =
+ msm_bus_scale_register_client(motg->pdata->bus_scale_table);
+ if (!motg->bus_perf_client)
+ dev_err(motg->phy.dev, "%s: Failed to register BUS\n"
+ "scaling client!!\n", __func__);
+ }
+ /* Hack to max out usb performace */
+ msm_otg_bus_vote(motg, USB_MAX_PERF_VOTE);
+
platform_set_drvdata(pdev, motg);
device_init_wakeup(&pdev->dev, 1);
register_reboot_notifier(&motg->reboot);
pm_runtime_set_active(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
return 0;
clk_disable_unprepare(motg->clk);
if (!IS_ERR(motg->core_clk))
clk_disable_unprepare(motg->core_clk);
+unregister_extcon:
+ extcon_unregister_notifier(motg->id.extcon,
+ EXTCON_USB_HOST, &motg->id.nb);
+ extcon_unregister_notifier(motg->vbus.extcon,
+ EXTCON_USB, &motg->vbus.nb);
+
return ret;
}
usb_remove_phy(phy);
disable_irq(motg->irq);
+ msm_bus_scale_unregister_client(motg->bus_perf_client);
/*
* Put PHY in low power mode.