Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2
  3/*
  4 * Sunplus SP7021 USB 2.0 phy driver
  5 *
  6 * Copyright (C) 2022 Sunplus Technology Inc., All rights reserved.
  7 *
  8 * Note 1 : non-posted write command for the registers accesses of
  9 * Sunplus SP7021.
 10 *
 11 */
 12
 13#include <linux/bitfield.h>
 14#include <linux/clk.h>
 15#include <linux/delay.h>
 16#include <linux/io.h>
 17#include <linux/module.h>
 18#include <linux/nvmem-consumer.h>
 19#include <linux/of.h>
 20#include <linux/phy/phy.h>
 21#include <linux/platform_device.h>
 22#include <linux/reset.h>
 23
 24#define HIGH_MASK_BITS				GENMASK(31, 16)
 25#define LOW_MASK_BITS				GENMASK(15, 0)
 26#define OTP_DISC_LEVEL_DEFAULT			0xd
 27
 28/* GROUP UPHY */
 29#define CONFIG1					0x4
 30#define J_HS_TX_PWRSAV				BIT(5)
 31#define CONFIG3					0xc
 32#define J_FORCE_DISC_ON				BIT(5)
 33#define J_DEBUG_CTRL_ADDR_MACRO			BIT(0)
 34#define CONFIG7					0x1c
 35#define J_DISC					0X1f
 36#define CONFIG9					0x24
 37#define J_ECO_PATH				BIT(6)
 38#define CONFIG16				0x40
 39#define J_TBCWAIT_MASK				GENMASK(6, 5)
 40#define J_TBCWAIT_1P1_MS			FIELD_PREP(J_TBCWAIT_MASK, 0)
 41#define J_TVDM_SRC_DIS_MASK			GENMASK(4, 3)
 42#define J_TVDM_SRC_DIS_8P2_MS			FIELD_PREP(J_TVDM_SRC_DIS_MASK, 3)
 43#define J_TVDM_SRC_EN_MASK			GENMASK(2, 1)
 44#define J_TVDM_SRC_EN_1P6_MS			FIELD_PREP(J_TVDM_SRC_EN_MASK, 0)
 45#define J_BC_EN					BIT(0)
 46#define CONFIG17				0x44
 47#define IBG_TRIM0_MASK				GENMASK(7, 5)
 48#define IBG_TRIM0_SSLVHT			FIELD_PREP(IBG_TRIM0_MASK, 4)
 49#define J_VDATREE_TRIM_MASK			GENMASK(4, 1)
 50#define J_VDATREE_TRIM_DEFAULT			FIELD_PREP(J_VDATREE_TRIM_MASK, 9)
 51#define CONFIG23				0x5c
 52#define PROB_MASK				GENMASK(5, 3)
 53#define PROB					FIELD_PREP(PROB_MASK, 7)
 54
 55/* GROUP MOON4 */
 56#define UPHY_CONTROL0				0x0
 57#define UPHY_CONTROL1				0x4
 58#define UPHY_CONTROL2				0x8
 59#define MO1_UPHY_RX_CLK_SEL			BIT(6)
 60#define MASK_MO1_UPHY_RX_CLK_SEL		BIT(6 + 16)
 61#define UPHY_CONTROL3				0xc
 62#define MO1_UPHY_PLL_POWER_OFF_SEL		BIT(7)
 63#define MASK_MO1_UPHY_PLL_POWER_OFF_SEL		BIT(7 + 16)
 64#define MO1_UPHY_PLL_POWER_OFF			BIT(3)
 65#define MASK_UPHY_PLL_POWER_OFF			BIT(3 + 16)
 66
 67struct sp_usbphy {
 68	struct device *dev;
 69	struct resource *phy_res_mem;
 70	struct resource *moon4_res_mem;
 71	struct reset_control *rstc;
 72	struct clk *phy_clk;
 73	void __iomem *phy_regs;
 74	void __iomem *moon4_regs;
 75	u32 disc_vol_addr_off;
 76};
 77
 78static int update_disc_vol(struct sp_usbphy *usbphy)
 79{
 80	struct nvmem_cell *cell;
 81	char *disc_name = "disc_vol";
 82	ssize_t otp_l = 0;
 83	char *otp_v;
 84	u32 val, set;
 85
 86	cell = nvmem_cell_get(usbphy->dev, disc_name);
 87	if (IS_ERR_OR_NULL(cell)) {
 88		if (PTR_ERR(cell) == -EPROBE_DEFER)
 89			return -EPROBE_DEFER;
 90	}
 91
 92	otp_v = nvmem_cell_read(cell, &otp_l);
 93	nvmem_cell_put(cell);
 94
 95	if (!IS_ERR(otp_v)) {
 96		set = *(otp_v + 1);
 97		set = (set << (sizeof(char) * 8)) | *otp_v;
 98		set = (set >> usbphy->disc_vol_addr_off) & J_DISC;
 99	}
100
101	if (IS_ERR(otp_v) || set == 0)
102		set = OTP_DISC_LEVEL_DEFAULT;
103
104	val = readl(usbphy->phy_regs + CONFIG7);
105	val = (val & ~J_DISC) | set;
106	writel(val, usbphy->phy_regs + CONFIG7);
107
108	return 0;
109}
110
111static int sp_uphy_init(struct phy *phy)
112{
113	struct sp_usbphy *usbphy = phy_get_drvdata(phy);
114	u32 val;
115	int ret;
116
117	ret = clk_prepare_enable(usbphy->phy_clk);
118	if (ret)
119		goto err_clk;
120
121	ret = reset_control_deassert(usbphy->rstc);
122	if (ret)
123		goto err_reset;
124
125	/* Default value modification */
126	writel(HIGH_MASK_BITS | 0x4002, usbphy->moon4_regs + UPHY_CONTROL0);
127	writel(HIGH_MASK_BITS | 0x8747, usbphy->moon4_regs + UPHY_CONTROL1);
128
129	/* disconnect voltage */
130	ret = update_disc_vol(usbphy);
131	if (ret < 0)
132		return ret;
133
134	/* board uphy 0 internal register modification for tid certification */
135	val = readl(usbphy->phy_regs + CONFIG9);
136	val &= ~(J_ECO_PATH);
137	writel(val, usbphy->phy_regs + CONFIG9);
138
139	val = readl(usbphy->phy_regs + CONFIG1);
140	val &= ~(J_HS_TX_PWRSAV);
141	writel(val, usbphy->phy_regs + CONFIG1);
142
143	val = readl(usbphy->phy_regs + CONFIG23);
144	val = (val & ~PROB) | PROB;
145	writel(val, usbphy->phy_regs + CONFIG23);
146
147	/* port 0 uphy clk fix */
148	writel(MASK_MO1_UPHY_RX_CLK_SEL | MO1_UPHY_RX_CLK_SEL,
149	       usbphy->moon4_regs + UPHY_CONTROL2);
150
151	/* battery charger */
152	writel(J_TBCWAIT_1P1_MS | J_TVDM_SRC_DIS_8P2_MS | J_TVDM_SRC_EN_1P6_MS | J_BC_EN,
153	       usbphy->phy_regs + CONFIG16);
154	writel(IBG_TRIM0_SSLVHT | J_VDATREE_TRIM_DEFAULT, usbphy->phy_regs + CONFIG17);
155
156	/* chirp mode */
157	writel(J_FORCE_DISC_ON | J_DEBUG_CTRL_ADDR_MACRO, usbphy->phy_regs + CONFIG3);
158
159	return 0;
160
161err_reset:
162	reset_control_assert(usbphy->rstc);
163err_clk:
164	clk_disable_unprepare(usbphy->phy_clk);
165
166	return ret;
167}
168
169static int sp_uphy_power_on(struct phy *phy)
170{
171	struct sp_usbphy *usbphy = phy_get_drvdata(phy);
172	u32 pll_pwr_on, pll_pwr_off;
173
174	/* PLL power off/on twice */
175	pll_pwr_off = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS)
176			| MO1_UPHY_PLL_POWER_OFF_SEL | MO1_UPHY_PLL_POWER_OFF;
177	pll_pwr_on = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS)
178			| MO1_UPHY_PLL_POWER_OFF_SEL;
179
180	writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off,
181	       usbphy->moon4_regs + UPHY_CONTROL3);
182	mdelay(1);
183	writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_on,
184	       usbphy->moon4_regs + UPHY_CONTROL3);
185	mdelay(1);
186	writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off,
187	       usbphy->moon4_regs + UPHY_CONTROL3);
188	mdelay(1);
189	writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_on,
190	       usbphy->moon4_regs + UPHY_CONTROL3);
191	mdelay(1);
192	writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | 0x0,
193	       usbphy->moon4_regs + UPHY_CONTROL3);
194
195	return 0;
196}
197
198static int sp_uphy_power_off(struct phy *phy)
199{
200	struct sp_usbphy *usbphy = phy_get_drvdata(phy);
201	u32 pll_pwr_off;
202
203	pll_pwr_off = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS)
204			| MO1_UPHY_PLL_POWER_OFF_SEL | MO1_UPHY_PLL_POWER_OFF;
205
206	writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off,
207	       usbphy->moon4_regs + UPHY_CONTROL3);
208	mdelay(1);
209	writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | 0x0,
210	       usbphy->moon4_regs + UPHY_CONTROL3);
211
212	return 0;
213}
214
215static int sp_uphy_exit(struct phy *phy)
216{
217	struct sp_usbphy *usbphy = phy_get_drvdata(phy);
218
219	reset_control_assert(usbphy->rstc);
220	clk_disable_unprepare(usbphy->phy_clk);
221
222	return 0;
223}
224
225static const struct phy_ops sp_uphy_ops = {
226	.init		= sp_uphy_init,
227	.power_on	= sp_uphy_power_on,
228	.power_off	= sp_uphy_power_off,
229	.exit		= sp_uphy_exit,
230};
231
232static const struct of_device_id sp_uphy_dt_ids[] = {
233	{.compatible = "sunplus,sp7021-usb2-phy", },
234	{ }
235};
236MODULE_DEVICE_TABLE(of, sp_uphy_dt_ids);
237
238static int sp_usb_phy_probe(struct platform_device *pdev)
239{
240	struct sp_usbphy *usbphy;
241	struct phy_provider *phy_provider;
242	struct phy *phy;
243	int ret;
244
245	usbphy = devm_kzalloc(&pdev->dev, sizeof(*usbphy), GFP_KERNEL);
246	if (!usbphy)
247		return -ENOMEM;
248
249	usbphy->dev = &pdev->dev;
250
251	usbphy->phy_res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
252	usbphy->phy_regs = devm_ioremap_resource(&pdev->dev, usbphy->phy_res_mem);
253	if (IS_ERR(usbphy->phy_regs))
254		return PTR_ERR(usbphy->phy_regs);
255
256	usbphy->moon4_res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "moon4");
257	if (!usbphy->moon4_res_mem)
258		return -EINVAL;
259
260	usbphy->moon4_regs = devm_ioremap(&pdev->dev, usbphy->moon4_res_mem->start,
261					  resource_size(usbphy->moon4_res_mem));
262	if (!usbphy->moon4_regs)
263		return -ENOMEM;
264
265	usbphy->phy_clk = devm_clk_get(&pdev->dev, NULL);
266	if (IS_ERR(usbphy->phy_clk))
267		return PTR_ERR(usbphy->phy_clk);
268
269	usbphy->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
270	if (IS_ERR(usbphy->rstc))
271		return PTR_ERR(usbphy->rstc);
272
273	of_property_read_u32(pdev->dev.of_node, "sunplus,disc-vol-addr-off",
274			     &usbphy->disc_vol_addr_off);
275
276	phy = devm_phy_create(&pdev->dev, NULL, &sp_uphy_ops);
277	if (IS_ERR(phy)) {
278		ret = PTR_ERR(phy);
279		return ret;
280	}
281
282	phy_set_drvdata(phy, usbphy);
283	phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
284
285	return PTR_ERR_OR_ZERO(phy_provider);
286}
287
288static struct platform_driver sunplus_usb_phy_driver = {
289	.probe		= sp_usb_phy_probe,
290	.driver		= {
291		.name	= "sunplus-usb2-phy",
292		.of_match_table = sp_uphy_dt_ids,
293	},
294};
295module_platform_driver(sunplus_usb_phy_driver);
296
297MODULE_AUTHOR("Vincent Shih <vincent.shih@sunplus.com>");
298MODULE_DESCRIPTION("Sunplus USB 2.0 phy driver");
299MODULE_LICENSE("GPL");