162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * omap-control-phy.c - The PHY part of control module. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com 662306a36Sopenharmony_ci * Author: Kishon Vijay Abraham I <kishon@ti.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/of_device.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/clk.h> 1762306a36Sopenharmony_ci#include <linux/phy/omap_control_phy.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/** 2062306a36Sopenharmony_ci * omap_control_pcie_pcs - set the PCS delay count 2162306a36Sopenharmony_ci * @dev: the control module device 2262306a36Sopenharmony_ci * @delay: 8 bit delay value 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_civoid omap_control_pcie_pcs(struct device *dev, u8 delay) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci u32 val; 2762306a36Sopenharmony_ci struct omap_control_phy *control_phy; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dev)) { 3062306a36Sopenharmony_ci pr_err("%s: invalid device\n", __func__); 3162306a36Sopenharmony_ci return; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci control_phy = dev_get_drvdata(dev); 3562306a36Sopenharmony_ci if (!control_phy) { 3662306a36Sopenharmony_ci dev_err(dev, "%s: invalid control phy device\n", __func__); 3762306a36Sopenharmony_ci return; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (control_phy->type != OMAP_CTRL_TYPE_PCIE) { 4162306a36Sopenharmony_ci dev_err(dev, "%s: unsupported operation\n", __func__); 4262306a36Sopenharmony_ci return; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci val = readl(control_phy->pcie_pcs); 4662306a36Sopenharmony_ci val &= ~(OMAP_CTRL_PCIE_PCS_MASK << 4762306a36Sopenharmony_ci OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); 4862306a36Sopenharmony_ci val |= (delay << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); 4962306a36Sopenharmony_ci writel(val, control_phy->pcie_pcs); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_control_pcie_pcs); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/** 5462306a36Sopenharmony_ci * omap_control_phy_power - power on/off the phy using control module reg 5562306a36Sopenharmony_ci * @dev: the control module device 5662306a36Sopenharmony_ci * @on: 0 or 1, based on powering on or off the PHY 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_civoid omap_control_phy_power(struct device *dev, int on) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci u32 val; 6162306a36Sopenharmony_ci unsigned long rate; 6262306a36Sopenharmony_ci struct omap_control_phy *control_phy; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dev)) { 6562306a36Sopenharmony_ci pr_err("%s: invalid device\n", __func__); 6662306a36Sopenharmony_ci return; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci control_phy = dev_get_drvdata(dev); 7062306a36Sopenharmony_ci if (!control_phy) { 7162306a36Sopenharmony_ci dev_err(dev, "%s: invalid control phy device\n", __func__); 7262306a36Sopenharmony_ci return; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) 7662306a36Sopenharmony_ci return; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci val = readl(control_phy->power); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci switch (control_phy->type) { 8162306a36Sopenharmony_ci case OMAP_CTRL_TYPE_USB2: 8262306a36Sopenharmony_ci if (on) 8362306a36Sopenharmony_ci val &= ~OMAP_CTRL_DEV_PHY_PD; 8462306a36Sopenharmony_ci else 8562306a36Sopenharmony_ci val |= OMAP_CTRL_DEV_PHY_PD; 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci case OMAP_CTRL_TYPE_PCIE: 8962306a36Sopenharmony_ci case OMAP_CTRL_TYPE_PIPE3: 9062306a36Sopenharmony_ci rate = clk_get_rate(control_phy->sys_clk); 9162306a36Sopenharmony_ci rate = rate/1000000; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (on) { 9462306a36Sopenharmony_ci val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | 9562306a36Sopenharmony_ci OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK); 9662306a36Sopenharmony_ci val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON << 9762306a36Sopenharmony_ci OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; 9862306a36Sopenharmony_ci val |= rate << 9962306a36Sopenharmony_ci OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; 10062306a36Sopenharmony_ci } else { 10162306a36Sopenharmony_ci val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK; 10262306a36Sopenharmony_ci val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF << 10362306a36Sopenharmony_ci OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci case OMAP_CTRL_TYPE_DRA7USB2: 10862306a36Sopenharmony_ci if (on) 10962306a36Sopenharmony_ci val &= ~OMAP_CTRL_USB2_PHY_PD; 11062306a36Sopenharmony_ci else 11162306a36Sopenharmony_ci val |= OMAP_CTRL_USB2_PHY_PD; 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci case OMAP_CTRL_TYPE_AM437USB2: 11562306a36Sopenharmony_ci if (on) { 11662306a36Sopenharmony_ci val &= ~(AM437X_CTRL_USB2_PHY_PD | 11762306a36Sopenharmony_ci AM437X_CTRL_USB2_OTG_PD); 11862306a36Sopenharmony_ci val |= (AM437X_CTRL_USB2_OTGVDET_EN | 11962306a36Sopenharmony_ci AM437X_CTRL_USB2_OTGSESSEND_EN); 12062306a36Sopenharmony_ci } else { 12162306a36Sopenharmony_ci val &= ~(AM437X_CTRL_USB2_OTGVDET_EN | 12262306a36Sopenharmony_ci AM437X_CTRL_USB2_OTGSESSEND_EN); 12362306a36Sopenharmony_ci val |= (AM437X_CTRL_USB2_PHY_PD | 12462306a36Sopenharmony_ci AM437X_CTRL_USB2_OTG_PD); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci default: 12862306a36Sopenharmony_ci dev_err(dev, "%s: type %d not recognized\n", 12962306a36Sopenharmony_ci __func__, control_phy->type); 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci writel(val, control_phy->power); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_control_phy_power); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/** 13862306a36Sopenharmony_ci * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded 13962306a36Sopenharmony_ci * @ctrl_phy: struct omap_control_phy * 14062306a36Sopenharmony_ci * 14162306a36Sopenharmony_ci * Writes to the mailbox register to notify the usb core that a usb 14262306a36Sopenharmony_ci * device has been connected. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_cistatic void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci u32 val; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci val = readl(ctrl_phy->otghs_control); 14962306a36Sopenharmony_ci val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); 15062306a36Sopenharmony_ci val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; 15162306a36Sopenharmony_ci writel(val, ctrl_phy->otghs_control); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/** 15562306a36Sopenharmony_ci * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high 15662306a36Sopenharmony_ci * impedance 15762306a36Sopenharmony_ci * @ctrl_phy: struct omap_control_phy * 15862306a36Sopenharmony_ci * 15962306a36Sopenharmony_ci * Writes to the mailbox register to notify the usb core that it has been 16062306a36Sopenharmony_ci * connected to a usb host. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_cistatic void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci u32 val; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci val = readl(ctrl_phy->otghs_control); 16762306a36Sopenharmony_ci val &= ~OMAP_CTRL_DEV_SESSEND; 16862306a36Sopenharmony_ci val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | 16962306a36Sopenharmony_ci OMAP_CTRL_DEV_VBUSVALID; 17062306a36Sopenharmony_ci writel(val, ctrl_phy->otghs_control); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/** 17462306a36Sopenharmony_ci * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high 17562306a36Sopenharmony_ci * impedance 17662306a36Sopenharmony_ci * @ctrl_phy: struct omap_control_phy * 17762306a36Sopenharmony_ci * 17862306a36Sopenharmony_ci * Writes to the mailbox register to notify the usb core it's now in 17962306a36Sopenharmony_ci * disconnected state. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_cistatic void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci u32 val; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci val = readl(ctrl_phy->otghs_control); 18662306a36Sopenharmony_ci val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); 18762306a36Sopenharmony_ci val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; 18862306a36Sopenharmony_ci writel(val, ctrl_phy->otghs_control); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/** 19262306a36Sopenharmony_ci * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode 19362306a36Sopenharmony_ci * or device mode or to denote disconnected state 19462306a36Sopenharmony_ci * @dev: the control module device 19562306a36Sopenharmony_ci * @mode: The mode to which usb should be configured 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * This is an API to write to the mailbox register to notify the usb core that 19862306a36Sopenharmony_ci * a usb device has been connected. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_civoid omap_control_usb_set_mode(struct device *dev, 20162306a36Sopenharmony_ci enum omap_control_usb_mode mode) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct omap_control_phy *ctrl_phy; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dev)) 20662306a36Sopenharmony_ci return; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci ctrl_phy = dev_get_drvdata(dev); 20962306a36Sopenharmony_ci if (!ctrl_phy) { 21062306a36Sopenharmony_ci dev_err(dev, "Invalid control phy device\n"); 21162306a36Sopenharmony_ci return; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS) 21562306a36Sopenharmony_ci return; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci switch (mode) { 21862306a36Sopenharmony_ci case USB_MODE_HOST: 21962306a36Sopenharmony_ci omap_control_usb_host_mode(ctrl_phy); 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci case USB_MODE_DEVICE: 22262306a36Sopenharmony_ci omap_control_usb_device_mode(ctrl_phy); 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci case USB_MODE_DISCONNECT: 22562306a36Sopenharmony_ci omap_control_usb_set_sessionend(ctrl_phy); 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci default: 22862306a36Sopenharmony_ci dev_vdbg(dev, "invalid omap control usb mode\n"); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_control_usb_set_mode); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS; 23462306a36Sopenharmony_cistatic const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2; 23562306a36Sopenharmony_cistatic const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; 23662306a36Sopenharmony_cistatic const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE; 23762306a36Sopenharmony_cistatic const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; 23862306a36Sopenharmony_cistatic const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic const struct of_device_id omap_control_phy_id_table[] = { 24162306a36Sopenharmony_ci { 24262306a36Sopenharmony_ci .compatible = "ti,control-phy-otghs", 24362306a36Sopenharmony_ci .data = &otghs_data, 24462306a36Sopenharmony_ci }, 24562306a36Sopenharmony_ci { 24662306a36Sopenharmony_ci .compatible = "ti,control-phy-usb2", 24762306a36Sopenharmony_ci .data = &usb2_data, 24862306a36Sopenharmony_ci }, 24962306a36Sopenharmony_ci { 25062306a36Sopenharmony_ci .compatible = "ti,control-phy-pipe3", 25162306a36Sopenharmony_ci .data = &pipe3_data, 25262306a36Sopenharmony_ci }, 25362306a36Sopenharmony_ci { 25462306a36Sopenharmony_ci .compatible = "ti,control-phy-pcie", 25562306a36Sopenharmony_ci .data = &pcie_data, 25662306a36Sopenharmony_ci }, 25762306a36Sopenharmony_ci { 25862306a36Sopenharmony_ci .compatible = "ti,control-phy-usb2-dra7", 25962306a36Sopenharmony_ci .data = &dra7usb2_data, 26062306a36Sopenharmony_ci }, 26162306a36Sopenharmony_ci { 26262306a36Sopenharmony_ci .compatible = "ti,control-phy-usb2-am437", 26362306a36Sopenharmony_ci .data = &am437usb2_data, 26462306a36Sopenharmony_ci }, 26562306a36Sopenharmony_ci {}, 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_control_phy_id_table); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int omap_control_phy_probe(struct platform_device *pdev) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci const struct of_device_id *of_id; 27262306a36Sopenharmony_ci struct omap_control_phy *control_phy; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci of_id = of_match_device(omap_control_phy_id_table, &pdev->dev); 27562306a36Sopenharmony_ci if (!of_id) 27662306a36Sopenharmony_ci return -EINVAL; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy), 27962306a36Sopenharmony_ci GFP_KERNEL); 28062306a36Sopenharmony_ci if (!control_phy) 28162306a36Sopenharmony_ci return -ENOMEM; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci control_phy->dev = &pdev->dev; 28462306a36Sopenharmony_ci control_phy->type = *(enum omap_control_phy_type *)of_id->data; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) { 28762306a36Sopenharmony_ci control_phy->otghs_control = 28862306a36Sopenharmony_ci devm_platform_ioremap_resource_byname(pdev, "otghs_control"); 28962306a36Sopenharmony_ci if (IS_ERR(control_phy->otghs_control)) 29062306a36Sopenharmony_ci return PTR_ERR(control_phy->otghs_control); 29162306a36Sopenharmony_ci } else { 29262306a36Sopenharmony_ci control_phy->power = 29362306a36Sopenharmony_ci devm_platform_ioremap_resource_byname(pdev, "power"); 29462306a36Sopenharmony_ci if (IS_ERR(control_phy->power)) { 29562306a36Sopenharmony_ci dev_err(&pdev->dev, "Couldn't get power register\n"); 29662306a36Sopenharmony_ci return PTR_ERR(control_phy->power); 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 || 30162306a36Sopenharmony_ci control_phy->type == OMAP_CTRL_TYPE_PCIE) { 30262306a36Sopenharmony_ci control_phy->sys_clk = devm_clk_get(control_phy->dev, 30362306a36Sopenharmony_ci "sys_clkin"); 30462306a36Sopenharmony_ci if (IS_ERR(control_phy->sys_clk)) { 30562306a36Sopenharmony_ci pr_err("%s: unable to get sys_clkin\n", __func__); 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (control_phy->type == OMAP_CTRL_TYPE_PCIE) { 31162306a36Sopenharmony_ci control_phy->pcie_pcs = 31262306a36Sopenharmony_ci devm_platform_ioremap_resource_byname(pdev, "pcie_pcs"); 31362306a36Sopenharmony_ci if (IS_ERR(control_phy->pcie_pcs)) 31462306a36Sopenharmony_ci return PTR_ERR(control_phy->pcie_pcs); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci dev_set_drvdata(control_phy->dev, control_phy); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic struct platform_driver omap_control_phy_driver = { 32362306a36Sopenharmony_ci .probe = omap_control_phy_probe, 32462306a36Sopenharmony_ci .driver = { 32562306a36Sopenharmony_ci .name = "omap-control-phy", 32662306a36Sopenharmony_ci .of_match_table = omap_control_phy_id_table, 32762306a36Sopenharmony_ci }, 32862306a36Sopenharmony_ci}; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int __init omap_control_phy_init(void) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci return platform_driver_register(&omap_control_phy_driver); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_cisubsys_initcall(omap_control_phy_init); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic void __exit omap_control_phy_exit(void) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci platform_driver_unregister(&omap_control_phy_driver); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_cimodule_exit(omap_control_phy_exit); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ciMODULE_ALIAS("platform:omap_control_phy"); 34362306a36Sopenharmony_ciMODULE_AUTHOR("Texas Instruments Inc."); 34462306a36Sopenharmony_ciMODULE_DESCRIPTION("OMAP Control Module PHY Driver"); 34562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 346