summaryrefslogtreecommitdiff
path: root/drivers/usb/phy
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-06-02 04:47:03 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-06-02 04:47:03 +0300
commite152813ff5fea74094ae296367f477411df1ba24 (patch)
tree6e8c38a3b83f0f07f75df50793332aa47a81c709 /drivers/usb/phy
parent74db22cb3a16dcd782a31236eb139f5865804ae6 (diff)
parente18b7975c885bc3a938b9a76daf32957ea0235fa (diff)
downloadlinux-e152813ff5fea74094ae296367f477411df1ba24.tar.xz
Merge tag 'usb-for-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
Felipe writes: usb: patches for v4.2 merge window - dwc2 adds hibernation support - preparation for sunxi glue to musb driver - new ULPI bus - new ULPI PHY driver for TUSB1210 - musb patches to support multiple DMA engines on same binary - support for R-Car E2 on renesas_usbhs Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/phy')
-rw-r--r--drivers/usb/phy/Kconfig3
-rw-r--r--drivers/usb/phy/phy-msm-usb.c110
-rw-r--r--drivers/usb/phy/phy.c97
3 files changed, 182 insertions, 28 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 3cd3bee54ca6..869c0cfcad98 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -91,7 +91,7 @@ config TWL6030_USB
config USB_GPIO_VBUS
tristate "GPIO based peripheral-only VBUS sensing 'transceiver'"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
select USB_PHY
help
Provides simple GPIO VBUS sensing for controllers with an
@@ -141,6 +141,7 @@ config USB_MSM_OTG
tristate "Qualcomm on-chip USB OTG controller support"
depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST)
depends on RESET_CONTROLLER
+ depends on EXTCON
select USB_PHY
help
Enable this to support the USB OTG transceiver on Qualcomm chips. It
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index c9156beeadef..00c49bb1bd29 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -240,8 +240,14 @@ static void ulpi_init(struct msm_otg *motg)
static int msm_phy_notify_disconnect(struct usb_phy *phy,
enum usb_device_speed speed)
{
+ struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
int val;
+ if (motg->manual_pullup) {
+ val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL;
+ usb_phy_io_write(phy, val, ULPI_CLR(ULPI_MISC_A));
+ }
+
/*
* Put the transceiver in non-driving mode. Otherwise host
* may not detect soft-disconnection.
@@ -422,6 +428,24 @@ static int msm_phy_init(struct usb_phy *phy)
ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL);
}
+ if (motg->manual_pullup) {
+ val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT;
+ ulpi_write(phy, val, ULPI_SET(ULPI_MISC_A));
+
+ val = readl(USB_GENCONFIG_2);
+ val |= GENCONFIG_2_SESS_VLD_CTRL_EN;
+ writel(val, USB_GENCONFIG_2);
+
+ val = readl(USB_USBCMD);
+ val |= USBCMD_SESS_VLD_CTRL;
+ writel(val, USB_USBCMD);
+
+ val = ulpi_read(phy, ULPI_FUNC_CTRL);
+ val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ val |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+ ulpi_write(phy, val, ULPI_FUNC_CTRL);
+ }
+
if (motg->phy_number)
writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2);
@@ -1436,9 +1460,42 @@ static const struct of_device_id msm_otg_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, msm_otg_dt_match);
+static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct msm_usb_cable *vbus = container_of(nb, struct msm_usb_cable, nb);
+ struct msm_otg *motg = container_of(vbus, struct msm_otg, vbus);
+
+ if (event)
+ set_bit(B_SESS_VLD, &motg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &motg->inputs);
+
+ schedule_work(&motg->sm_work);
+
+ return NOTIFY_DONE;
+}
+
+static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct msm_usb_cable *id = container_of(nb, struct msm_usb_cable, nb);
+ struct msm_otg *motg = container_of(id, struct msm_otg, id);
+
+ if (event)
+ clear_bit(ID, &motg->inputs);
+ else
+ set_bit(ID, &motg->inputs);
+
+ schedule_work(&motg->sm_work);
+
+ return NOTIFY_DONE;
+}
+
static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
{
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;
@@ -1487,6 +1544,54 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX];
}
+ motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup");
+
+ ext_id = ERR_PTR(-ENODEV);
+ ext_vbus = ERR_PTR(-ENODEV);
+ if (of_property_read_bool(node, "extcon")) {
+
+ /* Each one of them is not mandatory */
+ ext_vbus = extcon_get_edev_by_phandle(&pdev->dev, 0);
+ if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV)
+ return PTR_ERR(ext_vbus);
+
+ ext_id = extcon_get_edev_by_phandle(&pdev->dev, 1);
+ if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV)
+ return PTR_ERR(ext_id);
+ }
+
+ if (!IS_ERR(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);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "register VBUS notifier failed\n");
+ return ret;
+ }
+
+ ret = extcon_get_cable_state(ext_vbus, "USB");
+ if (ret)
+ set_bit(B_SESS_VLD, &motg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &motg->inputs);
+ }
+
+ if (!IS_ERR(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);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "register ID notifier failed\n");
+ return ret;
+ }
+
+ ret = extcon_get_cable_state(ext_id, "USB-HOST");
+ if (ret)
+ clear_bit(ID, &motg->inputs);
+ else
+ set_bit(ID, &motg->inputs);
+ }
+
prop = of_find_property(node, "qcom,phy-init-sequence", &len);
if (!prop || !len)
return 0;
@@ -1700,6 +1805,11 @@ 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);
+
msm_otg_debugfs_cleanup();
cancel_delayed_work_sync(&motg->chg_work);
cancel_work_sync(&motg->sm_work);
diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c
index d1cd6b50f520..98f75d2842b7 100644
--- a/drivers/usb/phy/phy.c
+++ b/drivers/usb/phy/phy.c
@@ -22,6 +22,11 @@ static LIST_HEAD(phy_list);
static LIST_HEAD(phy_bind_list);
static DEFINE_SPINLOCK(phy_lock);
+struct phy_devm {
+ struct usb_phy *phy;
+ struct notifier_block *nb;
+};
+
static struct usb_phy *__usb_find_phy(struct list_head *list,
enum usb_phy_type type)
{
@@ -79,6 +84,15 @@ static void devm_usb_phy_release(struct device *dev, void *res)
usb_put_phy(phy);
}
+static void devm_usb_phy_release2(struct device *dev, void *_res)
+{
+ struct phy_devm *res = _res;
+
+ if (res->nb)
+ usb_unregister_notifier(res->phy, res->nb);
+ usb_put_phy(res->phy);
+}
+
static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
{
struct usb_phy **phy = res;
@@ -153,40 +167,30 @@ err0:
EXPORT_SYMBOL_GPL(usb_get_phy);
/**
- * devm_usb_get_phy_by_phandle - find the USB PHY by phandle
+ * devm_usb_get_phy_by_node - find the USB PHY by device_node
* @dev - device that requests this phy
- * @phandle - name of the property holding the phy phandle value
- * @index - the index of the phy
+ * @node - the device_node for the phy device.
+ * @nb - a notifier_block to register with the phy.
*
- * Returns the phy driver associated with the given phandle value,
+ * Returns the phy driver associated with the given device_node,
* after getting a refcount to it, -ENODEV if there is no such phy or
- * -EPROBE_DEFER if there is a phandle to the phy, but the device is
- * not yet loaded. While at that, it also associates the device with
+ * -EPROBE_DEFER if the device is not yet loaded. While at that, it
+ * also associates the device with
* the phy using devres. On driver detach, release function is invoked
* on the devres data, then, devres data is freed.
*
- * For use by USB host and peripheral drivers.
+ * For use by peripheral drivers for devices related to a phy,
+ * such as a charger.
*/
-struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
- const char *phandle, u8 index)
+struct usb_phy *devm_usb_get_phy_by_node(struct device *dev,
+ struct device_node *node,
+ struct notifier_block *nb)
{
- struct usb_phy *phy = ERR_PTR(-ENOMEM), **ptr;
+ struct usb_phy *phy = ERR_PTR(-ENOMEM);
+ struct phy_devm *ptr;
unsigned long flags;
- struct device_node *node;
- if (!dev->of_node) {
- dev_dbg(dev, "device does not have a device node entry\n");
- return ERR_PTR(-EINVAL);
- }
-
- node = of_parse_phandle(dev->of_node, phandle, index);
- if (!node) {
- dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle,
- dev->of_node->full_name);
- return ERR_PTR(-ENODEV);
- }
-
- ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
+ ptr = devres_alloc(devm_usb_phy_release2, sizeof(*ptr), GFP_KERNEL);
if (!ptr) {
dev_dbg(dev, "failed to allocate memory for devres\n");
goto err0;
@@ -205,8 +209,10 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
devres_free(ptr);
goto err1;
}
-
- *ptr = phy;
+ if (nb)
+ usb_register_notifier(phy, nb);
+ ptr->phy = phy;
+ ptr->nb = nb;
devres_add(dev, ptr);
get_device(phy->dev);
@@ -215,10 +221,47 @@ err1:
spin_unlock_irqrestore(&phy_lock, flags);
err0:
- of_node_put(node);
return phy;
}
+EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_node);
+
+/**
+ * devm_usb_get_phy_by_phandle - find the USB PHY by phandle
+ * @dev - device that requests this phy
+ * @phandle - name of the property holding the phy phandle value
+ * @index - the index of the phy
+ *
+ * Returns the phy driver associated with the given phandle value,
+ * after getting a refcount to it, -ENODEV if there is no such phy or
+ * -EPROBE_DEFER if there is a phandle to the phy, but the device is
+ * not yet loaded. While at that, it also associates the device with
+ * the phy using devres. On driver detach, release function is invoked
+ * on the devres data, then, devres data is freed.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
+ const char *phandle, u8 index)
+{
+ struct device_node *node;
+ struct usb_phy *phy;
+
+ if (!dev->of_node) {
+ dev_dbg(dev, "device does not have a device node entry\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ node = of_parse_phandle(dev->of_node, phandle, index);
+ if (!node) {
+ dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle,
+ dev->of_node->full_name);
+ return ERR_PTR(-ENODEV);
+ }
+ phy = devm_usb_get_phy_by_node(dev, node, NULL);
+ of_node_put(node);
+ return phy;
+}
EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle);
/**