brintos

brintos / linux-shallow public Read only

0
0
Text · 13.1 KiB · 28cf3ef Raw
515 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * phy-uniphier-ahci.c - PHY driver for UniPhier AHCI controller4 * Copyright 2016-2020, Socionext Inc.5 * Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>6 */7 8#include <linux/bitfield.h>9#include <linux/bitops.h>10#include <linux/clk.h>11#include <linux/iopoll.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/of_platform.h>15#include <linux/phy/phy.h>16#include <linux/platform_device.h>17#include <linux/reset.h>18 19struct uniphier_ahciphy_priv {20	struct device *dev;21	void __iomem  *base;22	struct clk *clk, *clk_parent, *clk_parent_gio;23	struct reset_control *rst, *rst_parent, *rst_parent_gio;24	struct reset_control *rst_pm, *rst_tx, *rst_rx;25	const struct uniphier_ahciphy_soc_data *data;26};27 28struct uniphier_ahciphy_soc_data {29	int (*init)(struct uniphier_ahciphy_priv *priv);30	int (*power_on)(struct uniphier_ahciphy_priv *priv);31	int (*power_off)(struct uniphier_ahciphy_priv *priv);32	bool is_legacy;33	bool is_ready_high;34	bool is_phy_clk;35};36 37/* for Pro4 */38#define CKCTRL0				0x039#define CKCTRL0_CK_OFF			BIT(9)40#define CKCTRL0_NCY_MASK		GENMASK(8, 4)41#define CKCTRL0_NCY5_MASK		GENMASK(3, 2)42#define CKCTRL0_PRESCALE_MASK		GENMASK(1, 0)43#define CKCTRL1				0x444#define CKCTRL1_LOS_LVL_MASK		GENMASK(20, 16)45#define CKCTRL1_TX_LVL_MASK		GENMASK(12, 8)46#define RXTXCTRL			0x847#define RXTXCTRL_RX_EQ_VALL_MASK	GENMASK(31, 29)48#define RXTXCTRL_RX_DPLL_MODE_MASK	GENMASK(28, 26)49#define RXTXCTRL_TX_ATTEN_MASK		GENMASK(14, 12)50#define RXTXCTRL_TX_BOOST_MASK		GENMASK(11, 8)51#define RXTXCTRL_TX_EDGERATE_MASK	GENMASK(3, 2)52#define RXTXCTRL_TX_CKO_EN		BIT(0)53#define RSTPWR				0x3054#define RSTPWR_RX_EN_VAL		BIT(18)55 56/* for PXs2/PXs3 */57#define CKCTRL				0x058#define CKCTRL_P0_READY			BIT(15)59#define CKCTRL_P0_RESET			BIT(10)60#define CKCTRL_REF_SSP_EN		BIT(9)61#define TXCTRL0				0x462#define TXCTRL0_AMP_G3_MASK		GENMASK(22, 16)63#define TXCTRL0_AMP_G2_MASK		GENMASK(14, 8)64#define TXCTRL0_AMP_G1_MASK		GENMASK(6, 0)65#define TXCTRL1				0x866#define TXCTRL1_DEEMPH_G3_MASK		GENMASK(21, 16)67#define TXCTRL1_DEEMPH_G2_MASK		GENMASK(13, 8)68#define TXCTRL1_DEEMPH_G1_MASK		GENMASK(5, 0)69#define RXCTRL				0xc70#define RXCTRL_LOS_LVL_MASK		GENMASK(20, 16)71#define RXCTRL_LOS_BIAS_MASK		GENMASK(10, 8)72#define RXCTRL_RX_EQ_MASK		GENMASK(2, 0)73 74static int uniphier_ahciphy_pro4_init(struct uniphier_ahciphy_priv *priv)75{76	u32 val;77 78	/* set phy MPLL parameters */79	val = readl(priv->base + CKCTRL0);80	val &= ~CKCTRL0_NCY_MASK;81	val |= FIELD_PREP(CKCTRL0_NCY_MASK, 0x6);82	val &= ~CKCTRL0_NCY5_MASK;83	val |= FIELD_PREP(CKCTRL0_NCY5_MASK, 0x2);84	val &= ~CKCTRL0_PRESCALE_MASK;85	val |= FIELD_PREP(CKCTRL0_PRESCALE_MASK, 0x1);86	writel(val, priv->base + CKCTRL0);87 88	/* setup phy control parameters */89	val = readl(priv->base + CKCTRL1);90	val &= ~CKCTRL1_LOS_LVL_MASK;91	val |= FIELD_PREP(CKCTRL1_LOS_LVL_MASK, 0x10);92	val &= ~CKCTRL1_TX_LVL_MASK;93	val |= FIELD_PREP(CKCTRL1_TX_LVL_MASK, 0x06);94	writel(val, priv->base + CKCTRL1);95 96	val = readl(priv->base + RXTXCTRL);97	val &= ~RXTXCTRL_RX_EQ_VALL_MASK;98	val |= FIELD_PREP(RXTXCTRL_RX_EQ_VALL_MASK, 0x6);99	val &= ~RXTXCTRL_RX_DPLL_MODE_MASK;100	val |= FIELD_PREP(RXTXCTRL_RX_DPLL_MODE_MASK, 0x3);101	val &= ~RXTXCTRL_TX_ATTEN_MASK;102	val |= FIELD_PREP(RXTXCTRL_TX_ATTEN_MASK, 0x3);103	val &= ~RXTXCTRL_TX_BOOST_MASK;104	val |= FIELD_PREP(RXTXCTRL_TX_BOOST_MASK, 0x5);105	val &= ~RXTXCTRL_TX_EDGERATE_MASK;106	val |= FIELD_PREP(RXTXCTRL_TX_EDGERATE_MASK, 0x0);107	writel(val, priv->base + RXTXCTRL);108 109	return 0;110}111 112static int uniphier_ahciphy_pro4_power_on(struct uniphier_ahciphy_priv *priv)113{114	u32 val;115	int ret;116 117	/* enable reference clock for phy */118	val = readl(priv->base + CKCTRL0);119	val &= ~CKCTRL0_CK_OFF;120	writel(val, priv->base + CKCTRL0);121 122	/* enable TX clock */123	val = readl(priv->base + RXTXCTRL);124	val |= RXTXCTRL_TX_CKO_EN;125	writel(val, priv->base + RXTXCTRL);126 127	/* wait until RX is ready */128	ret = readl_poll_timeout(priv->base + RSTPWR, val,129				 !(val & RSTPWR_RX_EN_VAL), 200, 2000);130	if (ret) {131		dev_err(priv->dev, "Failed to check whether Rx is ready\n");132		goto out_disable_clock;133	}134 135	/* release all reset */136	ret = reset_control_deassert(priv->rst_pm);137	if (ret) {138		dev_err(priv->dev, "Failed to release PM reset\n");139		goto out_disable_clock;140	}141 142	ret = reset_control_deassert(priv->rst_tx);143	if (ret) {144		dev_err(priv->dev, "Failed to release Tx reset\n");145		goto out_reset_pm_assert;146	}147 148	ret = reset_control_deassert(priv->rst_rx);149	if (ret) {150		dev_err(priv->dev, "Failed to release Rx reset\n");151		goto out_reset_tx_assert;152	}153 154	return 0;155 156out_reset_tx_assert:157	reset_control_assert(priv->rst_tx);158out_reset_pm_assert:159	reset_control_assert(priv->rst_pm);160 161out_disable_clock:162	/* disable TX clock */163	val = readl(priv->base + RXTXCTRL);164	val &= ~RXTXCTRL_TX_CKO_EN;165	writel(val, priv->base + RXTXCTRL);166 167	/* disable reference clock for phy */168	val = readl(priv->base + CKCTRL0);169	val |= CKCTRL0_CK_OFF;170	writel(val, priv->base + CKCTRL0);171 172	return ret;173}174 175static int uniphier_ahciphy_pro4_power_off(struct uniphier_ahciphy_priv *priv)176{177	u32 val;178 179	reset_control_assert(priv->rst_rx);180	reset_control_assert(priv->rst_tx);181	reset_control_assert(priv->rst_pm);182 183	/* disable TX clock */184	val = readl(priv->base + RXTXCTRL);185	val &= ~RXTXCTRL_TX_CKO_EN;186	writel(val, priv->base + RXTXCTRL);187 188	/* disable reference clock for phy */189	val = readl(priv->base + CKCTRL0);190	val |= CKCTRL0_CK_OFF;191	writel(val, priv->base + CKCTRL0);192 193	return 0;194}195 196static void uniphier_ahciphy_pxs2_enable(struct uniphier_ahciphy_priv *priv,197					 bool enable)198{199	u32 val;200 201	val = readl(priv->base + CKCTRL);202 203	if (enable) {204		val |= CKCTRL_REF_SSP_EN;205		writel(val, priv->base + CKCTRL);206		val &= ~CKCTRL_P0_RESET;207		writel(val, priv->base + CKCTRL);208	} else {209		val |= CKCTRL_P0_RESET;210		writel(val, priv->base + CKCTRL);211		val &= ~CKCTRL_REF_SSP_EN;212		writel(val, priv->base + CKCTRL);213	}214}215 216static int uniphier_ahciphy_pxs2_power_on(struct uniphier_ahciphy_priv *priv)217{218	int ret;219	u32 val;220 221	uniphier_ahciphy_pxs2_enable(priv, true);222 223	/* wait until PLL is ready */224	if (priv->data->is_ready_high)225		ret = readl_poll_timeout(priv->base + CKCTRL, val,226					 (val & CKCTRL_P0_READY), 200, 400);227	else228		ret = readl_poll_timeout(priv->base + CKCTRL, val,229					 !(val & CKCTRL_P0_READY), 200, 400);230	if (ret) {231		dev_err(priv->dev, "Failed to check whether PHY PLL is ready\n");232		uniphier_ahciphy_pxs2_enable(priv, false);233	}234 235	return ret;236}237 238static int uniphier_ahciphy_pxs2_power_off(struct uniphier_ahciphy_priv *priv)239{240	uniphier_ahciphy_pxs2_enable(priv, false);241 242	return 0;243}244 245static int uniphier_ahciphy_pxs3_init(struct uniphier_ahciphy_priv *priv)246{247	int i;248	u32 val;249 250	/* setup port parameter */251	val = readl(priv->base + TXCTRL0);252	val &= ~TXCTRL0_AMP_G3_MASK;253	val |= FIELD_PREP(TXCTRL0_AMP_G3_MASK, 0x73);254	val &= ~TXCTRL0_AMP_G2_MASK;255	val |= FIELD_PREP(TXCTRL0_AMP_G2_MASK, 0x46);256	val &= ~TXCTRL0_AMP_G1_MASK;257	val |= FIELD_PREP(TXCTRL0_AMP_G1_MASK, 0x42);258	writel(val, priv->base + TXCTRL0);259 260	val = readl(priv->base + TXCTRL1);261	val &= ~TXCTRL1_DEEMPH_G3_MASK;262	val |= FIELD_PREP(TXCTRL1_DEEMPH_G3_MASK, 0x23);263	val &= ~TXCTRL1_DEEMPH_G2_MASK;264	val |= FIELD_PREP(TXCTRL1_DEEMPH_G2_MASK, 0x05);265	val &= ~TXCTRL1_DEEMPH_G1_MASK;266	val |= FIELD_PREP(TXCTRL1_DEEMPH_G1_MASK, 0x05);267 268	val = readl(priv->base + RXCTRL);269	val &= ~RXCTRL_LOS_LVL_MASK;270	val |= FIELD_PREP(RXCTRL_LOS_LVL_MASK, 0x9);271	val &= ~RXCTRL_LOS_BIAS_MASK;272	val |= FIELD_PREP(RXCTRL_LOS_BIAS_MASK, 0x2);273	val &= ~RXCTRL_RX_EQ_MASK;274	val |= FIELD_PREP(RXCTRL_RX_EQ_MASK, 0x1);275 276	/* dummy read 25 times to make a wait time for the phy to stabilize */277	for (i = 0; i < 25; i++)278		readl(priv->base + CKCTRL);279 280	return 0;281}282 283static int uniphier_ahciphy_init(struct phy *phy)284{285	struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);286	int ret;287 288	ret = clk_prepare_enable(priv->clk_parent_gio);289	if (ret)290		return ret;291 292	ret = clk_prepare_enable(priv->clk_parent);293	if (ret)294		goto out_clk_gio_disable;295 296	ret = reset_control_deassert(priv->rst_parent_gio);297	if (ret)298		goto out_clk_disable;299 300	ret = reset_control_deassert(priv->rst_parent);301	if (ret)302		goto out_rst_gio_assert;303 304	if (priv->data->init) {305		ret = priv->data->init(priv);306		if (ret)307			goto out_rst_assert;308	}309 310	return 0;311 312out_rst_assert:313	reset_control_assert(priv->rst_parent);314out_rst_gio_assert:315	reset_control_assert(priv->rst_parent_gio);316out_clk_disable:317	clk_disable_unprepare(priv->clk_parent);318out_clk_gio_disable:319	clk_disable_unprepare(priv->clk_parent_gio);320 321	return ret;322}323 324static int uniphier_ahciphy_exit(struct phy *phy)325{326	struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);327 328	reset_control_assert(priv->rst_parent);329	reset_control_assert(priv->rst_parent_gio);330	clk_disable_unprepare(priv->clk_parent);331	clk_disable_unprepare(priv->clk_parent_gio);332 333	return 0;334}335 336static int uniphier_ahciphy_power_on(struct phy *phy)337{338	struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);339	int ret = 0;340 341	ret = clk_prepare_enable(priv->clk);342	if (ret)343		return ret;344 345	ret = reset_control_deassert(priv->rst);346	if (ret)347		goto out_clk_disable;348 349	if (priv->data->power_on) {350		ret = priv->data->power_on(priv);351		if (ret)352			goto out_reset_assert;353	}354 355	return 0;356 357out_reset_assert:358	reset_control_assert(priv->rst);359out_clk_disable:360	clk_disable_unprepare(priv->clk);361 362	return ret;363}364 365static int uniphier_ahciphy_power_off(struct phy *phy)366{367	struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);368	int ret = 0;369 370	if (priv->data->power_off)371		ret = priv->data->power_off(priv);372 373	reset_control_assert(priv->rst);374	clk_disable_unprepare(priv->clk);375 376	return ret;377}378 379static const struct phy_ops uniphier_ahciphy_ops = {380	.init  = uniphier_ahciphy_init,381	.exit  = uniphier_ahciphy_exit,382	.power_on  = uniphier_ahciphy_power_on,383	.power_off = uniphier_ahciphy_power_off,384	.owner = THIS_MODULE,385};386 387static int uniphier_ahciphy_probe(struct platform_device *pdev)388{389	struct device *dev = &pdev->dev;390	struct uniphier_ahciphy_priv *priv;391	struct phy *phy;392	struct phy_provider *phy_provider;393 394	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);395	if (!priv)396		return -ENOMEM;397 398	priv->dev = dev;399	priv->data = of_device_get_match_data(dev);400	if (WARN_ON(!priv->data))401		return -EINVAL;402 403	priv->base = devm_platform_ioremap_resource(pdev, 0);404	if (IS_ERR(priv->base))405		return PTR_ERR(priv->base);406 407	priv->clk_parent = devm_clk_get(dev, "link");408	if (IS_ERR(priv->clk_parent))409		return PTR_ERR(priv->clk_parent);410 411	if (priv->data->is_phy_clk) {412		priv->clk = devm_clk_get(dev, "phy");413		if (IS_ERR(priv->clk))414			return PTR_ERR(priv->clk);415	}416 417	priv->rst_parent = devm_reset_control_get_shared(dev, "link");418	if (IS_ERR(priv->rst_parent))419		return PTR_ERR(priv->rst_parent);420 421	priv->rst = devm_reset_control_get_shared(dev, "phy");422	if (IS_ERR(priv->rst))423		return PTR_ERR(priv->rst);424 425	if (priv->data->is_legacy) {426		priv->clk_parent_gio = devm_clk_get(dev, "gio");427		if (IS_ERR(priv->clk_parent_gio))428			return PTR_ERR(priv->clk_parent_gio);429		priv->rst_parent_gio =430			devm_reset_control_get_shared(dev, "gio");431		if (IS_ERR(priv->rst_parent_gio))432			return PTR_ERR(priv->rst_parent_gio);433 434		priv->rst_pm = devm_reset_control_get_shared(dev, "pm");435		if (IS_ERR(priv->rst_pm))436			return PTR_ERR(priv->rst_pm);437 438		priv->rst_tx = devm_reset_control_get_shared(dev, "tx");439		if (IS_ERR(priv->rst_tx))440			return PTR_ERR(priv->rst_tx);441 442		priv->rst_rx = devm_reset_control_get_shared(dev, "rx");443		if (IS_ERR(priv->rst_rx))444			return PTR_ERR(priv->rst_rx);445	}446 447	phy = devm_phy_create(dev, dev->of_node, &uniphier_ahciphy_ops);448	if (IS_ERR(phy)) {449		dev_err(dev, "failed to create phy\n");450		return PTR_ERR(phy);451	}452 453	phy_set_drvdata(phy, priv);454	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);455	if (IS_ERR(phy_provider))456		return PTR_ERR(phy_provider);457 458	return 0;459}460 461static const struct uniphier_ahciphy_soc_data uniphier_pro4_data = {462	.init = uniphier_ahciphy_pro4_init,463	.power_on  = uniphier_ahciphy_pro4_power_on,464	.power_off = uniphier_ahciphy_pro4_power_off,465	.is_legacy = true,466	.is_phy_clk = false,467};468 469static const struct uniphier_ahciphy_soc_data uniphier_pxs2_data = {470	.power_on  = uniphier_ahciphy_pxs2_power_on,471	.power_off = uniphier_ahciphy_pxs2_power_off,472	.is_legacy = false,473	.is_ready_high = false,474	.is_phy_clk = false,475};476 477static const struct uniphier_ahciphy_soc_data uniphier_pxs3_data = {478	.init      = uniphier_ahciphy_pxs3_init,479	.power_on  = uniphier_ahciphy_pxs2_power_on,480	.power_off = uniphier_ahciphy_pxs2_power_off,481	.is_legacy = false,482	.is_ready_high = true,483	.is_phy_clk = true,484};485 486static const struct of_device_id uniphier_ahciphy_match[] = {487	{488		.compatible = "socionext,uniphier-pro4-ahci-phy",489		.data = &uniphier_pro4_data,490	},491	{492		.compatible = "socionext,uniphier-pxs2-ahci-phy",493		.data = &uniphier_pxs2_data,494	},495	{496		.compatible = "socionext,uniphier-pxs3-ahci-phy",497		.data = &uniphier_pxs3_data,498	},499	{ /* Sentinel */ },500};501MODULE_DEVICE_TABLE(of, uniphier_ahciphy_match);502 503static struct platform_driver uniphier_ahciphy_driver = {504	.probe = uniphier_ahciphy_probe,505	.driver = {506		.name = "uniphier-ahci-phy",507		.of_match_table = uniphier_ahciphy_match,508	},509};510module_platform_driver(uniphier_ahciphy_driver);511 512MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");513MODULE_DESCRIPTION("UniPhier PHY driver for AHCI controller");514MODULE_LICENSE("GPL v2");515