1e4e46bc7SJianjun Wang // SPDX-License-Identifier: GPL-2.0
2e4e46bc7SJianjun Wang /*
3e4e46bc7SJianjun Wang  * Copyright (c) 2022 MediaTek Inc.
4e4e46bc7SJianjun Wang  * Author: Jianjun Wang <jianjun.wang@mediatek.com>
5e4e46bc7SJianjun Wang  */
6e4e46bc7SJianjun Wang 
7e4e46bc7SJianjun Wang #include <linux/bitfield.h>
8e4e46bc7SJianjun Wang #include <linux/module.h>
9e4e46bc7SJianjun Wang #include <linux/nvmem-consumer.h>
10*7559e757SRob Herring #include <linux/of.h>
11e4e46bc7SJianjun Wang #include <linux/phy/phy.h>
12e4e46bc7SJianjun Wang #include <linux/platform_device.h>
13e4e46bc7SJianjun Wang #include <linux/slab.h>
14e4e46bc7SJianjun Wang 
15e4e46bc7SJianjun Wang #include "phy-mtk-io.h"
16e4e46bc7SJianjun Wang 
17e4e46bc7SJianjun Wang #define PEXTP_ANA_GLB_00_REG		0x9000
18e4e46bc7SJianjun Wang /* Internal Resistor Selection of TX Bias Current */
19e4e46bc7SJianjun Wang #define EFUSE_GLB_INTR_SEL		GENMASK(28, 24)
20e4e46bc7SJianjun Wang 
21e4e46bc7SJianjun Wang #define PEXTP_ANA_LN0_TRX_REG		0xa000
22e4e46bc7SJianjun Wang 
23e4e46bc7SJianjun Wang #define PEXTP_ANA_TX_REG		0x04
24e4e46bc7SJianjun Wang /* TX PMOS impedance selection */
25e4e46bc7SJianjun Wang #define EFUSE_LN_TX_PMOS_SEL		GENMASK(5, 2)
26e4e46bc7SJianjun Wang /* TX NMOS impedance selection */
27e4e46bc7SJianjun Wang #define EFUSE_LN_TX_NMOS_SEL		GENMASK(11, 8)
28e4e46bc7SJianjun Wang 
29e4e46bc7SJianjun Wang #define PEXTP_ANA_RX_REG		0x3c
30e4e46bc7SJianjun Wang /* RX impedance selection */
31e4e46bc7SJianjun Wang #define EFUSE_LN_RX_SEL			GENMASK(3, 0)
32e4e46bc7SJianjun Wang 
33e4e46bc7SJianjun Wang #define PEXTP_ANA_LANE_OFFSET		0x100
34e4e46bc7SJianjun Wang 
35e4e46bc7SJianjun Wang /**
36e4e46bc7SJianjun Wang  * struct mtk_pcie_lane_efuse - eFuse data for each lane
37e4e46bc7SJianjun Wang  * @tx_pmos: TX PMOS impedance selection data
38e4e46bc7SJianjun Wang  * @tx_nmos: TX NMOS impedance selection data
39e4e46bc7SJianjun Wang  * @rx_data: RX impedance selection data
40e4e46bc7SJianjun Wang  * @lane_efuse_supported: software eFuse data is supported for this lane
41e4e46bc7SJianjun Wang  */
42e4e46bc7SJianjun Wang struct mtk_pcie_lane_efuse {
43e4e46bc7SJianjun Wang 	u32 tx_pmos;
44e4e46bc7SJianjun Wang 	u32 tx_nmos;
45e4e46bc7SJianjun Wang 	u32 rx_data;
46e4e46bc7SJianjun Wang 	bool lane_efuse_supported;
47e4e46bc7SJianjun Wang };
48e4e46bc7SJianjun Wang 
49e4e46bc7SJianjun Wang /**
50e4e46bc7SJianjun Wang  * struct mtk_pcie_phy_data - phy data for each SoC
51e4e46bc7SJianjun Wang  * @num_lanes: supported lane numbers
52e4e46bc7SJianjun Wang  * @sw_efuse_supported: support software to load eFuse data
53e4e46bc7SJianjun Wang  */
54e4e46bc7SJianjun Wang struct mtk_pcie_phy_data {
55e4e46bc7SJianjun Wang 	int num_lanes;
56e4e46bc7SJianjun Wang 	bool sw_efuse_supported;
57e4e46bc7SJianjun Wang };
58e4e46bc7SJianjun Wang 
59e4e46bc7SJianjun Wang /**
60e4e46bc7SJianjun Wang  * struct mtk_pcie_phy - PCIe phy driver main structure
61e4e46bc7SJianjun Wang  * @dev: pointer to device
62e4e46bc7SJianjun Wang  * @phy: pointer to generic phy
63e4e46bc7SJianjun Wang  * @sif_base: IO mapped register base address of system interface
64e4e46bc7SJianjun Wang  * @data: pointer to SoC dependent data
65e4e46bc7SJianjun Wang  * @sw_efuse_en: software eFuse enable status
66e4e46bc7SJianjun Wang  * @efuse_glb_intr: internal resistor selection of TX bias current data
67e4e46bc7SJianjun Wang  * @efuse: pointer to eFuse data for each lane
68e4e46bc7SJianjun Wang  */
69e4e46bc7SJianjun Wang struct mtk_pcie_phy {
70e4e46bc7SJianjun Wang 	struct device *dev;
71e4e46bc7SJianjun Wang 	struct phy *phy;
72e4e46bc7SJianjun Wang 	void __iomem *sif_base;
73e4e46bc7SJianjun Wang 	const struct mtk_pcie_phy_data *data;
74e4e46bc7SJianjun Wang 
75e4e46bc7SJianjun Wang 	bool sw_efuse_en;
76e4e46bc7SJianjun Wang 	u32 efuse_glb_intr;
77e4e46bc7SJianjun Wang 	struct mtk_pcie_lane_efuse *efuse;
78e4e46bc7SJianjun Wang };
79e4e46bc7SJianjun Wang 
mtk_pcie_efuse_set_lane(struct mtk_pcie_phy * pcie_phy,unsigned int lane)80e4e46bc7SJianjun Wang static void mtk_pcie_efuse_set_lane(struct mtk_pcie_phy *pcie_phy,
81e4e46bc7SJianjun Wang 				    unsigned int lane)
82e4e46bc7SJianjun Wang {
83e4e46bc7SJianjun Wang 	struct mtk_pcie_lane_efuse *data = &pcie_phy->efuse[lane];
84e4e46bc7SJianjun Wang 	void __iomem *addr;
85e4e46bc7SJianjun Wang 
86e4e46bc7SJianjun Wang 	if (!data->lane_efuse_supported)
87e4e46bc7SJianjun Wang 		return;
88e4e46bc7SJianjun Wang 
89e4e46bc7SJianjun Wang 	addr = pcie_phy->sif_base + PEXTP_ANA_LN0_TRX_REG +
90e4e46bc7SJianjun Wang 	       lane * PEXTP_ANA_LANE_OFFSET;
91e4e46bc7SJianjun Wang 
92d87f2b83SChunfeng Yun 	mtk_phy_update_field(addr + PEXTP_ANA_TX_REG, EFUSE_LN_TX_PMOS_SEL,
93d87f2b83SChunfeng Yun 			     data->tx_pmos);
94e4e46bc7SJianjun Wang 
95d87f2b83SChunfeng Yun 	mtk_phy_update_field(addr + PEXTP_ANA_TX_REG, EFUSE_LN_TX_NMOS_SEL,
96d87f2b83SChunfeng Yun 			     data->tx_nmos);
97e4e46bc7SJianjun Wang 
98d87f2b83SChunfeng Yun 	mtk_phy_update_field(addr + PEXTP_ANA_RX_REG, EFUSE_LN_RX_SEL,
99d87f2b83SChunfeng Yun 			     data->rx_data);
100e4e46bc7SJianjun Wang }
101e4e46bc7SJianjun Wang 
102e4e46bc7SJianjun Wang /**
103e4e46bc7SJianjun Wang  * mtk_pcie_phy_init() - Initialize the phy
104e4e46bc7SJianjun Wang  * @phy: the phy to be initialized
105e4e46bc7SJianjun Wang  *
106e4e46bc7SJianjun Wang  * Initialize the phy by setting the efuse data.
107e4e46bc7SJianjun Wang  * The hardware settings will be reset during suspend, it should be
108e4e46bc7SJianjun Wang  * reinitialized when the consumer calls phy_init() again on resume.
109e4e46bc7SJianjun Wang  */
mtk_pcie_phy_init(struct phy * phy)110e4e46bc7SJianjun Wang static int mtk_pcie_phy_init(struct phy *phy)
111e4e46bc7SJianjun Wang {
112e4e46bc7SJianjun Wang 	struct mtk_pcie_phy *pcie_phy = phy_get_drvdata(phy);
113e4e46bc7SJianjun Wang 	int i;
114e4e46bc7SJianjun Wang 
115e4e46bc7SJianjun Wang 	if (!pcie_phy->sw_efuse_en)
116e4e46bc7SJianjun Wang 		return 0;
117e4e46bc7SJianjun Wang 
118e4e46bc7SJianjun Wang 	/* Set global data */
119d87f2b83SChunfeng Yun 	mtk_phy_update_field(pcie_phy->sif_base + PEXTP_ANA_GLB_00_REG,
120d87f2b83SChunfeng Yun 			     EFUSE_GLB_INTR_SEL, pcie_phy->efuse_glb_intr);
121e4e46bc7SJianjun Wang 
122e4e46bc7SJianjun Wang 	for (i = 0; i < pcie_phy->data->num_lanes; i++)
123e4e46bc7SJianjun Wang 		mtk_pcie_efuse_set_lane(pcie_phy, i);
124e4e46bc7SJianjun Wang 
125e4e46bc7SJianjun Wang 	return 0;
126e4e46bc7SJianjun Wang }
127e4e46bc7SJianjun Wang 
128e4e46bc7SJianjun Wang static const struct phy_ops mtk_pcie_phy_ops = {
129e4e46bc7SJianjun Wang 	.init	= mtk_pcie_phy_init,
130e4e46bc7SJianjun Wang 	.owner	= THIS_MODULE,
131e4e46bc7SJianjun Wang };
132e4e46bc7SJianjun Wang 
mtk_pcie_efuse_read_for_lane(struct mtk_pcie_phy * pcie_phy,unsigned int lane)133e4e46bc7SJianjun Wang static int mtk_pcie_efuse_read_for_lane(struct mtk_pcie_phy *pcie_phy,
134e4e46bc7SJianjun Wang 					unsigned int lane)
135e4e46bc7SJianjun Wang {
136e4e46bc7SJianjun Wang 	struct mtk_pcie_lane_efuse *efuse = &pcie_phy->efuse[lane];
137e4e46bc7SJianjun Wang 	struct device *dev = pcie_phy->dev;
138e4e46bc7SJianjun Wang 	char efuse_id[16];
139e4e46bc7SJianjun Wang 	int ret;
140e4e46bc7SJianjun Wang 
141e4e46bc7SJianjun Wang 	snprintf(efuse_id, sizeof(efuse_id), "tx_ln%d_pmos", lane);
142e4e46bc7SJianjun Wang 	ret = nvmem_cell_read_variable_le_u32(dev, efuse_id, &efuse->tx_pmos);
143e4e46bc7SJianjun Wang 	if (ret)
144e4e46bc7SJianjun Wang 		return dev_err_probe(dev, ret, "Failed to read %s\n", efuse_id);
145e4e46bc7SJianjun Wang 
146e4e46bc7SJianjun Wang 	snprintf(efuse_id, sizeof(efuse_id), "tx_ln%d_nmos", lane);
147e4e46bc7SJianjun Wang 	ret = nvmem_cell_read_variable_le_u32(dev, efuse_id, &efuse->tx_nmos);
148e4e46bc7SJianjun Wang 	if (ret)
149e4e46bc7SJianjun Wang 		return dev_err_probe(dev, ret, "Failed to read %s\n", efuse_id);
150e4e46bc7SJianjun Wang 
151e4e46bc7SJianjun Wang 	snprintf(efuse_id, sizeof(efuse_id), "rx_ln%d", lane);
152e4e46bc7SJianjun Wang 	ret = nvmem_cell_read_variable_le_u32(dev, efuse_id, &efuse->rx_data);
153e4e46bc7SJianjun Wang 	if (ret)
154e4e46bc7SJianjun Wang 		return dev_err_probe(dev, ret, "Failed to read %s\n", efuse_id);
155e4e46bc7SJianjun Wang 
156e4e46bc7SJianjun Wang 	if (!(efuse->tx_pmos || efuse->tx_nmos || efuse->rx_data))
157e4e46bc7SJianjun Wang 		return dev_err_probe(dev, -EINVAL,
158e4e46bc7SJianjun Wang 				     "No eFuse data found for lane%d, but dts enable it\n",
159e4e46bc7SJianjun Wang 				     lane);
160e4e46bc7SJianjun Wang 
161e4e46bc7SJianjun Wang 	efuse->lane_efuse_supported = true;
162e4e46bc7SJianjun Wang 
163e4e46bc7SJianjun Wang 	return 0;
164e4e46bc7SJianjun Wang }
165e4e46bc7SJianjun Wang 
mtk_pcie_read_efuse(struct mtk_pcie_phy * pcie_phy)166e4e46bc7SJianjun Wang static int mtk_pcie_read_efuse(struct mtk_pcie_phy *pcie_phy)
167e4e46bc7SJianjun Wang {
168e4e46bc7SJianjun Wang 	struct device *dev = pcie_phy->dev;
169e4e46bc7SJianjun Wang 	bool nvmem_enabled;
170e4e46bc7SJianjun Wang 	int ret, i;
171e4e46bc7SJianjun Wang 
172e4e46bc7SJianjun Wang 	/* nvmem data is optional */
173e4e46bc7SJianjun Wang 	nvmem_enabled = device_property_present(dev, "nvmem-cells");
174e4e46bc7SJianjun Wang 	if (!nvmem_enabled)
175e4e46bc7SJianjun Wang 		return 0;
176e4e46bc7SJianjun Wang 
177e4e46bc7SJianjun Wang 	ret = nvmem_cell_read_variable_le_u32(dev, "glb_intr",
178e4e46bc7SJianjun Wang 					      &pcie_phy->efuse_glb_intr);
179e4e46bc7SJianjun Wang 	if (ret)
180e4e46bc7SJianjun Wang 		return dev_err_probe(dev, ret, "Failed to read glb_intr\n");
181e4e46bc7SJianjun Wang 
182e4e46bc7SJianjun Wang 	pcie_phy->sw_efuse_en = true;
183e4e46bc7SJianjun Wang 
184e4e46bc7SJianjun Wang 	pcie_phy->efuse = devm_kzalloc(dev, pcie_phy->data->num_lanes *
185e4e46bc7SJianjun Wang 				       sizeof(*pcie_phy->efuse), GFP_KERNEL);
186e4e46bc7SJianjun Wang 	if (!pcie_phy->efuse)
187e4e46bc7SJianjun Wang 		return -ENOMEM;
188e4e46bc7SJianjun Wang 
189e4e46bc7SJianjun Wang 	for (i = 0; i < pcie_phy->data->num_lanes; i++) {
190e4e46bc7SJianjun Wang 		ret = mtk_pcie_efuse_read_for_lane(pcie_phy, i);
191e4e46bc7SJianjun Wang 		if (ret)
192e4e46bc7SJianjun Wang 			return ret;
193e4e46bc7SJianjun Wang 	}
194e4e46bc7SJianjun Wang 
195e4e46bc7SJianjun Wang 	return 0;
196e4e46bc7SJianjun Wang }
197e4e46bc7SJianjun Wang 
mtk_pcie_phy_probe(struct platform_device * pdev)198e4e46bc7SJianjun Wang static int mtk_pcie_phy_probe(struct platform_device *pdev)
199e4e46bc7SJianjun Wang {
200e4e46bc7SJianjun Wang 	struct device *dev = &pdev->dev;
201e4e46bc7SJianjun Wang 	struct phy_provider *provider;
202e4e46bc7SJianjun Wang 	struct mtk_pcie_phy *pcie_phy;
203e4e46bc7SJianjun Wang 	int ret;
204e4e46bc7SJianjun Wang 
205e4e46bc7SJianjun Wang 	pcie_phy = devm_kzalloc(dev, sizeof(*pcie_phy), GFP_KERNEL);
206e4e46bc7SJianjun Wang 	if (!pcie_phy)
207e4e46bc7SJianjun Wang 		return -ENOMEM;
208e4e46bc7SJianjun Wang 
209e4e46bc7SJianjun Wang 	pcie_phy->sif_base = devm_platform_ioremap_resource_byname(pdev, "sif");
210e4e46bc7SJianjun Wang 	if (IS_ERR(pcie_phy->sif_base))
211e4e46bc7SJianjun Wang 		return dev_err_probe(dev, PTR_ERR(pcie_phy->sif_base),
212e4e46bc7SJianjun Wang 				     "Failed to map phy-sif base\n");
213e4e46bc7SJianjun Wang 
214e4e46bc7SJianjun Wang 	pcie_phy->phy = devm_phy_create(dev, dev->of_node, &mtk_pcie_phy_ops);
215e4e46bc7SJianjun Wang 	if (IS_ERR(pcie_phy->phy))
216e4e46bc7SJianjun Wang 		return dev_err_probe(dev, PTR_ERR(pcie_phy->phy),
217e4e46bc7SJianjun Wang 				     "Failed to create PCIe phy\n");
218e4e46bc7SJianjun Wang 
219e4e46bc7SJianjun Wang 	pcie_phy->dev = dev;
220e4e46bc7SJianjun Wang 	pcie_phy->data = of_device_get_match_data(dev);
221e4e46bc7SJianjun Wang 	if (!pcie_phy->data)
222e4e46bc7SJianjun Wang 		return dev_err_probe(dev, -EINVAL, "Failed to get phy data\n");
223e4e46bc7SJianjun Wang 
224e4e46bc7SJianjun Wang 	if (pcie_phy->data->sw_efuse_supported) {
225e4e46bc7SJianjun Wang 		/*
226e4e46bc7SJianjun Wang 		 * Failed to read the efuse data is not a fatal problem,
227e4e46bc7SJianjun Wang 		 * ignore the failure and keep going.
228e4e46bc7SJianjun Wang 		 */
229e4e46bc7SJianjun Wang 		ret = mtk_pcie_read_efuse(pcie_phy);
230e4e46bc7SJianjun Wang 		if (ret == -EPROBE_DEFER || ret == -ENOMEM)
231e4e46bc7SJianjun Wang 			return ret;
232e4e46bc7SJianjun Wang 	}
233e4e46bc7SJianjun Wang 
234e4e46bc7SJianjun Wang 	phy_set_drvdata(pcie_phy->phy, pcie_phy);
235e4e46bc7SJianjun Wang 
236e4e46bc7SJianjun Wang 	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
237e4e46bc7SJianjun Wang 	if (IS_ERR(provider))
238e4e46bc7SJianjun Wang 		return dev_err_probe(dev, PTR_ERR(provider),
239e4e46bc7SJianjun Wang 				     "PCIe phy probe failed\n");
240e4e46bc7SJianjun Wang 
241e4e46bc7SJianjun Wang 	return 0;
242e4e46bc7SJianjun Wang }
243e4e46bc7SJianjun Wang 
244e4e46bc7SJianjun Wang static const struct mtk_pcie_phy_data mt8195_data = {
245e4e46bc7SJianjun Wang 	.num_lanes = 2,
246e4e46bc7SJianjun Wang 	.sw_efuse_supported = true,
247e4e46bc7SJianjun Wang };
248e4e46bc7SJianjun Wang 
249e4e46bc7SJianjun Wang static const struct of_device_id mtk_pcie_phy_of_match[] = {
250e4e46bc7SJianjun Wang 	{ .compatible = "mediatek,mt8195-pcie-phy", .data = &mt8195_data },
251e4e46bc7SJianjun Wang 	{ },
252e4e46bc7SJianjun Wang };
253e4e46bc7SJianjun Wang MODULE_DEVICE_TABLE(of, mtk_pcie_phy_of_match);
254e4e46bc7SJianjun Wang 
255e4e46bc7SJianjun Wang static struct platform_driver mtk_pcie_phy_driver = {
256e4e46bc7SJianjun Wang 	.probe	= mtk_pcie_phy_probe,
257e4e46bc7SJianjun Wang 	.driver	= {
258e4e46bc7SJianjun Wang 		.name = "mtk-pcie-phy",
259e4e46bc7SJianjun Wang 		.of_match_table = mtk_pcie_phy_of_match,
260e4e46bc7SJianjun Wang 	},
261e4e46bc7SJianjun Wang };
262e4e46bc7SJianjun Wang module_platform_driver(mtk_pcie_phy_driver);
263e4e46bc7SJianjun Wang 
264e4e46bc7SJianjun Wang MODULE_DESCRIPTION("MediaTek PCIe PHY driver");
265e4e46bc7SJianjun Wang MODULE_AUTHOR("Jianjun Wang <jianjun.wang@mediatek.com>");
266e4e46bc7SJianjun Wang MODULE_LICENSE("GPL");
267