summaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/Kconfig14
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-omap-control.c320
-rw-r--r--drivers/phy/phy-omap-usb2.c8
-rw-r--r--drivers/phy/phy-ti-pipe3.c8
5 files changed, 341 insertions, 10 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 1b607d7cb03..fe8c0096f9e 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -31,12 +31,22 @@ config PHY_MVEBU_SATA
depends on OF
select GENERIC_PHY
+config OMAP_CONTROL_PHY
+ tristate "OMAP CONTROL PHY Driver"
+ help
+ Enable this to add support for the PHY part present in the control
+ module. This driver has API to power on the USB2 PHY and to write to
+ the mailbox. The mailbox is present only in omap4 and the register to
+ power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an
+ additional register to power on USB3 PHY/SATA PHY/PCIE PHY
+ (PIPE3 PHY).
+
config OMAP_USB2
tristate "OMAP USB2 PHY Driver"
depends on ARCH_OMAP2PLUS
depends on USB_PHY
select GENERIC_PHY
- select OMAP_CONTROL_USB
+ select OMAP_CONTROL_PHY
help
Enable this to support the transceiver that is part of SOC. This
driver takes care of all the PHY functionality apart from comparator.
@@ -47,7 +57,7 @@ config TI_PIPE3
tristate "TI PIPE3 PHY Driver"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
select GENERIC_PHY
- select OMAP_CONTROL_USB
+ select OMAP_CONTROL_PHY
help
Enable this to support the PIPE3 PHY that is part of TI SOCs. This
driver takes care of all the PHY functionality apart from comparator.
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index ecf0d3f5bf1..8da05a84111 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
+obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
diff --git a/drivers/phy/phy-omap-control.c b/drivers/phy/phy-omap-control.c
new file mode 100644
index 00000000000..17fc200210c
--- /dev/null
+++ b/drivers/phy/phy-omap-control.c
@@ -0,0 +1,320 @@
+/*
+ * omap-control-phy.c - The PHY part of control module.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/phy/omap_control_phy.h>
+
+/**
+ * omap_control_phy_power - power on/off the phy using control module reg
+ * @dev: the control module device
+ * @on: 0 or 1, based on powering on or off the PHY
+ */
+void omap_control_phy_power(struct device *dev, int on)
+{
+ u32 val;
+ unsigned long rate;
+ struct omap_control_phy *control_phy;
+
+ if (IS_ERR(dev) || !dev) {
+ pr_err("%s: invalid device\n", __func__);
+ return;
+ }
+
+ control_phy = dev_get_drvdata(dev);
+ if (!control_phy) {
+ dev_err(dev, "%s: invalid control phy device\n", __func__);
+ return;
+ }
+
+ if (control_phy->type == OMAP_CTRL_TYPE_OTGHS)
+ return;
+
+ val = readl(control_phy->power);
+
+ switch (control_phy->type) {
+ case OMAP_CTRL_TYPE_USB2:
+ if (on)
+ val &= ~OMAP_CTRL_DEV_PHY_PD;
+ else
+ val |= OMAP_CTRL_DEV_PHY_PD;
+ break;
+
+ case OMAP_CTRL_TYPE_PIPE3:
+ rate = clk_get_rate(control_phy->sys_clk);
+ rate = rate/1000000;
+
+ if (on) {
+ val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
+ OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK);
+ val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON <<
+ OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
+ val |= rate <<
+ OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
+ } else {
+ val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK;
+ val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF <<
+ OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
+ }
+ break;
+
+ case OMAP_CTRL_TYPE_DRA7USB2:
+ if (on)
+ val &= ~OMAP_CTRL_USB2_PHY_PD;
+ else
+ val |= OMAP_CTRL_USB2_PHY_PD;
+ break;
+
+ case OMAP_CTRL_TYPE_AM437USB2:
+ if (on) {
+ val &= ~(AM437X_CTRL_USB2_PHY_PD |
+ AM437X_CTRL_USB2_OTG_PD);
+ val |= (AM437X_CTRL_USB2_OTGVDET_EN |
+ AM437X_CTRL_USB2_OTGSESSEND_EN);
+ } else {
+ val &= ~(AM437X_CTRL_USB2_OTGVDET_EN |
+ AM437X_CTRL_USB2_OTGSESSEND_EN);
+ val |= (AM437X_CTRL_USB2_PHY_PD |
+ AM437X_CTRL_USB2_OTG_PD);
+ }
+ break;
+ default:
+ dev_err(dev, "%s: type %d not recognized\n",
+ __func__, control_phy->type);
+ break;
+ }
+
+ writel(val, control_phy->power);
+}
+EXPORT_SYMBOL_GPL(omap_control_phy_power);
+
+/**
+ * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
+ * @ctrl_phy: struct omap_control_phy *
+ *
+ * Writes to the mailbox register to notify the usb core that a usb
+ * device has been connected.
+ */
+static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy)
+{
+ u32 val;
+
+ val = readl(ctrl_phy->otghs_control);
+ val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND);
+ val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID;
+ writel(val, ctrl_phy->otghs_control);
+}
+
+/**
+ * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high
+ * impedance
+ * @ctrl_phy: struct omap_control_phy *
+ *
+ * Writes to the mailbox register to notify the usb core that it has been
+ * connected to a usb host.
+ */
+static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy)
+{
+ u32 val;
+
+ val = readl(ctrl_phy->otghs_control);
+ val &= ~OMAP_CTRL_DEV_SESSEND;
+ val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID |
+ OMAP_CTRL_DEV_VBUSVALID;
+ writel(val, ctrl_phy->otghs_control);
+}
+
+/**
+ * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
+ * impedance
+ * @ctrl_phy: struct omap_control_phy *
+ *
+ * Writes to the mailbox register to notify the usb core it's now in
+ * disconnected state.
+ */
+static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy)
+{
+ u32 val;
+
+ val = readl(ctrl_phy->otghs_control);
+ val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID);
+ val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND;
+ writel(val, ctrl_phy->otghs_control);
+}
+
+/**
+ * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode
+ * or device mode or to denote disconnected state
+ * @dev: the control module device
+ * @mode: The mode to which usb should be configured
+ *
+ * This is an API to write to the mailbox register to notify the usb core that
+ * a usb device has been connected.
+ */
+void omap_control_usb_set_mode(struct device *dev,
+ enum omap_control_usb_mode mode)
+{
+ struct omap_control_phy *ctrl_phy;
+
+ if (IS_ERR(dev) || !dev)
+ return;
+
+ ctrl_phy = dev_get_drvdata(dev);
+
+ if (!ctrl_phy) {
+ dev_err(dev, "Invalid control phy device\n");
+ return;
+ }
+
+ if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS)
+ return;
+
+ switch (mode) {
+ case USB_MODE_HOST:
+ omap_control_usb_host_mode(ctrl_phy);
+ break;
+ case USB_MODE_DEVICE:
+ omap_control_usb_device_mode(ctrl_phy);
+ break;
+ case USB_MODE_DISCONNECT:
+ omap_control_usb_set_sessionend(ctrl_phy);
+ break;
+ default:
+ dev_vdbg(dev, "invalid omap control usb mode\n");
+ }
+}
+EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
+
+#ifdef CONFIG_OF
+
+static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
+static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2;
+static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
+static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
+static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
+
+static const struct of_device_id omap_control_phy_id_table[] = {
+ {
+ .compatible = "ti,control-phy-otghs",
+ .data = &otghs_data,
+ },
+ {
+ .compatible = "ti,control-phy-usb2",
+ .data = &usb2_data,
+ },
+ {
+ .compatible = "ti,control-phy-pipe3",
+ .data = &pipe3_data,
+ },
+ {
+ .compatible = "ti,control-phy-dra7usb2",
+ .data = &dra7usb2_data,
+ },
+ {
+ .compatible = "ti,control-phy-am437usb2",
+ .data = &am437usb2_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_control_phy_id_table);
+#endif
+
+
+static int omap_control_phy_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ const struct of_device_id *of_id;
+ struct omap_control_phy *control_phy;
+
+ of_id = of_match_device(of_match_ptr(omap_control_phy_id_table),
+ &pdev->dev);
+ if (!of_id)
+ return -EINVAL;
+
+ control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy),
+ GFP_KERNEL);
+ if (!control_phy) {
+ dev_err(&pdev->dev, "unable to alloc memory for control phy\n");
+ return -ENOMEM;
+ }
+
+ control_phy->dev = &pdev->dev;
+ control_phy->type = *(enum omap_control_phy_type *)of_id->data;
+
+ if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "otghs_control");
+ control_phy->otghs_control = devm_ioremap_resource(
+ &pdev->dev, res);
+ if (IS_ERR(control_phy->otghs_control))
+ return PTR_ERR(control_phy->otghs_control);
+ } else {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "power");
+ control_phy->power = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(control_phy->power)) {
+ dev_err(&pdev->dev, "Couldn't get power register\n");
+ return PTR_ERR(control_phy->power);
+ }
+ }
+
+ if (control_phy->type == OMAP_CTRL_TYPE_PIPE3) {
+ control_phy->sys_clk = devm_clk_get(control_phy->dev,
+ "sys_clkin");
+ if (IS_ERR(control_phy->sys_clk)) {
+ pr_err("%s: unable to get sys_clkin\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ dev_set_drvdata(control_phy->dev, control_phy);
+
+ return 0;
+}
+
+static struct platform_driver omap_control_phy_driver = {
+ .probe = omap_control_phy_probe,
+ .driver = {
+ .name = "omap-control-phy",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(omap_control_phy_id_table),
+ },
+};
+
+static int __init omap_control_phy_init(void)
+{
+ return platform_driver_register(&omap_control_phy_driver);
+}
+subsys_initcall(omap_control_phy_init);
+
+static void __exit omap_control_phy_exit(void)
+{
+ platform_driver_unregister(&omap_control_phy_driver);
+}
+module_exit(omap_control_phy_exit);
+
+MODULE_ALIAS("platform: omap_control_phy");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("OMAP Control Module PHY Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c
index 3cc4aba6e50..a2205a841e5 100644
--- a/drivers/phy/phy-omap-usb2.c
+++ b/drivers/phy/phy-omap-usb2.c
@@ -27,7 +27,7 @@
#include <linux/err.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
-#include <linux/usb/omap_control_usb.h>
+#include <linux/phy/omap_control_phy.h>
#include <linux/phy/phy.h>
#include <linux/of_platform.h>
@@ -105,7 +105,7 @@ static int omap_usb_power_off(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
- omap_control_usb_phy_power(phy->control_dev, 0);
+ omap_control_phy_power(phy->control_dev, 0);
return 0;
}
@@ -114,7 +114,7 @@ static int omap_usb_power_on(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
- omap_control_usb_phy_power(phy->control_dev, 1);
+ omap_control_phy_power(phy->control_dev, 1);
return 0;
}
@@ -251,7 +251,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
}
phy->control_dev = &control_pdev->dev;
- omap_control_usb_phy_power(phy->control_dev, 0);
+ omap_control_phy_power(phy->control_dev, 0);
otg->set_host = omap_usb_set_host;
otg->set_peripheral = omap_usb_set_peripheral;
diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c
index c8d16746a5e..fd029b14cb3 100644
--- a/drivers/phy/phy-ti-pipe3.c
+++ b/drivers/phy/phy-ti-pipe3.c
@@ -26,7 +26,7 @@
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
-#include <linux/usb/omap_control_usb.h>
+#include <linux/phy/omap_control_phy.h>
#include <linux/of_platform.h>
#define PLL_STATUS 0x00000004
@@ -134,7 +134,7 @@ static int ti_pipe3_power_off(struct phy *x)
return -EBUSY;
}
- omap_control_usb_phy_power(phy->control_dev, 0);
+ omap_control_phy_power(phy->control_dev, 0);
return 0;
}
@@ -232,7 +232,7 @@ static int ti_pipe3_init(struct phy *x)
if (ret)
return ret;
- omap_control_usb_phy_power(phy->control_dev, 1);
+ omap_control_phy_power(phy->control_dev, 1);
return 0;
}
@@ -304,7 +304,7 @@ static int ti_pipe3_probe(struct platform_device *pdev)
phy->control_dev = &control_pdev->dev;
- omap_control_usb_phy_power(phy->control_dev, 0);
+ omap_control_phy_power(phy->control_dev, 0);
platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev);