summaryrefslogtreecommitdiff
path: root/drivers/usb/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/phy')
-rw-r--r--drivers/usb/phy/Kconfig14
-rw-r--r--drivers/usb/phy/Makefile1
-rw-r--r--drivers/usb/phy/phy-generic.c6
-rw-r--r--drivers/usb/phy/phy-isp1301.c1
-rw-r--r--drivers/usb/phy/phy-keystone.c6
-rw-r--r--drivers/usb/phy/phy-msm-usb.c67
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c6
-rw-r--r--drivers/usb/phy/phy-omap-otg.c22
-rw-r--r--drivers/usb/phy/phy-qcom-8x16-usb.c436
-rw-r--r--drivers/usb/phy/phy-tahvo.c27
10 files changed, 539 insertions, 47 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 869c0cfca..173132416 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -152,6 +152,20 @@ config USB_MSM_OTG
This driver is not supported on boards like trout which
has an external PHY.
+config USB_QCOM_8X16_PHY
+ tristate "Qualcomm APQ8016/MSM8916 on-chip USB PHY controller support"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on RESET_CONTROLLER && EXTCON
+ select USB_PHY
+ select USB_ULPI_VIEWPORT
+ help
+ Enable this to support the USB transceiver on Qualcomm 8x16 chipsets.
+ It handles PHY initialization, clock management, power management,
+ and workarounds required after resetting the hardware.
+
+ To compile this driver as a module, choose M here: the
+ module will be called phy-qcom-8x16-usb.
+
config USB_MV_OTG
tristate "Marvell USB OTG support"
depends on USB_EHCI_MV && USB_MV_UDC && PM
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index e36ab1d46..19c0dccbb 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o
obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o
obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o
obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o
+obj-$(CONFIG_USB_QCOM_8X16_PHY) += phy-qcom-8x16-usb.o
obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o
obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index 0cd85f2cc..5320cb864 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -218,11 +218,13 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
clk_rate = 0;
needs_vcc = of_property_read_bool(node, "vcc-supply");
- nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset");
+ nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_ASIS);
err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
if (!err) {
nop->gpiod_vbus = devm_gpiod_get_optional(dev,
- "vbus-detect");
+ "vbus-detect",
+ GPIOD_ASIS);
err = PTR_ERR_OR_ZERO(nop->gpiod_vbus);
}
} else if (pdata) {
diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c
index 8a55b37d1..db6815656 100644
--- a/drivers/usb/phy/phy-isp1301.c
+++ b/drivers/usb/phy/phy-isp1301.c
@@ -31,6 +31,7 @@ static const struct i2c_device_id isp1301_id[] = {
{ "isp1301", 0 },
{ }
};
+MODULE_DEVICE_TABLE(i2c, isp1301_id);
static struct i2c_client *isp1301_i2c_client;
diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c
index e0556f783..01d4e4cdb 100644
--- a/drivers/usb/phy/phy-keystone.c
+++ b/drivers/usb/phy/phy-keystone.c
@@ -96,11 +96,7 @@ static int keystone_usbphy_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, k_phy);
- ret = usb_add_phy_dev(&k_phy->usb_phy_gen.phy);
- if (ret)
- return ret;
-
- return 0;
+ return usb_add_phy_dev(&k_phy->usb_phy_gen.phy);
}
static int keystone_usbphy_remove(struct platform_device *pdev)
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index 00c49bb1b..c58c3c0db 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/slab.h>
@@ -32,6 +33,7 @@
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/reboot.h>
#include <linux/reset.h>
#include <linux/usb.h>
@@ -1471,6 +1473,14 @@ static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event,
else
clear_bit(B_SESS_VLD, &motg->inputs);
+ if (test_bit(B_SESS_VLD, &motg->inputs)) {
+ /* Switch D+/D- lines to Device connector */
+ gpiod_set_value_cansleep(motg->switch_gpio, 0);
+ } else {
+ /* Switch D+/D- lines to Hub */
+ gpiod_set_value_cansleep(motg->switch_gpio, 1);
+ }
+
schedule_work(&motg->sm_work);
return NOTIFY_DONE;
@@ -1546,6 +1556,11 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup");
+ motg->switch_gpio = devm_gpiod_get_optional(&pdev->dev, "switch",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(motg->switch_gpio))
+ return PTR_ERR(motg->switch_gpio);
+
ext_id = ERR_PTR(-ENODEV);
ext_vbus = ERR_PTR(-ENODEV);
if (of_property_read_bool(node, "extcon")) {
@@ -1561,15 +1576,16 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
}
if (!IS_ERR(ext_vbus)) {
+ motg->vbus.extcon = ext_vbus;
motg->vbus.nb.notifier_call = msm_otg_vbus_notifier;
- ret = extcon_register_interest(&motg->vbus.conn, ext_vbus->name,
- "USB", &motg->vbus.nb);
+ ret = extcon_register_notifier(ext_vbus, EXTCON_USB,
+ &motg->vbus.nb);
if (ret < 0) {
dev_err(&pdev->dev, "register VBUS notifier failed\n");
return ret;
}
- ret = extcon_get_cable_state(ext_vbus, "USB");
+ ret = extcon_get_cable_state_(ext_vbus, EXTCON_USB);
if (ret)
set_bit(B_SESS_VLD, &motg->inputs);
else
@@ -1577,15 +1593,16 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
}
if (!IS_ERR(ext_id)) {
+ motg->id.extcon = ext_id;
motg->id.nb.notifier_call = msm_otg_id_notifier;
- ret = extcon_register_interest(&motg->id.conn, ext_id->name,
- "USB-HOST", &motg->id.nb);
+ ret = extcon_register_notifier(ext_id, EXTCON_USB_HOST,
+ &motg->id.nb);
if (ret < 0) {
dev_err(&pdev->dev, "register ID notifier failed\n");
return ret;
}
- ret = extcon_get_cable_state(ext_id, "USB-HOST");
+ ret = extcon_get_cable_state_(ext_id, EXTCON_USB_HOST);
if (ret)
clear_bit(ID, &motg->inputs);
else
@@ -1615,6 +1632,19 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
return 0;
}
+static int msm_otg_reboot_notify(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ struct msm_otg *motg = container_of(this, struct msm_otg, reboot);
+
+ /*
+ * Ensure that D+/D- lines are routed to uB connector, so
+ * we could load bootloader/kernel at next reboot
+ */
+ gpiod_set_value_cansleep(motg->switch_gpio, 0);
+ return NOTIFY_DONE;
+}
+
static int msm_otg_probe(struct platform_device *pdev)
{
struct regulator_bulk_data regs[3];
@@ -1779,6 +1809,17 @@ static int msm_otg_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "Can not create mode change file\n");
}
+ if (test_bit(B_SESS_VLD, &motg->inputs)) {
+ /* Switch D+/D- lines to Device connector */
+ gpiod_set_value_cansleep(motg->switch_gpio, 0);
+ } else {
+ /* Switch D+/D- lines to Hub */
+ gpiod_set_value_cansleep(motg->switch_gpio, 1);
+ }
+
+ motg->reboot.notifier_call = msm_otg_reboot_notify;
+ register_reboot_notifier(&motg->reboot);
+
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
@@ -1805,10 +1846,16 @@ static int msm_otg_remove(struct platform_device *pdev)
if (phy->otg->host || phy->otg->gadget)
return -EBUSY;
- if (motg->id.conn.edev)
- extcon_unregister_interest(&motg->id.conn);
- if (motg->vbus.conn.edev)
- extcon_unregister_interest(&motg->vbus.conn);
+ unregister_reboot_notifier(&motg->reboot);
+
+ /*
+ * Ensure that D+/D- lines are routed to uB connector, so
+ * we could load bootloader/kernel at next reboot
+ */
+ gpiod_set_value_cansleep(motg->switch_gpio, 0);
+
+ extcon_unregister_notifier(motg->id.extcon, EXTCON_USB_HOST, &motg->id.nb);
+ extcon_unregister_notifier(motg->vbus.extcon, EXTCON_USB, &motg->vbus.nb);
msm_otg_debugfs_cleanup();
cancel_delayed_work_sync(&motg->chg_work);
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index 3fcc0483a..4d863ebc1 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -506,11 +506,7 @@ static int mxs_phy_probe(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true);
- ret = usb_add_phy_dev(&mxs_phy->phy);
- if (ret)
- return ret;
-
- return 0;
+ return usb_add_phy_dev(&mxs_phy->phy);
}
static int mxs_phy_remove(struct platform_device *pdev)
diff --git a/drivers/usb/phy/phy-omap-otg.c b/drivers/usb/phy/phy-omap-otg.c
index 56ee76030..c4bf2de6d 100644
--- a/drivers/usb/phy/phy-omap-otg.c
+++ b/drivers/usb/phy/phy-omap-otg.c
@@ -30,8 +30,7 @@ struct otg_device {
void __iomem *base;
bool id;
bool vbus;
- struct extcon_specific_cable_nb vbus_dev;
- struct extcon_specific_cable_nb id_dev;
+ struct extcon_dev *extcon;
struct notifier_block vbus_nb;
struct notifier_block id_nb;
};
@@ -115,23 +114,23 @@ static int omap_otg_probe(struct platform_device *pdev)
if (IS_ERR(otg_dev->base))
return PTR_ERR(otg_dev->base);
+ otg_dev->extcon = extcon;
otg_dev->id_nb.notifier_call = omap_otg_id_notifier;
otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier;
- ret = extcon_register_interest(&otg_dev->id_dev, config->extcon,
- "USB-HOST", &otg_dev->id_nb);
+ ret = extcon_register_notifier(extcon, EXTCON_USB_HOST, &otg_dev->id_nb);
if (ret)
return ret;
- ret = extcon_register_interest(&otg_dev->vbus_dev, config->extcon,
- "USB", &otg_dev->vbus_nb);
+ ret = extcon_register_notifier(extcon, EXTCON_USB, &otg_dev->vbus_nb);
if (ret) {
- extcon_unregister_interest(&otg_dev->id_dev);
+ extcon_unregister_notifier(extcon, EXTCON_USB_HOST,
+ &otg_dev->id_nb);
return ret;
}
- otg_dev->id = extcon_get_cable_state(extcon, "USB-HOST");
- otg_dev->vbus = extcon_get_cable_state(extcon, "USB");
+ otg_dev->id = extcon_get_cable_state_(extcon, EXTCON_USB_HOST);
+ otg_dev->vbus = extcon_get_cable_state_(extcon, EXTCON_USB);
omap_otg_set_mode(otg_dev);
rev = readl(otg_dev->base);
@@ -147,9 +146,10 @@ static int omap_otg_probe(struct platform_device *pdev)
static int omap_otg_remove(struct platform_device *pdev)
{
struct otg_device *otg_dev = platform_get_drvdata(pdev);
+ struct extcon_dev *edev = otg_dev->extcon;
- extcon_unregister_interest(&otg_dev->id_dev);
- extcon_unregister_interest(&otg_dev->vbus_dev);
+ extcon_unregister_notifier(edev, EXTCON_USB_HOST,&otg_dev->id_nb);
+ extcon_unregister_notifier(edev, EXTCON_USB, &otg_dev->vbus_nb);
return 0;
}
diff --git a/drivers/usb/phy/phy-qcom-8x16-usb.c b/drivers/usb/phy/phy-qcom-8x16-usb.c
new file mode 100644
index 000000000..5d357a945
--- /dev/null
+++ b/drivers/usb/phy/phy-qcom-8x16-usb.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/gpio/consumer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/ulpi.h>
+
+#define HSPHY_AHBBURST 0x0090
+#define HSPHY_AHBMODE 0x0098
+#define HSPHY_GENCONFIG 0x009c
+#define HSPHY_GENCONFIG_2 0x00a0
+
+#define HSPHY_USBCMD 0x0140
+#define HSPHY_ULPI_VIEWPORT 0x0170
+#define HSPHY_CTRL 0x0240
+
+#define HSPHY_TXFIFO_IDLE_FORCE_DIS BIT(4)
+#define HSPHY_SESS_VLD_CTRL_EN BIT(7)
+#define HSPHY_POR_ASSERT BIT(0)
+#define HSPHY_RETEN BIT(1)
+
+#define HSPHY_SESS_VLD_CTRL BIT(25)
+
+#define ULPI_PWR_CLK_MNG_REG 0x88
+#define ULPI_PWR_OTG_COMP_DISABLE BIT(0)
+
+#define ULPI_MISC_A 0x96
+#define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1)
+#define ULPI_MISC_A_VBUSVLDEXT BIT(0)
+
+#define HSPHY_3P3_MIN 3050000 /* uV */
+#define HSPHY_3P3_MAX 3300000 /* uV */
+
+#define HSPHY_1P8_MIN 1800000 /* uV */
+#define HSPHY_1P8_MAX 1800000 /* uV */
+
+#define HSPHY_VDD_MIN 5
+#define HSPHY_VDD_MAX 7
+
+struct phy_8x16 {
+ struct usb_phy phy;
+ void __iomem *regs;
+ struct clk *core_clk;
+ struct clk *iface_clk;
+ struct regulator *v3p3;
+ struct regulator *v1p8;
+ struct regulator *vdd;
+
+ struct reset_control *phy_reset;
+
+ struct extcon_specific_cable_nb vbus_cable;
+ struct notifier_block vbus_notify;
+
+ struct gpio_desc *switch_gpio;
+ struct notifier_block reboot_notify;
+};
+
+static int phy_8x16_regulators_enable(struct phy_8x16 *qphy)
+{
+ int ret;
+
+ ret = regulator_set_voltage(qphy->vdd, HSPHY_VDD_MIN, HSPHY_VDD_MAX);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(qphy->vdd);
+ if (ret)
+ return ret;
+
+ ret = regulator_set_voltage(qphy->v3p3, HSPHY_3P3_MIN, HSPHY_3P3_MAX);
+ if (ret)
+ goto off_vdd;
+
+ ret = regulator_enable(qphy->v3p3);
+ if (ret)
+ goto off_vdd;
+
+ ret = regulator_set_voltage(qphy->v1p8, HSPHY_1P8_MIN, HSPHY_1P8_MAX);
+ if (ret)
+ goto off_3p3;
+
+ ret = regulator_enable(qphy->v1p8);
+ if (ret)
+ goto off_3p3;
+
+ return 0;
+
+off_3p3:
+ regulator_disable(qphy->v3p3);
+off_vdd:
+ regulator_disable(qphy->vdd);
+
+ return ret;
+}
+
+static void phy_8x16_regulators_disable(struct phy_8x16 *qphy)
+{
+ regulator_disable(qphy->v1p8);
+ regulator_disable(qphy->v3p3);
+ regulator_disable(qphy->vdd);
+}
+
+static int phy_8x16_notify_connect(struct usb_phy *phy,
+ enum usb_device_speed speed)
+{
+ struct phy_8x16 *qphy = container_of(phy, struct phy_8x16, phy);
+ u32 val;
+
+ val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT;
+ usb_phy_io_write(&qphy->phy, val, ULPI_SET(ULPI_MISC_A));
+
+ val = readl(qphy->regs + HSPHY_USBCMD);
+ val |= HSPHY_SESS_VLD_CTRL;
+ writel(val, qphy->regs + HSPHY_USBCMD);
+
+ return 0;
+}
+
+static int phy_8x16_notify_disconnect(struct usb_phy *phy,
+ enum usb_device_speed speed)
+{
+ struct phy_8x16 *qphy = container_of(phy, struct phy_8x16, phy);
+ u32 val;
+
+ val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL;
+ usb_phy_io_write(&qphy->phy, val, ULPI_CLR(ULPI_MISC_A));
+
+ val = readl(qphy->regs + HSPHY_USBCMD);
+ val &= ~HSPHY_SESS_VLD_CTRL;
+ writel(val, qphy->regs + HSPHY_USBCMD);
+
+ return 0;
+}
+
+static int phy_8x16_vbus_on(struct phy_8x16 *qphy)
+{
+ phy_8x16_notify_connect(&qphy->phy, USB_SPEED_UNKNOWN);
+
+ /* Switch D+/D- lines to Device connector */
+ gpiod_set_value_cansleep(qphy->switch_gpio, 0);
+
+ return 0;
+}
+
+static int phy_8x16_vbus_off(struct phy_8x16 *qphy)
+{
+ phy_8x16_notify_disconnect(&qphy->phy, USB_SPEED_UNKNOWN);
+
+ /* Switch D+/D- lines to USB HUB */
+ gpiod_set_value_cansleep(qphy->switch_gpio, 1);
+
+ return 0;
+}
+
+static int phy_8x16_vbus_notify(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct phy_8x16 *qphy = container_of(nb, struct phy_8x16, vbus_notify);
+
+ if (event)
+ phy_8x16_vbus_on(qphy);
+ else
+ phy_8x16_vbus_off(qphy);
+
+ return NOTIFY_DONE;
+}
+
+static int phy_8x16_init(struct usb_phy *phy)
+{
+ struct phy_8x16 *qphy = container_of(phy, struct phy_8x16, phy);
+ u32 val, init[] = {0x44, 0x6B, 0x24, 0x13};
+ u32 addr = ULPI_EXT_VENDOR_SPECIFIC;
+ int idx, state;
+
+ for (idx = 0; idx < ARRAY_SIZE(init); idx++)
+ usb_phy_io_write(phy, init[idx], addr + idx);
+
+ reset_control_reset(qphy->phy_reset);
+
+ /* Assert USB HSPHY_POR */
+ val = readl(qphy->regs + HSPHY_CTRL);
+ val |= HSPHY_POR_ASSERT;
+ writel(val, qphy->regs + HSPHY_CTRL);
+
+ /*
+ * wait for minimum 10 microseconds as suggested in HPG.
+ * Use a slightly larger value since the exact value didn't
+ * work 100% of the time.
+ */
+ usleep_range(12, 15);
+
+ /* Deassert USB HSPHY_POR */
+ val = readl(qphy->regs + HSPHY_CTRL);
+ val &= ~HSPHY_POR_ASSERT;
+ writel(val, qphy->regs + HSPHY_CTRL);
+
+ usleep_range(10, 15);
+
+ writel(0x00, qphy->regs + HSPHY_AHBBURST);
+ writel(0x08, qphy->regs + HSPHY_AHBMODE);
+
+ /* workaround for rx buffer collision issue */
+ val = readl(qphy->regs + HSPHY_GENCONFIG);
+ val &= ~HSPHY_TXFIFO_IDLE_FORCE_DIS;
+ writel(val, qphy->regs + HSPHY_GENCONFIG);
+
+ val = readl(qphy->regs + HSPHY_GENCONFIG_2);
+ val |= HSPHY_SESS_VLD_CTRL_EN;
+ writel(val, qphy->regs + HSPHY_GENCONFIG_2);
+
+ val = ULPI_PWR_OTG_COMP_DISABLE;
+ usb_phy_io_write(phy, val, ULPI_SET(ULPI_PWR_CLK_MNG_REG));
+
+ state = extcon_get_cable_state(qphy->vbus_cable.edev, "USB");
+ if (state)
+ phy_8x16_vbus_on(qphy);
+ else
+ phy_8x16_vbus_off(qphy);
+
+ val = usb_phy_io_read(&qphy->phy, ULPI_FUNC_CTRL);
+ val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ val |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+ usb_phy_io_write(&qphy->phy, val, ULPI_FUNC_CTRL);
+
+ return 0;
+}
+
+static void phy_8x16_shutdown(struct usb_phy *phy)
+{
+ u32 val;
+
+ /* Put the controller in non-driving mode */
+ val = usb_phy_io_read(phy, ULPI_FUNC_CTRL);
+ val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+ usb_phy_io_write(phy, val, ULPI_FUNC_CTRL);
+}
+
+static int phy_8x16_read_devicetree(struct phy_8x16 *qphy)
+{
+ struct regulator_bulk_data regs[3];
+ struct device *dev = qphy->phy.dev;
+ int ret;
+
+ qphy->core_clk = devm_clk_get(dev, "core");
+ if (IS_ERR(qphy->core_clk))
+ return PTR_ERR(qphy->core_clk);
+
+ qphy->iface_clk = devm_clk_get(dev, "iface");
+ if (IS_ERR(qphy->iface_clk))
+ return PTR_ERR(qphy->iface_clk);
+
+ regs[0].supply = "v3p3";
+ regs[1].supply = "v1p8";
+ regs[2].supply = "vddcx";
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(regs), regs);
+ if (ret)
+ return ret;
+
+ qphy->v3p3 = regs[0].consumer;
+ qphy->v1p8 = regs[1].consumer;
+ qphy->vdd = regs[2].consumer;
+
+ qphy->phy_reset = devm_reset_control_get(dev, "phy");
+ if (IS_ERR(qphy->phy_reset))
+ return PTR_ERR(qphy->phy_reset);
+
+ qphy->switch_gpio = devm_gpiod_get_optional(dev, "switch",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(qphy->switch_gpio))
+ return PTR_ERR(qphy->switch_gpio);
+
+ return 0;
+}
+
+static int phy_8x16_reboot_notify(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ struct phy_8x16 *qphy;
+
+ qphy = container_of(this, struct phy_8x16, reboot_notify);
+
+ /*
+ * Ensure that D+/D- lines are routed to uB connector, so
+ * we could load bootloader/kernel at next reboot_notify
+ */
+ gpiod_set_value_cansleep(qphy->switch_gpio, 0);
+ return NOTIFY_DONE;
+}
+
+static int phy_8x16_probe(struct platform_device *pdev)
+{
+ struct extcon_dev *edev;
+ struct phy_8x16 *qphy;
+ struct resource *res;
+ struct usb_phy *phy;
+ int ret;
+
+ qphy = devm_kzalloc(&pdev->dev, sizeof(*qphy), GFP_KERNEL);
+ if (!qphy)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, qphy);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ qphy->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!qphy->regs)
+ return -ENOMEM;
+
+ phy = &qphy->phy;
+ phy->dev = &pdev->dev;
+ phy->label = dev_name(&pdev->dev);
+ phy->init = phy_8x16_init;
+ phy->shutdown = phy_8x16_shutdown;
+ phy->notify_connect = phy_8x16_notify_connect;
+ phy->notify_disconnect = phy_8x16_notify_disconnect;
+ phy->io_priv = qphy->regs + HSPHY_ULPI_VIEWPORT;
+ phy->io_ops = &ulpi_viewport_access_ops;
+ phy->type = USB_PHY_TYPE_USB2;
+
+ ret = phy_8x16_read_devicetree(qphy);
+ if (ret < 0)
+ return ret;
+
+ edev = extcon_get_edev_by_phandle(phy->dev, 0);
+ if (IS_ERR(edev))
+ return PTR_ERR(edev);
+
+ ret = clk_set_rate(qphy->core_clk, INT_MAX);
+ if (ret < 0)
+ dev_dbg(phy->dev, "Can't boost core clock\n");
+
+ ret = clk_prepare_enable(qphy->core_clk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(qphy->iface_clk);
+ if (ret < 0)
+ goto off_core;
+
+ ret = phy_8x16_regulators_enable(qphy);
+ if (0 && ret)
+ goto off_clks;
+
+ qphy->vbus_notify.notifier_call = phy_8x16_vbus_notify;
+ ret = extcon_register_interest(&qphy->vbus_cable, edev->name,
+ "USB", &qphy->vbus_notify);
+ if (ret < 0)
+ goto off_power;
+
+ ret = usb_add_phy_dev(&qphy->phy);
+ if (ret)
+ goto off_extcon;
+
+ qphy->reboot_notify.notifier_call = phy_8x16_reboot_notify;
+ register_reboot_notifier(&qphy->reboot_notify);
+
+ return 0;
+
+off_extcon:
+ extcon_unregister_interest(&qphy->vbus_cable);
+off_power:
+ phy_8x16_regulators_disable(qphy);
+off_clks:
+ clk_disable_unprepare(qphy->iface_clk);
+off_core:
+ clk_disable_unprepare(qphy->core_clk);
+ return ret;
+}
+
+static int phy_8x16_remove(struct platform_device *pdev)
+{
+ struct phy_8x16 *qphy = platform_get_drvdata(pdev);
+
+ unregister_reboot_notifier(&qphy->reboot_notify);
+ extcon_unregister_interest(&qphy->vbus_cable);
+
+ /*
+ * Ensure that D+/D- lines are routed to uB connector, so
+ * we could load bootloader/kernel at next reboot_notify
+ */
+ gpiod_set_value_cansleep(qphy->switch_gpio, 0);
+
+ usb_remove_phy(&qphy->phy);
+
+ clk_disable_unprepare(qphy->iface_clk);
+ clk_disable_unprepare(qphy->core_clk);
+ phy_8x16_regulators_disable(qphy);
+ return 0;
+}
+
+static const struct of_device_id phy_8x16_dt_match[] = {
+ { .compatible = "qcom,usb-8x16-phy" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, phy_8x16_dt_match);
+
+static struct platform_driver phy_8x16_driver = {
+ .probe = phy_8x16_probe,
+ .remove = phy_8x16_remove,
+ .driver = {
+ .name = "phy-qcom-8x16-usb",
+ .of_match_table = phy_8x16_dt_match,
+ },
+};
+module_platform_driver(phy_8x16_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Qualcomm APQ8016/MSM8916 chipsets USB transceiver driver");
diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c
index b40d6a87d..ab5d364f6 100644
--- a/drivers/usb/phy/phy-tahvo.c
+++ b/drivers/usb/phy/phy-tahvo.c
@@ -57,7 +57,7 @@ struct tahvo_usb {
struct clk *ick;
int irq;
int tahvo_mode;
- struct extcon_dev extcon;
+ struct extcon_dev *extcon;
};
static const unsigned int tahvo_cable[] = {
@@ -121,7 +121,7 @@ static void check_vbus_state(struct tahvo_usb *tu)
prev_state = tu->vbus_state;
tu->vbus_state = reg & TAHVO_STAT_VBUS;
if (prev_state != tu->vbus_state) {
- extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state);
+ extcon_set_cable_state_(tu->extcon, EXTCON_USB, tu->vbus_state);
sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state");
}
}
@@ -130,7 +130,7 @@ static void tahvo_usb_become_host(struct tahvo_usb *tu)
{
struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
- extcon_set_cable_state(&tu->extcon, "USB-HOST", true);
+ extcon_set_cable_state_(tu->extcon, EXTCON_USB_HOST, true);
/* Power up the transceiver in USB host mode */
retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND |
@@ -149,7 +149,7 @@ static void tahvo_usb_become_peripheral(struct tahvo_usb *tu)
{
struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
- extcon_set_cable_state(&tu->extcon, "USB-HOST", false);
+ extcon_set_cable_state_(tu->extcon, EXTCON_USB_HOST, false);
/* Power up transceiver and set it in USB peripheral mode */
retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT |
@@ -365,11 +365,13 @@ static int tahvo_usb_probe(struct platform_device *pdev)
*/
tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS;
- tu->extcon.name = DRIVER_NAME;
- tu->extcon.supported_cable = tahvo_cable;
- tu->extcon.dev.parent = &pdev->dev;
+ tu->extcon = devm_extcon_dev_allocate(&pdev->dev, tahvo_cable);
+ if (IS_ERR(tu->extcon)) {
+ dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
+ return -ENOMEM;
+ }
- ret = extcon_dev_register(&tu->extcon);
+ ret = devm_extcon_dev_register(&pdev->dev, tu->extcon);
if (ret) {
dev_err(&pdev->dev, "could not register extcon device: %d\n",
ret);
@@ -377,9 +379,9 @@ static int tahvo_usb_probe(struct platform_device *pdev)
}
/* Set the initial cable state. */
- extcon_set_cable_state(&tu->extcon, "USB-HOST",
+ extcon_set_cable_state_(tu->extcon, EXTCON_USB_HOST,
tu->tahvo_mode == TAHVO_MODE_HOST);
- extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state);
+ extcon_set_cable_state_(tu->extcon, EXTCON_USB, tu->vbus_state);
/* Create OTG interface */
tahvo_usb_power_off(tu);
@@ -396,7 +398,7 @@ static int tahvo_usb_probe(struct platform_device *pdev)
if (ret < 0) {
dev_err(&pdev->dev, "cannot register USB transceiver: %d\n",
ret);
- goto err_extcon_unreg;
+ goto err_disable_clk;
}
dev_set_drvdata(&pdev->dev, tu);
@@ -424,8 +426,6 @@ err_free_irq:
free_irq(tu->irq, tu);
err_remove_phy:
usb_remove_phy(&tu->phy);
-err_extcon_unreg:
- extcon_dev_unregister(&tu->extcon);
err_disable_clk:
if (!IS_ERR(tu->ick))
clk_disable(tu->ick);
@@ -440,7 +440,6 @@ static int tahvo_usb_remove(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj, &tahvo_attr_group);
free_irq(tu->irq, tu);
usb_remove_phy(&tu->phy);
- extcon_dev_unregister(&tu->extcon);
if (!IS_ERR(tu->ick))
clk_disable(tu->ick);