xref: /openbmc/linux/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c (revision 0b56e9a7e8358e59b21d8a425e463072bfae523c)
1*0b56e9a7SVivek Gautam /*
2*0b56e9a7SVivek Gautam  * Copyright (C) 2015 Broadcom Corporation
3*0b56e9a7SVivek Gautam  *
4*0b56e9a7SVivek Gautam  * This program is free software; you can redistribute it and/or
5*0b56e9a7SVivek Gautam  * modify it under the terms of the GNU General Public License as
6*0b56e9a7SVivek Gautam  * published by the Free Software Foundation version 2.
7*0b56e9a7SVivek Gautam  *
8*0b56e9a7SVivek Gautam  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9*0b56e9a7SVivek Gautam  * kind, whether express or implied; without even the implied warranty
10*0b56e9a7SVivek Gautam  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11*0b56e9a7SVivek Gautam  * GNU General Public License for more details.
12*0b56e9a7SVivek Gautam  */
13*0b56e9a7SVivek Gautam 
14*0b56e9a7SVivek Gautam #include <linux/delay.h>
15*0b56e9a7SVivek Gautam #include <linux/io.h>
16*0b56e9a7SVivek Gautam #include <linux/module.h>
17*0b56e9a7SVivek Gautam #include <linux/of.h>
18*0b56e9a7SVivek Gautam #include <linux/phy/phy.h>
19*0b56e9a7SVivek Gautam #include <linux/platform_device.h>
20*0b56e9a7SVivek Gautam 
21*0b56e9a7SVivek Gautam #define PCIE_CFG_OFFSET         0x00
22*0b56e9a7SVivek Gautam #define PCIE1_PHY_IDDQ_SHIFT    10
23*0b56e9a7SVivek Gautam #define PCIE0_PHY_IDDQ_SHIFT    2
24*0b56e9a7SVivek Gautam 
25*0b56e9a7SVivek Gautam enum cygnus_pcie_phy_id {
26*0b56e9a7SVivek Gautam 	CYGNUS_PHY_PCIE0 = 0,
27*0b56e9a7SVivek Gautam 	CYGNUS_PHY_PCIE1,
28*0b56e9a7SVivek Gautam 	MAX_NUM_PHYS,
29*0b56e9a7SVivek Gautam };
30*0b56e9a7SVivek Gautam 
31*0b56e9a7SVivek Gautam struct cygnus_pcie_phy_core;
32*0b56e9a7SVivek Gautam 
33*0b56e9a7SVivek Gautam /**
34*0b56e9a7SVivek Gautam  * struct cygnus_pcie_phy - Cygnus PCIe PHY device
35*0b56e9a7SVivek Gautam  * @core: pointer to the Cygnus PCIe PHY core control
36*0b56e9a7SVivek Gautam  * @id: internal ID to identify the Cygnus PCIe PHY
37*0b56e9a7SVivek Gautam  * @phy: pointer to the kernel PHY device
38*0b56e9a7SVivek Gautam  */
39*0b56e9a7SVivek Gautam struct cygnus_pcie_phy {
40*0b56e9a7SVivek Gautam 	struct cygnus_pcie_phy_core *core;
41*0b56e9a7SVivek Gautam 	enum cygnus_pcie_phy_id id;
42*0b56e9a7SVivek Gautam 	struct phy *phy;
43*0b56e9a7SVivek Gautam };
44*0b56e9a7SVivek Gautam 
45*0b56e9a7SVivek Gautam /**
46*0b56e9a7SVivek Gautam  * struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control
47*0b56e9a7SVivek Gautam  * @dev: pointer to device
48*0b56e9a7SVivek Gautam  * @base: base register
49*0b56e9a7SVivek Gautam  * @lock: mutex to protect access to individual PHYs
50*0b56e9a7SVivek Gautam  * @phys: pointer to Cygnus PHY device
51*0b56e9a7SVivek Gautam  */
52*0b56e9a7SVivek Gautam struct cygnus_pcie_phy_core {
53*0b56e9a7SVivek Gautam 	struct device *dev;
54*0b56e9a7SVivek Gautam 	void __iomem *base;
55*0b56e9a7SVivek Gautam 	struct mutex lock;
56*0b56e9a7SVivek Gautam 	struct cygnus_pcie_phy phys[MAX_NUM_PHYS];
57*0b56e9a7SVivek Gautam };
58*0b56e9a7SVivek Gautam 
59*0b56e9a7SVivek Gautam static int cygnus_pcie_power_config(struct cygnus_pcie_phy *phy, bool enable)
60*0b56e9a7SVivek Gautam {
61*0b56e9a7SVivek Gautam 	struct cygnus_pcie_phy_core *core = phy->core;
62*0b56e9a7SVivek Gautam 	unsigned shift;
63*0b56e9a7SVivek Gautam 	u32 val;
64*0b56e9a7SVivek Gautam 
65*0b56e9a7SVivek Gautam 	mutex_lock(&core->lock);
66*0b56e9a7SVivek Gautam 
67*0b56e9a7SVivek Gautam 	switch (phy->id) {
68*0b56e9a7SVivek Gautam 	case CYGNUS_PHY_PCIE0:
69*0b56e9a7SVivek Gautam 		shift = PCIE0_PHY_IDDQ_SHIFT;
70*0b56e9a7SVivek Gautam 		break;
71*0b56e9a7SVivek Gautam 
72*0b56e9a7SVivek Gautam 	case CYGNUS_PHY_PCIE1:
73*0b56e9a7SVivek Gautam 		shift = PCIE1_PHY_IDDQ_SHIFT;
74*0b56e9a7SVivek Gautam 		break;
75*0b56e9a7SVivek Gautam 
76*0b56e9a7SVivek Gautam 	default:
77*0b56e9a7SVivek Gautam 		mutex_unlock(&core->lock);
78*0b56e9a7SVivek Gautam 		dev_err(core->dev, "PCIe PHY %d invalid\n", phy->id);
79*0b56e9a7SVivek Gautam 		return -EINVAL;
80*0b56e9a7SVivek Gautam 	}
81*0b56e9a7SVivek Gautam 
82*0b56e9a7SVivek Gautam 	if (enable) {
83*0b56e9a7SVivek Gautam 		val = readl(core->base + PCIE_CFG_OFFSET);
84*0b56e9a7SVivek Gautam 		val &= ~BIT(shift);
85*0b56e9a7SVivek Gautam 		writel(val, core->base + PCIE_CFG_OFFSET);
86*0b56e9a7SVivek Gautam 		/*
87*0b56e9a7SVivek Gautam 		 * Wait 50 ms for the PCIe Serdes to stabilize after the analog
88*0b56e9a7SVivek Gautam 		 * front end is brought up
89*0b56e9a7SVivek Gautam 		 */
90*0b56e9a7SVivek Gautam 		msleep(50);
91*0b56e9a7SVivek Gautam 	} else {
92*0b56e9a7SVivek Gautam 		val = readl(core->base + PCIE_CFG_OFFSET);
93*0b56e9a7SVivek Gautam 		val |= BIT(shift);
94*0b56e9a7SVivek Gautam 		writel(val, core->base + PCIE_CFG_OFFSET);
95*0b56e9a7SVivek Gautam 	}
96*0b56e9a7SVivek Gautam 
97*0b56e9a7SVivek Gautam 	mutex_unlock(&core->lock);
98*0b56e9a7SVivek Gautam 	dev_dbg(core->dev, "PCIe PHY %d %s\n", phy->id,
99*0b56e9a7SVivek Gautam 		enable ? "enabled" : "disabled");
100*0b56e9a7SVivek Gautam 	return 0;
101*0b56e9a7SVivek Gautam }
102*0b56e9a7SVivek Gautam 
103*0b56e9a7SVivek Gautam static int cygnus_pcie_phy_power_on(struct phy *p)
104*0b56e9a7SVivek Gautam {
105*0b56e9a7SVivek Gautam 	struct cygnus_pcie_phy *phy = phy_get_drvdata(p);
106*0b56e9a7SVivek Gautam 
107*0b56e9a7SVivek Gautam 	return cygnus_pcie_power_config(phy, true);
108*0b56e9a7SVivek Gautam }
109*0b56e9a7SVivek Gautam 
110*0b56e9a7SVivek Gautam static int cygnus_pcie_phy_power_off(struct phy *p)
111*0b56e9a7SVivek Gautam {
112*0b56e9a7SVivek Gautam 	struct cygnus_pcie_phy *phy = phy_get_drvdata(p);
113*0b56e9a7SVivek Gautam 
114*0b56e9a7SVivek Gautam 	return cygnus_pcie_power_config(phy, false);
115*0b56e9a7SVivek Gautam }
116*0b56e9a7SVivek Gautam 
117*0b56e9a7SVivek Gautam static const struct phy_ops cygnus_pcie_phy_ops = {
118*0b56e9a7SVivek Gautam 	.power_on = cygnus_pcie_phy_power_on,
119*0b56e9a7SVivek Gautam 	.power_off = cygnus_pcie_phy_power_off,
120*0b56e9a7SVivek Gautam 	.owner = THIS_MODULE,
121*0b56e9a7SVivek Gautam };
122*0b56e9a7SVivek Gautam 
123*0b56e9a7SVivek Gautam static int cygnus_pcie_phy_probe(struct platform_device *pdev)
124*0b56e9a7SVivek Gautam {
125*0b56e9a7SVivek Gautam 	struct device *dev = &pdev->dev;
126*0b56e9a7SVivek Gautam 	struct device_node *node = dev->of_node, *child;
127*0b56e9a7SVivek Gautam 	struct cygnus_pcie_phy_core *core;
128*0b56e9a7SVivek Gautam 	struct phy_provider *provider;
129*0b56e9a7SVivek Gautam 	struct resource *res;
130*0b56e9a7SVivek Gautam 	unsigned cnt = 0;
131*0b56e9a7SVivek Gautam 	int ret;
132*0b56e9a7SVivek Gautam 
133*0b56e9a7SVivek Gautam 	if (of_get_child_count(node) == 0) {
134*0b56e9a7SVivek Gautam 		dev_err(dev, "PHY no child node\n");
135*0b56e9a7SVivek Gautam 		return -ENODEV;
136*0b56e9a7SVivek Gautam 	}
137*0b56e9a7SVivek Gautam 
138*0b56e9a7SVivek Gautam 	core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL);
139*0b56e9a7SVivek Gautam 	if (!core)
140*0b56e9a7SVivek Gautam 		return -ENOMEM;
141*0b56e9a7SVivek Gautam 
142*0b56e9a7SVivek Gautam 	core->dev = dev;
143*0b56e9a7SVivek Gautam 
144*0b56e9a7SVivek Gautam 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
145*0b56e9a7SVivek Gautam 	core->base = devm_ioremap_resource(dev, res);
146*0b56e9a7SVivek Gautam 	if (IS_ERR(core->base))
147*0b56e9a7SVivek Gautam 		return PTR_ERR(core->base);
148*0b56e9a7SVivek Gautam 
149*0b56e9a7SVivek Gautam 	mutex_init(&core->lock);
150*0b56e9a7SVivek Gautam 
151*0b56e9a7SVivek Gautam 	for_each_available_child_of_node(node, child) {
152*0b56e9a7SVivek Gautam 		unsigned int id;
153*0b56e9a7SVivek Gautam 		struct cygnus_pcie_phy *p;
154*0b56e9a7SVivek Gautam 
155*0b56e9a7SVivek Gautam 		if (of_property_read_u32(child, "reg", &id)) {
156*0b56e9a7SVivek Gautam 			dev_err(dev, "missing reg property for %s\n",
157*0b56e9a7SVivek Gautam 				child->name);
158*0b56e9a7SVivek Gautam 			ret = -EINVAL;
159*0b56e9a7SVivek Gautam 			goto put_child;
160*0b56e9a7SVivek Gautam 		}
161*0b56e9a7SVivek Gautam 
162*0b56e9a7SVivek Gautam 		if (id >= MAX_NUM_PHYS) {
163*0b56e9a7SVivek Gautam 			dev_err(dev, "invalid PHY id: %u\n", id);
164*0b56e9a7SVivek Gautam 			ret = -EINVAL;
165*0b56e9a7SVivek Gautam 			goto put_child;
166*0b56e9a7SVivek Gautam 		}
167*0b56e9a7SVivek Gautam 
168*0b56e9a7SVivek Gautam 		if (core->phys[id].phy) {
169*0b56e9a7SVivek Gautam 			dev_err(dev, "duplicated PHY id: %u\n", id);
170*0b56e9a7SVivek Gautam 			ret = -EINVAL;
171*0b56e9a7SVivek Gautam 			goto put_child;
172*0b56e9a7SVivek Gautam 		}
173*0b56e9a7SVivek Gautam 
174*0b56e9a7SVivek Gautam 		p = &core->phys[id];
175*0b56e9a7SVivek Gautam 		p->phy = devm_phy_create(dev, child, &cygnus_pcie_phy_ops);
176*0b56e9a7SVivek Gautam 		if (IS_ERR(p->phy)) {
177*0b56e9a7SVivek Gautam 			dev_err(dev, "failed to create PHY\n");
178*0b56e9a7SVivek Gautam 			ret = PTR_ERR(p->phy);
179*0b56e9a7SVivek Gautam 			goto put_child;
180*0b56e9a7SVivek Gautam 		}
181*0b56e9a7SVivek Gautam 
182*0b56e9a7SVivek Gautam 		p->core = core;
183*0b56e9a7SVivek Gautam 		p->id = id;
184*0b56e9a7SVivek Gautam 		phy_set_drvdata(p->phy, p);
185*0b56e9a7SVivek Gautam 		cnt++;
186*0b56e9a7SVivek Gautam 	}
187*0b56e9a7SVivek Gautam 
188*0b56e9a7SVivek Gautam 	dev_set_drvdata(dev, core);
189*0b56e9a7SVivek Gautam 
190*0b56e9a7SVivek Gautam 	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
191*0b56e9a7SVivek Gautam 	if (IS_ERR(provider)) {
192*0b56e9a7SVivek Gautam 		dev_err(dev, "failed to register PHY provider\n");
193*0b56e9a7SVivek Gautam 		return PTR_ERR(provider);
194*0b56e9a7SVivek Gautam 	}
195*0b56e9a7SVivek Gautam 
196*0b56e9a7SVivek Gautam 	dev_dbg(dev, "registered %u PCIe PHY(s)\n", cnt);
197*0b56e9a7SVivek Gautam 
198*0b56e9a7SVivek Gautam 	return 0;
199*0b56e9a7SVivek Gautam put_child:
200*0b56e9a7SVivek Gautam 	of_node_put(child);
201*0b56e9a7SVivek Gautam 	return ret;
202*0b56e9a7SVivek Gautam }
203*0b56e9a7SVivek Gautam 
204*0b56e9a7SVivek Gautam static const struct of_device_id cygnus_pcie_phy_match_table[] = {
205*0b56e9a7SVivek Gautam 	{ .compatible = "brcm,cygnus-pcie-phy" },
206*0b56e9a7SVivek Gautam 	{ /* sentinel */ }
207*0b56e9a7SVivek Gautam };
208*0b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, cygnus_pcie_phy_match_table);
209*0b56e9a7SVivek Gautam 
210*0b56e9a7SVivek Gautam static struct platform_driver cygnus_pcie_phy_driver = {
211*0b56e9a7SVivek Gautam 	.driver = {
212*0b56e9a7SVivek Gautam 		.name = "cygnus-pcie-phy",
213*0b56e9a7SVivek Gautam 		.of_match_table = cygnus_pcie_phy_match_table,
214*0b56e9a7SVivek Gautam 	},
215*0b56e9a7SVivek Gautam 	.probe = cygnus_pcie_phy_probe,
216*0b56e9a7SVivek Gautam };
217*0b56e9a7SVivek Gautam module_platform_driver(cygnus_pcie_phy_driver);
218*0b56e9a7SVivek Gautam 
219*0b56e9a7SVivek Gautam MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
220*0b56e9a7SVivek Gautam MODULE_DESCRIPTION("Broadcom Cygnus PCIe PHY driver");
221*0b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2");
222