diff options
Diffstat (limited to 'drivers/phy')
-rw-r--r-- | drivers/phy/Kconfig | 14 | ||||
-rw-r--r-- | drivers/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/phy-omap-control.c | 320 | ||||
-rw-r--r-- | drivers/phy/phy-omap-usb2.c | 8 | ||||
-rw-r--r-- | drivers/phy/phy-ti-pipe3.c | 8 |
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); |