brintos

brintos / linux-shallow public Read only

0
0
Text · 10.9 KiB · adc6394 Raw
419 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Copyright (c) 2017 NXP. */3 4#include <linux/bitfield.h>5#include <linux/clk.h>6#include <linux/delay.h>7#include <linux/io.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/phy/phy.h>11#include <linux/platform_device.h>12#include <linux/regulator/consumer.h>13 14#define PHY_CTRL0			0x015#define PHY_CTRL0_REF_SSP_EN		BIT(2)16#define PHY_CTRL0_FSEL_MASK		GENMASK(10, 5)17#define PHY_CTRL0_FSEL_24M		0x2a18 19#define PHY_CTRL1			0x420#define PHY_CTRL1_RESET			BIT(0)21#define PHY_CTRL1_COMMONONN		BIT(1)22#define PHY_CTRL1_ATERESET		BIT(3)23#define PHY_CTRL1_VDATSRCENB0		BIT(19)24#define PHY_CTRL1_VDATDETENB0		BIT(20)25 26#define PHY_CTRL2			0x827#define PHY_CTRL2_TXENABLEN0		BIT(8)28#define PHY_CTRL2_OTG_DISABLE		BIT(9)29 30#define PHY_CTRL3			0xc31#define PHY_CTRL3_COMPDISTUNE_MASK	GENMASK(2, 0)32#define PHY_CTRL3_TXPREEMP_TUNE_MASK	GENMASK(16, 15)33#define PHY_CTRL3_TXRISE_TUNE_MASK	GENMASK(21, 20)34#define PHY_CTRL3_TXVREF_TUNE_MASK	GENMASK(25, 22)35#define PHY_CTRL3_TX_VBOOST_LEVEL_MASK	GENMASK(31, 29)36 37#define PHY_CTRL4			0x1038#define PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK	GENMASK(20, 15)39 40#define PHY_CTRL5			0x1441#define PHY_CTRL5_DMPWD_OVERRIDE_SEL	BIT(23)42#define PHY_CTRL5_DMPWD_OVERRIDE	BIT(22)43#define PHY_CTRL5_DPPWD_OVERRIDE_SEL	BIT(21)44#define PHY_CTRL5_DPPWD_OVERRIDE	BIT(20)45#define PHY_CTRL5_PCS_TX_SWING_FULL_MASK	GENMASK(6, 0)46 47#define PHY_CTRL6			0x1848#define PHY_CTRL6_ALT_CLK_EN		BIT(1)49#define PHY_CTRL6_ALT_CLK_SEL		BIT(0)50 51#define PHY_TUNE_DEFAULT		0xffffffff52 53struct imx8mq_usb_phy {54	struct phy *phy;55	struct clk *clk;56	void __iomem *base;57	struct regulator *vbus;58	u32 pcs_tx_swing_full;59	u32 pcs_tx_deemph_3p5db;60	u32 tx_vref_tune;61	u32 tx_rise_tune;62	u32 tx_preemp_amp_tune;63	u32 tx_vboost_level;64	u32 comp_dis_tune;65};66 67static u32 phy_tx_vref_tune_from_property(u32 percent)68{69	percent = clamp(percent, 94U, 124U);70 71	return DIV_ROUND_CLOSEST(percent - 94U, 2);72}73 74static u32 phy_tx_rise_tune_from_property(u32 percent)75{76	switch (percent) {77	case 0 ... 98:78		return 3;79	case 99:80		return 2;81	case 100 ... 101:82		return 1;83	default:84		return 0;85	}86}87 88static u32 phy_tx_preemp_amp_tune_from_property(u32 microamp)89{90	microamp = min(microamp, 1800U);91 92	return microamp / 600;93}94 95static u32 phy_tx_vboost_level_from_property(u32 microvolt)96{97	switch (microvolt) {98	case 0 ... 960:99		return 0;100	case 961 ... 1160:101		return 2;102	default:103		return 3;104	}105}106 107static u32 phy_pcs_tx_deemph_3p5db_from_property(u32 decibel)108{109	return min(decibel, 36U);110}111 112static u32 phy_comp_dis_tune_from_property(u32 percent)113{114	switch (percent) {115	case 0 ... 92:116		return 0;117	case 93 ... 95:118		return 1;119	case 96 ... 97:120		return 2;121	case 98 ... 102:122		return 3;123	case 103 ... 105:124		return 4;125	case 106 ... 109:126		return 5;127	case 110 ... 113:128		return 6;129	default:130		return 7;131	}132}133static u32 phy_pcs_tx_swing_full_from_property(u32 percent)134{135	percent = min(percent, 100U);136 137	return (percent * 127) / 100;138}139 140static void imx8m_get_phy_tuning_data(struct imx8mq_usb_phy *imx_phy)141{142	struct device *dev = imx_phy->phy->dev.parent;143 144	if (device_property_read_u32(dev, "fsl,phy-tx-vref-tune-percent",145				     &imx_phy->tx_vref_tune))146		imx_phy->tx_vref_tune = PHY_TUNE_DEFAULT;147	else148		imx_phy->tx_vref_tune =149			phy_tx_vref_tune_from_property(imx_phy->tx_vref_tune);150 151	if (device_property_read_u32(dev, "fsl,phy-tx-rise-tune-percent",152				     &imx_phy->tx_rise_tune))153		imx_phy->tx_rise_tune = PHY_TUNE_DEFAULT;154	else155		imx_phy->tx_rise_tune =156			phy_tx_rise_tune_from_property(imx_phy->tx_rise_tune);157 158	if (device_property_read_u32(dev, "fsl,phy-tx-preemp-amp-tune-microamp",159				     &imx_phy->tx_preemp_amp_tune))160		imx_phy->tx_preemp_amp_tune = PHY_TUNE_DEFAULT;161	else162		imx_phy->tx_preemp_amp_tune =163			phy_tx_preemp_amp_tune_from_property(imx_phy->tx_preemp_amp_tune);164 165	if (device_property_read_u32(dev, "fsl,phy-tx-vboost-level-microvolt",166				     &imx_phy->tx_vboost_level))167		imx_phy->tx_vboost_level = PHY_TUNE_DEFAULT;168	else169		imx_phy->tx_vboost_level =170			phy_tx_vboost_level_from_property(imx_phy->tx_vboost_level);171 172	if (device_property_read_u32(dev, "fsl,phy-comp-dis-tune-percent",173				     &imx_phy->comp_dis_tune))174		imx_phy->comp_dis_tune = PHY_TUNE_DEFAULT;175	else176		imx_phy->comp_dis_tune =177			phy_comp_dis_tune_from_property(imx_phy->comp_dis_tune);178 179	if (device_property_read_u32(dev, "fsl,phy-pcs-tx-deemph-3p5db-attenuation-db",180				     &imx_phy->pcs_tx_deemph_3p5db))181		imx_phy->pcs_tx_deemph_3p5db = PHY_TUNE_DEFAULT;182	else183		imx_phy->pcs_tx_deemph_3p5db =184			phy_pcs_tx_deemph_3p5db_from_property(imx_phy->pcs_tx_deemph_3p5db);185 186	if (device_property_read_u32(dev, "fsl,phy-pcs-tx-swing-full-percent",187				     &imx_phy->pcs_tx_swing_full))188		imx_phy->pcs_tx_swing_full = PHY_TUNE_DEFAULT;189	else190		imx_phy->pcs_tx_swing_full =191			phy_pcs_tx_swing_full_from_property(imx_phy->pcs_tx_swing_full);192}193 194static void imx8m_phy_tune(struct imx8mq_usb_phy *imx_phy)195{196	u32 value;197 198	/* PHY tuning */199	if (imx_phy->pcs_tx_deemph_3p5db != PHY_TUNE_DEFAULT) {200		value = readl(imx_phy->base + PHY_CTRL4);201		value &= ~PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK;202		value |= FIELD_PREP(PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK,203				   imx_phy->pcs_tx_deemph_3p5db);204		writel(value, imx_phy->base + PHY_CTRL4);205	}206 207	if (imx_phy->pcs_tx_swing_full != PHY_TUNE_DEFAULT) {208		value = readl(imx_phy->base + PHY_CTRL5);209		value |= FIELD_PREP(PHY_CTRL5_PCS_TX_SWING_FULL_MASK,210				   imx_phy->pcs_tx_swing_full);211		writel(value, imx_phy->base + PHY_CTRL5);212	}213 214	if ((imx_phy->tx_vref_tune & imx_phy->tx_rise_tune &215	     imx_phy->tx_preemp_amp_tune & imx_phy->comp_dis_tune &216	     imx_phy->tx_vboost_level) == PHY_TUNE_DEFAULT)217		/* If all are the default values, no need update. */218		return;219 220	value = readl(imx_phy->base + PHY_CTRL3);221 222	if (imx_phy->tx_vref_tune != PHY_TUNE_DEFAULT) {223		value &= ~PHY_CTRL3_TXVREF_TUNE_MASK;224		value |= FIELD_PREP(PHY_CTRL3_TXVREF_TUNE_MASK,225				   imx_phy->tx_vref_tune);226	}227 228	if (imx_phy->tx_rise_tune != PHY_TUNE_DEFAULT) {229		value &= ~PHY_CTRL3_TXRISE_TUNE_MASK;230		value |= FIELD_PREP(PHY_CTRL3_TXRISE_TUNE_MASK,231				    imx_phy->tx_rise_tune);232	}233 234	if (imx_phy->tx_preemp_amp_tune != PHY_TUNE_DEFAULT) {235		value &= ~PHY_CTRL3_TXPREEMP_TUNE_MASK;236		value |= FIELD_PREP(PHY_CTRL3_TXPREEMP_TUNE_MASK,237				imx_phy->tx_preemp_amp_tune);238	}239 240	if (imx_phy->comp_dis_tune != PHY_TUNE_DEFAULT) {241		value &= ~PHY_CTRL3_COMPDISTUNE_MASK;242		value |= FIELD_PREP(PHY_CTRL3_COMPDISTUNE_MASK,243				    imx_phy->comp_dis_tune);244	}245 246	if (imx_phy->tx_vboost_level != PHY_TUNE_DEFAULT) {247		value &= ~PHY_CTRL3_TX_VBOOST_LEVEL_MASK;248		value |= FIELD_PREP(PHY_CTRL3_TX_VBOOST_LEVEL_MASK,249				    imx_phy->tx_vboost_level);250	}251 252	writel(value, imx_phy->base + PHY_CTRL3);253}254 255static int imx8mq_usb_phy_init(struct phy *phy)256{257	struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);258	u32 value;259 260	value = readl(imx_phy->base + PHY_CTRL1);261	value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 |262		   PHY_CTRL1_COMMONONN);263	value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET;264	writel(value, imx_phy->base + PHY_CTRL1);265 266	value = readl(imx_phy->base + PHY_CTRL0);267	value |= PHY_CTRL0_REF_SSP_EN;268	writel(value, imx_phy->base + PHY_CTRL0);269 270	value = readl(imx_phy->base + PHY_CTRL2);271	value |= PHY_CTRL2_TXENABLEN0;272	writel(value, imx_phy->base + PHY_CTRL2);273 274	value = readl(imx_phy->base + PHY_CTRL1);275	value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);276	writel(value, imx_phy->base + PHY_CTRL1);277 278	return 0;279}280 281static int imx8mp_usb_phy_init(struct phy *phy)282{283	struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);284	u32 value;285 286	/* USB3.0 PHY signal fsel for 24M ref */287	value = readl(imx_phy->base + PHY_CTRL0);288	value &= ~PHY_CTRL0_FSEL_MASK;289	value |= FIELD_PREP(PHY_CTRL0_FSEL_MASK, PHY_CTRL0_FSEL_24M);290	writel(value, imx_phy->base + PHY_CTRL0);291 292	/* Disable alt_clk_en and use internal MPLL clocks */293	value = readl(imx_phy->base + PHY_CTRL6);294	value &= ~(PHY_CTRL6_ALT_CLK_SEL | PHY_CTRL6_ALT_CLK_EN);295	writel(value, imx_phy->base + PHY_CTRL6);296 297	value = readl(imx_phy->base + PHY_CTRL1);298	value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0);299	value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET;300	writel(value, imx_phy->base + PHY_CTRL1);301 302	value = readl(imx_phy->base + PHY_CTRL0);303	value |= PHY_CTRL0_REF_SSP_EN;304	writel(value, imx_phy->base + PHY_CTRL0);305 306	value = readl(imx_phy->base + PHY_CTRL2);307	value |= PHY_CTRL2_TXENABLEN0 | PHY_CTRL2_OTG_DISABLE;308	writel(value, imx_phy->base + PHY_CTRL2);309 310	udelay(10);311 312	value = readl(imx_phy->base + PHY_CTRL1);313	value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);314	writel(value, imx_phy->base + PHY_CTRL1);315 316	imx8m_phy_tune(imx_phy);317 318	return 0;319}320 321static int imx8mq_phy_power_on(struct phy *phy)322{323	struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);324	int ret;325 326	ret = regulator_enable(imx_phy->vbus);327	if (ret)328		return ret;329 330	return clk_prepare_enable(imx_phy->clk);331}332 333static int imx8mq_phy_power_off(struct phy *phy)334{335	struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);336 337	clk_disable_unprepare(imx_phy->clk);338	regulator_disable(imx_phy->vbus);339 340	return 0;341}342 343static const struct phy_ops imx8mq_usb_phy_ops = {344	.init		= imx8mq_usb_phy_init,345	.power_on	= imx8mq_phy_power_on,346	.power_off	= imx8mq_phy_power_off,347	.owner		= THIS_MODULE,348};349 350static const struct phy_ops imx8mp_usb_phy_ops = {351	.init		= imx8mp_usb_phy_init,352	.power_on	= imx8mq_phy_power_on,353	.power_off	= imx8mq_phy_power_off,354	.owner		= THIS_MODULE,355};356 357static const struct of_device_id imx8mq_usb_phy_of_match[] = {358	{.compatible = "fsl,imx8mq-usb-phy",359	 .data = &imx8mq_usb_phy_ops,},360	{.compatible = "fsl,imx8mp-usb-phy",361	 .data = &imx8mp_usb_phy_ops,},362	{ }363};364MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);365 366static int imx8mq_usb_phy_probe(struct platform_device *pdev)367{368	struct phy_provider *phy_provider;369	struct device *dev = &pdev->dev;370	struct imx8mq_usb_phy *imx_phy;371	const struct phy_ops *phy_ops;372 373	imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL);374	if (!imx_phy)375		return -ENOMEM;376 377	imx_phy->clk = devm_clk_get(dev, "phy");378	if (IS_ERR(imx_phy->clk)) {379		dev_err(dev, "failed to get imx8mq usb phy clock\n");380		return PTR_ERR(imx_phy->clk);381	}382 383	imx_phy->base = devm_platform_ioremap_resource(pdev, 0);384	if (IS_ERR(imx_phy->base))385		return PTR_ERR(imx_phy->base);386 387	phy_ops = of_device_get_match_data(dev);388	if (!phy_ops)389		return -EINVAL;390 391	imx_phy->phy = devm_phy_create(dev, NULL, phy_ops);392	if (IS_ERR(imx_phy->phy))393		return PTR_ERR(imx_phy->phy);394 395	imx_phy->vbus = devm_regulator_get(dev, "vbus");396	if (IS_ERR(imx_phy->vbus))397		return dev_err_probe(dev, PTR_ERR(imx_phy->vbus), "failed to get vbus\n");398 399	phy_set_drvdata(imx_phy->phy, imx_phy);400 401	imx8m_get_phy_tuning_data(imx_phy);402 403	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);404 405	return PTR_ERR_OR_ZERO(phy_provider);406}407 408static struct platform_driver imx8mq_usb_phy_driver = {409	.probe	= imx8mq_usb_phy_probe,410	.driver = {411		.name	= "imx8mq-usb-phy",412		.of_match_table	= imx8mq_usb_phy_of_match,413	}414};415module_platform_driver(imx8mq_usb_phy_driver);416 417MODULE_DESCRIPTION("FSL IMX8MQ USB PHY driver");418MODULE_LICENSE("GPL");419