brintos

brintos / linux-shallow public Read only

0
0
Text · 8.7 KiB · 2fdb8f4 Raw
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