xref: /linux/drivers/phy/axiado/phy-axiado-emmc.c (revision 637315cb400eed1dbe721954fea437a61a2d65b2)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Axiado eMMC PHY driver
4  *
5  * Copyright (C) 2017 Arasan Chip Systems Inc.
6  * Copyright (C) 2022-2026 Axiado Corporation (or its affiliates).
7  *
8  * Based on Arasan Driver (sdhci-pci-arasan.c)
9  * sdhci-pci-arasan.c - Driver for Arasan PCI Controller with integrated phy.
10  */
11 #include <linux/bitfield.h>
12 #include <linux/delay.h>
13 #include <linux/io.h>
14 #include <linux/iopoll.h>
15 #include <linux/module.h>
16 #include <linux/of.h>
17 #include <linux/phy/phy.h>
18 #include <linux/platform_device.h>
19 
20 /* Arasan eMMC 5.1 - PHY configuration registers */
21 #define CAP_REG_IN_S1_MSB		0x04
22 #define PHY_CTRL_1			0x38
23 #define PHY_CTRL_2			0x3c
24 #define PHY_CTRL_3			0x40
25 #define STATUS				0x50
26 
27 #define DLL_ENBL	BIT(26)
28 #define RTRIM_EN	BIT(21)
29 #define PDB_ENBL	BIT(23)
30 #define RETB_ENBL	BIT(1)
31 
32 #define REN_STRB	BIT(27)
33 #define REN_CMD_EN	GENMASK(20, 12)
34 
35 /* Pull-UP Enable on CMD Line */
36 #define PU_CMD_EN	GENMASK(11, 3)
37 
38 /* Selection value for the optimum delay from 1-32 output tap lines */
39 #define OTAP_DLY	0x02
40 /* DLL charge pump current trim default [1000] */
41 #define DLL_TRM_ICP	0x08
42 /* Select the frequency range of DLL Operation */
43 #define FRQ_SEL	0x01
44 
45 #define OTAP_SEL_MASK		GENMASK(10, 7)
46 #define DLL_TRM_MASK		GENMASK(25, 22)
47 #define DLL_FRQSEL_MASK		GENMASK(27, 25)
48 
49 #define OTAP_SEL(x)		(FIELD_PREP(OTAP_SEL_MASK, x) | OTAPDLY_EN)
50 #define DLL_TRM(x)		(FIELD_PREP(DLL_TRM_MASK, x) | DLL_ENBL)
51 #define DLL_FRQSEL(x)	FIELD_PREP(DLL_FRQSEL_MASK, x)
52 
53 #define OTAPDLY_EN	BIT(11)
54 
55 #define SEL_DLY_RXCLK	BIT(18)
56 #define SEL_DLY_TXCLK	BIT(19)
57 
58 #define CALDONE_MASK	0x40
59 #define DLL_RDY_MASK	0x1
60 #define MAX_CLK_BUF0	BIT(20)
61 #define MAX_CLK_BUF1	BIT(21)
62 #define MAX_CLK_BUF2	BIT(22)
63 
64 #define CLK_MULTIPLIER	0xc008e
65 #define POLL_TIMEOUT_MS	3000
66 #define POLL_DELAY_US	100
67 
68 struct axiado_emmc_phy {
69 	void __iomem *reg_base;
70 	struct device *dev;
71 };
72 
73 static int axiado_emmc_phy_init(struct phy *phy)
74 {
75 	struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy);
76 	struct device *dev = ax_phy->dev;
77 	u32 val;
78 	int ret;
79 
80 	val = readl(ax_phy->reg_base + PHY_CTRL_1);
81 	writel(val | RETB_ENBL | RTRIM_EN, ax_phy->reg_base + PHY_CTRL_1);
82 
83 	val = readl(ax_phy->reg_base + PHY_CTRL_3);
84 	writel(val | PDB_ENBL, ax_phy->reg_base + PHY_CTRL_3);
85 
86 	ret = readl_poll_timeout(ax_phy->reg_base + STATUS, val,
87 				 val & CALDONE_MASK, POLL_DELAY_US,
88 				 POLL_TIMEOUT_MS * 1000);
89 	if (ret) {
90 		dev_err(dev, "PHY calibration timeout\n");
91 		return ret;
92 	}
93 
94 	val = readl(ax_phy->reg_base + PHY_CTRL_1);
95 	writel(val | REN_CMD_EN | PU_CMD_EN, ax_phy->reg_base + PHY_CTRL_1);
96 
97 	val = readl(ax_phy->reg_base + PHY_CTRL_2);
98 	writel(val | REN_STRB, ax_phy->reg_base + PHY_CTRL_2);
99 
100 	val = readl(ax_phy->reg_base + PHY_CTRL_3);
101 	writel(val | MAX_CLK_BUF0 | MAX_CLK_BUF1 | MAX_CLK_BUF2,
102 	       ax_phy->reg_base + PHY_CTRL_3);
103 
104 	writel(CLK_MULTIPLIER, ax_phy->reg_base + CAP_REG_IN_S1_MSB);
105 
106 	val = readl(ax_phy->reg_base + PHY_CTRL_3);
107 	writel(val | SEL_DLY_RXCLK | SEL_DLY_TXCLK,
108 	       ax_phy->reg_base + PHY_CTRL_3);
109 
110 	return 0;
111 }
112 
113 static int axiado_emmc_phy_power_on(struct phy *phy)
114 {
115 	struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy);
116 	struct device *dev = ax_phy->dev;
117 	u32 val;
118 	int ret;
119 
120 	val = readl(ax_phy->reg_base + PHY_CTRL_1);
121 	writel(val | RETB_ENBL, ax_phy->reg_base + PHY_CTRL_1);
122 
123 	val = readl(ax_phy->reg_base + PHY_CTRL_3);
124 	writel(val | PDB_ENBL, ax_phy->reg_base + PHY_CTRL_3);
125 
126 	val = readl(ax_phy->reg_base + PHY_CTRL_2);
127 	writel(val | OTAP_SEL(OTAP_DLY), ax_phy->reg_base + PHY_CTRL_2);
128 
129 	val = readl(ax_phy->reg_base + PHY_CTRL_1);
130 	writel(val | DLL_TRM(DLL_TRM_ICP), ax_phy->reg_base + PHY_CTRL_1);
131 
132 	val = readl(ax_phy->reg_base + PHY_CTRL_3);
133 	writel(val | DLL_FRQSEL(FRQ_SEL), ax_phy->reg_base + PHY_CTRL_3);
134 
135 	ret = read_poll_timeout(readl, val, val & DLL_RDY_MASK, POLL_DELAY_US,
136 				POLL_TIMEOUT_MS * 1000, false,
137 				ax_phy->reg_base + STATUS);
138 	if (ret) {
139 		dev_err(dev, "DLL ready timeout\n");
140 		return ret;
141 	}
142 
143 	return 0;
144 }
145 
146 static int axiado_emmc_phy_power_off(struct phy *phy)
147 {
148 	struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy);
149 	u32 val;
150 
151 	val = readl(ax_phy->reg_base + PHY_CTRL_1);
152 	val &= ~(DLL_TRM_MASK | DLL_ENBL);
153 	writel(val, ax_phy->reg_base + PHY_CTRL_1);
154 
155 	val = readl(ax_phy->reg_base + PHY_CTRL_3);
156 	val &= ~(DLL_FRQSEL_MASK | PDB_ENBL);
157 	writel(val, ax_phy->reg_base + PHY_CTRL_3);
158 
159 	return 0;
160 }
161 
162 static const struct phy_ops axiado_emmc_phy_ops = {
163 	.init = axiado_emmc_phy_init,
164 	.power_on = axiado_emmc_phy_power_on,
165 	.power_off = axiado_emmc_phy_power_off,
166 	.owner = THIS_MODULE,
167 };
168 
169 static const struct of_device_id axiado_emmc_phy_of_match[] = {
170 	{ .compatible = "axiado,ax3000-emmc-phy" },
171 	{ /* sentinel */ },
172 };
173 MODULE_DEVICE_TABLE(of, axiado_emmc_phy_of_match);
174 
175 static int axiado_emmc_phy_probe(struct platform_device *pdev)
176 {
177 	struct axiado_emmc_phy *ax_phy;
178 	struct phy_provider *phy_provider;
179 	struct device *dev = &pdev->dev;
180 	struct phy *generic_phy;
181 
182 	if (!dev->of_node)
183 		return -ENODEV;
184 
185 	ax_phy = devm_kzalloc(dev, sizeof(*ax_phy), GFP_KERNEL);
186 	if (!ax_phy)
187 		return -ENOMEM;
188 
189 	ax_phy->reg_base = devm_platform_ioremap_resource(pdev, 0);
190 	if (IS_ERR(ax_phy->reg_base))
191 		return PTR_ERR(ax_phy->reg_base);
192 
193 	ax_phy->dev = dev;
194 
195 	generic_phy = devm_phy_create(dev, dev->of_node, &axiado_emmc_phy_ops);
196 	if (IS_ERR(generic_phy))
197 		return dev_err_probe(dev, PTR_ERR(generic_phy),
198 				     "failed to create PHY\n");
199 
200 	phy_set_drvdata(generic_phy, ax_phy);
201 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
202 
203 	return PTR_ERR_OR_ZERO(phy_provider);
204 }
205 
206 static struct platform_driver axiado_emmc_phy_driver = {
207 	.probe = axiado_emmc_phy_probe,
208 	.driver = {
209 		.name = "axiado-emmc-phy",
210 		.of_match_table = axiado_emmc_phy_of_match,
211 	},
212 };
213 module_platform_driver(axiado_emmc_phy_driver);
214 
215 MODULE_DESCRIPTION("AX3000 eMMC PHY Driver");
216 MODULE_AUTHOR("Axiado Corporation");
217 MODULE_LICENSE("GPL");
218