xref: /openbmc/linux/drivers/phy/st/phy-stih407-usb.c (revision 0b56e9a7e8358e59b21d8a425e463072bfae523c)
1*0b56e9a7SVivek Gautam /*
2*0b56e9a7SVivek Gautam  * Copyright (C) 2014 STMicroelectronics
3*0b56e9a7SVivek Gautam  *
4*0b56e9a7SVivek Gautam  * STMicroelectronics Generic PHY driver for STiH407 USB2.
5*0b56e9a7SVivek Gautam  *
6*0b56e9a7SVivek Gautam  * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
7*0b56e9a7SVivek Gautam  *
8*0b56e9a7SVivek Gautam  * This program is free software; you can redistribute it and/or modify
9*0b56e9a7SVivek Gautam  * it under the terms of the GNU General Public License version 2, as
10*0b56e9a7SVivek Gautam  * published by the Free Software Foundation.
11*0b56e9a7SVivek Gautam  *
12*0b56e9a7SVivek Gautam  */
13*0b56e9a7SVivek Gautam #include <linux/platform_device.h>
14*0b56e9a7SVivek Gautam #include <linux/io.h>
15*0b56e9a7SVivek Gautam #include <linux/kernel.h>
16*0b56e9a7SVivek Gautam #include <linux/module.h>
17*0b56e9a7SVivek Gautam #include <linux/of.h>
18*0b56e9a7SVivek Gautam #include <linux/of_platform.h>
19*0b56e9a7SVivek Gautam #include <linux/clk.h>
20*0b56e9a7SVivek Gautam #include <linux/regmap.h>
21*0b56e9a7SVivek Gautam #include <linux/reset.h>
22*0b56e9a7SVivek Gautam #include <linux/mfd/syscon.h>
23*0b56e9a7SVivek Gautam #include <linux/phy/phy.h>
24*0b56e9a7SVivek Gautam 
25*0b56e9a7SVivek Gautam #define PHYPARAM_REG	1
26*0b56e9a7SVivek Gautam #define PHYCTRL_REG	2
27*0b56e9a7SVivek Gautam 
28*0b56e9a7SVivek Gautam /* Default PHY_SEL and REFCLKSEL configuration */
29*0b56e9a7SVivek Gautam #define STIH407_USB_PICOPHY_CTRL_PORT_CONF	0x6
30*0b56e9a7SVivek Gautam #define STIH407_USB_PICOPHY_CTRL_PORT_MASK	0x1f
31*0b56e9a7SVivek Gautam 
32*0b56e9a7SVivek Gautam /* ports parameters overriding */
33*0b56e9a7SVivek Gautam #define STIH407_USB_PICOPHY_PARAM_DEF		0x39a4dc
34*0b56e9a7SVivek Gautam #define STIH407_USB_PICOPHY_PARAM_MASK		0xffffffff
35*0b56e9a7SVivek Gautam 
36*0b56e9a7SVivek Gautam struct stih407_usb2_picophy {
37*0b56e9a7SVivek Gautam 	struct phy *phy;
38*0b56e9a7SVivek Gautam 	struct regmap *regmap;
39*0b56e9a7SVivek Gautam 	struct device *dev;
40*0b56e9a7SVivek Gautam 	struct reset_control *rstc;
41*0b56e9a7SVivek Gautam 	struct reset_control *rstport;
42*0b56e9a7SVivek Gautam 	int ctrl;
43*0b56e9a7SVivek Gautam 	int param;
44*0b56e9a7SVivek Gautam };
45*0b56e9a7SVivek Gautam 
46*0b56e9a7SVivek Gautam static int stih407_usb2_pico_ctrl(struct stih407_usb2_picophy *phy_dev)
47*0b56e9a7SVivek Gautam {
48*0b56e9a7SVivek Gautam 	reset_control_deassert(phy_dev->rstc);
49*0b56e9a7SVivek Gautam 
50*0b56e9a7SVivek Gautam 	return regmap_update_bits(phy_dev->regmap, phy_dev->ctrl,
51*0b56e9a7SVivek Gautam 				  STIH407_USB_PICOPHY_CTRL_PORT_MASK,
52*0b56e9a7SVivek Gautam 				  STIH407_USB_PICOPHY_CTRL_PORT_CONF);
53*0b56e9a7SVivek Gautam }
54*0b56e9a7SVivek Gautam 
55*0b56e9a7SVivek Gautam static int stih407_usb2_init_port(struct phy *phy)
56*0b56e9a7SVivek Gautam {
57*0b56e9a7SVivek Gautam 	int ret;
58*0b56e9a7SVivek Gautam 	struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy);
59*0b56e9a7SVivek Gautam 
60*0b56e9a7SVivek Gautam 	stih407_usb2_pico_ctrl(phy_dev);
61*0b56e9a7SVivek Gautam 
62*0b56e9a7SVivek Gautam 	ret = regmap_update_bits(phy_dev->regmap,
63*0b56e9a7SVivek Gautam 				 phy_dev->param,
64*0b56e9a7SVivek Gautam 				 STIH407_USB_PICOPHY_PARAM_MASK,
65*0b56e9a7SVivek Gautam 				 STIH407_USB_PICOPHY_PARAM_DEF);
66*0b56e9a7SVivek Gautam 	if (ret)
67*0b56e9a7SVivek Gautam 		return ret;
68*0b56e9a7SVivek Gautam 
69*0b56e9a7SVivek Gautam 	return reset_control_deassert(phy_dev->rstport);
70*0b56e9a7SVivek Gautam }
71*0b56e9a7SVivek Gautam 
72*0b56e9a7SVivek Gautam static int stih407_usb2_exit_port(struct phy *phy)
73*0b56e9a7SVivek Gautam {
74*0b56e9a7SVivek Gautam 	struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy);
75*0b56e9a7SVivek Gautam 
76*0b56e9a7SVivek Gautam 	/*
77*0b56e9a7SVivek Gautam 	 * Only port reset is asserted, phy global reset is kept untouched
78*0b56e9a7SVivek Gautam 	 * as other ports may still be active. When all ports are in reset
79*0b56e9a7SVivek Gautam 	 * state, assumption is made that power will be cut off on the phy, in
80*0b56e9a7SVivek Gautam 	 * case of suspend for instance. Theoretically, asserting individual
81*0b56e9a7SVivek Gautam 	 * reset (like here) or global reset should be equivalent.
82*0b56e9a7SVivek Gautam 	 */
83*0b56e9a7SVivek Gautam 	return reset_control_assert(phy_dev->rstport);
84*0b56e9a7SVivek Gautam }
85*0b56e9a7SVivek Gautam 
86*0b56e9a7SVivek Gautam static const struct phy_ops stih407_usb2_picophy_data = {
87*0b56e9a7SVivek Gautam 	.init = stih407_usb2_init_port,
88*0b56e9a7SVivek Gautam 	.exit = stih407_usb2_exit_port,
89*0b56e9a7SVivek Gautam 	.owner = THIS_MODULE,
90*0b56e9a7SVivek Gautam };
91*0b56e9a7SVivek Gautam 
92*0b56e9a7SVivek Gautam static int stih407_usb2_picophy_probe(struct platform_device *pdev)
93*0b56e9a7SVivek Gautam {
94*0b56e9a7SVivek Gautam 	struct stih407_usb2_picophy *phy_dev;
95*0b56e9a7SVivek Gautam 	struct device *dev = &pdev->dev;
96*0b56e9a7SVivek Gautam 	struct device_node *np = dev->of_node;
97*0b56e9a7SVivek Gautam 	struct phy_provider *phy_provider;
98*0b56e9a7SVivek Gautam 	struct phy *phy;
99*0b56e9a7SVivek Gautam 	int ret;
100*0b56e9a7SVivek Gautam 
101*0b56e9a7SVivek Gautam 	phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL);
102*0b56e9a7SVivek Gautam 	if (!phy_dev)
103*0b56e9a7SVivek Gautam 		return -ENOMEM;
104*0b56e9a7SVivek Gautam 
105*0b56e9a7SVivek Gautam 	phy_dev->dev = dev;
106*0b56e9a7SVivek Gautam 	dev_set_drvdata(dev, phy_dev);
107*0b56e9a7SVivek Gautam 
108*0b56e9a7SVivek Gautam 	phy_dev->rstc = devm_reset_control_get_shared(dev, "global");
109*0b56e9a7SVivek Gautam 	if (IS_ERR(phy_dev->rstc)) {
110*0b56e9a7SVivek Gautam 		dev_err(dev, "failed to ctrl picoPHY reset\n");
111*0b56e9a7SVivek Gautam 		return PTR_ERR(phy_dev->rstc);
112*0b56e9a7SVivek Gautam 	}
113*0b56e9a7SVivek Gautam 
114*0b56e9a7SVivek Gautam 	phy_dev->rstport = devm_reset_control_get_exclusive(dev, "port");
115*0b56e9a7SVivek Gautam 	if (IS_ERR(phy_dev->rstport)) {
116*0b56e9a7SVivek Gautam 		dev_err(dev, "failed to ctrl picoPHY reset\n");
117*0b56e9a7SVivek Gautam 		return PTR_ERR(phy_dev->rstport);
118*0b56e9a7SVivek Gautam 	}
119*0b56e9a7SVivek Gautam 
120*0b56e9a7SVivek Gautam 	/* Reset port by default: only deassert it in phy init */
121*0b56e9a7SVivek Gautam 	reset_control_assert(phy_dev->rstport);
122*0b56e9a7SVivek Gautam 
123*0b56e9a7SVivek Gautam 	phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
124*0b56e9a7SVivek Gautam 	if (IS_ERR(phy_dev->regmap)) {
125*0b56e9a7SVivek Gautam 		dev_err(dev, "No syscfg phandle specified\n");
126*0b56e9a7SVivek Gautam 		return PTR_ERR(phy_dev->regmap);
127*0b56e9a7SVivek Gautam 	}
128*0b56e9a7SVivek Gautam 
129*0b56e9a7SVivek Gautam 	ret = of_property_read_u32_index(np, "st,syscfg", PHYPARAM_REG,
130*0b56e9a7SVivek Gautam 					&phy_dev->param);
131*0b56e9a7SVivek Gautam 	if (ret) {
132*0b56e9a7SVivek Gautam 		dev_err(dev, "can't get phyparam offset (%d)\n", ret);
133*0b56e9a7SVivek Gautam 		return ret;
134*0b56e9a7SVivek Gautam 	}
135*0b56e9a7SVivek Gautam 
136*0b56e9a7SVivek Gautam 	ret = of_property_read_u32_index(np, "st,syscfg", PHYCTRL_REG,
137*0b56e9a7SVivek Gautam 					&phy_dev->ctrl);
138*0b56e9a7SVivek Gautam 	if (ret) {
139*0b56e9a7SVivek Gautam 		dev_err(dev, "can't get phyctrl offset (%d)\n", ret);
140*0b56e9a7SVivek Gautam 		return ret;
141*0b56e9a7SVivek Gautam 	}
142*0b56e9a7SVivek Gautam 
143*0b56e9a7SVivek Gautam 	phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data);
144*0b56e9a7SVivek Gautam 	if (IS_ERR(phy)) {
145*0b56e9a7SVivek Gautam 		dev_err(dev, "failed to create Display Port PHY\n");
146*0b56e9a7SVivek Gautam 		return PTR_ERR(phy);
147*0b56e9a7SVivek Gautam 	}
148*0b56e9a7SVivek Gautam 
149*0b56e9a7SVivek Gautam 	phy_dev->phy = phy;
150*0b56e9a7SVivek Gautam 	phy_set_drvdata(phy, phy_dev);
151*0b56e9a7SVivek Gautam 
152*0b56e9a7SVivek Gautam 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
153*0b56e9a7SVivek Gautam 	if (IS_ERR(phy_provider))
154*0b56e9a7SVivek Gautam 		return PTR_ERR(phy_provider);
155*0b56e9a7SVivek Gautam 
156*0b56e9a7SVivek Gautam 	dev_info(dev, "STiH407 USB Generic picoPHY driver probed!");
157*0b56e9a7SVivek Gautam 
158*0b56e9a7SVivek Gautam 	return 0;
159*0b56e9a7SVivek Gautam }
160*0b56e9a7SVivek Gautam 
161*0b56e9a7SVivek Gautam static const struct of_device_id stih407_usb2_picophy_of_match[] = {
162*0b56e9a7SVivek Gautam 	{ .compatible = "st,stih407-usb2-phy" },
163*0b56e9a7SVivek Gautam 	{ /*sentinel */ },
164*0b56e9a7SVivek Gautam };
165*0b56e9a7SVivek Gautam 
166*0b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, stih407_usb2_picophy_of_match);
167*0b56e9a7SVivek Gautam 
168*0b56e9a7SVivek Gautam static struct platform_driver stih407_usb2_picophy_driver = {
169*0b56e9a7SVivek Gautam 	.probe = stih407_usb2_picophy_probe,
170*0b56e9a7SVivek Gautam 	.driver = {
171*0b56e9a7SVivek Gautam 		   .name = "stih407-usb-genphy",
172*0b56e9a7SVivek Gautam 		   .of_match_table = stih407_usb2_picophy_of_match,
173*0b56e9a7SVivek Gautam 		   }
174*0b56e9a7SVivek Gautam };
175*0b56e9a7SVivek Gautam 
176*0b56e9a7SVivek Gautam module_platform_driver(stih407_usb2_picophy_driver);
177*0b56e9a7SVivek Gautam 
178*0b56e9a7SVivek Gautam MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
179*0b56e9a7SVivek Gautam MODULE_DESCRIPTION("STMicroelectronics Generic picoPHY driver for STiH407");
180*0b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2");
181