Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Samsung Exynos SoC series PCIe PHY driver
  4 *
  5 * Phy provider for PCIe controller on Exynos SoC series
  6 *
  7 * Copyright (C) 2017 Samsung Electronics Co., Ltd.
  8 * Jaehoon Chung <jh80.chung@samsung.com>
  9 */
 10
 11#include <linux/delay.h>
 12#include <linux/io.h>
 13#include <linux/iopoll.h>
 14#include <linux/init.h>
 15#include <linux/mfd/syscon.h>
 16#include <linux/of.h>
 17#include <linux/of_address.h>
 18#include <linux/of_platform.h>
 19#include <linux/platform_device.h>
 20#include <linux/phy/phy.h>
 21#include <linux/regmap.h>
 22
 23/* PCIe Purple registers */
 24#define PCIE_PHY_GLOBAL_RESET		0x000
 25#define PCIE_PHY_COMMON_RESET		0x004
 26#define PCIE_PHY_CMN_REG		0x008
 27#define PCIE_PHY_MAC_RESET		0x00c
 28#define PCIE_PHY_PLL_LOCKED		0x010
 29#define PCIE_PHY_TRSVREG_RESET		0x020
 30#define PCIE_PHY_TRSV_RESET		0x024
 31
 32/* PCIe PHY registers */
 33#define PCIE_PHY_IMPEDANCE		0x004
 34#define PCIE_PHY_PLL_DIV_0		0x008
 35#define PCIE_PHY_PLL_BIAS		0x00c
 36#define PCIE_PHY_DCC_FEEDBACK		0x014
 37#define PCIE_PHY_PLL_DIV_1		0x05c
 38#define PCIE_PHY_COMMON_POWER		0x064
 39#define PCIE_PHY_COMMON_PD_CMN		BIT(3)
 40#define PCIE_PHY_TRSV0_EMP_LVL		0x084
 41#define PCIE_PHY_TRSV0_DRV_LVL		0x088
 42#define PCIE_PHY_TRSV0_RXCDR		0x0ac
 43#define PCIE_PHY_TRSV0_POWER		0x0c4
 44#define PCIE_PHY_TRSV0_PD_TSV		BIT(7)
 45#define PCIE_PHY_TRSV0_LVCC		0x0dc
 46#define PCIE_PHY_TRSV1_EMP_LVL		0x144
 47#define PCIE_PHY_TRSV1_RXCDR		0x16c
 48#define PCIE_PHY_TRSV1_POWER		0x184
 49#define PCIE_PHY_TRSV1_PD_TSV		BIT(7)
 50#define PCIE_PHY_TRSV1_LVCC		0x19c
 51#define PCIE_PHY_TRSV2_EMP_LVL		0x204
 52#define PCIE_PHY_TRSV2_RXCDR		0x22c
 53#define PCIE_PHY_TRSV2_POWER		0x244
 54#define PCIE_PHY_TRSV2_PD_TSV		BIT(7)
 55#define PCIE_PHY_TRSV2_LVCC		0x25c
 56#define PCIE_PHY_TRSV3_EMP_LVL		0x2c4
 57#define PCIE_PHY_TRSV3_RXCDR		0x2ec
 58#define PCIE_PHY_TRSV3_POWER		0x304
 59#define PCIE_PHY_TRSV3_PD_TSV		BIT(7)
 60#define PCIE_PHY_TRSV3_LVCC		0x31c
 61
 62struct exynos_pcie_phy_data {
 63	const struct phy_ops	*ops;
 64};
 65
 66/* For Exynos pcie phy */
 67struct exynos_pcie_phy {
 68	const struct exynos_pcie_phy_data *drv_data;
 69	void __iomem *phy_base;
 70	void __iomem *blk_base; /* For exynos5440 */
 71};
 72
 73static void exynos_pcie_phy_writel(void __iomem *base, u32 val, u32 offset)
 74{
 75	writel(val, base + offset);
 76}
 77
 78static u32 exynos_pcie_phy_readl(void __iomem *base, u32 offset)
 79{
 80	return readl(base + offset);
 81}
 82
 83/* For Exynos5440 specific functions */
 84static int exynos5440_pcie_phy_init(struct phy *phy)
 85{
 86	struct exynos_pcie_phy *ep = phy_get_drvdata(phy);
 87
 88	/* DCC feedback control off */
 89	exynos_pcie_phy_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
 90
 91	/* set TX/RX impedance */
 92	exynos_pcie_phy_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
 93
 94	/* set 50Mhz PHY clock */
 95	exynos_pcie_phy_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
 96	exynos_pcie_phy_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
 97
 98	/* set TX Differential output for lane 0 */
 99	exynos_pcie_phy_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
100
101	/* set TX Pre-emphasis Level Control for lane 0 to minimum */
102	exynos_pcie_phy_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
103
104	/* set RX clock and data recovery bandwidth */
105	exynos_pcie_phy_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
106	exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
107	exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
108	exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
109	exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
110
111	/* change TX Pre-emphasis Level Control for lanes */
112	exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
113	exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
114	exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
115	exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
116
117	/* set LVCC */
118	exynos_pcie_phy_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
119	exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
120	exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
121	exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
122
123	/* pulse for common reset */
124	exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_COMMON_RESET);
125	udelay(500);
126	exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET);
127
128	return 0;
129}
130
131static int exynos5440_pcie_phy_power_on(struct phy *phy)
132{
133	struct exynos_pcie_phy *ep = phy_get_drvdata(phy);
134	u32 val;
135
136	exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET);
137	exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_CMN_REG);
138	exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSVREG_RESET);
139	exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSV_RESET);
140
141	val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
142	val &= ~PCIE_PHY_COMMON_PD_CMN;
143	exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
144
145	val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
146	val &= ~PCIE_PHY_TRSV0_PD_TSV;
147	exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
148
149	val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
150	val &= ~PCIE_PHY_TRSV1_PD_TSV;
151	exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
152
153	val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
154	val &= ~PCIE_PHY_TRSV2_PD_TSV;
155	exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
156
157	val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
158	val &= ~PCIE_PHY_TRSV3_PD_TSV;
159	exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
160
161	return 0;
162}
163
164static int exynos5440_pcie_phy_power_off(struct phy *phy)
165{
166	struct exynos_pcie_phy *ep = phy_get_drvdata(phy);
167	u32 val;
168
169	if (readl_poll_timeout(ep->phy_base + PCIE_PHY_PLL_LOCKED, val,
170				(val != 0), 1, 500)) {
171		dev_err(&phy->dev, "PLL Locked: 0x%x\n", val);
172		return -ETIMEDOUT;
173	}
174
175	val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
176	val |= PCIE_PHY_COMMON_PD_CMN;
177	exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
178
179	val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
180	val |= PCIE_PHY_TRSV0_PD_TSV;
181	exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
182
183	val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
184	val |= PCIE_PHY_TRSV1_PD_TSV;
185	exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
186
187	val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
188	val |= PCIE_PHY_TRSV2_PD_TSV;
189	exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
190
191	val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
192	val |= PCIE_PHY_TRSV3_PD_TSV;
193	exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
194
195	return 0;
196}
197
198static int exynos5440_pcie_phy_reset(struct phy *phy)
199{
200	struct exynos_pcie_phy *ep = phy_get_drvdata(phy);
201
202	exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_MAC_RESET);
203	exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_GLOBAL_RESET);
204	exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_GLOBAL_RESET);
205
206	return 0;
207}
208
209static const struct phy_ops exynos5440_phy_ops = {
210	.init		= exynos5440_pcie_phy_init,
211	.power_on	= exynos5440_pcie_phy_power_on,
212	.power_off	= exynos5440_pcie_phy_power_off,
213	.reset		= exynos5440_pcie_phy_reset,
214	.owner		= THIS_MODULE,
215};
216
217static const struct exynos_pcie_phy_data exynos5440_pcie_phy_data = {
218	.ops		= &exynos5440_phy_ops,
219};
220
221static const struct of_device_id exynos_pcie_phy_match[] = {
222	{
223		.compatible = "samsung,exynos5440-pcie-phy",
224		.data = &exynos5440_pcie_phy_data,
225	},
226	{},
227};
228
229static int exynos_pcie_phy_probe(struct platform_device *pdev)
230{
231	struct device *dev = &pdev->dev;
232	struct exynos_pcie_phy *exynos_phy;
233	struct phy *generic_phy;
234	struct phy_provider *phy_provider;
235	struct resource *res;
236	const struct exynos_pcie_phy_data *drv_data;
237
238	drv_data = of_device_get_match_data(dev);
239	if (!drv_data)
240		return -ENODEV;
241
242	exynos_phy = devm_kzalloc(dev, sizeof(*exynos_phy), GFP_KERNEL);
243	if (!exynos_phy)
244		return -ENOMEM;
245
246	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
247	exynos_phy->phy_base = devm_ioremap_resource(dev, res);
248	if (IS_ERR(exynos_phy->phy_base))
249		return PTR_ERR(exynos_phy->phy_base);
250
251	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
252	exynos_phy->blk_base = devm_ioremap_resource(dev, res);
253	if (IS_ERR(exynos_phy->blk_base))
254		return PTR_ERR(exynos_phy->blk_base);
255
256	exynos_phy->drv_data = drv_data;
257
258	generic_phy = devm_phy_create(dev, dev->of_node, drv_data->ops);
259	if (IS_ERR(generic_phy)) {
260		dev_err(dev, "failed to create PHY\n");
261		return PTR_ERR(generic_phy);
262	}
263
264	phy_set_drvdata(generic_phy, exynos_phy);
265	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
266
267	return PTR_ERR_OR_ZERO(phy_provider);
268}
269
270static struct platform_driver exynos_pcie_phy_driver = {
271	.probe	= exynos_pcie_phy_probe,
272	.driver = {
273		.of_match_table	= exynos_pcie_phy_match,
274		.name		= "exynos_pcie_phy",
275		.suppress_bind_attrs = true,
276	}
277};
278
279builtin_platform_driver(exynos_pcie_phy_driver);