341 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * omap-control-phy.c - The PHY part of control module.4 *5 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com6 * Author: Kishon Vijay Abraham I <kishon@ti.com>7 */8 9#include <linux/module.h>10#include <linux/platform_device.h>11#include <linux/property.h>12#include <linux/slab.h>13#include <linux/of.h>14#include <linux/err.h>15#include <linux/io.h>16#include <linux/clk.h>17#include <linux/phy/omap_control_phy.h>18 19/**20 * omap_control_pcie_pcs - set the PCS delay count21 * @dev: the control module device22 * @delay: 8 bit delay value23 */24void omap_control_pcie_pcs(struct device *dev, u8 delay)25{26 u32 val;27 struct omap_control_phy *control_phy;28 29 if (IS_ERR_OR_NULL(dev)) {30 pr_err("%s: invalid device\n", __func__);31 return;32 }33 34 control_phy = dev_get_drvdata(dev);35 if (!control_phy) {36 dev_err(dev, "%s: invalid control phy device\n", __func__);37 return;38 }39 40 if (control_phy->type != OMAP_CTRL_TYPE_PCIE) {41 dev_err(dev, "%s: unsupported operation\n", __func__);42 return;43 }44 45 val = readl(control_phy->pcie_pcs);46 val &= ~(OMAP_CTRL_PCIE_PCS_MASK <<47 OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT);48 val |= (delay << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT);49 writel(val, control_phy->pcie_pcs);50}51EXPORT_SYMBOL_GPL(omap_control_pcie_pcs);52 53/**54 * omap_control_phy_power - power on/off the phy using control module reg55 * @dev: the control module device56 * @on: 0 or 1, based on powering on or off the PHY57 */58void omap_control_phy_power(struct device *dev, int on)59{60 u32 val;61 unsigned long rate;62 struct omap_control_phy *control_phy;63 64 if (IS_ERR_OR_NULL(dev)) {65 pr_err("%s: invalid device\n", __func__);66 return;67 }68 69 control_phy = dev_get_drvdata(dev);70 if (!control_phy) {71 dev_err(dev, "%s: invalid control phy device\n", __func__);72 return;73 }74 75 if (control_phy->type == OMAP_CTRL_TYPE_OTGHS)76 return;77 78 val = readl(control_phy->power);79 80 switch (control_phy->type) {81 case OMAP_CTRL_TYPE_USB2:82 if (on)83 val &= ~OMAP_CTRL_DEV_PHY_PD;84 else85 val |= OMAP_CTRL_DEV_PHY_PD;86 break;87 88 case OMAP_CTRL_TYPE_PCIE:89 case OMAP_CTRL_TYPE_PIPE3:90 rate = clk_get_rate(control_phy->sys_clk);91 rate = rate/1000000;92 93 if (on) {94 val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |95 OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK);96 val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON <<97 OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;98 val |= rate <<99 OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;100 } else {101 val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK;102 val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF <<103 OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;104 }105 break;106 107 case OMAP_CTRL_TYPE_DRA7USB2:108 if (on)109 val &= ~OMAP_CTRL_USB2_PHY_PD;110 else111 val |= OMAP_CTRL_USB2_PHY_PD;112 break;113 114 case OMAP_CTRL_TYPE_AM437USB2:115 if (on) {116 val &= ~(AM437X_CTRL_USB2_PHY_PD |117 AM437X_CTRL_USB2_OTG_PD);118 val |= (AM437X_CTRL_USB2_OTGVDET_EN |119 AM437X_CTRL_USB2_OTGSESSEND_EN);120 } else {121 val &= ~(AM437X_CTRL_USB2_OTGVDET_EN |122 AM437X_CTRL_USB2_OTGSESSEND_EN);123 val |= (AM437X_CTRL_USB2_PHY_PD |124 AM437X_CTRL_USB2_OTG_PD);125 }126 break;127 default:128 dev_err(dev, "%s: type %d not recognized\n",129 __func__, control_phy->type);130 break;131 }132 133 writel(val, control_phy->power);134}135EXPORT_SYMBOL_GPL(omap_control_phy_power);136 137/**138 * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded139 * @ctrl_phy: struct omap_control_phy *140 *141 * Writes to the mailbox register to notify the usb core that a usb142 * device has been connected.143 */144static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy)145{146 u32 val;147 148 val = readl(ctrl_phy->otghs_control);149 val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND);150 val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID;151 writel(val, ctrl_phy->otghs_control);152}153 154/**155 * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high156 * impedance157 * @ctrl_phy: struct omap_control_phy *158 *159 * Writes to the mailbox register to notify the usb core that it has been160 * connected to a usb host.161 */162static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy)163{164 u32 val;165 166 val = readl(ctrl_phy->otghs_control);167 val &= ~OMAP_CTRL_DEV_SESSEND;168 val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID |169 OMAP_CTRL_DEV_VBUSVALID;170 writel(val, ctrl_phy->otghs_control);171}172 173/**174 * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high175 * impedance176 * @ctrl_phy: struct omap_control_phy *177 *178 * Writes to the mailbox register to notify the usb core it's now in179 * disconnected state.180 */181static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy)182{183 u32 val;184 185 val = readl(ctrl_phy->otghs_control);186 val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID);187 val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND;188 writel(val, ctrl_phy->otghs_control);189}190 191/**192 * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode193 * or device mode or to denote disconnected state194 * @dev: the control module device195 * @mode: The mode to which usb should be configured196 *197 * This is an API to write to the mailbox register to notify the usb core that198 * a usb device has been connected.199 */200void omap_control_usb_set_mode(struct device *dev,201 enum omap_control_usb_mode mode)202{203 struct omap_control_phy *ctrl_phy;204 205 if (IS_ERR_OR_NULL(dev))206 return;207 208 ctrl_phy = dev_get_drvdata(dev);209 if (!ctrl_phy) {210 dev_err(dev, "Invalid control phy device\n");211 return;212 }213 214 if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS)215 return;216 217 switch (mode) {218 case USB_MODE_HOST:219 omap_control_usb_host_mode(ctrl_phy);220 break;221 case USB_MODE_DEVICE:222 omap_control_usb_device_mode(ctrl_phy);223 break;224 case USB_MODE_DISCONNECT:225 omap_control_usb_set_sessionend(ctrl_phy);226 break;227 default:228 dev_vdbg(dev, "invalid omap control usb mode\n");229 }230}231EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);232 233static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS;234static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2;235static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;236static const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE;237static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;238static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;239 240static const struct of_device_id omap_control_phy_id_table[] = {241 {242 .compatible = "ti,control-phy-otghs",243 .data = &otghs_data,244 },245 {246 .compatible = "ti,control-phy-usb2",247 .data = &usb2_data,248 },249 {250 .compatible = "ti,control-phy-pipe3",251 .data = &pipe3_data,252 },253 {254 .compatible = "ti,control-phy-pcie",255 .data = &pcie_data,256 },257 {258 .compatible = "ti,control-phy-usb2-dra7",259 .data = &dra7usb2_data,260 },261 {262 .compatible = "ti,control-phy-usb2-am437",263 .data = &am437usb2_data,264 },265 {},266};267MODULE_DEVICE_TABLE(of, omap_control_phy_id_table);268 269static int omap_control_phy_probe(struct platform_device *pdev)270{271 struct omap_control_phy *control_phy;272 273 control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy),274 GFP_KERNEL);275 if (!control_phy)276 return -ENOMEM;277 278 control_phy->dev = &pdev->dev;279 control_phy->type = *(enum omap_control_phy_type *)device_get_match_data(&pdev->dev);280 281 if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) {282 control_phy->otghs_control =283 devm_platform_ioremap_resource_byname(pdev, "otghs_control");284 if (IS_ERR(control_phy->otghs_control))285 return PTR_ERR(control_phy->otghs_control);286 } else {287 control_phy->power =288 devm_platform_ioremap_resource_byname(pdev, "power");289 if (IS_ERR(control_phy->power)) {290 dev_err(&pdev->dev, "Couldn't get power register\n");291 return PTR_ERR(control_phy->power);292 }293 }294 295 if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 ||296 control_phy->type == OMAP_CTRL_TYPE_PCIE) {297 control_phy->sys_clk = devm_clk_get(control_phy->dev,298 "sys_clkin");299 if (IS_ERR(control_phy->sys_clk)) {300 pr_err("%s: unable to get sys_clkin\n", __func__);301 return -EINVAL;302 }303 }304 305 if (control_phy->type == OMAP_CTRL_TYPE_PCIE) {306 control_phy->pcie_pcs =307 devm_platform_ioremap_resource_byname(pdev, "pcie_pcs");308 if (IS_ERR(control_phy->pcie_pcs))309 return PTR_ERR(control_phy->pcie_pcs);310 }311 312 dev_set_drvdata(control_phy->dev, control_phy);313 314 return 0;315}316 317static struct platform_driver omap_control_phy_driver = {318 .probe = omap_control_phy_probe,319 .driver = {320 .name = "omap-control-phy",321 .of_match_table = omap_control_phy_id_table,322 },323};324 325static int __init omap_control_phy_init(void)326{327 return platform_driver_register(&omap_control_phy_driver);328}329subsys_initcall(omap_control_phy_init);330 331static void __exit omap_control_phy_exit(void)332{333 platform_driver_unregister(&omap_control_phy_driver);334}335module_exit(omap_control_phy_exit);336 337MODULE_ALIAS("platform:omap_control_phy");338MODULE_AUTHOR("Texas Instruments Inc.");339MODULE_DESCRIPTION("OMAP Control Module PHY Driver");340MODULE_LICENSE("GPL v2");341