Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Meson GXL USB3 PHY and OTG mode detection driver
  4 *
  5 * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
  6 */
  7
  8#include <linux/bitfield.h>
  9#include <linux/bitops.h>
 10#include <linux/clk.h>
 11#include <linux/module.h>
 12#include <linux/of_device.h>
 13#include <linux/phy/phy.h>
 14#include <linux/regmap.h>
 15#include <linux/reset.h>
 16#include <linux/platform_device.h>
 17
 18#define USB_R0							0x00
 19	#define USB_R0_P30_FSEL_MASK				GENMASK(5, 0)
 20	#define USB_R0_P30_PHY_RESET				BIT(6)
 21	#define USB_R0_P30_TEST_POWERDOWN_HSP			BIT(7)
 22	#define USB_R0_P30_TEST_POWERDOWN_SSP			BIT(8)
 23	#define USB_R0_P30_ACJT_LEVEL_MASK			GENMASK(13, 9)
 24	#define USB_R0_P30_TX_BOOST_LEVEL_MASK			GENMASK(16, 14)
 25	#define USB_R0_P30_LANE0_TX2RX_LOOPBACK			BIT(17)
 26	#define USB_R0_P30_LANE0_EXT_PCLK_REQ			BIT(18)
 27	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
 28	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
 29	#define USB_R0_U2D_ACT					BIT(31)
 30
 31#define USB_R1							0x04
 32	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
 33	#define USB_R1_U3H_PME_ENABLE				BIT(1)
 34	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(6, 2)
 35	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(11, 7)
 36	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(15, 12)
 37	#define USB_R1_U3H_HOST_U3_PORT_DISABLE			BIT(16)
 38	#define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT	BIT(17)
 39	#define USB_R1_U3H_HOST_MSI_ENABLE			BIT(18)
 40	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
 41	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
 42
 43#define USB_R2							0x08
 44	#define USB_R2_P30_CR_DATA_IN_MASK			GENMASK(15, 0)
 45	#define USB_R2_P30_CR_READ				BIT(16)
 46	#define USB_R2_P30_CR_WRITE				BIT(17)
 47	#define USB_R2_P30_CR_CAP_ADDR				BIT(18)
 48	#define USB_R2_P30_CR_CAP_DATA				BIT(19)
 49	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
 50	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
 51
 52#define USB_R3							0x0c
 53	#define USB_R3_P30_SSC_ENABLE				BIT(0)
 54	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
 55	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
 56	#define USB_R3_P30_REF_SSP_EN				BIT(13)
 57	#define USB_R3_P30_LOS_BIAS_MASK			GENMASK(18, 16)
 58	#define USB_R3_P30_LOS_LEVEL_MASK			GENMASK(23, 19)
 59	#define USB_R3_P30_MPLL_MULTIPLIER_MASK			GENMASK(30, 24)
 60
 61#define USB_R4							0x10
 62	#define USB_R4_P21_PORT_RESET_0				BIT(0)
 63	#define USB_R4_P21_SLEEP_M0				BIT(1)
 64	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
 65	#define USB_R4_P21_ONLY					BIT(4)
 66
 67#define USB_R5							0x14
 68	#define USB_R5_ID_DIG_SYNC				BIT(0)
 69	#define USB_R5_ID_DIG_REG				BIT(1)
 70	#define USB_R5_ID_DIG_CFG_MASK				GENMASK(3, 2)
 71	#define USB_R5_ID_DIG_EN_0				BIT(4)
 72	#define USB_R5_ID_DIG_EN_1				BIT(5)
 73	#define USB_R5_ID_DIG_CURR				BIT(6)
 74	#define USB_R5_ID_DIG_IRQ				BIT(7)
 75	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
 76	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
 77
 78/* read-only register */
 79#define USB_R6							0x18
 80	#define USB_R6_P30_CR_DATA_OUT_MASK			GENMASK(15, 0)
 81	#define USB_R6_P30_CR_ACK				BIT(16)
 82
 83struct phy_meson_gxl_usb3_priv {
 84	struct regmap		*regmap;
 85	enum phy_mode		mode;
 86	struct clk		*clk_phy;
 87	struct clk		*clk_peripheral;
 88	struct reset_control	*reset;
 89};
 90
 91static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
 92	.reg_bits = 8,
 93	.val_bits = 32,
 94	.reg_stride = 4,
 95	.max_register = USB_R6,
 96};
 97
 98static int phy_meson_gxl_usb3_power_on(struct phy *phy)
 99{
100	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
101
102	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0,
103			   USB_R5_ID_DIG_EN_0);
104	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1,
105			   USB_R5_ID_DIG_EN_1);
106	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_TH_MASK,
107			   FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
108
109	return 0;
110}
111
112static int phy_meson_gxl_usb3_power_off(struct phy *phy)
113{
114	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
115
116	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0, 0);
117	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1, 0);
118
119	return 0;
120}
121
122static int phy_meson_gxl_usb3_set_mode(struct phy *phy, enum phy_mode mode)
123{
124	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
125
126	switch (mode) {
127	case PHY_MODE_USB_HOST:
128		regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, 0);
129		regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
130				   0);
131		break;
132
133	case PHY_MODE_USB_DEVICE:
134		regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT,
135				   USB_R0_U2D_ACT);
136		regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
137				   USB_R4_P21_SLEEP_M0);
138		break;
139
140	default:
141		dev_err(&phy->dev, "unsupported PHY mode %d\n", mode);
142		return -EINVAL;
143	}
144
145	priv->mode = mode;
146
147	return 0;
148}
149
150static int phy_meson_gxl_usb3_init(struct phy *phy)
151{
152	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
153	int ret;
154
155	ret = reset_control_reset(priv->reset);
156	if (ret)
157		goto err;
158
159	ret = clk_prepare_enable(priv->clk_phy);
160	if (ret)
161		goto err;
162
163	ret = clk_prepare_enable(priv->clk_peripheral);
164	if (ret)
165		goto err_disable_clk_phy;
166
167	ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode);
168	if (ret)
169		goto err_disable_clk_peripheral;
170
171	regmap_update_bits(priv->regmap, USB_R1,
172			   USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
173			   FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
174
175	return 0;
176
177err_disable_clk_peripheral:
178	clk_disable_unprepare(priv->clk_peripheral);
179err_disable_clk_phy:
180	clk_disable_unprepare(priv->clk_phy);
181err:
182	return ret;
183}
184
185static int phy_meson_gxl_usb3_exit(struct phy *phy)
186{
187	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
188
189	clk_disable_unprepare(priv->clk_peripheral);
190	clk_disable_unprepare(priv->clk_phy);
191
192	return 0;
193}
194
195static const struct phy_ops phy_meson_gxl_usb3_ops = {
196	.power_on	= phy_meson_gxl_usb3_power_on,
197	.power_off	= phy_meson_gxl_usb3_power_off,
198	.set_mode	= phy_meson_gxl_usb3_set_mode,
199	.init		= phy_meson_gxl_usb3_init,
200	.exit		= phy_meson_gxl_usb3_exit,
201	.owner		= THIS_MODULE,
202};
203
204static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
205{
206	struct device *dev = &pdev->dev;
207	struct device_node *np = dev->of_node;
208	struct phy_meson_gxl_usb3_priv *priv;
209	struct resource *res;
210	struct phy *phy;
211	struct phy_provider *phy_provider;
212	void __iomem *base;
213	int ret;
214
215	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
216	if (!priv)
217		return -ENOMEM;
218
219	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
220	base = devm_ioremap_resource(dev, res);
221	if (IS_ERR(base))
222		return PTR_ERR(base);
223
224	priv->regmap = devm_regmap_init_mmio(dev, base,
225					     &phy_meson_gxl_usb3_regmap_conf);
226	if (IS_ERR(priv->regmap))
227		return PTR_ERR(priv->regmap);
228
229	priv->clk_phy = devm_clk_get(dev, "phy");
230	if (IS_ERR(priv->clk_phy))
231		return PTR_ERR(priv->clk_phy);
232
233	priv->clk_peripheral = devm_clk_get(dev, "peripheral");
234	if (IS_ERR(priv->clk_peripheral))
235		return PTR_ERR(priv->clk_peripheral);
236
237	priv->reset = devm_reset_control_array_get_shared(dev);
238	if (IS_ERR(priv->reset))
239		return PTR_ERR(priv->reset);
240
241	/*
242	 * default to host mode as hardware defaults and/or boot-loader
243	 * behavior can result in this PHY starting up in device mode. this
244	 * default and the initialization in phy_meson_gxl_usb3_init ensure
245	 * that we reproducibly start in a known mode on all devices.
246	 */
247	priv->mode = PHY_MODE_USB_HOST;
248
249	phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops);
250	if (IS_ERR(phy)) {
251		ret = PTR_ERR(phy);
252		if (ret != -EPROBE_DEFER)
253			dev_err(dev, "failed to create PHY\n");
254
255		return ret;
256	}
257
258	phy_set_drvdata(phy, priv);
259
260	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
261
262	return PTR_ERR_OR_ZERO(phy_provider);
263}
264
265static const struct of_device_id phy_meson_gxl_usb3_of_match[] = {
266	{ .compatible = "amlogic,meson-gxl-usb3-phy", },
267	{ },
268};
269MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match);
270
271static struct platform_driver phy_meson_gxl_usb3_driver = {
272	.probe	= phy_meson_gxl_usb3_probe,
273	.driver	= {
274		.name		= "phy-meson-gxl-usb3",
275		.of_match_table	= phy_meson_gxl_usb3_of_match,
276	},
277};
278module_platform_driver(phy_meson_gxl_usb3_driver);
279
280MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
281MODULE_DESCRIPTION("Meson GXL USB3 PHY and OTG detection driver");
282MODULE_LICENSE("GPL v2");