brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · 68aa595 Raw
294 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver4 *5 * Copyright (C) 2016 David Lechner <david@lechnology.com>6 */7 8#include <linux/clk.h>9#include <linux/io.h>10#include <linux/of.h>11#include <linux/mfd/da8xx-cfgchip.h>12#include <linux/mfd/syscon.h>13#include <linux/module.h>14#include <linux/phy/phy.h>15#include <linux/platform_data/phy-da8xx-usb.h>16#include <linux/platform_device.h>17#include <linux/pm_runtime.h>18#include <linux/regmap.h>19 20#define PHY_INIT_BITS	(CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN)21 22struct da8xx_usb_phy {23	struct device		*dev;24	struct phy_provider	*phy_provider;25	struct phy		*usb11_phy;26	struct phy		*usb20_phy;27	struct clk		*usb11_clk;28	struct clk		*usb20_clk;29	struct regmap		*regmap;30};31 32static int da8xx_usb11_phy_power_on(struct phy *phy)33{34	struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);35	int ret;36 37	ret = clk_prepare_enable(d_phy->usb11_clk);38	if (ret)39		return ret;40 41	regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM,42			  CFGCHIP2_USB1SUSPENDM);43 44	/*45	 * USB1.1 can used USB2.0 output clock as reference clock so this is here to prevent USB2.046	 * from shutting PHY's power when USB1.1 might use it47	 */48	pm_runtime_get_sync(d_phy->dev);49 50	return 0;51}52 53static int da8xx_usb11_phy_power_off(struct phy *phy)54{55	struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);56 57	regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0);58 59	clk_disable_unprepare(d_phy->usb11_clk);60	pm_runtime_put_sync(d_phy->dev);61 62	return 0;63}64 65static const struct phy_ops da8xx_usb11_phy_ops = {66	.power_on	= da8xx_usb11_phy_power_on,67	.power_off	= da8xx_usb11_phy_power_off,68	.owner		= THIS_MODULE,69};70 71static int da8xx_usb20_phy_power_on(struct phy *phy)72{73	struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);74	int ret;75 76	ret = clk_prepare_enable(d_phy->usb20_clk);77	if (ret)78		return ret;79 80	regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 0);81 82	return 0;83}84 85static int da8xx_usb20_phy_power_off(struct phy *phy)86{87	struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);88 89	regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN,90			  CFGCHIP2_OTGPWRDN);91 92	clk_disable_unprepare(d_phy->usb20_clk);93 94	return 0;95}96 97static int da8xx_usb20_phy_set_mode(struct phy *phy,98				    enum phy_mode mode, int submode)99{100	struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);101	u32 val;102 103	switch (mode) {104	case PHY_MODE_USB_HOST:		/* Force VBUS valid, ID = 0 */105		val = CFGCHIP2_OTGMODE_FORCE_HOST;106		break;107	case PHY_MODE_USB_DEVICE:	/* Force VBUS valid, ID = 1 */108		val = CFGCHIP2_OTGMODE_FORCE_DEVICE;109		break;110	case PHY_MODE_USB_OTG:	/* Don't override the VBUS/ID comparators */111		val = CFGCHIP2_OTGMODE_NO_OVERRIDE;112		break;113	default:114		return -EINVAL;115	}116 117	regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGMODE_MASK,118			  val);119 120	return 0;121}122 123static const struct phy_ops da8xx_usb20_phy_ops = {124	.power_on	= da8xx_usb20_phy_power_on,125	.power_off	= da8xx_usb20_phy_power_off,126	.set_mode	= da8xx_usb20_phy_set_mode,127	.owner		= THIS_MODULE,128};129 130static int __maybe_unused da8xx_runtime_suspend(struct device *dev)131{132	struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);133 134	dev_dbg(dev, "Suspending ...\n");135 136	regmap_set_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN);137 138	return 0;139}140 141static int __maybe_unused da8xx_runtime_resume(struct device *dev)142{143	u32 mask = CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN | CFGCHIP2_PHY_PLLON;144	struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);145	u32 pll_status;146 147	regmap_update_bits(d_phy->regmap, CFGCHIP(2), mask, CFGCHIP2_PHY_PLLON);148 149	dev_dbg(dev, "Resuming ...\n");150 151	return regmap_read_poll_timeout(d_phy->regmap, CFGCHIP(2), pll_status,152					pll_status & CFGCHIP2_PHYCLKGD, 1000, 500000);153}154 155static const struct dev_pm_ops da8xx_usb_phy_pm_ops = {156	SET_RUNTIME_PM_OPS(da8xx_runtime_suspend, da8xx_runtime_resume, NULL)157};158 159static struct phy *da8xx_usb_phy_of_xlate(struct device *dev,160					 const struct of_phandle_args *args)161{162	struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);163 164	if (!d_phy)165		return ERR_PTR(-ENODEV);166 167	switch (args->args[0]) {168	case 0:169		return d_phy->usb20_phy;170	case 1:171		return d_phy->usb11_phy;172	default:173		return ERR_PTR(-EINVAL);174	}175}176 177static int da8xx_usb_phy_probe(struct platform_device *pdev)178{179	struct device		*dev = &pdev->dev;180	struct da8xx_usb_phy_platform_data *pdata = dev->platform_data;181	struct device_node	*node = dev->of_node;182	struct da8xx_usb_phy	*d_phy;183 184	d_phy = devm_kzalloc(dev, sizeof(*d_phy), GFP_KERNEL);185	if (!d_phy)186		return -ENOMEM;187 188	d_phy->dev = dev;189 190	if (pdata)191		d_phy->regmap = pdata->cfgchip;192	else193		d_phy->regmap = syscon_regmap_lookup_by_compatible(194							"ti,da830-cfgchip");195	if (IS_ERR(d_phy->regmap)) {196		dev_err(dev, "Failed to get syscon\n");197		return PTR_ERR(d_phy->regmap);198	}199 200	d_phy->usb11_clk = devm_clk_get(dev, "usb1_clk48");201	if (IS_ERR(d_phy->usb11_clk)) {202		dev_err(dev, "Failed to get usb1_clk48\n");203		return PTR_ERR(d_phy->usb11_clk);204	}205 206	d_phy->usb20_clk = devm_clk_get(dev, "usb0_clk48");207	if (IS_ERR(d_phy->usb20_clk)) {208		dev_err(dev, "Failed to get usb0_clk48\n");209		return PTR_ERR(d_phy->usb20_clk);210	}211 212	d_phy->usb11_phy = devm_phy_create(dev, node, &da8xx_usb11_phy_ops);213	if (IS_ERR(d_phy->usb11_phy)) {214		dev_err(dev, "Failed to create usb11 phy\n");215		return PTR_ERR(d_phy->usb11_phy);216	}217 218	d_phy->usb20_phy = devm_phy_create(dev, node, &da8xx_usb20_phy_ops);219	if (IS_ERR(d_phy->usb20_phy)) {220		dev_err(dev, "Failed to create usb20 phy\n");221		return PTR_ERR(d_phy->usb20_phy);222	}223 224	platform_set_drvdata(pdev, d_phy);225	phy_set_drvdata(d_phy->usb11_phy, d_phy);226	phy_set_drvdata(d_phy->usb20_phy, d_phy);227 228	if (node) {229		d_phy->phy_provider = devm_of_phy_provider_register(dev,230							da8xx_usb_phy_of_xlate);231		if (IS_ERR(d_phy->phy_provider)) {232			dev_err(dev, "Failed to create phy provider\n");233			return PTR_ERR(d_phy->phy_provider);234		}235	} else {236		int ret;237 238		ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy",239					"ohci-da8xx");240		if (ret)241			dev_warn(dev, "Failed to create usb11 phy lookup\n");242		ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy",243					"musb-da8xx");244		if (ret)245			dev_warn(dev, "Failed to create usb20 phy lookup\n");246	}247 248	regmap_write_bits(d_phy->regmap, CFGCHIP(2),249			  PHY_INIT_BITS, PHY_INIT_BITS);250 251	pm_runtime_set_active(dev);252	devm_pm_runtime_enable(dev);253	/*254	 * Prevent runtime pm from being ON by default. Users can enable255	 * it using power/control in sysfs.256	 */257	pm_runtime_forbid(dev);258 259	return 0;260}261 262static void da8xx_usb_phy_remove(struct platform_device *pdev)263{264	struct da8xx_usb_phy *d_phy = platform_get_drvdata(pdev);265 266	if (!pdev->dev.of_node) {267		phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx");268		phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci-da8xx");269	}270}271 272static const struct of_device_id da8xx_usb_phy_ids[] = {273	{ .compatible = "ti,da830-usb-phy" },274	{ }275};276MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids);277 278static struct platform_driver da8xx_usb_phy_driver = {279	.probe	= da8xx_usb_phy_probe,280	.remove_new = da8xx_usb_phy_remove,281	.driver	= {282		.name	= "da8xx-usb-phy",283		.pm	= &da8xx_usb_phy_pm_ops,284		.of_match_table = da8xx_usb_phy_ids,285	},286};287 288module_platform_driver(da8xx_usb_phy_driver);289 290MODULE_ALIAS("platform:da8xx-usb-phy");291MODULE_AUTHOR("David Lechner <david@lechnology.com>");292MODULE_DESCRIPTION("TI DA8xx USB PHY driver");293MODULE_LICENSE("GPL v2");294